Fix #412, auto team balance for autoassign warzones

This commit is contained in:
Connor Monahan 2017-07-29 01:26:30 -05:00
parent eb7b600aef
commit 22cbba62b3

View File

@ -1,37 +1,34 @@
package com.tommytony.war; package com.tommytony.war;
import java.sql.Connection; import com.google.common.collect.ImmutableList;
import java.sql.PreparedStatement; import com.tommytony.war.config.*;
import java.sql.ResultSet; import com.tommytony.war.event.WarBattleWinEvent;
import java.sql.SQLException; import com.tommytony.war.event.WarPlayerLeaveEvent;
import java.text.MessageFormat; import com.tommytony.war.event.WarPlayerThiefEvent;
import java.util.*; import com.tommytony.war.event.WarScoreCapEvent;
import java.util.logging.Level; import com.tommytony.war.job.InitZoneJob;
import com.tommytony.war.job.LoadoutResetJob;
import com.tommytony.war.job.LogKillsDeathsJob;
import com.tommytony.war.job.LogKillsDeathsJob.KillsDeathsRecord;
import com.tommytony.war.job.ZoneTimeJob; import com.tommytony.war.job.ZoneTimeJob;
import com.tommytony.war.mapper.LoadoutYmlMapper;
import com.tommytony.war.mapper.VolumeMapper;
import com.tommytony.war.mapper.ZoneVolumeMapper;
import com.tommytony.war.spout.SpoutDisplayer;
import com.tommytony.war.structure.*; import com.tommytony.war.structure.*;
import com.tommytony.war.utility.*;
import com.tommytony.war.volume.Volume;
import com.tommytony.war.volume.ZoneVolume;
import net.milkbowl.vault.economy.EconomyResponse; import net.milkbowl.vault.economy.EconomyResponse;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit; import org.bukkit.*;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.attribute.Attribute; import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance; import org.bukkit.attribute.AttributeInstance;
import org.bukkit.attribute.AttributeModifier; import org.bukkit.attribute.AttributeModifier;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.entity.Arrow; import org.bukkit.entity.*;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
@ -50,33 +47,13 @@ import org.bukkit.scoreboard.Scoreboard;
import org.getspout.spoutapi.SpoutManager; import org.getspout.spoutapi.SpoutManager;
import org.getspout.spoutapi.player.SpoutPlayer; import org.getspout.spoutapi.player.SpoutPlayer;
import com.google.common.collect.ImmutableList; import java.sql.Connection;
import com.tommytony.war.config.InventoryBag; import java.sql.PreparedStatement;
import com.tommytony.war.config.ScoreboardType; import java.sql.ResultSet;
import com.tommytony.war.config.TeamConfig; import java.sql.SQLException;
import com.tommytony.war.config.TeamConfigBag; import java.text.MessageFormat;
import com.tommytony.war.config.TeamKind; import java.util.*;
import com.tommytony.war.config.WarzoneConfig; import java.util.logging.Level;
import com.tommytony.war.config.WarzoneConfigBag;
import com.tommytony.war.event.WarBattleWinEvent;
import com.tommytony.war.event.WarPlayerLeaveEvent;
import com.tommytony.war.event.WarPlayerThiefEvent;
import com.tommytony.war.event.WarScoreCapEvent;
import com.tommytony.war.job.InitZoneJob;
import com.tommytony.war.job.LoadoutResetJob;
import com.tommytony.war.job.LogKillsDeathsJob;
import com.tommytony.war.job.LogKillsDeathsJob.KillsDeathsRecord;
import com.tommytony.war.mapper.LoadoutYmlMapper;
import com.tommytony.war.mapper.VolumeMapper;
import com.tommytony.war.mapper.ZoneVolumeMapper;
import com.tommytony.war.spout.SpoutDisplayer;
import com.tommytony.war.utility.Direction;
import com.tommytony.war.utility.Loadout;
import com.tommytony.war.utility.LoadoutSelection;
import com.tommytony.war.utility.PlayerState;
import com.tommytony.war.utility.PotionEffectHelper;
import com.tommytony.war.volume.Volume;
import com.tommytony.war.volume.ZoneVolume;
/** /**
* *
@ -86,28 +63,29 @@ import com.tommytony.war.volume.ZoneVolume;
public class Warzone { public class Warzone {
public enum LeaveCause { static final Comparator<Team> LEAST_PLAYER_COUNT_ORDER = new Comparator<Team>() {
COMMAND, DISCONNECT, SCORECAP, RESET; @Override
public boolean useRallyPoint() { public int compare(Team arg0, Team arg1) {
return this == SCORECAP ? true : false; return arg0.getPlayers().size() - arg1.getPlayers().size();
} }
} };
private String name;
private ZoneVolume volume;
private World world;
private final List<Team> teams = new ArrayList<Team>(); private final List<Team> teams = new ArrayList<Team>();
private final List<Monument> monuments = new ArrayList<Monument>(); private final List<Monument> monuments = new ArrayList<Monument>();
private final List<CapturePoint> capturePoints = new ArrayList<CapturePoint>(); private final List<CapturePoint> capturePoints = new ArrayList<CapturePoint>();
private final List<Bomb> bombs = new ArrayList<Bomb>(); private final List<Bomb> bombs = new ArrayList<Bomb>();
private final List<Cake> cakes = new ArrayList<Cake>(); private final List<Cake> cakes = new ArrayList<Cake>();
private final List<String> authors = new ArrayList<String>();
private final int minSafeDistanceFromWall = 6;
private final List<Player> respawn = new ArrayList<Player>();
private final List<String> reallyDeadFighters = new ArrayList<String>();
private final WarzoneConfigBag warzoneConfig;
private final TeamConfigBag teamDefaultConfig;
private String name;
private ZoneVolume volume;
private World world;
private Location teleport; private Location teleport;
private ZoneLobby lobby; private ZoneLobby lobby;
private Location rallyPoint; private Location rallyPoint;
private final List<String> authors = new ArrayList<String>();
private final int minSafeDistanceFromWall = 6;
private List<ZoneWallGuard> zoneWallGuards = new ArrayList<ZoneWallGuard>(); private List<ZoneWallGuard> zoneWallGuards = new ArrayList<ZoneWallGuard>();
private HashMap<String, PlayerState> playerStates = new HashMap<String, PlayerState>(); private HashMap<String, PlayerState> playerStates = new HashMap<String, PlayerState>();
private HashMap<UUID, Team> flagThieves = new HashMap<UUID, Team>(); private HashMap<UUID, Team> flagThieves = new HashMap<UUID, Team>();
@ -116,15 +94,9 @@ public class Warzone {
private HashMap<String, LoadoutSelection> loadoutSelections = new HashMap<String, LoadoutSelection>(); private HashMap<String, LoadoutSelection> loadoutSelections = new HashMap<String, LoadoutSelection>();
private HashMap<String, PlayerState> deadMenInventories = new HashMap<String, PlayerState>(); private HashMap<String, PlayerState> deadMenInventories = new HashMap<String, PlayerState>();
private HashMap<String, Integer> killCount = new HashMap<String, Integer>(); private HashMap<String, Integer> killCount = new HashMap<String, Integer>();
private final List<Player> respawn = new ArrayList<Player>();
private final List<String> reallyDeadFighters = new ArrayList<String>();
private HashMap<Player, PermissionAttachment> attachments = new HashMap<Player, PermissionAttachment>(); private HashMap<Player, PermissionAttachment> attachments = new HashMap<Player, PermissionAttachment>();
private HashMap<Player, Team> delayedJoinPlayers = new HashMap<Player, Team>(); private HashMap<Player, Team> delayedJoinPlayers = new HashMap<Player, Team>();
private List<LogKillsDeathsJob.KillsDeathsRecord> killsDeathsTracker = new ArrayList<KillsDeathsRecord>(); private List<LogKillsDeathsJob.KillsDeathsRecord> killsDeathsTracker = new ArrayList<KillsDeathsRecord>();
private final WarzoneConfigBag warzoneConfig;
private final TeamConfigBag teamDefaultConfig;
private InventoryBag defaultInventories = new InventoryBag(); private InventoryBag defaultInventories = new InventoryBag();
private Scoreboard scoreboard; private Scoreboard scoreboard;
@ -139,6 +111,9 @@ public class Warzone {
//private final Object gameEndLock = new Object(); //private final Object gameEndLock = new Object();
private boolean pvpReady = true; private boolean pvpReady = true;
private Random killSeed = new Random();
// prevent tryCallDelayedPlayers from being recursively called by Warzone#assign
private boolean activeDelayedCall = false;
public Warzone(World world, String name) { public Warzone(World world, String name) {
this.world = world; this.world = world;
@ -204,10 +179,7 @@ public class Warzone {
} }
public boolean ready() { public boolean ready() {
if (this.volume.hasTwoCorners() && !this.volume.tooSmall() && !this.volume.tooBig()) { return this.volume.hasTwoCorners() && !this.volume.tooSmall() && !this.volume.tooBig();
return true;
}
return false;
} }
public List<Team> getTeams() { public List<Team> getTeams() {
@ -244,19 +216,24 @@ public class Warzone {
return this.name; return this.name;
} }
public void setName(String newName) {
this.name = newName;
this.volume.setName(newName);
}
@Override @Override
public String toString() { public String toString() {
return this.getName(); return this.getName();
} }
public void setTeleport(Location location) {
this.teleport = location;
}
public Location getTeleport() { public Location getTeleport() {
return this.teleport; return this.teleport;
} }
public void setTeleport(Location location) {
this.teleport = location;
}
public int saveState(boolean clearArtifacts) { public int saveState(boolean clearArtifacts) {
if (this.ready()) { if (this.ready()) {
if (clearArtifacts) { if (clearArtifacts) {
@ -1025,20 +1002,13 @@ public class Warzone {
playerGuards.clear(); playerGuards.clear();
} }
public void setLobby(ZoneLobby lobby) {
this.lobby = lobby;
}
public ZoneLobby getLobby() { public ZoneLobby getLobby() {
return this.lobby; return this.lobby;
} }
static final Comparator<Team> LEAST_PLAYER_COUNT_ORDER = new Comparator<Team>() { public void setLobby(ZoneLobby lobby) {
@Override this.lobby = lobby;
public int compare(Team arg0, Team arg1) {
return arg0.getPlayers().size() - arg1.getPlayers().size();
} }
};
public Team autoAssign(Player player) { public Team autoAssign(Player player) {
Collections.sort(teams, LEAST_PLAYER_COUNT_ORDER); Collections.sort(teams, LEAST_PLAYER_COUNT_ORDER);
@ -1101,8 +1071,6 @@ public class Warzone {
} }
} }
private Random killSeed = new Random();
/** /**
* Send death messages and process other records before passing off the * Send death messages and process other records before passing off the
* death to the {@link #handleDeath(Player)} method. * death to the {@link #handleDeath(Player)} method.
@ -1311,12 +1279,51 @@ public class Warzone {
War.war.getLogger().log(Level.INFO, "Last player left warzone {0}. Warzone blocks resetting automatically...", new Object[] {this.getName()}); War.war.getLogger().log(Level.INFO, "Last player left warzone {0}. Warzone blocks resetting automatically...", new Object[] {this.getName()});
} }
} }
this.autoTeamBalance();
WarPlayerLeaveEvent event1 = new WarPlayerLeaveEvent(player.getName()); WarPlayerLeaveEvent event1 = new WarPlayerLeaveEvent(player.getName());
War.war.getServer().getPluginManager().callEvent(event1); War.war.getServer().getPluginManager().callEvent(event1);
} }
} }
/**
* Moves players from team to team if the player size delta is greater than or equal to 2.
* Only works for autoassign zones.
*/
private void autoTeamBalance() {
if (!this.getWarzoneConfig().getBoolean(WarzoneConfig.AUTOASSIGN)) {
return;
}
boolean rerun = false;
for (Team team1 : this.teams) {
for (Team team2 : this.teams) {
if (team1 == team2) {
continue;
}
int t1p = team1.getPlayers().size();
int t2p = team2.getPlayers().size();
if (t1p - t2p >= 2) {
Player eject = team1.getPlayers().get(killSeed.nextInt(t1p));
team1.removePlayer(eject);
team1.resetSign();
this.assign(eject, team2);
rerun = true;
break;
} else if (t2p - t1p >= 2) {
Player eject = team2.getPlayers().get(killSeed.nextInt(t2p));
team2.removePlayer(eject);
team2.resetSign();
this.assign(eject, team1);
rerun = true;
break;
}
}
}
if (rerun) {
this.autoTeamBalance();
}
}
public boolean isEnemyTeamFlagBlock(Team playerTeam, Block block) { public boolean isEnemyTeamFlagBlock(Team playerTeam, Block block) {
for (Team team : this.teams) { for (Team team : this.teams) {
if (!team.getName().equals(playerTeam.getName()) && team.isTeamFlagBlock(block)) { if (!team.getName().equals(playerTeam.getName()) && team.isTeamFlagBlock(block)) {
@ -1414,12 +1421,12 @@ public class Warzone {
return this.bombThieves.get(thief.getUniqueId()); return this.bombThieves.get(thief.getUniqueId());
} }
// Cake
public void removeBombThief(Player thief) { public void removeBombThief(Player thief) {
this.bombThieves.remove(thief.getUniqueId()); this.bombThieves.remove(thief.getUniqueId());
} }
// Cake
public void addCakeThief(Cake cake, Player cakeThief) { public void addCakeThief(Cake cake, Player cakeThief) {
this.cakeThieves.put(cakeThief.getUniqueId(), cake); this.cakeThieves.put(cakeThief.getUniqueId(), cake);
WarPlayerThiefEvent event1 = new WarPlayerThiefEvent(cakeThief, WarPlayerThiefEvent.StolenObject.CAKE); WarPlayerThiefEvent event1 = new WarPlayerThiefEvent(cakeThief, WarPlayerThiefEvent.StolenObject.CAKE);
@ -1526,10 +1533,7 @@ public class Warzone {
} }
public boolean isDeadMan(String playerName) { public boolean isDeadMan(String playerName) {
if (this.deadMenInventories.containsKey(playerName)) { return this.deadMenInventories.containsKey(playerName);
return true;
}
return false;
} }
public void restoreDeadmanInventory(Player player) { public void restoreDeadmanInventory(Player player) {
@ -1539,14 +1543,14 @@ public class Warzone {
} }
} }
public void setRallyPoint(Location location) {
this.rallyPoint = location;
}
public Location getRallyPoint() { public Location getRallyPoint() {
return this.rallyPoint; return this.rallyPoint;
} }
public void setRallyPoint(Location location) {
this.rallyPoint = location;
}
public void unload() { public void unload() {
War.war.log("Unloading zone " + this.getName() + "...", Level.INFO); War.war.log("Unloading zone " + this.getName() + "...", Level.INFO);
for (Team team : this.getTeams()) { for (Team team : this.getTeams()) {
@ -1572,10 +1576,7 @@ public class Warzone {
teamsWithEnough++; teamsWithEnough++;
} }
} }
if (teamsWithEnough >= this.getWarzoneConfig().getInt(WarzoneConfig.MINTEAMS)) { return teamsWithEnough >= this.getWarzoneConfig().getInt(WarzoneConfig.MINTEAMS);
return true;
}
return false;
} }
/** /**
@ -1616,8 +1617,6 @@ public class Warzone {
tryCallDelayedPlayers(); tryCallDelayedPlayers();
} }
// prevent tryCallDelayedPlayers from being recursively called by Warzone#assign
private boolean activeDelayedCall = false;
private void tryCallDelayedPlayers() { private void tryCallDelayedPlayers() {
if (activeDelayedCall || (!isEnoughPlayers() && !testEnoughPlayers(null, true))) { if (activeDelayedCall || (!isEnoughPlayers() && !testEnoughPlayers(null, true))) {
return; return;
@ -1693,7 +1692,7 @@ public class Warzone {
int i = 0; int i = 0;
Iterator<String> it = sortedNames.iterator(); Iterator<String> it = sortedNames.iterator();
while (it.hasNext()) { while (it.hasNext()) {
String name = (String) it.next(); String name = it.next();
if (i == currentIndex) { if (i == currentIndex) {
if (playerTeam.getTeamConfig().resolveBoolean(TeamConfig.PLAYERLOADOUTASDEFAULT) && name.equals("default")) { if (playerTeam.getTeamConfig().resolveBoolean(TeamConfig.PLAYERLOADOUTASDEFAULT) && name.equals("default")) {
// Use player's own inventory as loadout // Use player's own inventory as loadout
@ -1789,11 +1788,6 @@ public class Warzone {
// return gameEndLock; // return gameEndLock;
// } // }
public void setName(String newName) {
this.name = newName;
this.volume.setName(newName);
}
public HubLobbyMaterials getLobbyMaterials() { public HubLobbyMaterials getLobbyMaterials() {
return this.lobbyMaterials; return this.lobbyMaterials;
} }
@ -1824,14 +1818,14 @@ public class Warzone {
return false; return false;
} }
public void setWarzoneMaterials(WarzoneMaterials warzoneMaterials) {
this.warzoneMaterials = warzoneMaterials;
}
public WarzoneMaterials getWarzoneMaterials() { public WarzoneMaterials getWarzoneMaterials() {
return warzoneMaterials; return warzoneMaterials;
} }
public void setWarzoneMaterials(WarzoneMaterials warzoneMaterials) {
this.warzoneMaterials = warzoneMaterials;
}
public Scoreboard getScoreboard() { public Scoreboard getScoreboard() {
return scoreboard; return scoreboard;
} }
@ -1839,6 +1833,7 @@ public class Warzone {
public ScoreboardType getScoreboardType() { public ScoreboardType getScoreboardType() {
return this.getWarzoneConfig().getScoreboardType(WarzoneConfig.SCOREBOARD); return this.getWarzoneConfig().getScoreboardType(WarzoneConfig.SCOREBOARD);
} }
public boolean hasKillCount(String player) { public boolean hasKillCount(String player) {
return killCount.containsKey(player); return killCount.containsKey(player);
} }
@ -1934,7 +1929,6 @@ public class Warzone {
return zoneCap; return zoneCap;
} }
/** /**
* Get the amount of players in all teams in this warzone. Same as * Get the amount of players in all teams in this warzone. Same as
* {@link #getPlayerCount()}, except only checks teams that the specified * {@link #getPlayerCount()}, except only checks teams that the specified
@ -2117,11 +2111,19 @@ public class Warzone {
return this.isFlagThief(suspect) || this.isBombThief(suspect) || this.isCakeThief(suspect); return this.isFlagThief(suspect) || this.isBombThief(suspect) || this.isCakeThief(suspect);
} }
public boolean getPvpReady() {
return this.pvpReady;
}
public void setPvpReady(boolean ready) { public void setPvpReady(boolean ready) {
this.pvpReady = ready; this.pvpReady = ready;
} }
public boolean getPvpReady() { public enum LeaveCause {
return this.pvpReady; COMMAND, DISCONNECT, SCORECAP, RESET;
public boolean useRallyPoint() {
return this == SCORECAP;
}
} }
} }