UHC improvements

Lots of hacky NMS stuff. Ask me if you're confused

Basically, decorate the world async
Keep spawn chunks loaded
Make sure view distance is capped so no chunks are genned on teleport
Add regeneration and resistance on spawn to prevent damage
Set health objective score manually
Teleport player two times to ensure no block glitching
Disable kicking and NCP while teleporting
This commit is contained in:
samczsun 2016-06-29 00:22:44 -04:00 committed by cnr
parent 045e1b3064
commit 3e1c44bfa3
7 changed files with 638 additions and 62 deletions

View File

@ -45,21 +45,25 @@ public class Pair<L, R> implements Serializable {
return getLeft().toString() + ":" + getRight().toString();
}
@SuppressWarnings("rawtypes")
@Override
public boolean equals(Object obj)
public boolean equals(Object o)
{
if (this == obj)
return true;
if (!(obj instanceof Pair))
return false;
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pair localPair = (Pair) obj;
Pair<?, ?> pair = (Pair<?, ?>) o;
if (getLeft() != null ? !getLeft().equals(localPair.getLeft()) : localPair.getLeft() != null)
return false;
if (getRight() != null ? !getRight().equals(localPair.getRight()) : localPair.getRight() != null)
return false;
return true;
if (left != null ? !left.equals(pair.left) : pair.left != null) return false;
return right != null ? right.equals(pair.right) : pair.right == null;
}
@Override
public int hashCode()
{
int result = left != null ? left.hashCode() : 0;
result = 31 * result + (right != null ? right.hashCode() : 0);
return result;
}
}

View File

