Add battle cooldown

Cooldown (default 5 seconds) prevents an entity that left battle from
attacking or being-attacked for the duration of the cooldown.
This commit is contained in:
Stephen Seo 2018-10-17 18:28:47 +09:00
parent 8bcecd21a9
commit f55b2e89ab
9 changed files with 156 additions and 54 deletions

View file

@ -224,6 +224,6 @@ public class ClientProxy extends CommonProxy
@Override @Override
public void createLocalBattle(int id) public void createLocalBattle(int id)
{ {
localBattle = new Battle(id, null, null, false); localBattle = new Battle(null, id, null, null, false);
} }
} }

View file

@ -64,13 +64,20 @@ public class AttackEventHandler
{ {
return; return;
} }
Config config = TurnBasedMinecraftMod.proxy.getConfig();
if(!isAttackerValid(event) BattleManager battleManager = TurnBasedMinecraftMod.proxy.getBattleManager();
if((event.getEntity() != null && battleManager.isRecentlyLeftBattle(event.getEntity().getEntityId()))
|| (event.getSource().getTrueSource() != null && battleManager.isRecentlyLeftBattle(event.getSource().getTrueSource().getEntityId())))
{
event.setCanceled(true);
return;
}
else if(!isAttackerValid(event)
&& event.getEntity() != null && event.getEntity() != null
&& event.getSource().getTrueSource() != null && event.getSource().getTrueSource() != null
&& !TurnBasedMinecraftMod.proxy.getConfig().getBattleIgnoringPlayers().contains(event.getSource().getTrueSource().getEntityId()) && !config.getBattleIgnoringPlayers().contains(event.getSource().getTrueSource().getEntityId())
&& !TurnBasedMinecraftMod.proxy.getConfig().getBattleIgnoringPlayers().contains(event.getEntity().getEntityId()) && !config.getBattleIgnoringPlayers().contains(event.getEntity().getEntityId())
&& TurnBasedMinecraftMod.proxy.getBattleManager().checkAttack(event)) && battleManager.checkAttack(event))
{ {
// TurnBasedMinecraftMod.logger.debug("Canceled LivingAttackEvent between " + TurnBasedMinecraftMod.commonProxy.getAttackingEntity() + " and " + event.getEntity()); // TurnBasedMinecraftMod.logger.debug("Canceled LivingAttackEvent between " + TurnBasedMinecraftMod.commonProxy.getAttackingEntity() + " and " + event.getEntity());
event.setCanceled(true); event.setCanceled(true);
@ -88,14 +95,19 @@ public class AttackEventHandler
@SubscribeEvent @SubscribeEvent
public void entityTargeted(LivingSetAttackTargetEvent event) public void entityTargeted(LivingSetAttackTargetEvent event)
{ {
if(event.getEntity().world.isRemote || TurnBasedMinecraftMod.proxy.getConfig().isOldBattleBehaviorEnabled()) Config config = TurnBasedMinecraftMod.proxy.getConfig();
BattleManager battleManager = TurnBasedMinecraftMod.proxy.getBattleManager();
if(event.getEntity().world.isRemote
|| config.isOldBattleBehaviorEnabled()
|| (event.getEntity() != null && battleManager.isRecentlyLeftBattle(event.getEntity().getEntityId()))
|| (event.getTarget() != null && battleManager.isRecentlyLeftBattle(event.getTarget().getEntityId())))
{ {
return; return;
} }
else if(event.getEntity() != null else if(event.getEntity() != null
&& event.getTarget() != null && event.getTarget() != null
&& !TurnBasedMinecraftMod.proxy.getConfig().getBattleIgnoringPlayers().contains(event.getEntity().getEntityId()) && !config.getBattleIgnoringPlayers().contains(event.getEntity().getEntityId())
&& !TurnBasedMinecraftMod.proxy.getConfig().getBattleIgnoringPlayers().contains(event.getTarget().getEntityId())) && !config.getBattleIgnoringPlayers().contains(event.getTarget().getEntityId()))
{ {
TurnBasedMinecraftMod.proxy.getBattleManager().checkTargeted(event); TurnBasedMinecraftMod.proxy.getBattleManager().checkTargeted(event);
} }

View file

@ -49,6 +49,8 @@ public class Battle
private boolean isServer; private boolean isServer;
private boolean battleEnded; private boolean battleEnded;
private BattleManager battleManager;
public enum State public enum State
{ {
DECISION(0), DECISION(0),
@ -118,8 +120,9 @@ public class Battle
} }
} }
public Battle(int id, Collection<Entity> sideA, Collection<Entity> sideB, boolean isServer) public Battle(BattleManager battleManager, int id, Collection<Entity> sideA, Collection<Entity> sideB, boolean isServer)
{ {
this.battleManager = battleManager;
this.isServer = isServer; this.isServer = isServer;
this.id = id; this.id = id;
this.sideA = new Hashtable<Integer, Combatant>(); this.sideA = new Hashtable<Integer, Combatant>();
@ -515,16 +518,12 @@ public class Battle
*/ */
private boolean healthCheck() private boolean healthCheck()
{ {
Queue<Integer> removeQueue = new ArrayDeque<Integer>(); Queue<Combatant> removeQueue = new ArrayDeque<Combatant>();
for(Combatant c : sideA.values()) for(Combatant c : sideA.values())
{ {
if(!c.entity.isEntityAlive()) if(!c.entity.isEntityAlive())
{ {
removeQueue.add(c.entity.getEntityId()); removeQueue.add(c);
if(c.entity instanceof EntityPlayer)
{
TurnBasedMinecraftMod.NWINSTANCE.sendTo(new PacketBattleMessage(PacketBattleMessage.MessageType.ENDED, c.entity.getEntityId(), 0, 0), (EntityPlayerMP)c.entity);
}
String category = new String(); String category = new String();
if(c.entityInfo != null) if(c.entityInfo != null)
{ {
@ -541,11 +540,7 @@ public class Battle
{ {
if(!c.entity.isEntityAlive()) if(!c.entity.isEntityAlive())
{ {
removeQueue.add(c.entity.getEntityId()); removeQueue.add(c);
if(c.entity instanceof EntityPlayer)
{
TurnBasedMinecraftMod.NWINSTANCE.sendTo(new PacketBattleMessage(PacketBattleMessage.MessageType.ENDED, c.entity.getEntityId(), 0, 0), (EntityPlayerMP)c.entity);
}
String category = new String(); String category = new String();
if(c.entityInfo != null) if(c.entityInfo != null)
{ {
@ -559,14 +554,9 @@ public class Battle
} }
} }
boolean didRemove = !removeQueue.isEmpty(); boolean didRemove = !removeQueue.isEmpty();
for(Integer toRemove = removeQueue.poll(); toRemove != null; toRemove = removeQueue.poll()) for(Combatant toRemove = removeQueue.poll(); toRemove != null; toRemove = removeQueue.poll())
{ {
sideA.remove(toRemove); removeCombatant(toRemove);
sideB.remove(toRemove);
if(players.remove(toRemove) != null)
{
playerCount.decrementAndGet();
}
} }
if(players.isEmpty() || sideA.isEmpty() || sideB.isEmpty()) if(players.isEmpty() || sideA.isEmpty() || sideB.isEmpty())
{ {
@ -586,24 +576,20 @@ public class Battle
*/ */
private boolean isCreativeCheck() private boolean isCreativeCheck()
{ {
Queue<Integer> removeQueue = new ArrayDeque<Integer>(); Queue<Combatant> removeQueue = new ArrayDeque<Combatant>();
for(Combatant c : players.values()) for(Combatant c : players.values())
{ {
if(c.entity != null && ((EntityPlayer)c.entity).isCreative()) if(c.entity != null && ((EntityPlayer)c.entity).isCreative())
{ {
TurnBasedMinecraftMod.NWINSTANCE.sendTo(new PacketBattleMessage(PacketBattleMessage.MessageType.ENDED, c.entity.getEntityId(), 0, 0), (EntityPlayerMP)c.entity); removeQueue.add(c);
removeQueue.add(c.entity.getEntityId());
} }
} }
boolean didRemove = false; boolean didRemove = false;
for(Integer toRemove = removeQueue.poll(); toRemove != null; toRemove = removeQueue.poll()) for(Combatant toRemove = removeQueue.poll(); toRemove != null; toRemove = removeQueue.poll())
{ {
didRemove = true; didRemove = true;
sideA.remove(toRemove); removeCombatant(toRemove);
sideB.remove(toRemove); sendMessageToAllPlayers(PacketBattleMessage.MessageType.BECAME_CREATIVE, toRemove.entity.getEntityId(), 0, 0);
players.remove(toRemove);
playerCount.decrementAndGet();
sendMessageToAllPlayers(PacketBattleMessage.MessageType.BECAME_CREATIVE, toRemove, 0, 0);
} }
if(didRemove) if(didRemove)
{ {
@ -640,6 +626,18 @@ public class Battle
} }
} }
private void removeCombatant(Combatant c)
{
sideA.remove(c.entity.getEntityId());
sideB.remove(c.entity.getEntityId());
if(players.remove(c.entity.getEntityId()) != null)
{
playerCount.decrementAndGet();
TurnBasedMinecraftMod.NWINSTANCE.sendTo(new PacketBattleMessage(PacketBattleMessage.MessageType.ENDED, 0, 0, 0), (EntityPlayerMP)c.entity);
}
battleManager.addRecentlyLeftBattle(c);
}
/** /**
* @return True if battle has ended * @return True if battle has ended
*/ */
@ -651,6 +649,13 @@ public class Battle
} }
else if(battleEnded) else if(battleEnded)
{ {
Collection<Combatant> combatants = new ArrayList<Combatant>();
combatants.addAll(sideA.values());
combatants.addAll(sideB.values());
for(Combatant c : combatants)
{
removeCombatant(c);
}
return true; return true;
} }
long nextInstant = System.nanoTime(); long nextInstant = System.nanoTime();
@ -663,6 +668,13 @@ public class Battle
{ {
if(battleEnded) if(battleEnded)
{ {
Collection<Combatant> combatants = new ArrayList<Combatant>();
combatants.addAll(sideA.values());
combatants.addAll(sideB.values());
for(Combatant c : combatants)
{
removeCombatant(c);
}
return true; return true;
} }
boolean combatantsChanged = false; boolean combatantsChanged = false;
@ -1066,14 +1078,6 @@ public class Battle
if((int)(Math.random() * 100) < fleeProbability) if((int)(Math.random() * 100) < fleeProbability)
{ {
// flee success // flee success
if(next.isSideA)
{
sideA.remove(next.entity.getEntityId());
}
else
{
sideB.remove(next.entity.getEntityId());
}
combatantsChanged = true; combatantsChanged = true;
String fleeingCategory = new String(); String fleeingCategory = new String();
if(next.entityInfo != null) if(next.entityInfo != null)
@ -1085,12 +1089,7 @@ public class Battle
fleeingCategory = "player"; fleeingCategory = "player";
} }
sendMessageToAllPlayers(PacketBattleMessage.MessageType.FLEE, next.entity.getEntityId(), 0, 1, fleeingCategory); sendMessageToAllPlayers(PacketBattleMessage.MessageType.FLEE, next.entity.getEntityId(), 0, 1, fleeingCategory);
if(next.entity instanceof EntityPlayer) removeCombatant(next);
{
players.remove(next.entity.getEntityId());
playerCount.decrementAndGet();
TurnBasedMinecraftMod.NWINSTANCE.sendTo(new PacketBattleMessage(PacketBattleMessage.MessageType.ENDED, 0, 0, 0), (EntityPlayerMP)next.entity);
}
} }
else else
{ {
@ -1180,6 +1179,16 @@ public class Battle
{ {
notifyPlayersBattleInfo(); notifyPlayersBattleInfo();
} }
if(battleEnded)
{
Collection<Combatant> combatants = new ArrayList<Combatant>();
combatants.addAll(sideA.values());
combatants.addAll(sideB.values());
for(Combatant c : combatants)
{
removeCombatant(c);
}
}
return battleEnded; return battleEnded;
} // update(final long dt) } // update(final long dt)
} }

View file

@ -1,14 +1,22 @@
package com.seodisparate.TurnBasedMinecraft.common; package com.seodisparate.TurnBasedMinecraft.common;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Map; import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
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.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;
@ -19,6 +27,7 @@ public class BattleManager
private Thread updaterThread; private Thread updaterThread;
private BattleUpdater battleUpdater; private BattleUpdater battleUpdater;
private Logger logger; private Logger logger;
private Map<Integer, Combatant> recentlyLeftBattle;
public BattleManager(Logger logger) public BattleManager(Logger logger)
{ {
@ -27,6 +36,7 @@ public class BattleManager
battleUpdater = new BattleUpdater(this); battleUpdater = new BattleUpdater(this);
updaterThread = new Thread(battleUpdater); updaterThread = new Thread(battleUpdater);
updaterThread.start(); updaterThread.start();
recentlyLeftBattle = new HashMap<Integer, Combatant>();
} }
/** /**
@ -245,7 +255,7 @@ public class BattleManager
{ {
++IDCounter; ++IDCounter;
} }
Battle newBattle = new Battle(IDCounter, sideA, sideB, true); Battle newBattle = new Battle(this, IDCounter, sideA, sideB, true);
battleMap.put(IDCounter, newBattle); battleMap.put(IDCounter, newBattle);
newBattle.notifyPlayersBattleInfo(); newBattle.notifyPlayersBattleInfo();
return newBattle; return newBattle;
@ -263,4 +273,50 @@ public class BattleManager
updaterThread = null; updaterThread = null;
battleMap.clear(); battleMap.clear();
} }
protected void addRecentlyLeftBattle(Combatant c)
{
c.time = System.nanoTime();
Config config = TurnBasedMinecraftMod.proxy.getConfig();
if(c.entity instanceof EntityPlayerMP)
{
TurnBasedMinecraftMod.NWINSTANCE.sendTo(new PacketGeneralMessage("You just left battle! " + config.getLeaveBattleCooldownSeconds() + " seconds until you can attack/be-attacked again!"), (EntityPlayerMP)c.entity);
}
synchronized(recentlyLeftBattle)
{
recentlyLeftBattle.put(c.entity.getEntityId(), c);
}
}
protected void updateRecentlyLeftBattle()
{
long current = System.nanoTime();
Queue<Combatant> removeQueue = new ArrayDeque<Combatant>();
synchronized(recentlyLeftBattle)
{
for(Combatant c : recentlyLeftBattle.values())
{
if(current - c.time > TurnBasedMinecraftMod.proxy.getConfig().getLeaveBattleCooldownNanos())
{
removeQueue.add(c);
}
}
for(Combatant c = removeQueue.poll(); c != null; c = removeQueue.poll())
{
if(c.entity instanceof EntityPlayerMP)
{
TurnBasedMinecraftMod.NWINSTANCE.sendTo(new PacketGeneralMessage("Timer ended, you can now attack/be-attacked again."), (EntityPlayerMP)c.entity);
}
recentlyLeftBattle.remove(c.entity.getEntityId());
}
}
}
public boolean isRecentlyLeftBattle(int entityID)
{
synchronized(recentlyLeftBattle)
{
return recentlyLeftBattle.containsKey(entityID);
}
}
} }

View file

@ -33,6 +33,7 @@ public class BattleUpdater implements Runnable
{ {
manager.battleMap.remove(ended); manager.battleMap.remove(ended);
} }
manager.updateRecentlyLeftBattle();
try { Thread.sleep(250); } catch (Throwable t) { /* ignored */ } try { Thread.sleep(250); } catch (Throwable t) { /* ignored */ }
} }
} }

View file

@ -24,6 +24,7 @@ public class Combatant
public double z; public double z;
public float yaw; public float yaw;
public float pitch; public float pitch;
public long time;
public Combatant() public Combatant()
{ {

View file

@ -5,7 +5,6 @@ import com.seodisparate.TurnBasedMinecraft.common.networking.PacketGeneralMessag
import net.minecraft.command.CommandBase; import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException; import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender; import net.minecraft.command.ICommandSender;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;

View file

@ -8,7 +8,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -49,6 +48,7 @@ public class Config
private boolean onlyOPsSelfDisableTB = true; private boolean onlyOPsSelfDisableTB = true;
private boolean battleDisabledForAll = false; private boolean battleDisabledForAll = false;
private boolean oldBattleBehaviorEnabled = false; private boolean oldBattleBehaviorEnabled = false;
private int leaveBattleCooldownSeconds = 5;
public Config(Logger logger) public Config(Logger logger)
{ {
@ -175,6 +175,18 @@ public class Config
{ {
continue; continue;
} }
else if(xmlReader.getLocalName().equals("LeaveBattleCooldown"))
{
leaveBattleCooldownSeconds = Integer.parseInt(xmlReader.getElementText());
if(leaveBattleCooldownSeconds <= 0)
{
leaveBattleCooldownSeconds = 1;
}
else if(leaveBattleCooldownSeconds > 10)
{
leaveBattleCooldownSeconds = 10;
}
}
else if(xmlReader.getLocalName().equals("OldBattleBehavior")) else if(xmlReader.getLocalName().equals("OldBattleBehavior"))
{ {
if(xmlReader.getElementText().toLowerCase().equals("false")) if(xmlReader.getElementText().toLowerCase().equals("false"))
@ -634,4 +646,14 @@ public class Config
{ {
return oldBattleBehaviorEnabled; return oldBattleBehaviorEnabled;
} }
public int getLeaveBattleCooldownSeconds()
{
return leaveBattleCooldownSeconds;
}
public long getLeaveBattleCooldownNanos()
{
return (long)leaveBattleCooldownSeconds * 1000000000L;
}
} }

View file

@ -1,6 +1,8 @@
<TurnBasedMinecraftConfig> <TurnBasedMinecraftConfig>
<!-- If the mod has a newer version config, it will rename the existing config and place the new config --> <!-- If the mod has a newer version config, it will rename the existing config and place the new config -->
<Version>5</Version> <Version>5</Version>
<!-- Number of seconds that an entity cannot enter battle after having just left one. Minimum 1, maximum 10-->
<LeaveBattleCooldown>5</LeaveBattleCooldown>
<!-- If not "false", uses old battle behavior where battles only start on attack/hit. Otherwise, battles can <!-- If not "false", uses old battle behavior where battles only start on attack/hit. Otherwise, battles can
start when a hostile mob targets a player or another entity in battle. --> start when a hostile mob targets a player or another entity in battle. -->
<OldBattleBehavior>false</OldBattleBehavior> <OldBattleBehavior>false</OldBattleBehavior>