diff --git a/FightSystem_15/src/de/steamwar/fightsystem/record/RecordSystem_15.java b/FightSystem_15/src/de/steamwar/fightsystem/record/RecordSystem_15.java new file mode 100644 index 0000000..6d1a1e3 --- /dev/null +++ b/FightSystem_15/src/de/steamwar/fightsystem/record/RecordSystem_15.java @@ -0,0 +1,31 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.fightsystem.record; + +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v1_15_R1.block.CraftBlock; + +class RecordSystem_15 { + private RecordSystem_15(){} + + static int blockToId(Block block){ + return net.minecraft.server.v1_15_R1.Block.REGISTRY_ID.getId(((CraftBlock)block).getNMS()); + } +} diff --git a/FightSystem_8/src/de/steamwar/fightsystem/record/RecordSystem_8.java b/FightSystem_8/src/de/steamwar/fightsystem/record/RecordSystem_8.java new file mode 100644 index 0000000..207a3fd --- /dev/null +++ b/FightSystem_8/src/de/steamwar/fightsystem/record/RecordSystem_8.java @@ -0,0 +1,31 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.fightsystem.record; + +import org.bukkit.block.Block; + +class RecordSystem_8 { + private RecordSystem_8(){} + + @SuppressWarnings("deprecation") + static int blockToId(Block block){ + return block.getTypeId() << 4 + block.getData(); + } +} diff --git a/FightSystem_API/src/de/steamwar/fightsystem/Config.java b/FightSystem_API/src/de/steamwar/fightsystem/Config.java index 5510486..1469700 100644 --- a/FightSystem_API/src/de/steamwar/fightsystem/Config.java +++ b/FightSystem_API/src/de/steamwar/fightsystem/Config.java @@ -145,6 +145,10 @@ public class Config { //check parameter public static final int CheckSchemID; + //live recorder parameter + public static final String spectateIP = "127.0.0.1"; + public static final int spectatePort = 2222; + static{ File worldConfigFile = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "config.yml"); if(!worldConfigFile.exists()) { @@ -408,4 +412,7 @@ public class Config { public static boolean check(){ return CheckSchemID != 0; } + public static boolean recording(){ + return event() && SpectateSystem; + } } diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/FightSystem.java b/FightSystem_Main/src/de/steamwar/fightsystem/FightSystem.java index 4c7e65f..7394a12 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/FightSystem.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/FightSystem.java @@ -28,6 +28,8 @@ import de.steamwar.fightsystem.fight.FightPlayer; 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.*; @@ -89,6 +91,7 @@ public class FightSystem extends JavaPlugin { new InFightInventoryListener(); new FreezeWorldStateListener(); new EventJoinListener(); + new EventRecordListener(); new CheckListener(); new TestListener(); new NormalJoinListener(); @@ -129,6 +132,7 @@ public class FightSystem extends JavaPlugin { Objects.requireNonNull(getCommand("ready")).setExecutor(new EventDummyCommand()); Objects.requireNonNull(getCommand("ak")).setExecutor(new EventDummyCommand()); Objects.requireNonNull(getCommand("leader")).setExecutor(new EventDummyCommand()); + RecordSystem.init(); setPreSchemState(); }else if(Config.test()){ @@ -144,6 +148,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 7b1c2fe..6618edd 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/countdown/Countdown.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/countdown/Countdown.java @@ -20,9 +20,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; @@ -71,6 +73,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/fight/Fight.java b/FightSystem_Main/src/de/steamwar/fightsystem/fight/Fight.java index 595e107..30d4087 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/fight/Fight.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/fight/Fight.java @@ -27,12 +27,10 @@ import com.comphenix.protocol.wrappers.PlayerInfoData; import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedGameProfile; import de.steamwar.fightsystem.Config; +import de.steamwar.fightsystem.record.RecordSystem; import de.steamwar.fightsystem.FightSystem; import de.steamwar.sql.Schematic; -import org.bukkit.Bukkit; -import org.bukkit.GameMode; -import org.bukkit.Material; -import org.bukkit.Sound; +import org.bukkit.*; import org.bukkit.entity.Player; import java.lang.reflect.InvocationTargetException; @@ -95,6 +93,8 @@ public class Fight { } public static void playSound(Sound sound, float volume, float pitch) { + if(Config.recording()) + RecordSystem.soundAtPlayer(sound.name(), volume, pitch); //volume: max. 100, pitch: max. 2 Bukkit.getServer().getOnlinePlayers().forEach(player -> player.playSound(player.getLocation(), sound, volume, pitch)); } diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/fight/FightTeam.java b/FightSystem_Main/src/de/steamwar/fightsystem/fight/FightTeam.java index cfaed39..66cf9ae 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/fight/FightTeam.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/fight/FightTeam.java @@ -27,6 +27,7 @@ import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.IFightSystem; import de.steamwar.fightsystem.kit.KitManager; import de.steamwar.fightsystem.listener.PersonalKitCreator; +import de.steamwar.fightsystem.record.RecordSystem; import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.utils.ColorConverter; import de.steamwar.fightsystem.utils.FightScoreboard; @@ -267,6 +268,13 @@ public class FightTeam implements IFightTeam{ } public void pasteSchematic(){ + if(Config.recording()){ + if(blue) + RecordSystem.blueSchem(schematic); + else + RecordSystem.redSchem(schematic); + } + FreezeWorld freezer = new FreezeWorld(); DyeColor c = ColorConverter.chat2dye(color); Schematic schem; diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/listener/EventRecordListener.java b/FightSystem_Main/src/de/steamwar/fightsystem/listener/EventRecordListener.java new file mode 100644 index 0000000..8dcc023 --- /dev/null +++ b/FightSystem_Main/src/de/steamwar/fightsystem/listener/EventRecordListener.java @@ -0,0 +1,244 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +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.*; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.TNTPrimed; +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.entity.ProjectileLaunchEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.*; +import org.bukkit.event.server.BroadcastMessageEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.EnumSet; +import java.util.Random; + +public class EventRecordListener extends BasicListener { + + private static final int AIR = 0; + private static final Random random = new Random(); + + public EventRecordListener() { + super(Config.recording() ? EnumSet.allOf(FightState.class) : EnumSet.noneOf(FightState.class)); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerJoin(PlayerJoinEvent e){ + if(isNotSent(e.getPlayer())) + return; + + RecordSystem.playerJoins(e.getPlayer()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerLeave(PlayerQuitEvent e) { + if(isNotSent(e.getPlayer())) + return; + + RecordSystem.entityDespawns(e.getPlayer()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onPlayerMove(PlayerMoveEvent e){ + if(isNotSent(e.getPlayer())) + return; + + RecordSystem.entityMoves(e.getPlayer()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPlayerDeath(PlayerDeathEvent e){ + if(isNotSent(e.getEntity())) + return; + + RecordSystem.entityDespawns(e.getEntity()); + } + + @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){ + if(e.getBlock() != e.getSourceBlock()) + return; + 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_HUGE.name()); + RecordSystem.sound(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), Sound.ENTITY_GENERIC_EXPLODE.name(), SoundCategory.BLOCKS.name(), 4.0F, (1.0F + (random.nextFloat() - random.nextFloat()) * 0.2F) * 0.7F); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onItem(PlayerItemHeldEvent e){ + if(isNotSent(e.getPlayer())) + return; + + RecordSystem.item(e.getPlayer(), disarmNull(e.getPlayer().getInventory().getItem(e.getNewSlot())), "MAINHAND"); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onItemSwap(PlayerSwapHandItemsEvent e){ + if(isNotSent(e.getPlayer())) + return; + + Player player = e.getPlayer(); + RecordSystem.item(player, disarmNull(e.getMainHandItem()), "MAINHAND"); + RecordSystem.item(player, disarmNull(e.getOffHandItem()), "OFFHAND"); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onProjectileSpawn(ProjectileLaunchEvent e){ + if(e.getEntityType() == EntityType.FIREBALL) + RecordSystem.fireballSpawn(e.getEntity()); + else if(e.getEntityType() == EntityType.ARROW) + RecordSystem.arrowSpawn(e.getEntity()); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onInventoryClick(InventoryClickEvent e){ + Player player = (Player) e.getWhoClicked(); + if(isNotSent(player)) + return; + + if(e.getSlotType() != InventoryType.SlotType.ARMOR) + return; + + switch(e.getSlot()){ + case 103: + RecordSystem.item(player, disarmNull(e.getCurrentItem()), "HEAD"); + break; + case 102: + RecordSystem.item(player, disarmNull(e.getCurrentItem()), "CHEST"); + break; + case 101: + RecordSystem.item(player, disarmNull(e.getCurrentItem()), "LEGS"); + break; + case 100: + default: + RecordSystem.item(player, disarmNull(e.getCurrentItem()), "FEET"); + } + } + + @Override + public void stateChange(FightState state) { + if(state == FightState.PRE_RUNNING) { + Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> { + setKitItems(Fight.getBlueTeam()); + setKitItems(Fight.getRedTeam()); + }, 1); + }else if(state == FightState.SPECTATE){ + despawnTeam(Fight.getRedTeam()); + despawnTeam(Fight.getBlueTeam()); + despawnTNT(); + } + } + + private void setKitItems(FightTeam team){ + if(FightSystem.getFightState() != FightState.PRE_RUNNING) + return; + + for(FightPlayer fp : team.getPlayers()){ + if(!fp.isLiving()) + continue; + + Player player = fp.getPlayer(); + RecordSystem.item(player, disarmNull(player.getInventory().getItemInMainHand()), "MAINHAND"); + RecordSystem.item(player, disarmNull(player.getInventory().getItemInOffHand()), "OFFHAND"); + RecordSystem.item(player, disarmNull(player.getInventory().getHelmet()), "HEAD"); + RecordSystem.item(player, disarmNull(player.getInventory().getChestplate()), "CHEST"); + RecordSystem.item(player, disarmNull(player.getInventory().getLeggings()), "LEGS"); + RecordSystem.item(player, disarmNull(player.getInventory().getBoots()), "FEET"); + } + } + + private ItemStack disarmNull(ItemStack stack){ + if(stack == null) + return new ItemStack(Material.AIR); + return stack; + } + + private void despawnTeam(FightTeam team){ + for(FightPlayer player : team.getPlayers()){ + if(player.isLiving()) + RecordSystem.entityDespawns(player.getPlayer()); + } + } + + private void despawnTNT(){ + for(TNTPrimed tnt : Bukkit.getWorlds().get(0).getEntitiesByClass(TNTPrimed.class)) + RecordSystem.entityDespawns(tnt); + } + + 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 c627ad0..394f109 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/listener/PlayerChatListener.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/listener/PlayerChatListener.java @@ -23,6 +23,7 @@ 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.record.RecordSystem; import de.steamwar.fightsystem.states.FightState; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.TextComponent; @@ -61,7 +62,9 @@ public class PlayerChatListener extends BasicListener { event.setCancelled(true); } - private void broadcastChat(String message){ + private void broadcastChat(String message) { + if (Config.recording()) + RecordSystem.chat(message); BaseComponent[] msg = TextComponent.fromLegacyText(message); for(Player p : Bukkit.getOnlinePlayers()) toChat(p, msg); 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..3a0c616 --- /dev/null +++ b/FightSystem_Main/src/de/steamwar/fightsystem/record/FileRecorder.java @@ -0,0 +1,146 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.fightsystem.record; + +import org.bukkit.Bukkit; +import org.bukkit.World; + +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(); + World world = Bukkit.getWorlds().get(0); + File file = new File(world.getWorldFolder(), world.getName() + ".recording"); + try{ + file.createNewFile(); + outputStream = new DataOutputStream(new GZIPOutputStream(new FileOutputStream(file), 4096)); + }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 new file mode 100644 index 0000000..84660b4 --- /dev/null +++ b/FightSystem_Main/src/de/steamwar/fightsystem/record/RecordSystem.java @@ -0,0 +1,303 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.fightsystem.record; + +import de.steamwar.core.Core; +import de.steamwar.fightsystem.Config; +import de.steamwar.fightsystem.FightSystem; +import de.steamwar.fightsystem.states.FightState; +import de.steamwar.sql.SteamwarUser; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +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.inventory.ItemStack; +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.recording()) + return; + + Bukkit.getScheduler().runTaskTimer(FightSystem.getPlugin(), RecordSystem::checkWorldState, 1, 1); + new SpectateConnection(); + new FileRecorder(); + if(Config.event()) + teamIds(Config.EventTeamBlueID, Config.EventTeamRedID); + } + + /* + * PlayerJoinPacket (0x00) + int EntityId + int SWUserId + * 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 + * PlayerItemPacket (0x07) + int EntityId + String item + boolean enchanted + String slot + * ArrowSpawnPacket (0x08) + int EntityId + * FireballSpawnPacket (0x09) + int EntityId + * 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 soundCategory + float volume, pitch + * ShortBlockPacket (0x33) + pos relative to ArenaMinX,ArenaMinZ byte, byte, byte + short BlockState + * SoundAtPlayerPacket (0x34) + string (soundType, soundCategory) + float volume, pitch + * + * + * ChatPacket (0xA0) + String message + * ActionBarPacket (0xA1) + String message + * SystemPacket (0xA2) + String message + * BlueSchemPacket (0xB0) + int blueSchemId + * RedSchemPacket (0xB1) + int redSchemId + * TeamIDPacket (0xB2) + int blueTeamId, redTeamId + * ScoreboardTitlePacket (0xC0) + String scoreboardTitle + * ScoreboardDataPacket (0xC1) + String key + int value + * + * CommentPacket (0xfe) + String comment + * TickPacket (0xff) + * */ + + public static synchronized void playerJoins(Player p){ + SteamwarUser user = SteamwarUser.get(p.getUniqueId()); + + Recorder.rByte(0x00); + Recorder.rInt(p.getEntityId()); + Recorder.rInt(user.getId()); + entityMoves(p); + } + + public static synchronized void entityMoves(Entity e){ + Location location = e.getLocation(); + + Recorder.rByte(0x01); + Recorder.rInt(e.getEntityId()); + Recorder.rDouble(location.getX()); + Recorder.rDouble(location.getY()); + Recorder.rDouble(location.getZ()); + Recorder.rFloat(location.getPitch()); + Recorder.rFloat(location.getYaw()); + Recorder.rByte((int)(((CraftEntity)e).getHandle().getHeadRotation() * 256 / 360)); + Recorder.flush(); + } + + public static synchronized void entityDespawns(Entity e){ + Recorder.rByte(0x02); + Recorder.rInt(e.getEntityId()); + 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); + spawnEntity(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 item(Player p, ItemStack item, String slot){ + Recorder.rByte(0x07); + Recorder.rInt(p.getEntityId()); + Recorder.rString(item.getType().getKey().toString()); + Recorder.rBoolean(!item.getEnchantments().isEmpty()); + Recorder.rString(slot); + Recorder.flush(); + } + + public static synchronized void arrowSpawn(Entity e){ + Recorder.rByte(0x08); + spawnEntity(e); + } + + public static synchronized void fireballSpawn(Entity e){ + Recorder.rByte(0x09); + spawnEntity(e); + } + + public static synchronized void blockChange(Block block){ + int blockState = blockToId(block); + + int shortX = block.getX() - Config.ArenaMinX; + int shortZ = block.getZ() - Config.ArenaMinZ; + if((short)blockState == blockState && shortX > 0 && shortX < 256 && shortZ > 0 && shortZ < 256){ + //Short block packet + Recorder.rByte(0x33); + Recorder.rByte(shortX); + Recorder.rByte(block.getY()); + Recorder.rByte(shortZ); + Recorder.rShort((short)blockState); + }else{ + //Block packet + Recorder.rByte(0x30); + Recorder.rInt(block.getX()); + Recorder.rByte(block.getY()); + Recorder.rInt(block.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 soundAtPlayer(String soundType, float volume, float pitch){ + Recorder.rByte(0x34); + Recorder.rString(soundType); + Recorder.rFloat(volume); + Recorder.rFloat(pitch); + Recorder.flush(); + } + + public static synchronized void chat(String s) { + Recorder.rByte(0xA0); + Recorder.rString(s); + Recorder.flush(); + } + + public static synchronized void actionBar(String s) { + Recorder.rByte(0xA1); + Recorder.rString(s); + Recorder.flush(); + } + + public static synchronized void systemChat(String s) { + Recorder.rByte(0xA2); + Recorder.rString(s); + Recorder.flush(); + } + + public static synchronized void blueSchem(int schemId) { + Recorder.rByte(0xB0); + Recorder.rInt(schemId); + Recorder.flush(); + } + + public static synchronized void redSchem(int schemId) { + Recorder.rByte(0xB1); + Recorder.rInt(schemId); + Recorder.flush(); + } + + public static synchronized void teamIds(int blueTeamId, int redTeamId) { + Recorder.rByte(0xB2); + Recorder.rInt(blueTeamId); + Recorder.rInt(redTeamId); + Recorder.flush(); + } + + public static synchronized void scoreboardTitle(String title){ + Recorder.rByte(0xC0); + Recorder.rString(title); + Recorder.flush(); + } + + public static synchronized void scoreboardData(String key, int value){ + Recorder.rByte(0xC1); + Recorder.rString(key); + Recorder.rInt(value); + Recorder.flush(); + } + + public static synchronized void tick(){ + Recorder.rByte(0xff); + Recorder.flush(); + } + + private static void checkWorldState(){ + tick(); + + if(FightSystem.getFightState() == FightState.SPECTATE) + return; + + for(TNTPrimed tnt : WORLD.getEntitiesByClass(TNTPrimed.class)){ + entityMoves(tnt); + entitySpeed(tnt); + } + } + + private static void spawnEntity(Entity e){ + Recorder.rInt(e.getEntityId()); + entityMoves(e); + entitySpeed(e); + } + + private static int blockToId(Block block){ + switch(Core.getVersion()){ + case 8: + case 9: + case 10: + case 12: + return RecordSystem_8.blockToId(block); + case 15: + default: + return RecordSystem_15.blockToId(block); + } + } +} diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/record/Recorder.java b/FightSystem_Main/src/de/steamwar/fightsystem/record/Recorder.java new file mode 100644 index 0000000..a332331 --- /dev/null +++ b/FightSystem_Main/src/de/steamwar/fightsystem/record/Recorder.java @@ -0,0 +1,90 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.fightsystem.record; + +import java.util.ArrayList; +import java.util.List; + +public abstract class Recorder { + + private static final 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)); + } + + public static void rShort(short s){ + recorders.forEach((recorder) -> recorder.writeShort(s)); + } + + public static void rInt(int i){ + recorders.forEach((recorder) -> recorder.writeInt(i)); + } + + public static void rLong(long l){ + recorders.forEach((recorder) -> recorder.writeLong(l)); + } + + public static void rFloat(float f){ + recorders.forEach((recorder) -> recorder.writeFloat(f)); + } + + public static void rDouble(double d){ + recorders.forEach((recorder) -> recorder.writeDouble(d)); + } + + public static void rString(String s){ + recorders.forEach((recorder) -> recorder.writeString(s)); + } + + public static void flush(){ + recorders.forEach(Recorder::doFlush); + } + + public static void closeAll(){ + while(!recorders.isEmpty()) + recorders.get(0).close(); + } + + protected Recorder(){ + recorders.add(this); + } + + protected void close(){ + closeRecorder(); + 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); + protected abstract void writeLong(long l); + protected abstract void writeFloat(float f); + protected abstract void writeDouble(double d); + protected abstract void writeString(String s); + protected abstract void doFlush(); + + protected abstract void closeRecorder(); +} diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/record/SpectateConnection.java b/FightSystem_Main/src/de/steamwar/fightsystem/record/SpectateConnection.java new file mode 100644 index 0000000..8db7f20 --- /dev/null +++ b/FightSystem_Main/src/de/steamwar/fightsystem/record/SpectateConnection.java @@ -0,0 +1,146 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2020 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package de.steamwar.fightsystem.record; + +import de.steamwar.fightsystem.Config; +import org.bukkit.Bukkit; + +import java.io.*; +import java.net.Socket; +import java.util.logging.Level; + +public class SpectateConnection extends Recorder{ + + private Socket socket; + private DataOutputStream outputStream; + + SpectateConnection(){ + super(); + try { + this.socket = new Socket(Config.spectateIP, Config.spectatePort); + socket.setSoTimeout(1); // Wait a maximum of 1ms on a blocking operation (flush) + socket.setSoLinger(true, 1); // Wait a maximum of 1ms on close + socket.setTcpNoDelay(true); // Don't wait always on ack + this.outputStream = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "Could not init connection", e); + } + } + + @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{ + outputStream.writeByte(b); + } catch (IOException e) { + close(); + Bukkit.getLogger().log(Level.SEVERE, "Could not send", e); + } + } + + @Override + protected void writeShort(short s) { + try{ + outputStream.writeShort(s); + } catch (IOException e) { + close(); + Bukkit.getLogger().log(Level.SEVERE, "Could not send", e); + } + } + + @Override + protected void writeInt(int i) { + try{ + outputStream.writeInt(i); + } catch (IOException e) { + close(); + Bukkit.getLogger().log(Level.SEVERE, "Could not send", e); + } + } + + @Override + protected void writeLong(long l) { + try{ + outputStream.writeLong(l); + } catch (IOException e) { + close(); + Bukkit.getLogger().log(Level.SEVERE, "Could not send", e); + } + } + + @Override + protected void writeFloat(float f) { + try{ + outputStream.writeFloat(f); + } catch (IOException e) { + close(); + Bukkit.getLogger().log(Level.SEVERE, "Could not send", e); + } + } + + @Override + protected void writeDouble(double d) { + try{ + outputStream.writeDouble(d); + } catch (IOException e) { + close(); + Bukkit.getLogger().log(Level.SEVERE, "Could not send", e); + } + } + + @Override + protected void writeString(String s) { + try{ + outputStream.writeUTF(s); + } catch (IOException e) { + close(); + Bukkit.getLogger().log(Level.SEVERE, "Could not send", e); + } + } + + @Override + protected void doFlush() { + try{ + outputStream.flush(); + } catch (IOException e) { + close(); + Bukkit.getLogger().log(Level.SEVERE, "Could not flush", e); + } + } + + @Override + protected void closeRecorder() { + try { + socket.close(); + outputStream.close(); + } catch (IOException e) { + Bukkit.getLogger().log(Level.SEVERE, "IOException on socket close", e); + } + } +} diff --git a/FightSystem_Main/src/de/steamwar/fightsystem/utils/FightScoreboard.java b/FightSystem_Main/src/de/steamwar/fightsystem/utils/FightScoreboard.java index 5d75181..8be7cf7 100644 --- a/FightSystem_Main/src/de/steamwar/fightsystem/utils/FightScoreboard.java +++ b/FightSystem_Main/src/de/steamwar/fightsystem/utils/FightScoreboard.java @@ -23,6 +23,7 @@ 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.record.RecordSystem; import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.winconditions.*; import org.bukkit.Bukkit; @@ -74,6 +75,8 @@ public class FightScoreboard { private static void teamScoreboard(FightTeam fightTeam){ objective.setDisplayName(fightTeam.getColoredName()); + if(Config.recording()) + RecordSystem.scoreboardTitle(fightTeam.getColoredName()); fightTeam.getPlayers().forEach(fp -> { if(fp.isLiving()) objective.getScore(fightTeam.getPrefix() + fp.getPlayer().getName()).setScore((int) Math.ceil(fp.getPlayer().getHealth())); @@ -81,35 +84,43 @@ public class FightScoreboard { } private static void generalScoreboard(){ - objective.setDisplayName("§6Kampf"); + objective.setDisplayName("§eKampf"); + if(Config.recording()) + RecordSystem.scoreboardTitle("§eKampf"); if (Config.Timeout || Config.Points || Config.HeartRatioTimeout) { int fightTime = FightSystem.getFightTime(); if (fightTime >= 60) - objective.getScore("§7Zeit: §a" + fightTime / 60 + "m " + fightTime % 60 + "s").setScore(3); + setScore("§7Zeit: §a" + fightTime / 60 + "m " + fightTime % 60 + "s", 3); else - objective.getScore("§7Zeit: §a" + fightTime + "s").setScore(3); + setScore("§7Zeit: §a" + fightTime + "s", 3); } if(fullScoreboard.contains(FightSystem.getFightState())){ if (Config.PercentSystem){ - objective.getScore(Fight.getRedTeam().getPrefix() + "Schaden: " + (Math.round(100.0 * WinconditionPercentSystem.getRedPercent()) / 100.0) + "%").setScore(1); - objective.getScore(Fight.getBlueTeam().getPrefix() + "Schaden: " + (Math.round(100.0 * WinconditionPercentSystem.getBluePercent()) / 100.0) + "%").setScore(0); + setScore(Fight.getRedTeam().getPrefix() + "Schaden: " + (Math.round(100.0 * WinconditionPercentSystem.getRedPercent()) / 100.0) + "%", 1); + setScore(Fight.getBlueTeam().getPrefix() + "Schaden: " + (Math.round(100.0 * WinconditionPercentSystem.getBluePercent()) / 100.0) + "%", 0); }else if(Config.WaterTechKO){ - objective.getScore(Fight.getRedTeam().getPrefix() + "Wasser: " + WinconditionWaterTechKO.getTeamRedWater()).setScore(1); - objective.getScore(Fight.getBlueTeam().getPrefix() + "Wasser: " + WinconditionWaterTechKO.getTeamBlueWater()).setScore(0); + setScore(Fight.getRedTeam().getPrefix() + "Wasser: " + WinconditionWaterTechKO.getTeamRedWater(),1); + setScore(Fight.getBlueTeam().getPrefix() + "Wasser: " + WinconditionWaterTechKO.getTeamBlueWater(), 0); }else if(Config.RelativePercent){ - objective.getScore(Fight.getRedTeam().getPrefix() + "Schaden: " + WinconditionRelativePercent.getRed().getPrintablePercent() + "%").setScore(1); - objective.getScore(Fight.getBlueTeam().getPrefix() + "Schaden: " + WinconditionRelativePercent.getBlue().getPrintablePercent() + "%").setScore(0); + setScore(Fight.getRedTeam().getPrefix() + "Schaden: " + WinconditionRelativePercent.getRed().getPrintablePercent() + "%", 1); + setScore(Fight.getBlueTeam().getPrefix() + "Schaden: " + WinconditionRelativePercent.getBlue().getPrintablePercent() + "%", 0); }else if(Config.Points){ - objective.getScore(Fight.getRedTeam().getPrefix() + "Punkte: " + WinconditionPoints.getRed().getPoints()).setScore(1); - objective.getScore(Fight.getBlueTeam().getPrefix() + "Punkte: " + WinconditionPoints.getBlue().getPoints()).setScore(0); + setScore(Fight.getRedTeam().getPrefix() + "Punkte: " + WinconditionPoints.getRed().getPoints(), 1); + setScore(Fight.getBlueTeam().getPrefix() + "Punkte: " + WinconditionPoints.getBlue().getPoints(), 0); }else if(Config.PumpkinTechKO){ - objective.getScore(Fight.getRedTeam().getPrefix() + "Kanonen: " + WinconditionPumpkinTechKO.getTeamRedPumpkins()).setScore(1); - objective.getScore(Fight.getBlueTeam().getPrefix() + "Kanonen: " + WinconditionPumpkinTechKO.getTeamBluePumpkins()).setScore(0); + setScore(Fight.getRedTeam().getPrefix() + "Kanonen: " + WinconditionPumpkinTechKO.getTeamRedPumpkins(), 1); + setScore(Fight.getBlueTeam().getPrefix() + "Kanonen: " + WinconditionPumpkinTechKO.getTeamBluePumpkins(), 0); } } } + private static void setScore(String key, int value){ + objective.getScore(key).setScore(value); + if(Config.recording()) + RecordSystem.scoreboardData(key, value); + } + private static FightTeam getIndexDisplay() { index++; if(index == 1)