@ -19,5 +19,9 @@
<artifactId>mineplex-minecraft-game-classcombat</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>fr.neatmonster</groupId>
<artifactId>nocheatplus</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -268,11 +268,18 @@ public class GameTeam
return GetColor() + "§l" + GetName();
}
public void SpawnTeleport(Player player)
{
public Location SpawnTeleport(Player player)
{
Location l = GetSpawn();
player.teleport(l);
return l;
}
public void SpawnTeleport(Player player, Location location)
{
player.leaveVehicle();
player.eject();
player.teleport(GetSpawn());
player.teleport(location);
}
public void SpawnTeleport()

View File

@ -1,14 +1,27 @@
package nautilus.game.arcade.game.games.uhc;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import com.mineplex.spigot.ChunkPreLoadEvent;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.access.IViolationInfo;
import fr.neatmonster.nocheatplus.hooks.NCPHook;
import fr.neatmonster.nocheatplus.hooks.NCPHookManager;
import mineplex.core.common.Pair;
import mineplex.core.common.util.C;
import mineplex.core.common.util.F;
import mineplex.core.common.util.NautHashMap;
@ -34,38 +47,35 @@ import mineplex.minecraft.game.core.damage.CustomDamageEvent;
import mineplex.serverdata.Utility;
import nautilus.game.arcade.ArcadeManager;
import nautilus.game.arcade.GameType;
import nautilus.game.arcade.events.GamePrepareCountdownCommence;
import nautilus.game.arcade.events.GameStateChangeEvent;
import nautilus.game.arcade.events.PlayerPrepareTeleportEvent;
import nautilus.game.arcade.game.Game;
import nautilus.game.arcade.game.GameTeam;
import nautilus.game.arcade.game.TeamGame;
import nautilus.game.arcade.game.Game.GameState;
import nautilus.game.arcade.kit.Kit;
import net.minecraft.server.v1_8_R3.BiomeCache;
import net.minecraft.server.v1_8_R3.ChunkCoordIntPair;
import net.minecraft.server.v1_8_R3.ChunkProviderServer;
import net.minecraft.server.v1_8_R3.EmptyChunk;
import net.minecraft.server.v1_8_R3.ExceptionWorldConflict;
import net.minecraft.server.v1_8_R3.ChunkRegionLoader;
import net.minecraft.server.v1_8_R3.FileIOThread;
import net.minecraft.server.v1_8_R3.IChunkLoader;
import net.minecraft.server.v1_8_R3.IChunkProvider;
import net.minecraft.server.v1_8_R3.LongHashMap;
import net.minecraft.server.v1_8_R3.MinecraftServer;
import net.minecraft.server.v1_8_R3.NBTTagCompound;
import net.minecraft.server.v1_8_R3.World;
import net.minecraft.server.v1_8_R3.WorldChunkManager;
import net.minecraft.server.v1_8_R3.WorldServer;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Chunk;
import org.bukkit.Difficulty;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.WorldBorder;
import org.bukkit.World.Environment;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_8_R3.generator.NormalChunkGenerator;
import org.bukkit.craftbukkit.v1_8_R3.util.LongHash;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Entity;
@ -74,24 +84,27 @@ import org.bukkit.entity.Ghast;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Monster;
import org.bukkit.entity.Player;
import org.bukkit.entity.Villager;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
import org.bukkit.event.entity.EntitySpawnEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.inventory.PrepareItemCraftEvent;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
import org.bukkit.event.player.PlayerPortalEvent;
import org.bukkit.event.player.PlayerQuitEvent;
@ -107,10 +120,16 @@ import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.spigotmc.WatchdogThread;
import org.spigotmc.AsyncCatcher;
public class UHC extends TeamGame
public class UHC extends TeamGame implements NCPHook
{
// The view distance of UHC. The amount of time and ram needed grows polynomially
private static final int VIEW_DISTANCE = 5;
// The number of threads to use for reading chunks from disk
private static final int THREADS_FOR_CHUNK_LOADING = 4;
private NautHashMap<Player, Player> _teamReqs = new NautHashMap<Player, Player>();
private NautHashMap<String, Long> _deathTime = new NautHashMap<String, Long>();
@ -145,6 +164,14 @@ public class UHC extends TeamGame
private double _previousBorder = 1000;
private long _borderStartedMoving;
private volatile boolean _isDecorating = false;
private volatile boolean _allowSpawning = true;
private int _teleportedPlayers = -1;
private int _totalPlayers = 0;
private AtomicInteger actual = new AtomicInteger();
private AtomicInteger expected = new AtomicInteger(23000); // Most likely it'll be around 23000
public UHC(ArcadeManager manager)
{
this(manager, GameType.UHC);
@ -157,7 +184,7 @@ public class UHC extends TeamGame
DamageDealt
);
}
public UHC(ArcadeManager manager, GameType type)
{
super(manager, type,
@ -181,7 +208,7 @@ public class UHC extends TeamGame
this.GameTimeout = 10800000;
this.DamagePvP = false;
this.DamagePvP = true;
this.DeathDropItems = true;
@ -213,8 +240,6 @@ public class UHC extends TeamGame
this.WorldBoundaryKill = false;
this.TickPerTeleport = 3;
this.GemBoosterEnabled = false;
this.GemDoubleEnabled = false;
this.GemHunterEnabled = false;
@ -243,6 +268,8 @@ public class UHC extends TeamGame
_createTime = System.currentTimeMillis();
_serverTime = Utility.currentTimeMillis();
NCPHookManager.addHook(CheckType.ALL, this);
}
@Override
@ -274,23 +301,6 @@ public class UHC extends TeamGame
border.setWarningTime(-99);
}
@EventHandler
public void onDamage(CustomDamageEvent event)
{
if (!IsLive())
return;
if (UtilTime.elapsed(getGameLiveTime(), 20000))
return;
if (!(event.GetDamageeEntity() instanceof Player))
return;
event.SetCancelled("Spawn Invincibility");
event.GetDamageeEntity().setFireTicks(0);
}
@EventHandler
public void onSecond(UpdateEvent event)
{
@ -479,6 +489,333 @@ public class UHC extends TeamGame
}
TimingManager.stop("UHC Spawn Generation");
Location spawn = GetRandomSpawn(WorldData.World.getSpawnLocation());
WorldData.World.setSpawnLocation(spawn.getBlockX(), spawn.getBlockY(), spawn.getBlockZ());
WorldServer worldServer = ((CraftWorld) WorldData.World).getHandle();
// Update view distance
worldServer.spigotConfig.viewDistance = VIEW_DISTANCE;
worldServer.getPlayerChunkMap().a(VIEW_DISTANCE);
if (Runtime.getRuntime().maxMemory() / 1024 / 1024 < 2048)
{
Announce(C.cGreen + C.Bold + "Skipping spawn pregeneration", false);
return;
}
// Ensures the server does not tick us
worldServer.getMinecraftServer().worlds.remove(worldServer);
_allowSpawning = false;
new Thread(() ->
{
try
{
TimingManager.start("UHC Chunk Loading");
ChunkProviderServer chunkProviderServer = worldServer.chunkProviderServer;
Field chunkLoaderField = chunkProviderServer.getClass().getDeclaredField("chunkLoader");
chunkLoaderField.setAccessible(true);
ChunkRegionLoader loader = (ChunkRegionLoader) chunkLoaderField.get(chunkProviderServer);
Map<Long, net.minecraft.server.v1_8_R3.Chunk> loaded = new ConcurrentHashMap<>();
Map<Long, NBTTagCompound> compounds = new ConcurrentHashMap<>();
// Step 1: Read all the required chunks from the disk
// We're going to read all the required chunks from disk async
{
Set<Pair<Integer, Integer>> coordPairs = new HashSet<>();
// Special case for 0, 0
{
int x = spawn.getBlockX() >> 4;
int z = spawn.getBlockZ() >> 4;
for (int dx = -VIEW_DISTANCE; dx <= VIEW_DISTANCE; dx++)
{
for (int dz = -VIEW_DISTANCE; dz <= VIEW_DISTANCE; dz++)
{
coordPairs.add(Pair.create(x + dx, z + dz));
}
}
}
// All the team spawns
{
for (int i = 0; i < GetTeamList().size(); i++)
{
GameTeam team = GetTeamList().get(i);
for (Location l : team.GetSpawns())
{
int x = l.getChunk().getX();
int z = l.getChunk().getZ();
for (int dx = -VIEW_DISTANCE; dx <= VIEW_DISTANCE; dx++)
{
for (int dz = -VIEW_DISTANCE; dz <= VIEW_DISTANCE; dz++)
{
coordPairs.add(Pair.create(x + dx, z + dz));
}
}
}
}
}
// Sigh... I don't want this to be here but it needs to be set somewhere...
// Multiply by 3 because there are 3 stages
expected.set(coordPairs.size() * 3);
// Load them now
ExecutorService chunkLoaders = Executors.newFixedThreadPool(THREADS_FOR_CHUNK_LOADING);
for (Pair<Integer, Integer> coords : coordPairs)
{
chunkLoaders.submit(() ->
{
try
{
Object[] data = loader.loadChunk(worldServer, coords.getLeft(), coords.getRight());
if (data != null)
{
NBTTagCompound compound = (NBTTagCompound) data[1];
net.minecraft.server.v1_8_R3.Chunk chunk = (net.minecraft.server.v1_8_R3.Chunk) data[0];
loaded.put(LongHash.toLong(coords.getLeft(), coords.getRight()), chunk);
compounds.put(LongHash.toLong(coords.getLeft(), coords.getRight()), compound);
}
else
{
System.out.println("Failed to load chunk " + coords.getLeft() + "," + coords.getRight());
}
}
catch (Throwable t)
{
t.printStackTrace();
}
finally
{
actual.getAndIncrement();
}
});
}
chunkLoaders.shutdown();
// We've got plenty of time to wait
System.out.println("Finished submitting tasks to executor, waiting...");
chunkLoaders.awaitTermination(1, TimeUnit.DAYS);
System.out.println("Loaded: " + loaded.size() + " and coords: " + coordPairs.size());
}
// Step 2: Recreate structures, update neighbors, load entities
// This step should be super quick so there's no point in scheduling it elsewhere
// Code is plain copypasted from ChunkIOProvider
{
for (net.minecraft.server.v1_8_R3.Chunk chunk : loaded.values())
{
NBTTagCompound compound = compounds.get(LongHash.toLong(chunk.locX, chunk.locZ));
loader.loadEntities(chunk, compound.getCompound("Level"), worldServer);
chunk.setLastSaved(chunkProviderServer.world.getTime());
if (chunkProviderServer.chunkProvider != null)
{
chunkProviderServer.chunkProvider.recreateStructures(chunk, chunk.locX, chunk.locZ);
}
for (int x = -2; x < 3; ++x)
{
for (int z = -2; z < 3; ++z)
{
if (x != 0 || z != 0)
{
net.minecraft.server.v1_8_R3.Chunk neighbor = loaded.get(LongHash.toLong(chunk.locX + x, chunk.locZ + z));
if (neighbor != null)
{
neighbor.setNeighborLoaded(-x, -z);
chunk.setNeighborLoaded(x, z);
}
}
}
}
actual.getAndIncrement();
}
}
AtomicBoolean lockCompleted = new AtomicBoolean(false);
Object lock = new Object();
// Hop back onto the main thread
Manager.runSync(() ->
{
// We want to add all the chunks to the chunkmap so that the server is not out of sync
for (Map.Entry<Long, net.minecraft.server.v1_8_R3.Chunk> ent : loaded.entrySet())
{
ent.getValue().addEntities();
chunkProviderServer.chunks.put(ent.getKey(), ent.getValue());
}
lockCompleted.set(true);
synchronized (lock)
{
lock.notifyAll();
}
});
if (!lockCompleted.get())
{
synchronized (lock)
{
lock.wait();
}
}
if (!lockCompleted.get())
{
throw new IllegalStateException("Lock was not completed");
}
// Step 3: Decorate the chunks. This step must be performed async as otherwise the server lags way too hard
// Notes: Do not allow the server to tick the world. If this is allowed EntityTracker will raise CME
// NextTickList will also raise errors
// And worst case the server will crash
{
// Live life on the edge
AsyncCatcher.enabled = false;
_isDecorating = true;
int x = 0;
for (net.minecraft.server.v1_8_R3.Chunk chunk : loaded.values())
{
loadNearby(chunkProviderServer, chunkProviderServer, chunk.locX, chunk.locZ, worldServer, chunk);
x++;
if (x % 100 == 0)
{
System.out.println(x);
}
actual.getAndIncrement();
}
TimingManager.stop("UHC Chunk Loading");
_isDecorating = false;
AsyncCatcher.enabled = true;
System.out.println("Expected: " + expected.get() + ", actual: " + actual.get());
Manager.runSync(() ->
{
World world = worldServer.getWorld();
for (Entity entity : world.getLivingEntities())
{
if (!(entity instanceof Player) && !(entity instanceof Villager))
{
entity.remove();
}
}
// You may tick again
worldServer.getMinecraftServer().worlds.add(worldServer);
// Well, if they're not equal, not much we can do. We've hit the end
actual.set(expected.get());
});
}
}
catch (Throwable t)
{
t.printStackTrace();
}
}, "Chunk Loader").start();
}
// Plain copypasted from Chunk, but modified so that it doesn't load surrounding chunks if they're nonexistant
// This decorates the chunks. Do not remove!
public void loadNearby(ChunkProviderServer ichunkprovider, ChunkProviderServer ichunkprovider1, int i, int j, net.minecraft.server.v1_8_R3.World world, net.minecraft.server.v1_8_R3.Chunk chunk1)
{
boolean flag = ichunkprovider.isChunkLoaded(i, j - 1);
boolean flag1 = ichunkprovider.isChunkLoaded(i + 1, j);
boolean flag2 = ichunkprovider.isChunkLoaded(i, j + 1);
boolean flag3 = ichunkprovider.isChunkLoaded(i - 1, j);
boolean flag4 = ichunkprovider.isChunkLoaded(i - 1, j - 1);
boolean flag5 = ichunkprovider.isChunkLoaded(i + 1, j + 1);
boolean flag6 = ichunkprovider.isChunkLoaded(i - 1, j + 1);
boolean flag7 = ichunkprovider.isChunkLoaded(i + 1, j - 1);
if (flag1 && flag2 && flag5)
{
if (!chunk1.isDone())
{
ichunkprovider.getChunkAt(ichunkprovider1, i, j);
}
else
{
ichunkprovider.a(ichunkprovider1, chunk1, i, j);
}
}
net.minecraft.server.v1_8_R3.Chunk chunk;
if (flag3 && flag2 && flag6)
{
chunk = ichunkprovider.getChunkIfLoaded(i - 1, j);
if (chunk != null)
{
if (!chunk.isDone())
{
ichunkprovider.getChunkAt(ichunkprovider1, i - 1, j);
}
else
{
ichunkprovider.a(ichunkprovider1, chunk, i - 1, j);
}
}
}
if (flag && flag1 && flag7)
{
chunk = ichunkprovider.getChunkIfLoaded(i, j - 1);
if (chunk != null)
{
if (!chunk.isDone())
{
ichunkprovider.getChunkAt(ichunkprovider1, i, j - 1);
}
else
{
ichunkprovider.a(ichunkprovider1, chunk, i, j - 1);
}
}
}
if (flag4 && flag && flag3)
{
chunk = ichunkprovider.getChunkIfLoaded(i - 1, j - 1);
if (chunk != null)
{
if (!chunk.isDone())
{
ichunkprovider.getChunkAt(ichunkprovider1, i - 1, j - 1);
}
else
{
ichunkprovider.a(ichunkprovider1, chunk, i - 1, j - 1);
}
}
}
}
@EventHandler
public void on(EntitySpawnEvent event)
{
// Don't allow entity spawns while decorating, period
if (_isDecorating || !_allowSpawning)
{
if (event.getLocation().getWorld().getUID() == WorldData.World.getUID())
{
event.setCancelled(true);
}
}
}
private boolean areSpawnsGenerated()
{
return actual.get() == expected.get();
}
@EventHandler
@ -675,7 +1012,7 @@ public class UHC extends TeamGame
while (!stopGen)
{
long now = System.currentTimeMillis();
if ((now - last) >= 4000)
if ((now - last) >= 10 * 1000)
{
Announce(C.cGreen + C.Bold + "Generating Map: " + C.cWhite + getMapLoadETA() + " Remaining...", false);
last = now;
@ -745,6 +1082,170 @@ public class UHC extends TeamGame
}, "WorldGen Thread").start();
}
@EventHandler
public void on(ChunkUnloadEvent event)
{
if (IsLive())
return;
event.setCancelled(true);
}
private volatile boolean _isTeleporting = false;
@EventHandler
public void on(ChunkPreLoadEvent event)
{
if (_isTeleporting)
{
System.out.println("WARNING: TRIED TO LOAD CHUNK WHILE TELEPORTING: " + event.getX() + " " + event.getZ());
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void on(PlayerKickEvent event)
{
// Don't kick players while teleporting. Probably NCP trying to kick for fly or something
if (_isTeleporting)
{
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.LOW)
public void PlayerPrepare(GameStateChangeEvent event)
{
final Game game = event.GetGame();
if (event.GetState() != GameState.Prepare)
return;
_isTeleporting = true;
List<Player> players = game.GetPlayers(true);
Location zero = WorldData.World.getSpawnLocation();
for (Player player : players)
{
player.teleport(zero);
// Heal
player.setHealth(player.getMaxHealth());
// Resistance and regen
player.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, 30 * 20, 128), true);
player.addPotionEffect(new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 30 * 20, 128), true);
}
Announce(C.cGreen + C.Bold + "Please wait while you are teleported to your spawn", false);
_totalPlayers = players.size();
Map<UUID, Location> teleportedLocations = new HashMap<>();
AtomicInteger id = new AtomicInteger();
id.set(UtilServer.getServer().getScheduler().runTaskTimer(Manager.getPlugin(), () ->
{
_teleportedPlayers++;
if (_teleportedPlayers >= players.size())
{
Manager.runSyncLater(() ->
{
try
{
for (Player player : players)
{
GameTeam team = game.GetTeam(player);
if (team != null)
{
if (teleportedLocations.get(player.getUniqueId()) != null)
{
team.SpawnTeleport(player, teleportedLocations.get(player.getUniqueId()));
}
}
// Heal
player.setHealth(player.getMaxHealth());
// Resistance and regen
player.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, 30 * 20, 128), true);
player.addPotionEffect(new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 30 * 20, 128), true);
}
}
finally
{
game.AnnounceGame();
game.StartPrepareCountdown();
//Event
GamePrepareCountdownCommence gamePrepareCountdownCommence = new GamePrepareCountdownCommence(game);
UtilServer.getServer().getPluginManager().callEvent(gamePrepareCountdownCommence);
_isTeleporting = false;
Manager.runSyncLater(() ->
{
for (Player player : players)
{
player.removePotionEffect(PotionEffectType.DAMAGE_RESISTANCE);
player.removePotionEffect(PotionEffectType.REGENERATION);
}
}, 10 * 20L);
// Yes, right now they're both set to 10 seconds, but this may change in the future
Manager.runSyncLater(() ->
{
_allowSpawning = true;
}, 10 * 20L);
}
}, 3 * 20L);
Bukkit.getServer().getScheduler().cancelTask(id.get());
return;
}
Player player = players.get(_teleportedPlayers);
GameTeam team = game.GetTeam(player);
// This could happen if the player left (and rejoined) while teleporting
// Team maps based on player as a key
if (team != null)
{
// Save where they teleported
teleportedLocations.put(player.getUniqueId(), team.SpawnTeleport(player));
// Update scoreboard
_scoreObj.getScore(player).setScore((int) player.getMaxHealth());
game.addPlayerInTime(player);
Manager.Clear(player);
UtilInv.Clear(player);
// Heal
player.setHealth(player.getMaxHealth());
// Resistance and regen
player.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, 30 * 20, 128), true);
player.addPotionEffect(new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, 30 * 20, 128), true);
game.ValidateKit(player, game.GetTeam(player));
if (game.GetKit(player) != null)
game.GetKit(player).ApplyKit(player);
//Event
PlayerPrepareTeleportEvent playerStateEvent = new PlayerPrepareTeleportEvent(game, player);
UtilServer.getServer().getPluginManager().callEvent(playerStateEvent);
}
}, 4 * 20L, 4L).getTaskId());
//Spectators Move
for (Player player : UtilServer.getPlayers())
{
if (Manager.GetGame().IsAlive(player))
continue;
Manager.addSpectator(player, true);
}
}
@EventHandler
public void WorldBoundaryYLimit(BlockPlaceEvent event)
{
@ -758,10 +1259,14 @@ public class UHC extends TeamGame
public Location GetRandomSpawn(Location around)
{
// Sometimes getting a random spawn at 0,0 hangs forever
int tries = 0;
Location loc = null;
while (loc == null)
{
tries++;
Block block = null;
// Get Team Location
@ -776,8 +1281,8 @@ public class UHC extends TeamGame
// Get Radius Location
else
{
block = UtilBlock.getHighest(WorldData.World, around.getBlockX() - 4 + UtilMath.r(8), around.getBlockZ() - 4
+ UtilMath.r(8), null);
block = UtilBlock.getHighest(WorldData.World, around.getBlockX() - 4 + UtilMath.r(tries < 10 ? 8 : 30), around.getBlockZ() - 4
+ UtilMath.r(tries < 10 ? 8 : 30), null);
}
// Check Validity
@ -786,6 +1291,9 @@ public class UHC extends TeamGame
if (block.getRelative(BlockFace.DOWN).isLiquid())
continue;
if (block.getRelative(BlockFace.DOWN).getType() == Material.AIR)
continue;
// Suffocated
if (block.getType() != Material.AIR || block.getRelative(BlockFace.UP).getType() != Material.AIR)
continue;
@ -1273,8 +1781,18 @@ public class UHC extends TeamGame
}
Scoreboard.WriteBlank();
Scoreboard.Write(C.cYellow + C.Bold + "Time");
Scoreboard.Write(UtilTime.MakeStr(System.currentTimeMillis() - GetStateTime()));
if (GetState() == GameState.Prepare)
{
Scoreboard.Write(C.cYellow + C.Bold + "Status");
int players = _teleportedPlayers + 1;
if (players > _totalPlayers) players = _totalPlayers;
Scoreboard.Write("Teleporting Players (" + players + "/" + _totalPlayers + ")");
}
else
{
Scoreboard.Write(C.cYellow + C.Bold + "Time");
Scoreboard.Write(UtilTime.MakeStr(System.currentTimeMillis() - GetStateTime()));
}
Scoreboard.WriteBlank();
Scoreboard.Write(C.cYellow + C.Bold + "Borders");
@ -1584,8 +2102,8 @@ public class UHC extends TeamGame
if (visible)
break;
}
if(visible)
if (visible)
setOreType(vein);
// Remove Vein
@ -1609,15 +2127,15 @@ public class UHC extends TeamGame
}
}
}
public void setOreType(ArrayList<Block> blocks)
{
}
public boolean isMapLoaded()
{
return _mapLoaded;
return _mapLoaded && areSpawnsGenerated();
}
public String getMapLoadPercent()
@ -1625,6 +2143,23 @@ public class UHC extends TeamGame
return (int) (_mapLoadPercent * 100) + "%";
}
public String getSpawnGenPercent()
{
return UtilMath.clamp((int) ((actual.get() * 1.0 / expected.get()) * 100), 0, 100) + "%";
}
public String getObjectiveName(boolean _colorTick)
{
if (!getMapLoadPercent().equals("100%"))
{
return getMapLoadPercent() + " " + (_colorTick ? ChatColor.GREEN : ChatColor.YELLOW) + "§l" + "Generating Map";
}
else
{
return getSpawnGenPercent() + " " + (_colorTick ? ChatColor.GREEN : ChatColor.YELLOW) + "§l" + "Generating Spawns";
}
}
public String getMapLoadETA()
{
int chunksToGo = _chunkTotal - _chunksLoaded;
@ -1768,4 +2303,26 @@ public class UHC extends TeamGame
return null;
}
@Override
public String getHookName()
{
return "UHC Hook";
}
@Override
public String getHookVersion()
{
return "0.0.1";
}
@Override
public boolean onCheckFailure(CheckType checkType, Player player, IViolationInfo iViolationInfo)
{
if (GetState() == GameState.Prepare)
{
return false;
}
return true;
}
}

View File

@ -1048,7 +1048,7 @@ public class GameLobbyManager implements Listener
{
if (Manager.GetGame() instanceof UHC && !((UHC)Manager.GetGame()).isMapLoaded())
{
objective.setDisplayName(((UHC)Manager.GetGame()).getMapLoadPercent() + " " + (_colorTick ? ChatColor.GREEN : ChatColor.YELLOW) + "§l" + "Generating Map");
objective.setDisplayName(((UHC) Manager.GetGame()).getObjectiveName(_colorTick));
}
else
{

View File

@ -741,6 +741,10 @@ public class GameManager implements Listener
if (event.GetState() != GameState.Prepare)
return;
// Sir, I'll handle this.
if (game instanceof UHC)
return;
final ArrayList<Player> players = game.GetPlayers(true);
//Prepare Players

View File

@ -250,7 +250,7 @@ public class GameScoreboard
if (i >= _chars.length)
break;
String str = ChatColor.COLOR_CHAR + "" + _chars[i];
String str = ChatColor.COLOR_CHAR + "" + _chars[i] + C.Reset;
Score score = GetObjectiveSide().getScore(str);