diff --git a/FightSystem_API/src/de/steamwar/fightsystem/Config.java b/FightSystem_API/src/de/steamwar/fightsystem/Config.java index ce965e9..cadb5c3 100644 --- a/FightSystem_API/src/de/steamwar/fightsystem/Config.java +++ b/FightSystem_API/src/de/steamwar/fightsystem/Config.java @@ -128,7 +128,6 @@ public class Config { //live recorder parameter public static final String spectateIP = "127.0.0.1"; public static final int spectatePort = 2222; - public static final boolean recording; static{ File worldConfigFile = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "config.yml"); @@ -380,8 +379,6 @@ public class Config { CheckSchemID = Integer.parseInt(System.getProperty("checkSchemID", "0")); Ranked = Boolean.parseBoolean(System.getProperty("ranked", "false")); - - recording = event(); } public static boolean event(){ @@ -393,4 +390,7 @@ public class Config { public static boolean check(){ return CheckSchemID != 0; } + public static boolean recording(){ + return event(); + } } diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/FightSystem.java b/FightSystem_Main/src/de/steamwar/fightsystem/FightSystem.java index 766e05a..5a6dec8 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/FightSystem.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/FightSystem.java @@ -10,6 +10,7 @@ import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.fightsystem.kit.KitManager; import de.steamwar.fightsystem.listener.*; import de.steamwar.fightsystem.record.RecordSystem; +import de.steamwar.fightsystem.record.Recorder; import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.StateDependent; import de.steamwar.fightsystem.utils.*; @@ -128,6 +129,12 @@ public class FightSystem extends JavaPlugin { } } + @Override + public void onDisable() { + + Recorder.closeAll(); + } + public static void setPreSchemState() { if(fightState != FightState.PRE_LEADER_SETUP) throw new SecurityException(fightState.name()); diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/countdown/Countdown.java b/FightSystem_Main/src/de/steamwar/fightsystem/countdown/Countdown.java index d330b6c..e8766fd 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/countdown/Countdown.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/countdown/Countdown.java @@ -1,9 +1,11 @@ package de.steamwar.fightsystem.countdown; import de.steamwar.core.Core; +import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.fight.Fight; import de.steamwar.fightsystem.listener.BasicListener; +import de.steamwar.fightsystem.record.RecordSystem; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; @@ -54,6 +56,8 @@ public abstract class Countdown { } private void broadcast(String message){ + if(Config.recording()) + RecordSystem.actionBar(message); BaseComponent[] msg = TextComponent.fromLegacyText(message); for(Player p : Bukkit.getOnlinePlayers()) BasicListener.toActionbar(p, msg); diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/listener/EventRecordListener.java b/FightSystem_Main/src/de/steamwar/fightsystem/listener/EventRecordListener.java index 9b52a0f..db21a2a 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/listener/EventRecordListener.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/listener/EventRecordListener.java @@ -1,35 +1,40 @@ package de.steamwar.fightsystem.listener; import de.steamwar.fightsystem.Config; +import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.fight.Fight; import de.steamwar.fightsystem.fight.FightPlayer; +import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.fightsystem.record.RecordSystem; import de.steamwar.fightsystem.states.FightState; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntitySpawnEvent; import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.*; +import org.bukkit.event.server.BroadcastMessageEvent; import java.util.EnumSet; public class EventRecordListener extends BasicListener { + private static final int AIR = 0; + public EventRecordListener() { super(Config.event() ? EnumSet.allOf(FightState.class) : EnumSet.noneOf(FightState.class)); } - @Override - public void enable() { - RecordSystem.init(); - super.enable(); - } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onPlayerJoin(PlayerJoinEvent e){ - FightPlayer fp = Fight.getFightPlayer(e.getPlayer()); - if(fp == null || !fp.isLiving()) + if(isNotSent(e.getPlayer())) return; RecordSystem.playerJoins(e.getPlayer()); @@ -37,21 +42,84 @@ public class EventRecordListener extends BasicListener { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onPlayerMove(PlayerMoveEvent e){ - FightPlayer fp = Fight.getFightPlayer(e.getPlayer()); - if(fp == null || !fp.isLiving()) + if(isNotSent(e.getPlayer())) return; RecordSystem.entityMoves(e.getPlayer()); } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPlayerDeath(PlayerDeathEvent e){ - FightPlayer fp = Fight.getFightPlayer(e.getEntity()); - if(fp == null || fp.isLiving()) + if(isNotSent(e.getEntity())) return; RecordSystem.entityDespawns(e.getEntity()); } - //TODO: Listener, if player gets out (leaves, dies or starts to spectate), alternatively: track sent players + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBroadcast(BroadcastMessageEvent e){ + RecordSystem.systemChat(e.getMessage()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onSneak(PlayerToggleSneakEvent e){ + if(isNotSent(e.getPlayer())) + return; + + RecordSystem.playerSneak(e.getPlayer(), e.isSneaking()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onAnimation(PlayerAnimationEvent e){ + if(isNotSent(e.getPlayer())) + return; + + if(e.getAnimationType() == PlayerAnimationType.ARM_SWING) + RecordSystem.entityAnimation(e.getPlayer(), AIR); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onTNTSpawn(EntitySpawnEvent e){ + //TODO: Falling block + if(e.getEntityType() != EntityType.PRIMED_TNT) + return; + + RecordSystem.tntSpawn(e.getEntity()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onBlockPhysics(BlockPhysicsEvent e){ + RecordSystem.blockChange(e.getBlock()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onExplosion(EntityExplodeEvent e){ + if(e.getEntityType() != EntityType.PRIMED_TNT) + return; + + Location loc = e.getLocation(); + RecordSystem.entityDespawns(e.getEntity()); + RecordSystem.particle(loc.getX(), loc.getY(), loc.getZ(), Particle.EXPLOSION_LARGE.name()); + RecordSystem.sound(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), Sound.ENTITY_GENERIC_EXPLODE.name(), SoundCategory.BLOCKS.name(), 255.0f, 1.0f); + } + + @Override + public void stateChange(FightState state) { + if(state == FightState.SPECTATE){ + despawnTeam(Fight.getRedTeam()); + despawnTeam(Fight.getBlueTeam()); + } + } + + private void despawnTeam(FightTeam team){ + for(FightPlayer player : team.getPlayers()){ + if(player.isLiving()) + RecordSystem.entityDespawns(player.getPlayer()); + } + } + + private boolean isNotSent(Player p){ + FightPlayer fp = Fight.getFightPlayer(p); + return fp == null || !fp.isLiving() || FightSystem.getFightState() == FightState.SPECTATE; + } } diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/listener/PlayerChatListener.java b/FightSystem_Main/src/de/steamwar/fightsystem/listener/PlayerChatListener.java index 3688609..debdf3f 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/listener/PlayerChatListener.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/listener/PlayerChatListener.java @@ -44,7 +44,7 @@ public class PlayerChatListener extends BasicListener { } private void broadcastChat(String message) { - if (Config.recording) + if (Config.recording()) RecordSystem.chat(message); BaseComponent[] msg = TextComponent.fromLegacyText(message); for(Player p : Bukkit.getOnlinePlayers()) diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/record/FileRecorder.java b/FightSystem_Main/src/de/steamwar/fightsystem/record/FileRecorder.java new file mode 100644 index 0000000..182a2bd --- /dev/null +++ b/FightSystem_Main/src/de/steamwar/fightsystem/record/FileRecorder.java @@ -0,0 +1,126 @@ +package de.steamwar.fightsystem.record; + +import de.steamwar.fightsystem.FightSystem; +import org.bukkit.Bukkit; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.logging.Level; +import java.util.zip.GZIPOutputStream; + +public class FileRecorder extends Recorder { + + private final DataOutputStream outputStream; + + public FileRecorder(){ + super(); + File file = new File(FightSystem.getPlugin().getDataFolder(), "recording"); + try{ + file.createNewFile(); + outputStream = new DataOutputStream(new GZIPOutputStream(new FileOutputStream(file))); + }catch(IOException e){ + throw new SecurityException("Could not open file", e); + } + } + + @Override + protected void writeBoolean(boolean b) { + try { + outputStream.writeBoolean(b); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "Could not write", e); + close(); + } + } + + @Override + protected void writeByte(int b) { + try { + outputStream.writeByte(b); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "Could not write", e); + close(); + } + } + + @Override + protected void writeShort(short s) { + try { + outputStream.writeShort(s); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "Could not write", e); + close(); + } + } + + @Override + protected void writeInt(int i) { + try { + outputStream.writeInt(i); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "Could not write", e); + close(); + } + } + + @Override + protected void writeLong(long l) { + try { + outputStream.writeLong(l); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "Could not write", e); + close(); + } + } + + @Override + protected void writeFloat(float f) { + try { + outputStream.writeFloat(f); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "Could not write", e); + close(); + } + } + + @Override + protected void writeDouble(double d) { + try { + outputStream.writeDouble(d); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "Could not write", e); + close(); + } + } + + @Override + protected void writeString(String s) { + try { + outputStream.writeUTF(s); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "Could not write", e); + close(); + } + } + + @Override + protected void doFlush() { + try { + outputStream.flush(); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "Could not flush", e); + close(); + } + } + + @Override + protected void closeRecorder() { + try { + outputStream.close(); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "Could not close OutputStream", e); + } + } +} diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/record/RecordSystem.java b/FightSystem_Main/src/de/steamwar/fightsystem/record/RecordSystem.java index e3e066a..f2f14ee 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/record/RecordSystem.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/record/RecordSystem.java @@ -3,41 +3,57 @@ package de.steamwar.fightsystem.record; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.FightSystem; import de.steamwar.sql.SteamwarUser; +import net.minecraft.server.v1_15_R1.BlockPosition; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v1_15_R1.block.CraftBlock; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.util.Vector; public class RecordSystem { + private RecordSystem(){} + + private static final World WORLD = Bukkit.getWorlds().get(0); public static void init(){ - if(!Config.event()) + if(!Config.recording()) return; Bukkit.getScheduler().runTaskTimer(FightSystem.getPlugin(), RecordSystem::checkWorldState, 1, 1); new SpectateConnection(); + new FileRecorder(); } /* * PlayerJoinPacket (0x00) + int EntityId + int SWUserId - * EntityMovePacket (0x01) + int EntityId + double x, y, z + float pitch, yaw + * EntityMovePacket (0x01) + int EntityId + double x, y, z + float pitch, yaw + byte headyaw * EntityDespawnsPacket (0x02) + int EntityId + * PlayerSneakPacket (0x03) + int EntityId + boolean sneaks + * EntityAnimationPacket (0x04) + int EntityId + byte animation + * TNTSpawnPacket (0x05) + int EntityId + * EntitySpeedPacket (0x06) + int EntityId + double dx, dy, dz * * TODO (Player-Oriented): * ItemInHandPacket (0x03) + int EntityId - * LeftClickPacket (0x04) + int EntityId - * RightClickPacket (0x05) + int EntityId TODO Bow spanning - * - * TODO: Block Change Recordings - * - * - * + * TODO Bow spanning * * * + * BlockPacket (0x30) + pos int, byte, int + int BlockState + * ParticlePacket (0x31) + double x, y, z + string particleType + * SoundPacket (0x32) + int x, y, z + string soundType + string soundType + float volume, pitch * * + * ChatPacket (0xa0) + String message + * ActionBarPacket (0xa1) + String message + * SystemPacket (0xa2) + String message * + * TickPacket (0xff) * */ public static synchronized void playerJoins(Player p){ @@ -59,6 +75,7 @@ public class RecordSystem { Recorder.rDouble(location.getZ()); Recorder.rFloat(location.getPitch()); Recorder.rFloat(location.getYaw()); + Recorder.rByte((int)(((CraftEntity)e).getHandle().getHeadRotation() * 256 / 360)); Recorder.flush(); } @@ -68,25 +85,99 @@ public class RecordSystem { Recorder.flush(); } + public static synchronized void playerSneak(Player p, boolean sneaks){ + Recorder.rByte(0x03); + Recorder.rInt(p.getEntityId()); + Recorder.rBoolean(sneaks); + Recorder.flush(); + } + + public static synchronized void entityAnimation(Entity e, int animation){ + Recorder.rByte(0x04); + Recorder.rInt(e.getEntityId()); + Recorder.rByte(animation); + Recorder.flush(); + } + + public static synchronized void tntSpawn(Entity e){ + Recorder.rByte(0x05); + Recorder.rInt(e.getEntityId()); + entityMoves(e); + entitySpeed(e); + } + + public static synchronized void entitySpeed(Entity e){ + Vector velocity = e.getVelocity(); + Recorder.rByte(0x06); + Recorder.rInt(e.getEntityId()); + Recorder.rDouble(velocity.getX()); + Recorder.rDouble(velocity.getY()); + Recorder.rDouble(velocity.getZ()); + Recorder.flush(); + } + + public static synchronized void blockChange(BlockPosition pos, int blockState){ + Recorder.rByte(0x30); + Recorder.rInt(pos.getX()); + Recorder.rByte(pos.getY()); + Recorder.rInt(pos.getZ()); + Recorder.rInt(blockState); + Recorder.flush(); + } + + public static synchronized void particle(double x, double y, double z, String particleType){ + Recorder.rByte(0x31); + Recorder.rDouble(x); + Recorder.rDouble(y); + Recorder.rDouble(z); + Recorder.rString(particleType); + Recorder.flush(); + } + + public static synchronized void sound(int x, int y, int z, String soundType, String soundCategory, float volume, float pitch){ + Recorder.rByte(0x32); + Recorder.rInt(x); + Recorder.rInt(y); + Recorder.rInt(z); + Recorder.rString(soundType); + Recorder.rString(soundCategory); + Recorder.rFloat(volume); + Recorder.rFloat(pitch); + Recorder.flush(); + } + public static synchronized void chat(String s) { - Recorder.rByte(0xA0); + Recorder.rByte(0xa0); Recorder.rString(s); Recorder.flush(); } public static synchronized void actionBar(String s) { - Recorder.rByte(0xA1); + Recorder.rByte(0xa1); Recorder.rString(s); Recorder.flush(); } public static synchronized void systemChat(String s) { - Recorder.rByte(0xA2); + Recorder.rByte(0xa2); Recorder.rString(s); Recorder.flush(); } + public static synchronized void tick(){ + Recorder.rByte(0xff); + Recorder.flush(); + } + + public static synchronized void blockChange(Block block){ + blockChange(((CraftBlock)block).getPosition(), net.minecraft.server.v1_15_R1.Block.REGISTRY_ID.getId(((CraftBlock)block).getNMS())); + } + private static void checkWorldState(){ - //TODO: Entity position transmissions + tick(); + for(TNTPrimed tnt : WORLD.getEntitiesByClass(TNTPrimed.class)){ + entityMoves(tnt); + entitySpeed(tnt); + } } } diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/record/Recorder.java b/FightSystem_Main/src/de/steamwar/fightsystem/record/Recorder.java index 998e143..5072b9d 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/record/Recorder.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/record/Recorder.java @@ -7,6 +7,10 @@ public abstract class Recorder { private static List recorders = new ArrayList<>(); + public static void rBoolean(boolean b){ + recorders.forEach((recorder) -> recorder.writeBoolean(b)); + } + public static void rByte(int b){ recorders.forEach((recorder) -> recorder.writeByte(b)); } @@ -52,6 +56,7 @@ public abstract class Recorder { recorders.remove(this); } + protected abstract void writeBoolean(boolean b); protected abstract void writeByte(int b); protected abstract void writeShort(short s); protected abstract void writeInt(int i); diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/record/SpectateConnection.java b/FightSystem_Main/src/de/steamwar/fightsystem/record/SpectateConnection.java index 092f0ce..df1d7ea 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/record/SpectateConnection.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/record/SpectateConnection.java @@ -24,6 +24,16 @@ public class SpectateConnection extends Recorder{ } } + @Override + protected void writeBoolean(boolean b) { + try{ + outputStream.writeBoolean(b); + } catch (IOException e) { + close(); + Bukkit.getLogger().log(Level.SEVERE, "Could not send", e); + } + } + @Override protected void writeByte(int b) { try{