Some work on BattleManager and BattleUpdater

This commit is contained in:
Stephen Seo 2019-10-25 16:42:54 +09:00
parent b48406299a
commit f4b1bc9487
4 changed files with 91 additions and 112 deletions

View file

@ -6,13 +6,15 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.network.PacketDistributor;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import com.seodisparate.TurnBasedMinecraft.common.networking.PacketGeneralMessage; import com.seodisparate.TurnBasedMinecraft.common.networking.PacketGeneralMessage;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraftforge.event.entity.living.LivingAttackEvent; import net.minecraftforge.event.entity.living.LivingAttackEvent;
import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
@ -20,19 +22,17 @@ public class BattleManager
{ {
private int IDCounter = 0; private int IDCounter = 0;
protected Map<Integer, Battle> battleMap; protected Map<Integer, Battle> battleMap;
private Thread updaterThread;
private BattleUpdater battleUpdater;
private Logger logger; private Logger logger;
private Map<Integer, Combatant> recentlyLeftBattle; private Map<Integer, Combatant> recentlyLeftBattle;
private BattleUpdater battleUpdater;
public BattleManager(Logger logger) public BattleManager(Logger logger)
{ {
this.logger = logger; this.logger = logger;
battleMap = new HashMap<Integer, Battle>(); battleMap = new HashMap<Integer, Battle>();
battleUpdater = new BattleUpdater(this);
updaterThread = new Thread(battleUpdater);
updaterThread.start();
recentlyLeftBattle = new HashMap<Integer, Combatant>(); recentlyLeftBattle = new HashMap<Integer, Combatant>();
battleUpdater = new BattleUpdater(this);
MinecraftForge.EVENT_BUS.register(battleUpdater);
} }
/** /**
@ -47,14 +47,34 @@ public class BattleManager
{ {
Config config = TurnBasedMinecraftMod.proxy.getConfig(); Config config = TurnBasedMinecraftMod.proxy.getConfig();
// verify that both entities are EntityPlayer and not in creative or has a corresponding EntityInfo // verify that both entities are EntityPlayer and not in creative or has a corresponding EntityInfo
if(!((event.getEntity() instanceof EntityPlayer && !((EntityPlayer)event.getEntity()).isCreative()) || (config.getEntityInfoReference(event.getEntity().getClass().getName()) != null || config.getCustomEntityInfoReference(event.getEntity().getCustomNameTag()) != null)) String receiverClassName = event.getEntity().getClass().getName();
|| !((event.getSource().getTrueSource() instanceof EntityPlayer && !((EntityPlayer)event.getSource().getTrueSource()).isCreative()) || (config.getEntityInfoReference(event.getSource().getTrueSource().getClass().getName()) != null || config.getCustomEntityInfoReference(event.getSource().getTrueSource().getCustomNameTag()) != null))) String receiverCustomName;
try {
receiverCustomName = event.getEntity().getCustomName().getUnformattedComponentText();
} catch (NullPointerException e) {
receiverCustomName = null;
}
String attackerClassName;
try {
attackerClassName = event.getSource().getTrueSource().getClass().getName();
} catch (NullPointerException e) {
attackerClassName = null;
}
String attackerCustomName;
try {
attackerCustomName = event.getSource().getTrueSource().getCustomName().getUnformattedComponentText();
} catch (NullPointerException e) {
attackerCustomName = null;
}
if(!((event.getEntity() instanceof PlayerEntity && !((PlayerEntity)event.getEntity()).isCreative()) || (config.getEntityInfoReference(receiverClassName) != null || config.getCustomEntityInfoReference(receiverCustomName) != null))
|| !((event.getSource().getTrueSource() instanceof PlayerEntity && !((PlayerEntity)event.getSource().getTrueSource()).isCreative()) || (config.getEntityInfoReference(attackerClassName) != null || config.getCustomEntityInfoReference(attackerCustomName) != null)))
{ {
return false; return false;
} }
// check if ignore battle in config // check if ignore battle in config
EntityInfo entityInfo = config.getCustomEntityInfoReference(event.getEntity().getCustomNameTag()); EntityInfo entityInfo = config.getCustomEntityInfoReference(receiverCustomName);
if(entityInfo == null) if(entityInfo == null)
{ {
entityInfo = config.getMatchingEntityInfo(event.getEntity()); entityInfo = config.getMatchingEntityInfo(event.getEntity());
@ -78,7 +98,7 @@ public class BattleManager
return false; return false;
} }
entityInfo = config.getCustomEntityInfoReference(event.getSource().getTrueSource().getCustomNameTag()); entityInfo = config.getCustomEntityInfoReference(attackerCustomName);
if(entityInfo == null) if(entityInfo == null)
{ {
entityInfo = config.getMatchingEntityInfo(event.getSource().getTrueSource()); entityInfo = config.getMatchingEntityInfo(event.getSource().getTrueSource());
@ -147,7 +167,7 @@ public class BattleManager
if(inBattle == null) if(inBattle == null)
{ {
// neither entity is in battle // neither entity is in battle
if(event.getEntity() instanceof EntityPlayer || event.getSource().getTrueSource() instanceof EntityPlayer) if(event.getEntity() instanceof PlayerEntity || event.getSource().getTrueSource() instanceof PlayerEntity)
{ {
// at least one of the entities is a player, create Battle // at least one of the entities is a player, create Battle
Collection<Entity> sideA = new ArrayList<Entity>(1); Collection<Entity> sideA = new ArrayList<Entity>(1);
@ -185,26 +205,39 @@ public class BattleManager
public void checkTargeted(LivingSetAttackTargetEvent event) public void checkTargeted(LivingSetAttackTargetEvent event)
{ {
EntityInfo attackerInfo = TurnBasedMinecraftMod.proxy.getConfig().getCustomEntityInfoReference(event.getEntity().getCustomNameTag()); String targetedCustomName;
try {
targetedCustomName = event.getTarget().getCustomName().getUnformattedComponentText();
} catch (NullPointerException e) {
targetedCustomName = null;
}
String attackerCustomName;
try {
attackerCustomName = event.getEntity().getCustomName().getUnformattedComponentText();
} catch (NullPointerException e) {
attackerCustomName = null;
}
EntityInfo attackerInfo = TurnBasedMinecraftMod.proxy.getConfig().getCustomEntityInfoReference(attackerCustomName);
if(attackerInfo == null) if(attackerInfo == null)
{ {
attackerInfo = TurnBasedMinecraftMod.proxy.getConfig().getMatchingEntityInfo(event.getEntity()); attackerInfo = TurnBasedMinecraftMod.proxy.getConfig().getMatchingEntityInfo(event.getEntity());
} }
EntityInfo targetedInfo; EntityInfo targetedInfo;
if(event.getTarget() instanceof EntityPlayer) if(event.getTarget() instanceof PlayerEntity)
{ {
targetedInfo = null; targetedInfo = null;
} }
else else
{ {
targetedInfo = TurnBasedMinecraftMod.proxy.getConfig().getCustomEntityInfoReference(event.getTarget().getCustomNameTag()); targetedInfo = TurnBasedMinecraftMod.proxy.getConfig().getCustomEntityInfoReference(targetedCustomName);
if(targetedInfo == null) if(targetedInfo == null)
{ {
targetedInfo = TurnBasedMinecraftMod.proxy.getConfig().getMatchingEntityInfo(event.getTarget()); targetedInfo = TurnBasedMinecraftMod.proxy.getConfig().getMatchingEntityInfo(event.getTarget());
} }
} }
if((event.getTarget() instanceof EntityPlayer && ((EntityPlayer)event.getTarget()).isCreative()) if((event.getTarget() instanceof PlayerEntity && ((PlayerEntity)event.getTarget()).isCreative())
|| attackerInfo == null || attackerInfo == null
|| attackerInfo.ignoreBattle || attackerInfo.ignoreBattle
|| TurnBasedMinecraftMod.proxy.getConfig().isIgnoreBattleType(attackerInfo.category) || TurnBasedMinecraftMod.proxy.getConfig().isIgnoreBattleType(attackerInfo.category)
@ -257,7 +290,7 @@ public class BattleManager
if(battle == null) if(battle == null)
{ {
// neither in battle // neither in battle
if(event.getEntity() instanceof EntityPlayer || event.getTarget() instanceof EntityPlayer) if(event.getEntity() instanceof PlayerEntity || event.getTarget() instanceof PlayerEntity)
{ {
// at least one is a player, create battle // at least one is a player, create battle
Collection<Entity> sideA = new ArrayList<Entity>(1); Collection<Entity> sideA = new ArrayList<Entity>(1);
@ -312,25 +345,23 @@ public class BattleManager
public void cleanup() public void cleanup()
{ {
battleUpdater.setIsRunning(false); battleUpdater.setRunning(false);
battleUpdater = null; MinecraftForge.EVENT_BUS.unregister(battleUpdater);
updaterThread = null;
synchronized(battleMap) synchronized(battleMap)
{ {
battleMap.clear(); battleMap.clear();
} }
battleUpdater = null;
} }
protected void addRecentlyLeftBattle(Combatant c) protected void addRecentlyLeftBattle(Combatant c)
{ {
c.time = System.nanoTime(); c.time = System.nanoTime();
Config config = TurnBasedMinecraftMod.proxy.getConfig(); Config config = TurnBasedMinecraftMod.proxy.getConfig();
if(c.entity instanceof EntityPlayerMP) if(c.entity instanceof ServerPlayerEntity) {
{ TurnBasedMinecraftMod.getHandler().send(PacketDistributor.PLAYER.with(()->(ServerPlayerEntity) c.entity), new PacketGeneralMessage("You just left battle! " + config.getLeaveBattleCooldownSeconds() + " seconds until you can attack/be-attacked again!"));
TurnBasedMinecraftMod.NWINSTANCE.sendTo(new PacketGeneralMessage("You just left battle! " + config.getLeaveBattleCooldownSeconds() + " seconds until you can attack/be-attacked again!"), (EntityPlayerMP)c.entity);
} }
synchronized(recentlyLeftBattle) synchronized(recentlyLeftBattle) {
{
recentlyLeftBattle.put(c.entity.getEntityId(), c); recentlyLeftBattle.put(c.entity.getEntityId(), c);
} }
} }
@ -346,9 +377,9 @@ public class BattleManager
if(current - entry.getValue().time > TurnBasedMinecraftMod.proxy.getConfig().getLeaveBattleCooldownNanos()) if(current - entry.getValue().time > TurnBasedMinecraftMod.proxy.getConfig().getLeaveBattleCooldownNanos())
{ {
iter.remove(); iter.remove();
if(entry.getValue().entity instanceof EntityPlayerMP) if(entry.getValue().entity instanceof ServerPlayerEntity)
{ {
TurnBasedMinecraftMod.NWINSTANCE.sendTo(new PacketGeneralMessage("Timer ended, you can now attack/be-attacked again."), (EntityPlayerMP)entry.getValue().entity); TurnBasedMinecraftMod.getHandler().send(PacketDistributor.PLAYER.with(()->(ServerPlayerEntity)entry.getValue().entity), new PacketGeneralMessage("Timer ended, you can now attack/be-attacked again."));
} }
} }
} }

View file

@ -1,98 +1,35 @@
package com.seodisparate.TurnBasedMinecraft.common; package com.seodisparate.TurnBasedMinecraft.common;
import java.util.Iterator;
import java.util.Map; import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
public class BattleUpdater implements Runnable public class BattleUpdater
{ {
private BattleManager manager; private BattleManager manager;
private AtomicBoolean isRunning; private AtomicBoolean isRunning;
private int tick;
private class UpdateRunnable implements Runnable private final int tickLimit = 10;
{
private Battle battle;
private AtomicBoolean finished = new AtomicBoolean(false);
private AtomicBoolean battleFinished = new AtomicBoolean(false);
public UpdateRunnable(Battle battle)
{
this.battle = battle;
}
public boolean isFinished()
{
return finished.get();
}
public boolean isBattleFinished()
{
return battleFinished.get();
}
@Override
public void run()
{
battleFinished.set(battle.update());
finished.set(true);
}
}
public BattleUpdater(BattleManager manager) public BattleUpdater(BattleManager manager)
{ {
this.manager = manager; this.manager = manager;
isRunning = new AtomicBoolean(true); isRunning = new AtomicBoolean(true);
tick = 0;
} }
@Override public void setRunning(boolean isRunning) {
public void run()
{
while(isRunning.get())
{
synchronized(manager.battleMap)
{
for(Iterator<Map.Entry<Integer, Battle>> iter = manager.battleMap.entrySet().iterator(); iter.hasNext();)
{
Map.Entry<Integer, Battle> entry = iter.next();
UpdateRunnable updateRunnable = new UpdateRunnable(entry.getValue());
Thread updateThread = new Thread(updateRunnable);
updateThread.start();
try { updateThread.join(2000); } catch(InterruptedException e){ /* exception ignored */ }
if(!updateRunnable.isFinished())
{
TurnBasedMinecraftMod.logger.warn("Battle (" + entry.getValue().getId() + "; " + entry.getValue().debugLog + ") update hanged for 2 seconds!");
try { updateThread.join(2000); } catch(InterruptedException e){ /* exception ignored */ }
if(!updateRunnable.isFinished())
{
TurnBasedMinecraftMod.logger.warn("Battle (" + entry.getValue().getId() + "; " + entry.getValue().debugLog + ") update hanged for 4 seconds!");
try { updateThread.join(2000); } catch(InterruptedException e){ /* exception ignored */ }
if(!updateRunnable.isFinished())
{
TurnBasedMinecraftMod.logger.error("Battle (" + entry.getValue().getId() + "; " + entry.getValue().debugLog + ") update timed out (6 seconds)!");
updateThread.interrupt();
try { updateThread.join(2000); } catch(InterruptedException e){ /* exception ignored */ }
if(!updateRunnable.isFinished())
{
// TODO this is an ugly fix to a still-not-found freeze bug in Battle.update()
TurnBasedMinecraftMod.logger.error("Battle update will not stop, forcing it to stop (8 seconds)!");
updateThread.stop();
}
}
}
}
if(updateRunnable.isFinished() && updateRunnable.isBattleFinished())
{
iter.remove();
}
}
}
manager.updateRecentlyLeftBattle();
try { Thread.sleep(250); } catch (Throwable t) { /* ignored */ }
}
}
public void setIsRunning(boolean isRunning)
{
this.isRunning.set(isRunning); this.isRunning.set(isRunning);
} }
@SubscribeEvent
public void update(TickEvent.ServerTickEvent tickEvent) {
if(tickEvent.phase != TickEvent.Phase.START && isRunning.get() && ++tick > tickLimit) {
tick = 0;
manager.battleMap.entrySet().removeIf(entry -> entry.getValue().update());
manager.updateRecentlyLeftBattle();
}
}
} }

