From 4d5f4e9be6c1e411551d1390ac44359a411df7cc Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 16 Dec 2023 21:59:56 +0100 Subject: [PATCH] =?UTF-8?q?Fast=20recursive=2045=C2=B0=20approx=20hullhide?= =?UTF-8?q?r=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lixfel --- .../de/steamwar/fightsystem/FightSystem.java | 7 +- .../fightsystem/fight/FightSchematic.java | 2 +- .../steamwar/fightsystem/utils/HullHider.java | 217 +++++++++--------- 3 files changed, 111 insertions(+), 115 deletions(-) diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java b/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java index fcbafbc..48fe546 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java @@ -54,6 +54,7 @@ public class FightSystem extends JavaPlugin { private FightTeam lastWinner; private String lastWinreason; private TechHiderWrapper techHider; + private HullHider hullHider; @Override public void onLoad() { @@ -108,7 +109,7 @@ public class FightSystem extends JavaPlugin { new EnterHandler(); techHider = new TechHiderWrapper(); - new HullHider(); + hullHider = new HullHider(); new FightWorld(); new FightUI(); new FightStatistics(); @@ -219,6 +220,10 @@ public class FightSystem extends JavaPlugin { return plugin.techHider; } + public static HullHider getHullHider() { + return plugin.hullHider; + } + public static void shutdown() { //Staggered kick to prevent lobby overloading if(Bukkit.getOnlinePlayers().isEmpty()){ diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java b/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java index ee1ab0a..e22c50c 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java @@ -45,7 +45,6 @@ import java.io.IOException; import java.util.List; import java.util.Random; import java.util.logging.Level; -import java.util.stream.Stream; public class FightSchematic extends StateDependent { @@ -149,6 +148,7 @@ public class FightSchematic extends StateDependent { ).add(new Vector(rotate ? 1 : 0, 0, rotate ? 1 : 0)), new AffineTransform().rotateY(rotate ? 180 : 0) ); + FightSystem.getHullHider().initialize(team); Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), freezer::disable, 3); Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), team::teleportToSpawn, 40); diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java b/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java index 08ae9c7..775c34d 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java @@ -19,16 +19,13 @@ package de.steamwar.fightsystem.utils; -import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.fight.Fight; import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.fightsystem.states.FightState; -import de.steamwar.fightsystem.states.OneShotStateDependent; import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.techhider.BlockIds; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.event.EventHandler; @@ -38,11 +35,11 @@ import org.bukkit.event.block.BlockPhysicsEvent; import java.util.*; import java.util.function.BiConsumer; +import java.util.logging.Level; public class HullHider implements Listener { - private final Hull blue = new Hull(Fight.getBlueTeam()); - private final Hull red = new Hull(Fight.getRedTeam()); + private final Map hulls = new HashMap<>(); //SpawnPackets: PacketPlayOutSpawnEntity, PacketPlayOutSpawnEntityWeather, PacketPlayOutSpawnEntityLiving, PacketPlayOutSpawnEntityPainting, PacketPlayOutSpawnEntityPlayer //One-timePackets: PacketPlayOutEntityAnimation, PacketPlayOutBlockBreakAnimation, PacketPlayOutEntityStatus, PacketPlayOutEntityPosition, PacketPlayOutEntityPositionAndRotation, PacketPlayOutEntityRotation, PacketPlayOutEntityMovement, EntityHeadLook, EntitySoundEffect, CollectItem, EntityTeleport, @@ -50,41 +47,40 @@ public class HullHider implements Listener { //Other: Effect, Particle, Explosion //Death: DestroyEntities public HullHider() { + if(TechHiderWrapper.ENABLED) + Fight.teams().forEach(team -> hulls.put(team, new Hull(team))); + + new StateDependentListener(TechHiderWrapper.ENABLED, FightState.Schem, this); //TODO player enters/leaves team - new OneShotStateDependent(ArenaMode.AntiTest, FightState.Schem, () -> Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> { - blue.newSchem(); - red.newSchem(); - }, 1)); //TODO better paste timer, SYNC PASTE - new StateDependentListener(ArenaMode.AntiTest, FightState.Schem, this); + } + + public void initialize(FightTeam team) { + if(!TechHiderWrapper.ENABLED) + return; + + hulls.get(team).initialize(); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onBlockPhysic(BlockPhysicsEvent e) { - Block b = e.getBlock(); - //TODO adapt to 1.8 etc., Look out for Occluding->NotOccluding change - if(e.getSourceBlock() == b && b.getType().isOccluding() && !e.getChangedType().isOccluding()) { - if(blue.region.inRegion(b)) { - blue.updateBlockVisibility(new IntVector(b.getX(), b.getY(), b.getZ())); - blue.printDebug(b.getX(), b.getY(), b.getZ()); - } - if(red.region.inRegion(b)) { - red.updateBlockVisibility(new IntVector(b.getX(), b.getY(), b.getZ())); - red.printDebug(b.getX(), b.getY(), b.getZ()); - } + if(FlatteningWrapper.impl.doRecord(e)) { + hulls.values().forEach(hull -> hull.updateBlockVisibility(e.getBlock(), e.getChangedType())); } } private static class Hull { private final Region region; + private final boolean groundVisible; + private final BitSet occluding; private final BitSet visibility; private final Map> blockVisibility = new HashMap<>(); - private final boolean groundVisible; public Hull(FightTeam team) { this.region = team.getSchemRegion(); this.groundVisible = region.getMinY() != Config.PlayerRegion.getMinY(); + this.occluding = new BitSet(region.volume()); this.visibility = new BitSet(region.volume()); IntVector[] directions; @@ -107,6 +103,7 @@ public class HullHider implements Listener { }; } + // Generate quadrants for each direction for(IntVector direction : directions) { Map map = new HashMap<>(); for(int z = (direction.z == 0 ? -1 : 0); z <= 1; z+=2) { @@ -120,16 +117,62 @@ public class HullHider implements Listener { } } + public void initialize() { + visibility.clear(); + occluding.clear(); + for(Map direction : blockVisibility.values()) { + for(BitSet set : direction.values()) + set.clear(); + } + + long start = System.currentTimeMillis(); + region.forEach((x, y, z) -> { + IntVector block = new IntVector(x, y, z); + if(Config.world.getBlockAt(x, y, z).getType().isOccluding()) //TODO more accurate check + occluding.set(block.toId(region)); + }); + forEachBorder((root, direction) -> { + for(Map.Entry quadrant : blockVisibility.get(direction).entrySet()) { + checkBlock(root, direction, quadrant.getKey(), quadrant.getValue()); + } + }); + FightSystem.getPlugin().getLogger().log(Level.INFO, () -> "[HullHider] initialisation finished: " + (System.currentTimeMillis() - start) + " ms, visible blocks: " + visibility.stream().count()); + + region.forEach(this::printDebug); + } + + public void updateBlockVisibility(Block b, Material changedType) { + IntVector root = new IntVector(b.getX(), b.getY(), b.getZ()); + if(root.notInRegion(region)) + return; + + int id = root.toId(region); + if(!occluding.get(id) || changedType.isOccluding()) //TODO more accurate check + return; + + occluding.clear(id); + for(Map.Entry> direction : blockVisibility.entrySet()) { + for(Map.Entry quadrant : direction.getValue().entrySet()) { + if(quadrant.getValue().get(id)) { + quadrant.getValue().clear(id); + checkBlock(root, direction.getKey(), quadrant.getKey(), quadrant.getValue()); + } + } + } + + printDebug(root.x, root.y, root.z); + } + private final int air = BlockIds.impl.materialToId(Material.AIR); private final int stone = BlockIds.impl.materialToId(Material.STONE); private final int red = BlockIds.impl.materialToId(Material.RED_CONCRETE); - public void printDebug(int x, int y, int z) { - int id = coordToId(x, y, z); + private void printDebug(int x, int y, int z) { + int id = new IntVector(x, y, z).toId(region); - BlockIdWrapper.impl.setBlock(Config.world, x, y + Config.BlueExtendRegion.getSizeY(), z, visibility.get(id) ? (new IntVector(x, y, z).isOccluding() ? red : air) : stone); + BlockIdWrapper.impl.setBlock(Config.world, x, y + Config.BlueExtendRegion.getSizeY(), z, visibility.get(id) ? (occluding.get(id) ? red : air) : stone); } - public void forEachBorder(BiConsumer f) { + private void forEachBorder(BiConsumer f) { for(int x = region.getMinX(); x < region.getMaxX(); x++) { for(int z = region.getMinZ(); z < region.getMaxZ(); z++) { if(groundVisible) @@ -153,92 +196,44 @@ public class HullHider implements Listener { } } - public void newSchem() { - visibility.clear(); - for(Map direction : blockVisibility.values()) { - for(BitSet set : direction.values()) - set.clear(); - } - - long start = System.currentTimeMillis(); - forEachBorder(this::sideInit); - System.out.println("finish " + (System.currentTimeMillis() - start) + " ms " + visibility.stream().count()); - - region.forEach(this::printDebug); - } - - private void sideInit(IntVector root, IntVector direction) { - for(Map.Entry quadrant : blockVisibility.get(direction).entrySet()) { - checkBlock(root, direction, quadrant.getKey(), quadrant.getValue()); - } - } - - private void checkBlock(IntVector root, IntVector direction, IntVector quadrant, BitSet quadVisibility) { - Set checkSet = new HashSet<>(); - checkSet.add(root); - - while(!checkSet.isEmpty()) { - Set nextSet = new HashSet<>(); - - for(IntVector block : checkSet) { - if(!block.inRegion(region)) - continue; - - int id = block.toId(region); - if(quadVisibility.get(id)) - continue; - - quadVisibility.set(id); - visibility.set(id); - if(block.isOccluding()) - continue; - - IntVector neighbour = block.add(direction); - nextSet.add(neighbour); - boolean neigbourTransparent = !neighbour.isOccluding(); - if(direction.x == 0 && (neigbourTransparent || !block.add(quadrant.x, 0, 0).isOccluding())) { - nextSet.add(neighbour.add(quadrant.x, 0, 0)); - if(!neighbour.add(quadrant.x, 0, 0).isOccluding()) - nextSet.add(neighbour.add(quadrant)); - } - - if(direction.y == 0 && (neigbourTransparent || !block.add(0, quadrant.y, 0).isOccluding())){ - nextSet.add(neighbour.add(0, quadrant.y, 0)); - if(!neighbour.add(0, quadrant.y, 0).isOccluding()) - nextSet.add(neighbour.add(quadrant)); - } - - if(direction.z == 0 && (neigbourTransparent || !block.add(0, 0, quadrant.z).isOccluding())) { - nextSet.add(neighbour.add(0, 0, quadrant.z)); - if(!neighbour.add(0, 0, quadrant.z).isOccluding()) - nextSet.add(neighbour.add(quadrant)); - } - } - - checkSet = nextSet; - } - } - - private void updateBlockVisibility(IntVector root) { - if(!root.inRegion(region)) + private void checkBlock(IntVector block, IntVector direction, IntVector quadrant, BitSet quadVisibility) { + if(block.notInRegion(region)) return; - int id = root.toId(region); - for(Map.Entry> direction : blockVisibility.entrySet()) { - for(Map.Entry quadrant : direction.getValue().entrySet()) { - if(quadrant.getValue().get(id)) { - quadrant.getValue().clear(id); - checkBlock(root, direction.getKey(), quadrant.getKey(), quadrant.getValue()); - } - } + int id = block.toId(region); + if(quadVisibility.get(id)) + return; + + quadVisibility.set(id); + visibility.set(id); + if(occluding.get(id)) + return; + + IntVector neighbour = block.add(direction); + checkBlock(neighbour, direction, quadrant, quadVisibility); + boolean neigbourTransparent = boundedNonOccluding(neighbour); + boolean diagonalReachable = false; + if(direction.x == 0 && (neigbourTransparent || boundedNonOccluding(block.add(quadrant.x, 0, 0)))) { + checkBlock(neighbour.add(quadrant.x, 0, 0), direction, quadrant, quadVisibility); + diagonalReachable = boundedNonOccluding(neighbour.add(quadrant.x, 0, 0)); } + + if(direction.y == 0 && (neigbourTransparent || boundedNonOccluding(block.add(0, quadrant.y, 0)))) { + checkBlock(neighbour.add(0, quadrant.y, 0), direction, quadrant, quadVisibility); + diagonalReachable = diagonalReachable || boundedNonOccluding(neighbour.add(0, quadrant.y, 0)); + } + + if(direction.z == 0 && (neigbourTransparent || boundedNonOccluding(block.add(0, 0, quadrant.z)))) { + checkBlock(neighbour.add(0, 0, quadrant.z), direction, quadrant, quadVisibility); + diagonalReachable = diagonalReachable || boundedNonOccluding(neighbour.add(0, 0, quadrant.z)); + } + + if(diagonalReachable) + checkBlock(neighbour.add(quadrant), direction, quadrant, quadVisibility); } - private int coordToId(int x, int y, int z) { - return regionCoordToId(x - region.getMinX(), y - region.getMinY(), z - region.getMinZ()); - } - private int regionCoordToId(int x, int y, int z) { - return (y * region.getSizeZ() + z) * region.getSizeX() + x; + private boolean boundedNonOccluding(IntVector block) { + return !(block.notInRegion(region) || occluding.get(block.toId(region))); } } @@ -253,18 +248,14 @@ public class HullHider implements Listener { this.z = z; } - public boolean inRegion(Region region) { - return region.inRegion(x, y, z); + public boolean notInRegion(Region region) { + return !region.inRegion(x, y, z); } public int toId(Region region) { return ((y - region.getMinY()) * region.getSizeZ() + (z - region.getMinZ())) * region.getSizeX() + (x - region.getMinX()); } - public boolean isOccluding() { - return Config.world.getBlockAt(x, y, z).getType().isOccluding(); //TODO techhiderblocks? - } - public IntVector add(int x, int y, int z) { return new IntVector(this.x + x, this.y + y, this.z + z); }