From 1934debfd7d6f876e9ae97fe7ed9076d89cc17b0 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 3 Jun 2023 14:15:35 +0200 Subject: [PATCH] Prepare RLE Recordings Signed-off-by: Lixfel --- .../utils/FlatteningWrapper14.java | 6 +- .../fightsystem/utils/FlatteningWrapper8.java | 4 +- .../de/steamwar/fightsystem/FightSystem.java | 2 +- .../fightsystem/listener/PrepareSchem.java | 7 +- .../fightsystem/listener/Recording.java | 80 +++++++++++++++++-- .../fightsystem/record/FileSource.java | 4 +- .../fightsystem/record/PacketProcessor.java | 13 ++- .../fightsystem/utils/FlatteningWrapper.java | 3 +- 8 files changed, 99 insertions(+), 20 deletions(-) diff --git a/FightSystem_14/src/de/steamwar/fightsystem/utils/FlatteningWrapper14.java b/FightSystem_14/src/de/steamwar/fightsystem/utils/FlatteningWrapper14.java index 5fe98d8..f472f4d 100644 --- a/FightSystem_14/src/de/steamwar/fightsystem/utils/FlatteningWrapper14.java +++ b/FightSystem_14/src/de/steamwar/fightsystem/utils/FlatteningWrapper14.java @@ -85,7 +85,7 @@ public class FlatteningWrapper14 implements FlatteningWrapper { @Override public boolean doRecord(BlockPhysicsEvent e) { - return e.getBlock() == e.getSourceBlock() || e.getChangedType() == Material.AIR; + return e.getBlock() == e.getSourceBlock() || e.getChangedType() == Material.AIR; //TODO why air } @Override @@ -94,8 +94,8 @@ public class FlatteningWrapper14 implements FlatteningWrapper { } @Override - public boolean checkPistonMoving(Block block) { - return block.getType() == Material.MOVING_PISTON; + public Material getMovingPiston() { + return Material.MOVING_PISTON; } @Override diff --git a/FightSystem_8/src/de/steamwar/fightsystem/utils/FlatteningWrapper8.java b/FightSystem_8/src/de/steamwar/fightsystem/utils/FlatteningWrapper8.java index 7b645b8..51b9ceb 100644 --- a/FightSystem_8/src/de/steamwar/fightsystem/utils/FlatteningWrapper8.java +++ b/FightSystem_8/src/de/steamwar/fightsystem/utils/FlatteningWrapper8.java @@ -70,8 +70,8 @@ public class FlatteningWrapper8 implements FlatteningWrapper { } @Override - public boolean checkPistonMoving(Block block) { - return block.getType() == Material.PISTON_MOVING_PIECE; + public Material getMovingPiston() { + return Material.PISTON_MOVING_PIECE; } @Override diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java b/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java index e22f5bd..dbcd027 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java @@ -104,7 +104,7 @@ public class FightSystem extends JavaPlugin { techHider = new TechHiderWrapper(); new FightWorld(); new FightUI(); - new FightStatistics(); + //new FightStatistics(); new BungeeFightInfo(); new WinconditionAllDead(); diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/listener/PrepareSchem.java b/FightSystem_Core/src/de/steamwar/fightsystem/listener/PrepareSchem.java index 288eda7..9a318c2 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/listener/PrepareSchem.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/listener/PrepareSchem.java @@ -33,6 +33,7 @@ import de.steamwar.fightsystem.utils.Region; import de.steamwar.fightsystem.utils.WorldeditWrapper; import de.steamwar.sql.SchematicNode; import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -44,13 +45,15 @@ import java.util.Set; public class PrepareSchem implements Listener { + private static final Material MOVING_PISTON = FlatteningWrapper.impl.getMovingPiston(); + private final Set stationaryMovingPistons = new HashSet<>(); public PrepareSchem() { new OneShotStateDependent(ArenaMode.Prepare, FightState.PostSchemSetup, () -> Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> { stationaryMovingPistons.clear(); Fight.getUnrotated().getSchemRegion().forEach((x, y, z) -> { - if(FlatteningWrapper.impl.checkPistonMoving(Config.world.getBlockAt(x, y, z))) + if(Config.world.getBlockAt(x, y, z).getType() == MOVING_PISTON) stationaryMovingPistons.add(new Vector(x, y, z)); }); }, 1)); @@ -73,7 +76,7 @@ public class PrepareSchem implements Listener { try{ region.forEach((x, y, z) -> { - if(FlatteningWrapper.impl.checkPistonMoving(Config.world.getBlockAt(x, y, z)) && !stationaryMovingPistons.contains(new Vector(x, y, z))){ + if(Config.world.getBlockAt(x, y, z).getType() == MOVING_PISTON && !stationaryMovingPistons.contains(new Vector(x, y, z))){ FightSystem.getMessage().broadcast("PREPARE_ACTIVE_PISTON"); Bukkit.shutdown(); throw new IllegalStateException(); diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java b/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java index ca3c654..4264484 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java @@ -22,6 +22,7 @@ package de.steamwar.fightsystem.listener; import com.comphenix.tinyprotocol.Reflection; import com.comphenix.tinyprotocol.TinyProtocol; 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.FightPlayer; @@ -32,9 +33,11 @@ import de.steamwar.fightsystem.states.StateDependent; import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.fightsystem.states.StateDependentTask; import de.steamwar.fightsystem.utils.*; +import de.steamwar.techhider.BlockIds; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; @@ -48,7 +51,7 @@ import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.player.*; import org.bukkit.inventory.ItemStack; -import java.util.Random; +import java.util.*; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Predicate; @@ -75,6 +78,8 @@ public class Recording implements Listener { CraftbukkitWrapper.impl.entityIterator().filter(filter).map(entity -> (Entity) getBukkitEntity.invoke(entity)).forEach(consumer); } + private final HashMap> changedBlocks = new HashMap<>(); + public Recording() { new StateDependentListener(ArenaMode.AntiReplay, FightState.All, this); new StateDependentListener(ArenaMode.AntiReplay, FightState.All, BountifulWrapper.impl.newHandSwapRecorder()); @@ -88,6 +93,7 @@ public class Recording implements Listener { public void disable() { Fight.teams().forEach(Recording.this::despawnTeam); despawnTNT(); + sendChangedBlocks(); } }.register(); new StateDependent(ArenaMode.AntiReplay, FightState.Ingame) { @@ -107,18 +113,21 @@ public class Recording implements Listener { } }.register(); new StateDependentTask(ArenaMode.AntiReplay, FightState.All, () -> { - GlobalRecorder.getInstance().tick(); - if(FightState.getFightState() == FightState.SPECTATE || !GlobalRecorder.getInstance().recording()) return; + sendChangedBlocks(); iterateOverEntities(primedTnt::isInstance, this::trackEntity); - }, 1, 1); + + GlobalRecorder.getInstance().tick(); + }, 0, 1); } private void trackEntity(Entity entity) { - GlobalRecorder.getInstance().entityMoves(entity); - GlobalRecorder.getInstance().entitySpeed(entity); + if(entity.getVelocity().lengthSquared() != 0) { + GlobalRecorder.getInstance().entityMoves(entity); + GlobalRecorder.getInstance().entitySpeed(entity); + } } private static final Class blockDigPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInBlockDig"); @@ -163,10 +172,65 @@ public class Recording implements Listener { GlobalRecorder.getInstance().entityDespawns(e.getPlayer()); } + private static final Set movingPistonIds = BlockIds.impl.materialToAllIds(FlatteningWrapper.impl.getMovingPiston()); @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onBlockPhysics(BlockPhysicsEvent e){ - if(FlatteningWrapper.impl.doRecord(e)) - GlobalRecorder.getInstance().blockChange(e.getBlock()); + Block block = e.getBlock(); + if(!FlatteningWrapper.impl.doRecord(e) || !Config.PlayerRegion.inRegion(block)) + return; + + int blockId = BlockIdWrapper.impl.blockToId(block); + if(movingPistonIds.contains(blockId)) + return; + + changedBlocks.computeIfAbsent(blockId, blockId_ -> new TreeSet<>(Recording::compareBlocks)).add(block); + } + + private static int compareBlocks(Block b1, Block b2) { + //negative if first arg is less than + return b1.getY() - b2.getY(); //TODO + } + + private void sendChangedBlocks() { + HashMap> cache = new HashMap<>(); + changedBlocks.forEach((chunkId, blocks) -> { + if(blocks.isEmpty()) + return; + + //TODO filter blocks (outside of Y range) + blocks.forEach(block -> cache.computeIfAbsent(BlockIdWrapper.impl.blockToId(block), blockId -> new TreeSet<>()).add((short) ((block.getY() - Config.PlayerRegion.getMinY()) << 8 + (block.getZ() % 16) << 4 + (block.getX() % 16)))); + blocks.clear(); + + cache.forEach((blockId, positions) -> { + if(positions.isEmpty()) + return; + + if(movingPistonIds.contains(blockId)) { + positions.clear(); + return; + } + + if(positions.size() < 2) { + GlobalRecorder.getInstance().blockChange(); //TODO block + positions.clear(); + return; + } + }); + + blocks.forEach(GlobalRecorder.getInstance()::blockChange); + // byte chunkX << 4 + chunkZ & 0xF (ArenaRelative) + // short blockState + // VarInts: NoChange, ChangedBlocks interleaved until 256 height (65536 blocks) reached + // 7 byte none, 8-10 byte 1 block + // TODO use starting from 2 blocks? + //Run length encoding for block types (assuming 256 block height) + //VarInt RLE + //TODO analyze amount of int/large block states + // 181MB ShortBlockPackets, 30MB BlockPackets + + //TODO ChunkIndependent over whole arena (Arenarelative 10Mio Blocks) + //TODO RLE interleaved or RLE till Block + }); } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/record/FileSource.java b/FightSystem_Core/src/de/steamwar/fightsystem/record/FileSource.java index 2fcaa59..010afe4 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/record/FileSource.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/record/FileSource.java @@ -20,7 +20,6 @@ package de.steamwar.fightsystem.record; import de.steamwar.fightsystem.Config; -import de.steamwar.sql.Replay; import java.io.File; import java.io.FileInputStream; @@ -41,7 +40,8 @@ public class FileSource extends PacketSource { if(Config.ReplayID > 0) { try { - new FileSource(Replay.get(Config.ReplayID).getReplay()); + //new FileSource(Replay.get(Config.ReplayID).getReplay()); + new FileSource(new File("/home/lixfel/GRMAHeXa.recording")); } catch (IOException e) { throw new SecurityException("Could not start replay", e); } diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java b/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java index d7c31fd..22a76e3 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java @@ -626,10 +626,12 @@ public class PacketProcessor implements Listener { tickFinished = true; } + private final int[] stats = new int[256]; private void process(){ tickFinished = false; + int ticks = 0; try{ - while(!source.isClosed() && !tickFinished){ + while(!source.isClosed() && ticks < 30){ int packetType = Byte.toUnsignedInt(source.readByte()); lastPackets.add(packetType); @@ -637,8 +639,13 @@ public class PacketProcessor implements Listener { lastPackets.remove(0); PacketParser parser = packetDecoder[packetType]; + stats[packetType]++; if(parser != null){ parser.process(); + if(tickFinished) { + ticks++; + tickFinished = false; + } }else{ Bukkit.getLogger().log(Level.SEVERE, "Unknown packet " + packetType + " recieved, closing. LastPacket: " + Arrays.toString(lastPackets.toArray())); source.close(); @@ -654,6 +661,10 @@ public class PacketProcessor implements Listener { if(source.isClosed()){ execSync(() -> Bukkit.getScheduler().runTask(FightSystem.getPlugin(), this::endReplay)); + for(int i = 0; i < 256; i++) { + if(stats[i] != 0) + System.out.println(Integer.toHexString(i) + ": " + stats[i]); + } } } diff --git a/FightSystem_Core/src/de/steamwar/fightsystem/utils/FlatteningWrapper.java b/FightSystem_Core/src/de/steamwar/fightsystem/utils/FlatteningWrapper.java index 4f76656..51820d5 100644 --- a/FightSystem_Core/src/de/steamwar/fightsystem/utils/FlatteningWrapper.java +++ b/FightSystem_Core/src/de/steamwar/fightsystem/utils/FlatteningWrapper.java @@ -22,6 +22,7 @@ package de.steamwar.fightsystem.utils; import de.steamwar.core.VersionDependent; import de.steamwar.fightsystem.FightSystem; import org.bukkit.DyeColor; +import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Player; @@ -44,7 +45,7 @@ public interface FlatteningWrapper { void forceLoadChunk(World world, int cX, int cZ); - boolean checkPistonMoving(Block block); + Material getMovingPiston(); boolean isFacingWater(Block dispenser);