Sieges: reinforcements log in for registered clans and log out after, walls excluded, siege state/skills set for bots; bots count as online clan members (clan skills); dashboard telemetry panel
This commit is contained in:
@@ -38,6 +38,8 @@ canvas{width:100%;height:100%;display:block;background:#0d1014}
|
||||
<div class="sub">живой срез мира, обновление каждые 5 сек</div>
|
||||
<div class="lbl">Итого</div>
|
||||
<div id="stats"></div>
|
||||
<div class="lbl">Что реально сделали</div>
|
||||
<div id="telemetry" class="sub">…</div>
|
||||
<div class="lbl">Фильтр</div>
|
||||
<input id="q" placeholder="поиск по нику...">
|
||||
<div style="margin-top:6px">
|
||||
@@ -180,6 +182,17 @@ document.getElementById('pGo').onclick=()=>{
|
||||
})};
|
||||
|
||||
refresh();setInterval(refresh,5000);
|
||||
function telemetry(){
|
||||
fetch('/api/stats').then(r=>r.json()).then(t=>{
|
||||
const keys=['kills','exp','loot','itemsSold','adenaEarned','adenaSpent','stores','skillsCast','rests','lines','replies','parties','migrations','professions','sieges','pvpKills','deaths'];
|
||||
let h='';
|
||||
keys.forEach(k=>{h+='<div class="stat"><span>'+k+'</span><b>'+(t[k]??0)+'</b></div>';});
|
||||
h+='<div class="stat"><span>сейчас</span><b>бой '+t.fighting+' · сидят '+t.sitting+' · торгуют '+t.trading+' · в пати '+t.inParty+' · на осаде '+t.besieging+'</b></div>';
|
||||
h+='<div class="stat"><span>аптайм</span><b>'+t.uptimeMin+' мин</b></div>';
|
||||
document.getElementById('telemetry').innerHTML=h;
|
||||
}).catch(()=>{});
|
||||
}
|
||||
telemetry();setInterval(telemetry,10000);
|
||||
window.addEventListener('resize',draw);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
+1
@@ -79,6 +79,7 @@ public class FakePlayerBotState
|
||||
public boolean giftedStock = false;
|
||||
|
||||
// --- siege ---
|
||||
public boolean temporary = false; // Logged in on demand for a siege; leaves the world afterwards.
|
||||
public byte siegeSide = 0; // 0 none, 1 attacker, 2 defender.
|
||||
public int siegeCastleId = 0;
|
||||
public long siegeNextThink = 0;
|
||||
|
||||
+54
-2
@@ -191,7 +191,9 @@ public class FakePlayerHeadlessManager
|
||||
|
||||
// Believable player title instead of a personality label.
|
||||
player.setTitle(TITLES[Rnd.get(TITLES.length)]);
|
||||
// Restore clan membership so crests, clan wars and sieges work like for real players.
|
||||
// The bot is online for the engine (clan member lists, sieges, parties count it like a logged in player).
|
||||
player.setOnlineStatus(true, false);
|
||||
// Restore clan membership so crests, clan skills, clan wars and sieges work like for real players.
|
||||
if (player.getClanId() > 0)
|
||||
{
|
||||
final Clan clan = ClanTable.getInstance().getClan(player.getClanId());
|
||||
@@ -200,6 +202,11 @@ public class FakePlayerHeadlessManager
|
||||
player.setClan(clan);
|
||||
player.setPledgeType(0);
|
||||
player.setPowerGrade((clan.getLeaderId() == player.getObjectId()) ? 1 : 6);
|
||||
final org.l2jmobius.gameserver.entity.clan.ClanMember member = clan.getClanMember(player.getObjectId());
|
||||
if (member != null)
|
||||
{
|
||||
member.setPlayer(player); // Clan skills, siege skills for the leader, "online" for the clan.
|
||||
}
|
||||
}
|
||||
}
|
||||
// Place the bot in a level appropriate farming zone (shared zone table). Clanmates of a
|
||||
@@ -247,6 +254,16 @@ public class FakePlayerHeadlessManager
|
||||
* @return the headless player, or null.
|
||||
*/
|
||||
public Player ensureOnline(int charId)
|
||||
{
|
||||
return ensureOnline(charId, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param charId the character id.
|
||||
* @param temporary true when the bot should log out again after its job (siege reinforcement).
|
||||
* @return the headless player, or null.
|
||||
*/
|
||||
public Player ensureOnline(int charId, boolean temporary)
|
||||
{
|
||||
if (charId <= 0)
|
||||
{
|
||||
@@ -266,11 +283,46 @@ public class FakePlayerHeadlessManager
|
||||
final Player bot = spawnOne(charId);
|
||||
if (bot != null)
|
||||
{
|
||||
LOGGER.info(getClass().getSimpleName() + ": " + bot.getName() + " logged in on demand (" + _bots.size() + " headless players now).");
|
||||
final FakePlayerBotState state = stateOf(bot);
|
||||
if ((state != null) && temporary)
|
||||
{
|
||||
state.temporary = true;
|
||||
}
|
||||
LOGGER.info(getClass().getSimpleName() + ": " + bot.getName() + " logged in on demand" + (temporary ? " for a siege" : "") + " (" + _bots.size() + " headless players now).");
|
||||
}
|
||||
return bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a temporary bot out (siege reinforcements after the battle): stores it and removes it from the world.
|
||||
* @param bot the bot.
|
||||
*/
|
||||
public void despawn(Player bot)
|
||||
{
|
||||
final FakePlayerBotState state = stateOf(bot);
|
||||
if ((state == null) || !state.temporary || (state.siegeSide != 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
closeStore(bot);
|
||||
if (bot.getParty() != null)
|
||||
{
|
||||
bot.leaveParty();
|
||||
}
|
||||
_states.remove(bot.getObjectId());
|
||||
_bots.remove(bot);
|
||||
bot.setOnlineStatus(false, false);
|
||||
bot.deleteMe();
|
||||
LOGGER.info(getClass().getSimpleName() + ": " + bot.getName() + " logged out (" + _bots.size() + " headless players now).");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not log out " + bot.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== Registry ========================
|
||||
|
||||
/**
|
||||
|
||||
+69
-6
@@ -287,17 +287,60 @@ public class FakePlayerSiegeManager
|
||||
}
|
||||
_battles.put(castle.getResidenceId(), battle);
|
||||
|
||||
// Reinforcements: a registered clan brings its strongest members even if they were not in the
|
||||
// random population sample (they log out again after the battle).
|
||||
final Set<Integer> battleClans = new HashSet<>(battle.attackerClans);
|
||||
battleClans.addAll(battle.defenderClans);
|
||||
for (int clanId : battleClans)
|
||||
{
|
||||
final Clan clan = ClanTable.getInstance().getClan(clanId);
|
||||
if (clan == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int online = 0;
|
||||
for (Player bot : FakePlayerHeadlessManager.getInstance().bots())
|
||||
{
|
||||
if ((bot.getClanId() == clanId) && (bot.getLevel() >= (MIN_LEVEL - 10)))
|
||||
{
|
||||
online++;
|
||||
}
|
||||
}
|
||||
final List<org.l2jmobius.gameserver.entity.clan.ClanMember> strongest = new ArrayList<>(clan.getMembers());
|
||||
strongest.sort((a, b) -> Integer.compare(b.getLevel(), a.getLevel()));
|
||||
for (org.l2jmobius.gameserver.entity.clan.ClanMember member : strongest)
|
||||
{
|
||||
if ((online >= 8) || (member.getLevel() < MIN_LEVEL))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (FakePlayerHeadlessManager.getInstance().ensureOnline(member.getObjectId(), true) != null)
|
||||
{
|
||||
online++;
|
||||
}
|
||||
}
|
||||
FakePlayerHeadlessManager.getInstance().ensureOnline(clan.getLeaderId(), true);
|
||||
}
|
||||
|
||||
// Participants: online headless members of the registered clans.
|
||||
final Map<Integer, Integer> perClan = new HashMap<>();
|
||||
int members = 0;
|
||||
int tooLow = 0;
|
||||
for (Player bot : FakePlayerHeadlessManager.getInstance().bots())
|
||||
{
|
||||
final int clanId = bot.getClanId();
|
||||
final boolean attacker = battle.attackerClans.contains(clanId);
|
||||
final boolean defender = battle.defenderClans.contains(clanId);
|
||||
if ((!attacker && !defender) || (bot.getLevel() < (MIN_LEVEL - 10)))
|
||||
if (!attacker && !defender)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
members++;
|
||||
if (bot.getLevel() < (MIN_LEVEL - 10))
|
||||
{
|
||||
tooLow++;
|
||||
continue;
|
||||
}
|
||||
final int count = perClan.getOrDefault(clanId, 0);
|
||||
if ((count >= MAX_PER_CLAN) && !bot.isClanLeader())
|
||||
{
|
||||
@@ -315,6 +358,12 @@ public class FakePlayerSiegeManager
|
||||
state.siegesJoined.incrementAndGet();
|
||||
state.action((attacker ? "siege attack " : "siege defend ") + castle.getName());
|
||||
bot.setSiegeState(state.siegeSide);
|
||||
bot.setSiegeSide(castle.getResidenceId());
|
||||
if (bot.isClanLeader() && (bot.getKnownSkill(CommonSkill.SEAL_OF_RULER.getId()) == null))
|
||||
{
|
||||
SiegeManager.getInstance().addSiegeSkills(bot);
|
||||
}
|
||||
bot.updateUserInfo();
|
||||
FakePlayerBotContext.setPlan(state.seedId, "siege", 3 * 3600000L);
|
||||
FakePlayerBotContext.setSlot(state.seedId, "castle", castle.getName());
|
||||
battle.participants.add(bot);
|
||||
@@ -345,7 +394,7 @@ public class FakePlayerSiegeManager
|
||||
ThreadPool.schedule(() -> cry(bot, seam), 6000 + Rnd.get(4000));
|
||||
}
|
||||
}
|
||||
LOGGER.info(getClass().getSimpleName() + ": Siege of " + castle.getName() + " - " + battle.participants.size() + " bots joined (attacker clans " + battle.attackerClans + ", defender clans " + battle.defenderClans + ").");
|
||||
LOGGER.info(getClass().getSimpleName() + ": Siege of " + castle.getName() + " - " + battle.participants.size() + " bots joined of " + members + " clan members online (" + tooLow + " too low), attacker clans " + battle.attackerClans + ", defender clans " + battle.defenderClans + ".");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -365,7 +414,7 @@ public class FakePlayerSiegeManager
|
||||
double best = -1;
|
||||
for (Door door : castle.getDoors())
|
||||
{
|
||||
if (!door.isShowHp())
|
||||
if (!door.isShowHp() || door.isWall())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -487,7 +536,7 @@ public class FakePlayerSiegeManager
|
||||
Door gate = null;
|
||||
for (Door door : battle.castle.getDoors())
|
||||
{
|
||||
if (door.isOpen() || door.isDead() || !door.isAutoAttackable(bot))
|
||||
if (door.isOpen() || door.isDead() || door.isWall() || !door.isAutoAttackable(bot))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -563,13 +612,27 @@ public class FakePlayerSiegeManager
|
||||
}
|
||||
state.siegeSide = 0;
|
||||
state.siegeCastleId = 0;
|
||||
bot.setSiegeState((byte) 0);
|
||||
bot.setSiegeSide(0);
|
||||
bot.setInSiege(false);
|
||||
bot.updateUserInfo();
|
||||
FakePlayerBotContext.setPlan(state.seedId, "farm", 0);
|
||||
FakePlayerBotContext.setEvent(state.seedId, "siege_finished");
|
||||
state.action("siege over");
|
||||
// Back to the farming spot after a while (the engine already sends everybody to town).
|
||||
// Back to the farming spot after a while (the engine already sends everybody to town);
|
||||
// reinforcements that only came for the battle log out instead.
|
||||
ThreadPool.schedule(() ->
|
||||
{
|
||||
if (!bot.isDead() && (FakePlayerHeadlessManager.getInstance().stateOf(bot) != null) && (FakePlayerHeadlessManager.getInstance().stateOf(bot).siegeSide == 0))
|
||||
final FakePlayerBotState current = FakePlayerHeadlessManager.getInstance().stateOf(bot);
|
||||
if ((current == null) || (current.siegeSide != 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (current.temporary)
|
||||
{
|
||||
FakePlayerHeadlessManager.getInstance().despawn(bot);
|
||||
}
|
||||
else if (!bot.isDead())
|
||||
{
|
||||
FakePlayerHeadlessManager.getInstance().returnToSpot(bot);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user