View file

@ -4,6 +4,7 @@ import java.util.*;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.common.MinecraftForge;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;

View file

@ -862,6 +862,9 @@ public class Config
*/ */
public EntityInfo getEntityInfo(String classFullName) public EntityInfo getEntityInfo(String classFullName)
{ {
if(classFullName == null) {
return null;
}
EntityInfo eInfo = entityInfoMap.get(classFullName); EntityInfo eInfo = entityInfoMap.get(classFullName);
if(eInfo != null) if(eInfo != null)
{ {
@ -872,18 +875,19 @@ public class Config
protected EntityInfo getEntityInfoReference(String classFullName) protected EntityInfo getEntityInfoReference(String classFullName)
{ {
if(classFullName == null) {
return null;
}
return entityInfoMap.get(classFullName); return entityInfoMap.get(classFullName);
} }
protected EntityInfo getMatchingEntityInfo(Object entity) protected EntityInfo getMatchingEntityInfo(Object entity)
{ {
if(entity == null) if(entity == null) {
{
return null; return null;
} }
EntityInfo matching = entityInfoMap.get(entity.getClass().getName()); EntityInfo matching = entityInfoMap.get(entity.getClass().getName());
if(matching != null && matching.classType.isInstance(entity)) if(matching != null && matching.classType.isInstance(entity)) {
{
return matching; return matching;
} }
return null; return null;
@ -896,6 +900,9 @@ public class Config
*/ */
public EntityInfo getCustomEntityInfo(String customName) public EntityInfo getCustomEntityInfo(String customName)
{ {
if(customName == null) {
return null;
}
EntityInfo eInfo = customEntityInfoMap.get(customName); EntityInfo eInfo = customEntityInfoMap.get(customName);
if(eInfo != null) if(eInfo != null)
{ {
@ -906,6 +913,9 @@ public class Config
protected EntityInfo getCustomEntityInfoReference(String customName) protected EntityInfo getCustomEntityInfoReference(String customName)
{ {
if(customName == null) {
return null;
}
return customEntityInfoMap.get(customName); return customEntityInfoMap.get(customName);
} }