diff --git a/FightSystem/FightSystem_10/build.gradle.kts b/FightSystem/FightSystem_10/build.gradle.kts
new file mode 100644
index 00000000..dfab54e2
--- /dev/null
+++ b/FightSystem/FightSystem_10/build.gradle.kts
@@ -0,0 +1,58 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":FightSystem:FightSystem_Core"))
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:spigot:1.10")
+ compileOnly("de.steamwar:worldedit:1.12")
+}
diff --git a/FightSystem/FightSystem_10/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper10.java b/FightSystem/FightSystem_10/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper10.java
new file mode 100644
index 00000000..78dd79a5
--- /dev/null
+++ b/FightSystem/FightSystem_10/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper10.java
@@ -0,0 +1,58 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.FightWorld;
+import net.minecraft.server.v1_10_R1.Chunk;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_10_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_10_R1.entity.CraftEntity;
+import org.bukkit.entity.Entity;
+
+import java.util.stream.Stream;
+
+public class CraftbukkitWrapper10 implements CraftbukkitWrapper {
+ @Override
+ public void resetChunk(World world, World backup, int x, int z) {
+ net.minecraft.server.v1_10_R1.World w = ((CraftWorld) world).getHandle();
+ Chunk chunk = w.getChunkAt(x, z);
+ Chunk backupChunk = ((CraftWorld) backup).getHandle().getChunkAt(x, z);
+
+ System.arraycopy(backupChunk.getSections(), 0, chunk.getSections(), 0, chunk.getSections().length);
+ System.arraycopy(backupChunk.heightMap, 0, chunk.heightMap, 0, chunk.heightMap.length);
+ w.tileEntityListTick.removeAll(chunk.tileEntities.values());
+ if (!FightWorld.isPAPER()) {
+ w.tileEntityList.removeAll(chunk.tileEntities.values());
+ }
+ chunk.tileEntities.clear();
+ chunk.tileEntities.putAll(backupChunk.tileEntities);
+ }
+
+ @Override
+ public float headRotation(Entity e) {
+ return ((CraftEntity)e).getHandle().getHeadRotation();
+ }
+
+ @Override
+ public Stream> entityIterator() {
+ return ((CraftWorld) Config.world).getHandle().entityList.stream();
+ }
+}
diff --git a/FightSystem/FightSystem_12/build.gradle.kts b/FightSystem/FightSystem_12/build.gradle.kts
new file mode 100644
index 00000000..e64720ce
--- /dev/null
+++ b/FightSystem/FightSystem_12/build.gradle.kts
@@ -0,0 +1,59 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":FightSystem:FightSystem_Core"))
+ compileOnly(project(":FightSystem:FightSystem_8"))
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:spigot:1.12")
+ compileOnly("de.steamwar:worldedit:1.12")
+}
diff --git a/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper12.java b/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper12.java
new file mode 100644
index 00000000..f8e843bc
--- /dev/null
+++ b/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper12.java
@@ -0,0 +1,58 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.FightWorld;
+import net.minecraft.server.v1_12_R1.Chunk;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_12_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_12_R1.entity.CraftEntity;
+import org.bukkit.entity.Entity;
+
+import java.util.stream.Stream;
+
+public class CraftbukkitWrapper12 implements CraftbukkitWrapper {
+ @Override
+ public void resetChunk(World world, World backup, int x, int z) {
+ net.minecraft.server.v1_12_R1.World w = ((CraftWorld) world).getHandle();
+ Chunk chunk = w.getChunkAt(x, z);
+ Chunk backupChunk = ((CraftWorld) backup).getHandle().getChunkAt(x, z);
+
+ System.arraycopy(backupChunk.getSections(), 0, chunk.getSections(), 0, chunk.getSections().length);
+ System.arraycopy(backupChunk.heightMap, 0, chunk.heightMap, 0, chunk.heightMap.length);
+ w.tileEntityListTick.removeAll(chunk.tileEntities.values());
+ if (!FightWorld.isPAPER()) {
+ w.tileEntityList.removeAll(chunk.tileEntities.values());
+ }
+ chunk.tileEntities.clear();
+ chunk.tileEntities.putAll(backupChunk.tileEntities);
+ }
+
+ @Override
+ public float headRotation(Entity e) {
+ return ((CraftEntity)e).getHandle().getHeadRotation();
+ }
+
+ @Override
+ public Stream> entityIterator() {
+ return ((CraftWorld) Config.world).getHandle().entityList.stream();
+ }
+}
diff --git a/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/WorldOfColorWrapper12.java b/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/WorldOfColorWrapper12.java
new file mode 100644
index 00000000..b4be805b
--- /dev/null
+++ b/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/WorldOfColorWrapper12.java
@@ -0,0 +1,53 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import org.bukkit.ChatColor;
+import org.bukkit.Location;
+import org.bukkit.Sound;
+import org.bukkit.SoundCategory;
+import org.bukkit.entity.Arrow;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Projectile;
+import org.bukkit.scoreboard.Team;
+
+public class WorldOfColorWrapper12 implements WorldOfColorWrapper {
+ @Override
+ public void setTeamColor(Team team, ChatColor color) {
+ team.setColor(color);
+ }
+
+ @Override
+ public boolean isInBlock(Projectile e) {
+ if(e instanceof Arrow)
+ return ((Arrow) e).isInBlock();
+ return false;
+ }
+
+ @Override
+ public void playSound(Location location, Sound sound, String soundCategory, float volume, float pitch) {
+ location.getWorld().playSound(location, sound, SoundCategory.valueOf(soundCategory), volume, pitch);
+ }
+
+ @Override
+ public void sendTitle(Player player, String title, String subtitle, int start, int hold, int stop) {
+ player.sendTitle(title, subtitle, start, hold, stop);
+ }
+}
diff --git a/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/WorldeditWrapper12.java b/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/WorldeditWrapper12.java
new file mode 100644
index 00000000..7dd977b5
--- /dev/null
+++ b/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/WorldeditWrapper12.java
@@ -0,0 +1,32 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import com.sk89q.worldedit.blocks.BaseBlock;
+import org.bukkit.Material;
+
+@SuppressWarnings("deprecation")
+public class WorldeditWrapper12 extends WorldeditWrapper8 {
+
+ static {
+ colorBlocks.add(new BaseBlock(Material.CONCRETE.getId(), COLOR_TO_REPLACE));
+ colorBlocks.add(new BaseBlock(Material.CONCRETE_POWDER.getId(), COLOR_TO_REPLACE));
+ }
+}
diff --git a/FightSystem/FightSystem_14/build.gradle.kts b/FightSystem/FightSystem_14/build.gradle.kts
new file mode 100644
index 00000000..16e779aa
--- /dev/null
+++ b/FightSystem/FightSystem_14/build.gradle.kts
@@ -0,0 +1,62 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":FightSystem:FightSystem_Core"))
+ compileOnly(project(":FightSystem:FightSystem_8"))
+ compileOnly(project(":FightSystem:FightSystem_9"))
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:spigot:1.14")
+ compileOnly("de.steamwar:worldedit:1.15")
+
+ compileOnly("it.unimi.dsi:fastutil:8.5.6")
+}
diff --git a/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/BlockIdWrapper14.java b/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/BlockIdWrapper14.java
new file mode 100644
index 00000000..f2330e12
--- /dev/null
+++ b/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/BlockIdWrapper14.java
@@ -0,0 +1,95 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.core.Core;
+import de.steamwar.fightsystem.Config;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+
+import java.util.Map;
+
+public class BlockIdWrapper14 implements BlockIdWrapper {
+
+ private static final Class> chunkProviderServer = Reflection.getClass("{nms.server.level}.ChunkProviderServer");
+ private static final Reflection.MethodInvoker getChunkProvider = Reflection.getTypedMethod(worldServer, null, chunkProviderServer);
+ private static final Class> playerChunkMap = Reflection.getClass("{nms.server.level}.PlayerChunkMap");
+ private static final Reflection.FieldAccessor> getPlayerChunkMap = Reflection.getField(chunkProviderServer, playerChunkMap, 0);
+ private static final Reflection.FieldAccessor extends Map> entityTrackers = Core.getVersion() > 15 ? Reflection.getField(playerChunkMap, Int2ObjectMap.class, 0) : Reflection.getField(playerChunkMap, org.bukkit.craftbukkit.libs.it.unimi.dsi.fastutil.ints.Int2ObjectMap.class, 0);
+ private static final Class> block = Reflection.getClass("{nms.world.level.block}.Block");
+ private static final Class> iBlockData = Reflection.getClass("{nms.world.level.block.state}.IBlockData");
+ private static final Class> blockPosition = Reflection.getClass("{nms.core}.BlockPosition");
+
+ private final Map trackers;
+ public BlockIdWrapper14() {
+ trackers = entityTrackers.get(getPlayerChunkMap.get(getChunkProvider.invoke(getWorldHandle.invoke(Config.world))));
+ }
+
+ private static final Reflection.MethodInvoker getCombinedId = Reflection.getTypedMethod(block, null, int.class, iBlockData);
+ private static final Reflection.MethodInvoker getNMS = Reflection.getTypedMethod(Reflection.getClass("{obc}.block.CraftBlock"), "getNMS", iBlockData);
+ @Override
+ public int blockToId(Block block) {
+ return (int) getCombinedId.invoke(null, getNMS.invoke(block));
+ }
+
+ private static final Reflection.MethodInvoker getByCombinedId = Reflection.getTypedMethod(block, null, iBlockData, int.class);
+ private static final Reflection.ConstructorInvoker newBlockPosition = Reflection.getConstructor(blockPosition, int.class, int.class, int.class);
+ private static final Reflection.MethodInvoker getTypeAndData = Reflection.getMethod(worldServer, null, blockPosition, iBlockData, int.class);
+ private static final Reflection.MethodInvoker removeTileEntity = Reflection.getMethod(worldServer, Core.getVersion() > 15 ? "m" : "removeTileEntity", blockPosition);
+ private static final Reflection.MethodInvoker flagDirty = Reflection.getMethod(chunkProviderServer, null, blockPosition);
+ @Override
+ public void setBlock(World world, int x, int y, int z, int blockState) {
+ Object blockData = getByCombinedId.invoke(null, blockState);
+ Object nworld = getWorldHandle.invoke(world);
+ Object pos = newBlockPosition.invoke(x, y, z);
+
+ removeTileEntity.invoke(nworld, pos);
+ getTypeAndData.invoke(nworld, pos, blockData, 1042);
+ flagDirty.invoke(getChunkProvider.invoke(nworld), pos);
+ }
+
+ private static final Class> entityTracker = Reflection.getClass("{nms.server.level}.PlayerChunkMap$EntityTracker");
+ private static final Reflection.MethodInvoker updatePlayer = Reflection.getMethod(entityTracker, Core.getVersion() > 15 ? "b" : "updatePlayer", entityPlayer);
+ @Override
+ public void trackEntity(Player player, int entity) {
+ Object tracker = trackers.get(entity);
+ if(tracker != null)
+ updatePlayer.invoke(tracker, getPlayer.invoke(player));
+ }
+
+ private static final Reflection.MethodInvoker clearPlayer = Reflection.getMethod(entityTracker, Core.getVersion() > 15 ? "a" : "clear", entityPlayer);
+ @Override
+ public void untrackEntity(Player player, int entity) {
+ Object tracker = trackers.get(entity);
+ if(tracker != null)
+ clearPlayer.invoke(tracker, getPlayer.invoke(player));
+ }
+
+ private static final Reflection.MethodInvoker getMaterialByBlock = Reflection.getTypedMethod(Reflection.getClass("{obc}.util.CraftMagicNumbers"), "getMaterial", Material.class, block);
+ private static final Reflection.MethodInvoker getBlockByBlockData = Reflection.getTypedMethod(iBlockData, null, block);
+ @Override
+ public Material idToMaterial(int blockState) {
+ return (Material)getMaterialByBlock.invoke(null, getBlockByBlockData.invoke(getByCombinedId.invoke(null, blockState)));
+ }
+}
diff --git a/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper14.java b/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper14.java
new file mode 100644
index 00000000..07c525d4
--- /dev/null
+++ b/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper14.java
@@ -0,0 +1,59 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.FightWorld;
+import net.minecraft.server.v1_14_R1.Chunk;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_14_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_14_R1.entity.CraftEntity;
+import org.bukkit.entity.Entity;
+
+import java.util.stream.Stream;
+
+public class CraftbukkitWrapper14 implements CraftbukkitWrapper {
+ @Override
+ public void resetChunk(World world, World backup, int x, int z) {
+ net.minecraft.server.v1_14_R1.World w = ((CraftWorld) world).getHandle();
+ Chunk chunk = w.getChunkAt(x, z);
+ Chunk backupChunk = ((CraftWorld) backup).getHandle().getChunkAt(x, z);
+
+ System.arraycopy(backupChunk.getSections(), 0, chunk.getSections(), 0, chunk.getSections().length);
+ w.tileEntityListTick.removeAll(chunk.tileEntities.values());
+ if (!FightWorld.isPAPER()) {
+ w.tileEntityList.removeAll(chunk.tileEntities.values());
+ }
+ chunk.tileEntities.clear();
+ chunk.tileEntities.putAll(backupChunk.tileEntities);
+ chunk.heightMap.clear();
+ chunk.heightMap.putAll(backupChunk.heightMap);
+ }
+
+ @Override
+ public float headRotation(Entity e) {
+ return ((CraftEntity)e).getHandle().getHeadRotation();
+ }
+
+ @Override
+ public Stream> entityIterator() {
+ return ((CraftWorld) Config.world).getHandle().entitiesById.values().stream();
+ }
+}
diff --git a/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/FlatteningWrapper14.java b/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/FlatteningWrapper14.java
new file mode 100644
index 00000000..2cce880d
--- /dev/null
+++ b/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/FlatteningWrapper14.java
@@ -0,0 +1,116 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import org.bukkit.DyeColor;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.block.data.Waterlogged;
+import org.bukkit.block.data.type.Dispenser;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Pose;
+import org.bukkit.event.block.BlockPhysicsEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.BlockDataMeta;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.Objects;
+
+public class FlatteningWrapper14 implements FlatteningWrapper {
+ @Override
+ public DyeColor getSilver() {
+ return DyeColor.LIGHT_GRAY;
+ }
+
+ @Override
+ public boolean isWater(Block block) {
+ if(block.getType() == Material.WATER)
+ return true;
+
+ BlockData data = block.getBlockData();
+ if(!(data instanceof Waterlogged))
+ return false;
+
+ return ((Waterlogged) data).isWaterlogged();
+ }
+
+ @Override
+ public boolean removeWater(Block block) {
+ Material type = block.getType();
+ if(type == Material.WATER || type == Material.LAVA){
+ block.setType(Material.AIR);
+ return true;
+ }
+
+ BlockData data = block.getBlockData();
+ if(!(data instanceof Waterlogged))
+ return false;
+
+ Waterlogged waterlogged = (Waterlogged) data;
+ if(waterlogged.isWaterlogged()){
+ block.setType(Material.AIR);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean containsBlockMeta(ItemMeta meta) {
+ return meta instanceof BlockDataMeta && ((BlockDataMeta)meta).hasBlockData();
+ }
+
+ @Override
+ public boolean hasAttributeModifier(ItemStack stack) {
+ return stack.hasItemMeta() && Objects.requireNonNull(stack.getItemMeta()).hasAttributeModifiers();
+ }
+
+ @Override
+ public boolean doRecord(BlockPhysicsEvent e) {
+ return e.getBlock() == e.getSourceBlock() || e.getChangedType() == Material.AIR;
+ }
+
+ @Override
+ public void forceLoadChunk(World world, int cX, int cZ) {
+ world.setChunkForceLoaded(cX, cZ, true);
+ }
+
+ @Override
+ public boolean checkPistonMoving(Block block) {
+ return block.getType() == Material.MOVING_PISTON;
+ }
+
+ @Override
+ public boolean isFacingWater(Block dispenser) {
+ return dispenser.getRelative(((Dispenser) dispenser.getBlockData()).getFacing()).isLiquid();
+ }
+
+ @Override
+ public boolean isCrouching(Player player) {
+ return player.getPose() == Pose.SWIMMING;
+ }
+
+ @Override
+ public void sendBlockChange(Player player, Block block, Material type) {
+ player.sendBlockChange(block.getLocation(), type.createBlockData());
+ }
+}
diff --git a/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/SWSound14.java b/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/SWSound14.java
new file mode 100644
index 00000000..77fde1dc
--- /dev/null
+++ b/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/SWSound14.java
@@ -0,0 +1,40 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import org.bukkit.Sound;
+
+public class SWSound14 implements SWSound.ISWSound {
+ @Override
+ public Sound getSound(SWSound sound) {
+ switch(sound){
+ case ENTITY_WITHER_DEATH:
+ return Sound.ENTITY_WITHER_DEATH;
+ case BLOCK_NOTE_BASS:
+ return Sound.BLOCK_NOTE_BLOCK_BASS;
+ case BLOCK_NOTE_PLING:
+ return Sound.BLOCK_NOTE_BLOCK_PLING;
+ case ENTITY_GENERIC_EXPLODE:
+ return Sound.ENTITY_GENERIC_EXPLODE;
+ default:
+ return null;
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/WorldeditWrapper14.java b/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/WorldeditWrapper14.java
new file mode 100644
index 00000000..288b89e8
--- /dev/null
+++ b/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/WorldeditWrapper14.java
@@ -0,0 +1,149 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import com.sk89q.jnbt.NBTInputStream;
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.bukkit.BukkitAdapter;
+import com.sk89q.worldedit.bukkit.BukkitWorld;
+import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
+import com.sk89q.worldedit.extent.clipboard.Clipboard;
+import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
+import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
+import com.sk89q.worldedit.extent.clipboard.io.SpongeSchematicReader;
+import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
+import com.sk89q.worldedit.function.operation.Operations;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.math.Vector3;
+import com.sk89q.worldedit.math.transform.AffineTransform;
+import com.sk89q.worldedit.regions.CuboidRegion;
+import com.sk89q.worldedit.session.ClipboardHolder;
+import com.sk89q.worldedit.world.World;
+import com.sk89q.worldedit.world.block.BaseBlock;
+import com.sk89q.worldedit.world.block.BlockTypes;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.sql.SchematicData;
+import de.steamwar.sql.SchematicNode;
+import org.bukkit.DyeColor;
+import org.bukkit.Location;
+import org.bukkit.util.Vector;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.zip.GZIPInputStream;
+
+public class WorldeditWrapper14 implements WorldeditWrapper {
+
+ private static final Map colorBlocks = new HashMap<>();
+
+ static {
+ colorBlocks.put(Objects.requireNonNull(BlockTypes.PINK_WOOL).getDefaultState().toBaseBlock(), "_wool");
+ colorBlocks.put(Objects.requireNonNull(BlockTypes.PINK_TERRACOTTA).getDefaultState().toBaseBlock(), "_terracotta");
+ colorBlocks.put(Objects.requireNonNull(BlockTypes.PINK_STAINED_GLASS).getDefaultState().toBaseBlock(), "_stained_glass");
+ colorBlocks.put(Objects.requireNonNull(BlockTypes.PINK_STAINED_GLASS_PANE).getDefaultState().toBaseBlock(), "_stained_glass_pane");
+ colorBlocks.put(Objects.requireNonNull(BlockTypes.PINK_CONCRETE).getDefaultState().toBaseBlock(), "_concrete");
+ colorBlocks.put(Objects.requireNonNull(BlockTypes.PINK_CONCRETE_POWDER).getDefaultState().toBaseBlock(), "_concrete_powder");
+ colorBlocks.put(Objects.requireNonNull(BlockTypes.PINK_CARPET).getDefaultState().toBaseBlock(), "_carpet");
+ }
+
+ @Override
+ public void replaceTeamColor(Clipboard clipboard, DyeColor c) throws WorldEditException {
+ BlockVector3 minimum = clipboard.getRegion().getMinimumPoint();
+ Map replaceMap = new HashMap<>();
+ colorBlocks.forEach((base, postfix) -> replaceMap.put(base, Objects.requireNonNull(BlockTypes.get(c.name().toLowerCase() + postfix)).getDefaultState().toBaseBlock()));
+
+ for(int x = 0; x < clipboard.getDimensions().getX(); x++){
+ for(int y = 0; y < clipboard.getDimensions().getY(); y++){
+ for(int z = 0; z < clipboard.getDimensions().getZ(); z++){
+ BlockVector3 pos = minimum.add(x, y, z);
+ BaseBlock replacement = replaceMap.get(clipboard.getFullBlock(pos));
+ if(replacement != null)
+ clipboard.setBlock(pos, replacement);
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getWaterDepth(Clipboard clipboard) {
+ BlockVector3 it = clipboard.getMinimumPoint().add(0, 0, 1);
+ int depth = 0;
+ while(!clipboard.getBlock(it).getBlockType().getMaterial().isAir()){
+ depth++;
+ it = it.add(0, 1, 0);
+ }
+ return depth;
+ }
+
+ @Override
+ public void pasteClipboard(Clipboard clipboard, Location position, Vector offset, AffineTransform aT) {
+ EditSession e = WorldEdit.getInstance().getEditSessionFactory().getEditSession(new BukkitWorld(position.getWorld()), -1);
+ ClipboardHolder ch = new ClipboardHolder(clipboard);
+ ch.setTransform(aT);
+ Operations.completeBlindly(ch.createPaste(e).to(BukkitAdapter.asVector(position).add(
+ aT.apply(Vector3.at(offset.getX(), offset.getY(), offset.getZ()).add(clipboard.getOrigin().toVector3()).subtract(clipboard.getMinimumPoint().toVector3()))
+ ).toBlockPoint()).build());
+ e.flushSession();
+ }
+
+ @Override
+ public Vector getDimensions(Clipboard clipboard) {
+ BlockVector3 dims = clipboard.getDimensions();
+ return new Vector(dims.getX(), dims.getY(), dims.getZ());
+ }
+
+ @Override
+ public Clipboard loadChar(String charName) throws IOException {
+ return new SpongeSchematicReader(new NBTInputStream(new GZIPInputStream(new FileInputStream(new File(FightSystem.getPlugin().getDataFolder(), "text/" + charName + ".schem"))))).read();
+ }
+
+ @Override
+ public void saveSchem(SchematicNode schem, Region region, int minY) throws WorldEditException {
+ World w = new BukkitWorld(Config.world);
+ BlockVector3 min = BlockVector3.at(region.getMinX(), minY, region.getMinZ());
+ CuboidRegion cuboidRegion = new CuboidRegion(w, min, BlockVector3.at(region.getMaxX(), region.getMaxY(), region.getMaxZ()).subtract(BlockVector3.ONE));
+ BlockArrayClipboard clipboard = new BlockArrayClipboard(cuboidRegion);
+
+ ForwardExtentCopy forwardExtentCopy = new ForwardExtentCopy(
+ WorldEdit.getInstance().getEditSessionFactory().getEditSession(w, -1), cuboidRegion, clipboard, min
+ );
+ forwardExtentCopy.setCopyingEntities(false);
+ Operations.complete(forwardExtentCopy);
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ try {
+ ClipboardWriter writer = BuiltInClipboardFormat.SPONGE_SCHEMATIC.getWriter(outputStream);
+ writer.write(clipboard);
+ writer.close();
+ } catch (IOException e) {
+ throw new SecurityException(e);
+ }
+
+ new SchematicData(schem).saveFromBytes(outputStream.toByteArray(), true);
+ }
+}
diff --git a/FightSystem/FightSystem_15/build.gradle.kts b/FightSystem/FightSystem_15/build.gradle.kts
new file mode 100644
index 00000000..b321f1b6
--- /dev/null
+++ b/FightSystem/FightSystem_15/build.gradle.kts
@@ -0,0 +1,58 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":FightSystem:FightSystem_Core"))
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:spigot:1.15")
+ compileOnly("de.steamwar:worldedit:1.15")
+}
diff --git a/FightSystem/FightSystem_15/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper15.java b/FightSystem/FightSystem_15/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper15.java
new file mode 100644
index 00000000..70d9e8c5
--- /dev/null
+++ b/FightSystem/FightSystem_15/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper15.java
@@ -0,0 +1,59 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.FightWorld;
+import net.minecraft.server.v1_15_R1.Chunk;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
+import org.bukkit.craftbukkit.v1_15_R1.entity.CraftEntity;
+import org.bukkit.entity.Entity;
+
+import java.util.stream.Stream;
+
+public class CraftbukkitWrapper15 implements CraftbukkitWrapper {
+ @Override
+ public void resetChunk(World world, World backup, int x, int z) {
+ net.minecraft.server.v1_15_R1.World w = ((CraftWorld) world).getHandle();
+ Chunk chunk = w.getChunkAt(x, z);
+ Chunk backupChunk = ((CraftWorld) backup).getHandle().getChunkAt(x, z);
+
+ System.arraycopy(backupChunk.getSections(), 0, chunk.getSections(), 0, chunk.getSections().length);
+ w.tileEntityListTick.removeAll(chunk.tileEntities.values());
+ if (!FightWorld.isPAPER()) {
+ w.tileEntityList.removeAll(chunk.tileEntities.values());
+ }
+ chunk.tileEntities.clear();
+ chunk.tileEntities.putAll(backupChunk.tileEntities);
+ chunk.heightMap.clear();
+ chunk.heightMap.putAll(backupChunk.heightMap);
+ }
+
+ @Override
+ public float headRotation(Entity e) {
+ return ((CraftEntity)e).getHandle().getHeadRotation();
+ }
+
+ @Override
+ public Stream> entityIterator() {
+ return ((CraftWorld) Config.world).getHandle().entitiesById.values().stream();
+ }
+}
diff --git a/FightSystem/FightSystem_18/build.gradle.kts b/FightSystem/FightSystem_18/build.gradle.kts
new file mode 100644
index 00000000..604c7354
--- /dev/null
+++ b/FightSystem/FightSystem_18/build.gradle.kts
@@ -0,0 +1,64 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":FightSystem:FightSystem_Core"))
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:fastasyncworldedit:1.18")
+ compileOnly("de.steamwar:spigot:1.18")
+
+ compileOnly("org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT")
+ compileOnly("com.mojang:datafixerupper:4.0.26")
+ compileOnly("io.netty:netty-all:4.1.68.Final")
+ compileOnly("com.mojang:authlib:1.5.25")
+ compileOnly("it.unimi.dsi:fastutil:8.5.6")
+}
diff --git a/FightSystem/FightSystem_18/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper18.java b/FightSystem/FightSystem_18/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper18.java
new file mode 100644
index 00000000..72c14f8f
--- /dev/null
+++ b/FightSystem/FightSystem_18/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper18.java
@@ -0,0 +1,65 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.fightsystem.Config;
+import net.minecraft.server.level.WorldServer;
+import net.minecraft.world.level.chunk.Chunk;
+import net.minecraft.world.level.chunk.ChunkSection;
+import net.minecraft.world.level.entity.LevelEntityGetter;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+public class CraftbukkitWrapper18 implements CraftbukkitWrapper {
+
+ private static final Reflection.MethodInvoker getWorld = Reflection.getMethod(Reflection.getClass("{obc}.CraftWorld"), "getHandle");
+ private static final Reflection.MethodInvoker getChunk = Reflection.getTypedMethod(net.minecraft.world.level.World.class, null, Chunk.class, int.class, int.class);
+ private static final Reflection.MethodInvoker getChunkSections = Reflection.getTypedMethod(Chunk.class, null, ChunkSection[].class);
+ private ChunkSection[] getChunkSections(World world, int x, int z) {
+ return (ChunkSection[]) getChunkSections.invoke(getChunk.invoke(getWorld.invoke(world), x, z));
+ }
+
+ @Override
+ public void resetChunk(World world, World backup, int x, int z) {
+ ChunkSection[] sections = getChunkSections(world, x, z);
+ System.arraycopy(getChunkSections(backup, x, z), 0, sections, 0, sections.length);
+ }
+
+ private static final Reflection.MethodInvoker getEntity = Reflection.getTypedMethod(Reflection.getClass("{obc}.entity.CraftEntity"), "getHandle", net.minecraft.world.entity.Entity.class);
+ protected net.minecraft.world.entity.Entity getEntity(Entity e) {
+ return (net.minecraft.world.entity.Entity) getEntity.invoke(e);
+ }
+
+ @Override
+ public float headRotation(Entity e) {
+ return getEntity(e).ce();
+ }
+
+ private static final Reflection.MethodInvoker getWorldEntities = Reflection.getTypedMethod(WorldServer.class, null, LevelEntityGetter.class);
+ private static final Reflection.MethodInvoker getIterable = Reflection.getTypedMethod(LevelEntityGetter.class, null, Iterable.class);
+ @Override
+ public Stream> entityIterator() {
+ return StreamSupport.stream(((Iterable>) getIterable.invoke(getWorldEntities.invoke(getWorld.invoke(Config.world)))).spliterator(), false);
+ }
+}
diff --git a/FightSystem/FightSystem_18/src/de/steamwar/fightsystem/utils/HullHiderWrapper18.java b/FightSystem/FightSystem_18/src/de/steamwar/fightsystem/utils/HullHiderWrapper18.java
new file mode 100644
index 00000000..8f81cf95
--- /dev/null
+++ b/FightSystem/FightSystem_18/src/de/steamwar/fightsystem/utils/HullHiderWrapper18.java
@@ -0,0 +1,82 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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.utils;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.fightsystem.Config;
+import it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap;
+import net.minecraft.core.BlockPosition;
+import net.minecraft.core.SectionPosition;
+import net.minecraft.network.protocol.game.PacketPlayOutBlockChange;
+import net.minecraft.network.protocol.game.PacketPlayOutMultiBlockChange;
+import net.minecraft.world.level.block.state.IBlockData;
+import org.bukkit.block.data.BlockData;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class HullHiderWrapper18 implements HullHiderWrapper {
+
+ private static final Reflection.MethodInvoker getState = Reflection.getTypedMethod(Reflection.getClass("{obc}.block.data.CraftBlockData"), "getState", IBlockData.class);
+ @Override
+ public Object generateBlockChangePacket(List changes) {
+ List blockdata = new ArrayList<>(changes.size());
+
+ changes.removeIf(change -> {
+ BlockData data = Config.world.getBlockData(change.getX(), change.getY(), change.getZ());
+ boolean unchanged = data.getMaterial() == Config.ObfuscateWith || Config.HiddenBlocks.contains(data.getMaterial());
+ if(!unchanged)
+ blockdata.add(getState.invoke(data));
+ return unchanged;
+ });
+
+ if(changes.isEmpty())
+ return null;
+
+ return generateBlockChangePacket(changes, blockdata.toArray());
+ }
+
+ private Object generateBlockChangePacket(List changes, Object[] blockdata) {
+ if(changes.size() > 1) {
+ Hull.IntVector section = changes.get(0);
+ section = new Hull.IntVector(section.getX() >> 4, section.getY() >> 4, section.getZ() >> 4);
+ int xOffset = 16*section.getX();
+ int yOffset = 16*section.getY();
+ int zOffset = 16*section.getZ();
+
+ short[] pos = new short[changes.size()];
+ for(int i = 0; i < changes.size(); i++) {
+ Hull.IntVector change = changes.get(i);
+
+ pos[i] = (short) (((change.getX()-xOffset) << 8) + ((change.getZ()-zOffset) << 4) + (change.getY()-yOffset));
+ }
+
+ return constructMultiBlockChange(section, pos, blockdata);
+ } else {
+ Hull.IntVector pos = changes.get(0);
+ return new PacketPlayOutBlockChange(new BlockPosition(pos.getX(), pos.getY(), pos.getZ()), (IBlockData) blockdata[0]);
+ }
+ }
+
+ protected Object constructMultiBlockChange(Hull.IntVector section, short[] pos, Object[] blockdata) {
+ return new PacketPlayOutMultiBlockChange(SectionPosition.a(section.getX(), section.getY(), section.getZ()), new Short2ObjectArrayMap<>(pos, blockdata, blockdata.length), false);
+ }
+}
diff --git a/FightSystem/FightSystem_19/build.gradle.kts b/FightSystem/FightSystem_19/build.gradle.kts
new file mode 100644
index 00000000..b3475ff2
--- /dev/null
+++ b/FightSystem/FightSystem_19/build.gradle.kts
@@ -0,0 +1,64 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":FightSystem:FightSystem_Core"))
+ compileOnly(project(":FightSystem:FightSystem_18"))
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:worldedit:1.15")
+ compileOnly("de.steamwar:spigot:1.19")
+
+ compileOnly("org.spigotmc:spigot-api:1.19-R0.1-SNAPSHOT")
+ compileOnly("com.mojang:brigadier:1.0.18")
+ compileOnly("com.mojang:datafixerupper:4.0.26")
+ compileOnly("com.mojang:authlib:1.5.25")
+}
diff --git a/FightSystem/FightSystem_19/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper19.java b/FightSystem/FightSystem_19/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper19.java
new file mode 100644
index 00000000..1a18a2a7
--- /dev/null
+++ b/FightSystem/FightSystem_19/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper19.java
@@ -0,0 +1,30 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import org.bukkit.entity.Entity;
+
+public class CraftbukkitWrapper19 extends CraftbukkitWrapper18 {
+
+ @Override
+ public float headRotation(Entity e) {
+ return getEntity(e).ck();
+ }
+}
diff --git a/FightSystem/FightSystem_20/build.gradle.kts b/FightSystem/FightSystem_20/build.gradle.kts
new file mode 100644
index 00000000..51b0862f
--- /dev/null
+++ b/FightSystem/FightSystem_20/build.gradle.kts
@@ -0,0 +1,60 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":FightSystem:FightSystem_Core"))
+ compileOnly(project(":FightSystem:FightSystem_18"))
+
+ compileOnly("de.steamwar:spigot:1.20")
+
+ compileOnly("org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT")
+ compileOnly("it.unimi.dsi:fastutil:8.5.6")
+}
diff --git a/FightSystem/FightSystem_20/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper20.java b/FightSystem/FightSystem_20/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper20.java
new file mode 100644
index 00000000..e0dabe46
--- /dev/null
+++ b/FightSystem/FightSystem_20/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper20.java
@@ -0,0 +1,30 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import org.bukkit.entity.Entity;
+
+public class CraftbukkitWrapper20 extends CraftbukkitWrapper18 {
+
+ @Override
+ public float headRotation(Entity e) {
+ return getEntity(e).cm();
+ }
+}
diff --git a/FightSystem/FightSystem_20/src/de/steamwar/fightsystem/utils/HullHiderWrapper20.java b/FightSystem/FightSystem_20/src/de/steamwar/fightsystem/utils/HullHiderWrapper20.java
new file mode 100644
index 00000000..48bf0ad5
--- /dev/null
+++ b/FightSystem/FightSystem_20/src/de/steamwar/fightsystem/utils/HullHiderWrapper20.java
@@ -0,0 +1,32 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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.utils;
+
+import it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap;
+import net.minecraft.core.SectionPosition;
+import net.minecraft.network.protocol.game.PacketPlayOutMultiBlockChange;
+
+public class HullHiderWrapper20 extends HullHiderWrapper18 {
+
+ @Override
+ protected Object constructMultiBlockChange(Hull.IntVector section, short[] pos, Object[] blockdata) {
+ return new PacketPlayOutMultiBlockChange(SectionPosition.a(section.getX(), section.getY(), section.getZ()), new Short2ObjectArrayMap<>(pos, blockdata, blockdata.length));
+ }
+}
diff --git a/FightSystem/FightSystem_8/build.gradle.kts b/FightSystem/FightSystem_8/build.gradle.kts
new file mode 100644
index 00000000..c316418d
--- /dev/null
+++ b/FightSystem/FightSystem_8/build.gradle.kts
@@ -0,0 +1,58 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":FightSystem:FightSystem_Core"))
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:spigot:1.8")
+ compileOnly("de.steamwar:worldedit:1.12")
+}
diff --git a/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/BlockIdWrapper8.java b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/BlockIdWrapper8.java
new file mode 100644
index 00000000..d330cfbc
--- /dev/null
+++ b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/BlockIdWrapper8.java
@@ -0,0 +1,82 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.fightsystem.Config;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+
+public class BlockIdWrapper8 implements BlockIdWrapper {
+
+ private static final Class> entityTracker = Reflection.getClass("{nms}.EntityTracker");
+ private static final Reflection.FieldAccessor> getEntityTracker = Reflection.getField(worldServer, entityTracker, 0);
+ private static final Class> intHashMap = Reflection.getClass("{nms}.IntHashMap");
+ private static final Reflection.FieldAccessor> getTrackedEntities = Reflection.getField(entityTracker, intHashMap, 0);
+
+ private final Object trackers;
+ public BlockIdWrapper8() {
+ trackers = getTrackedEntities.get(getEntityTracker.get(getWorldHandle.invoke(Config.world)));
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public int blockToId(Block block) {
+ return block.getTypeId() << 4 + block.getData();
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void setBlock(World world, int x, int y, int z, int blockState) {
+ if((blockState >> 4) > 256) // Illegal blockstate / corrupted replay
+ blockState = 0;
+
+ world.getBlockAt(x, y, z).setTypeIdAndData(blockState >> 4, (byte)(blockState & 0b1111), false);
+ }
+
+ private static final Class> entityTrackerEntry = Reflection.getClass("{nms}.EntityTrackerEntry");
+ private static final Reflection.MethodInvoker get = Reflection.getTypedMethod(intHashMap, "get", Object.class, int.class);
+ private static final Reflection.MethodInvoker updatePlayer = Reflection.getMethod(entityTrackerEntry, "updatePlayer", entityPlayer);
+ @Override
+ public void trackEntity(Player player, int entity) {
+ Object tracker = get.invoke(trackers, entity);
+ if(tracker != null)
+ updatePlayer.invoke(tracker, getPlayer.invoke(player));
+ }
+
+ private static final Reflection.MethodInvoker clearPlayer = Reflection.getMethod(entityTrackerEntry, "a", entityPlayer);
+ @Override
+ public void untrackEntity(Player player, int entity) {
+ Object tracker = get.invoke(trackers, entity);
+ if(tracker != null)
+ clearPlayer.invoke(tracker, getPlayer.invoke(player));
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public Material idToMaterial(int blockState) {
+ if((blockState >> 4) > 256) // Illegal blockstate / corrupted replay
+ blockState = 0;
+
+ return Material.getMaterial(blockState >> 4);
+ }
+}
diff --git a/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/BountifulWrapper8.java b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/BountifulWrapper8.java
new file mode 100644
index 00000000..3d124ff9
--- /dev/null
+++ b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/BountifulWrapper8.java
@@ -0,0 +1,116 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.listener.Recording;
+import de.steamwar.fightsystem.record.GlobalRecorder;
+import net.minecraft.server.v1_8_R3.DataWatcher;
+import net.minecraft.server.v1_8_R3.EntityEnderDragon;
+import net.minecraft.server.v1_8_R3.PacketPlayOutEntityMetadata;
+import net.minecraft.server.v1_8_R3.PacketPlayOutSpawnEntityLiving;
+import org.bukkit.Effect;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Listener;
+import org.bukkit.scoreboard.Team;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class BountifulWrapper8 implements BountifulWrapper {
+
+ public BountifulWrapper8() {
+ EntityEnderDragon dragon = new EntityEnderDragon(null);
+ dragon.setLocation(Config.ArenaRegion.centerX(), -100, Config.ArenaRegion.centerZ(), 0, 0);
+ this.spawnDragonId = dragon.getId();
+ this.spawnDragon = new PacketPlayOutSpawnEntityLiving(dragon);
+ }
+
+ @Override
+ public boolean mainHand(Object packet) {
+ return true;
+ }
+
+ @Override
+ public boolean bowInHand(boolean mainHand, Player p) {
+ return p.getInventory().getItemInHand().getType() == Material.BOW;
+ }
+
+ @Override
+ public void setAttackSpeed(Player player) {
+ // nothing to do
+ }
+
+ @Override
+ public void setNametagVisibility(Team team) {
+ //nothing to do
+ }
+
+ @Override
+ public Listener newDenyArrowPickupListener() {
+ return new Listener() {};
+ }
+
+ @Override
+ public Listener newDenyHandSwapListener() {
+ return new Listener() {};
+ }
+
+ @Override
+ public void recordHandItems(Player player) {
+ GlobalRecorder.getInstance().item(player, Recording.disarmNull(player.getInventory().getItemInHand()), "MAINHAND");
+ }
+
+ @Override
+ public Listener newHandSwapRecorder() {
+ return new Listener() {};
+ }
+
+ @Override
+ public void spawnParticle(World world, String particleName, double x, double y, double z) {
+ world.playEffect(new Location(world, x, y, z), Effect.valueOf(particleName), 1);
+ }
+
+ private final Set seesDragon = new HashSet<>();
+ private final PacketPlayOutSpawnEntityLiving spawnDragon;
+ private final int spawnDragonId;
+ @Override
+ public void sendBar(Player player, FightTeam team, double progress, String text) {
+ seesDragon.removeIf(p -> !p.isOnline());
+
+ if(!seesDragon.contains(player)) {
+ ((CraftPlayer)player).getHandle().playerConnection.sendPacket(spawnDragon);
+ seesDragon.add(player);
+ }
+
+ DataWatcher watcher = new DataWatcher(null);
+ watcher.a(0, (byte) 0x20);
+ watcher.a(2, text);
+ watcher.a(3, (byte) 1);
+ watcher.a(4, (byte) 1);
+ watcher.a(6, (float)(progress * 200));
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(new PacketPlayOutEntityMetadata(spawnDragonId, watcher, true));
+ }
+}
diff --git a/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper8.java b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper8.java
new file mode 100644
index 00000000..8d743ae7
--- /dev/null
+++ b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper8.java
@@ -0,0 +1,55 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.fightsystem.Config;
+import net.minecraft.server.v1_8_R3.Chunk;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
+import org.bukkit.craftbukkit.v1_8_R3.entity.CraftEntity;
+import org.bukkit.entity.Entity;
+
+import java.util.stream.Stream;
+
+public class CraftbukkitWrapper8 implements CraftbukkitWrapper {
+ @Override
+ public void resetChunk(World world, World backup, int x, int z) {
+ net.minecraft.server.v1_8_R3.World w = ((CraftWorld) world).getHandle();
+ Chunk chunk = w.getChunkAt(x, z);
+ ((CraftWorld) backup).getHandle().chunkProviderServer.forceChunkLoad = true;
+ Chunk backupChunk = ((CraftWorld) backup).getHandle().getChunkAt(x, z);
+
+ System.arraycopy(backupChunk.getSections(), 0, chunk.getSections(), 0, chunk.getSections().length);
+ System.arraycopy(backupChunk.heightMap, 0, chunk.heightMap, 0, chunk.heightMap.length);
+ w.tileEntityList.removeAll(chunk.tileEntities.values());
+ chunk.tileEntities.clear();
+ chunk.tileEntities.putAll(backupChunk.tileEntities);
+ }
+
+ @Override
+ public float headRotation(Entity e) {
+ return ((CraftEntity)e).getHandle().getHeadRotation();
+ }
+
+ @Override
+ public Stream> entityIterator() {
+ return ((CraftWorld) Config.world).getHandle().entityList.stream();
+ }
+}
diff --git a/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/FlatteningWrapper8.java b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/FlatteningWrapper8.java
new file mode 100644
index 00000000..c788f060
--- /dev/null
+++ b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/FlatteningWrapper8.java
@@ -0,0 +1,92 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import org.bukkit.DyeColor;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.bukkit.event.block.BlockPhysicsEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+public class FlatteningWrapper8 implements FlatteningWrapper {
+ @Override
+ public DyeColor getSilver() {
+ return DyeColor.SILVER;
+ }
+
+ @Override
+ public boolean isWater(Block block) {
+ Material type = block.getType();
+ return type == Material.WATER || type == Material.STATIONARY_WATER || type == Material.LAVA || type == Material.STATIONARY_LAVA;
+ }
+
+ @Override
+ public boolean removeWater(Block block) {
+ if(isWater(block)){
+ block.setType(Material.AIR);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean containsBlockMeta(ItemMeta meta) {
+ return false;
+ }
+
+ @Override
+ public boolean hasAttributeModifier(ItemStack stack) {
+ return false;
+ }
+
+ @Override
+ public boolean doRecord(BlockPhysicsEvent e) {
+ return e.getChangedType() != e.getBlock().getType();
+ }
+
+ @Override
+ public void forceLoadChunk(World world, int cX, int cZ) {
+ world.setKeepSpawnInMemory(true);
+ }
+
+ @Override
+ public boolean checkPistonMoving(Block block) {
+ return block.getType() == Material.PISTON_MOVING_PIECE;
+ }
+
+ @Override
+ public boolean isFacingWater(Block dispenser) {
+ return false;
+ }
+
+ @Override
+ public boolean isCrouching(Player player) {
+ return false;
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void sendBlockChange(Player player, Block block, Material type) {
+ player.sendBlockChange(block.getLocation(), type, (byte)0);
+ }
+}
diff --git a/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/HullHiderWrapper8.java b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/HullHiderWrapper8.java
new file mode 100644
index 00000000..524842e6
--- /dev/null
+++ b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/HullHiderWrapper8.java
@@ -0,0 +1,55 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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.utils;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.fightsystem.Config;
+import org.bukkit.Material;
+
+import java.util.List;
+
+public class HullHiderWrapper8 implements HullHiderWrapper {
+
+ private static final Reflection.ConstructorInvoker newMultiBlockChange = Reflection.getConstructor("{nms}.PacketPlayOutMultiBlockChange", int.class, short[].class, Reflection.getClass("{nms}.Chunk"));
+ private static final Reflection.MethodInvoker getHandle = Reflection.getMethod("{obc}.CraftChunk", "getHandle");
+ @Override
+ public Object generateBlockChangePacket(List changes) {
+ changes.removeIf(change -> {
+ Material material = Config.world.getBlockAt(change.getX(), change.getY(), change.getZ()).getType();
+ return material == Config.ObfuscateWith || Config.HiddenBlocks.contains(material);
+ });
+
+ if(changes.isEmpty())
+ return null;
+
+ Hull.IntVector chunk = changes.get(0);
+ chunk = new Hull.IntVector(chunk.getX() >> 4, chunk.getY() >> 4, chunk.getZ() >> 4);
+ int xOffset = 16*chunk.getX();
+ int zOffset = 16*chunk.getZ();
+ short[] pos = new short[changes.size()];
+
+ for(int i = 0; i < changes.size(); i++) {
+ Hull.IntVector change = changes.get(i);
+ pos[i] = (short) (((change.getX()-xOffset) << 12) + ((change.getZ()-zOffset) << 8) + change.getY());
+ }
+
+ return newMultiBlockChange.invoke(pos.length, pos, getHandle.invoke(Config.world.getChunkAt(chunk.getX(), chunk.getZ())));
+ }
+}
diff --git a/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/SWSound8.java b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/SWSound8.java
new file mode 100644
index 00000000..d4a6e210
--- /dev/null
+++ b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/SWSound8.java
@@ -0,0 +1,40 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import org.bukkit.Sound;
+
+public class SWSound8 implements SWSound.ISWSound {
+ @Override
+ public Sound getSound(SWSound sound) {
+ switch(sound){
+ case ENTITY_WITHER_DEATH:
+ return Sound.WITHER_DEATH;
+ case BLOCK_NOTE_BASS:
+ return Sound.NOTE_BASS;
+ case BLOCK_NOTE_PLING:
+ return Sound.NOTE_PLING;
+ case ENTITY_GENERIC_EXPLODE:
+ return Sound.EXPLODE;
+ default:
+ return null;
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/WorldOfColorWrapper8.java b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/WorldOfColorWrapper8.java
new file mode 100644
index 00000000..4ddc6844
--- /dev/null
+++ b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/WorldOfColorWrapper8.java
@@ -0,0 +1,50 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import org.bukkit.ChatColor;
+import org.bukkit.Location;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Projectile;
+import org.bukkit.scoreboard.Team;
+
+public class WorldOfColorWrapper8 implements WorldOfColorWrapper {
+ @Override
+ public void setTeamColor(Team team, ChatColor color) {
+ team.setPrefix("§" + color.getChar());
+ }
+
+ @Override
+ public boolean isInBlock(Projectile e) {
+ return false;
+ }
+
+ @Override
+ public void playSound(Location location, Sound sound, String soundCategory, float volume, float pitch) {
+ location.getWorld().playSound(location, sound, volume, pitch);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void sendTitle(Player player, String title, String subtitle, int start, int hold, int stop) {
+ player.sendTitle(title, subtitle);
+ }
+}
diff --git a/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/WorldeditWrapper8.java b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/WorldeditWrapper8.java
new file mode 100644
index 00000000..19854db8
--- /dev/null
+++ b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/WorldeditWrapper8.java
@@ -0,0 +1,147 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import com.sk89q.jnbt.NBTInputStream;
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.Vector;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.blocks.BaseBlock;
+import com.sk89q.worldedit.bukkit.BukkitWorld;
+import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
+import com.sk89q.worldedit.extent.clipboard.Clipboard;
+import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
+import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
+import com.sk89q.worldedit.extent.clipboard.io.SchematicReader;
+import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
+import com.sk89q.worldedit.function.operation.Operations;
+import com.sk89q.worldedit.math.transform.AffineTransform;
+import com.sk89q.worldedit.regions.CuboidRegion;
+import com.sk89q.worldedit.session.ClipboardHolder;
+import com.sk89q.worldedit.world.World;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.sql.SchematicData;
+import de.steamwar.sql.SchematicNode;
+import org.bukkit.DyeColor;
+import org.bukkit.Location;
+import org.bukkit.Material;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.zip.GZIPInputStream;
+
+@SuppressWarnings("deprecation")
+public class WorldeditWrapper8 implements WorldeditWrapper {
+
+ protected static final int COLOR_TO_REPLACE = DyeColor.PINK.getWoolData();
+ protected static final Set colorBlocks = new HashSet<>();
+
+ static {
+ colorBlocks.add(new BaseBlock(Material.WOOL.getId(), COLOR_TO_REPLACE));
+ colorBlocks.add(new BaseBlock(Material.STAINED_GLASS.getId(), COLOR_TO_REPLACE));
+ colorBlocks.add(new BaseBlock(Material.CLAY.getId(), COLOR_TO_REPLACE));
+ colorBlocks.add(new BaseBlock(Material.STAINED_GLASS_PANE.getId(), COLOR_TO_REPLACE));
+ colorBlocks.add(new BaseBlock(Material.CARPET.getId(), COLOR_TO_REPLACE));
+ }
+
+ @Override
+ public void replaceTeamColor(Clipboard clipboard, DyeColor c) throws WorldEditException {
+ Vector minimum = clipboard.getRegion().getMinimumPoint();
+ Map replaceMap = new HashMap<>();
+ colorBlocks.forEach(base -> replaceMap.put(base, new BaseBlock(base.getId(), c.getWoolData())));
+
+ for(int x = 0; x < clipboard.getDimensions().getX(); x++){
+ for(int y = 0; y < clipboard.getDimensions().getY(); y++){
+ for(int z = 0; z < clipboard.getDimensions().getZ(); z++){
+ Vector pos = minimum.add(x, y, z);
+ BaseBlock replacement = replaceMap.get(clipboard.getBlock(pos));
+ if(replacement != null)
+ clipboard.setBlock(pos, replacement);
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getWaterDepth(Clipboard clipboard) {
+ Vector it = clipboard.getMinimumPoint().add(0, 0, 1);
+ int depth = 0;
+ while(!clipboard.getBlock(it).isAir()) {
+ depth++;
+ it = it.add(0, 1, 0);
+ }
+ return depth;
+ }
+
+ @Override
+ public void pasteClipboard(Clipboard clipboard, Location position, org.bukkit.util.Vector offset, AffineTransform aT) {
+ World w = new BukkitWorld(position.getWorld());
+ EditSession e = WorldEdit.getInstance().getEditSessionFactory().getEditSession(w, -1);
+ ClipboardHolder ch = new ClipboardHolder(clipboard, w.getWorldData());
+ ch.setTransform(aT);
+ Operations.completeBlindly(ch.createPaste(e, w.getWorldData()).to(new Vector(position.getX(), position.getY(), position.getZ()).add(
+ aT.apply(new Vector(offset.getX(), offset.getY(), offset.getZ()).add(clipboard.getOrigin()).subtract(clipboard.getMinimumPoint()))
+ ).toBlockPoint()).build());
+ e.flushQueue();
+ }
+
+ @Override
+ public org.bukkit.util.Vector getDimensions(Clipboard clipboard) {
+ Vector dims = clipboard.getDimensions();
+ return new org.bukkit.util.Vector(dims.getBlockX(), dims.getBlockY(), dims.getBlockZ());
+ }
+
+ @Override
+ public Clipboard loadChar(String charName) throws IOException {
+ return new SchematicReader(new NBTInputStream(new GZIPInputStream(new FileInputStream(new File(FightSystem.getPlugin().getDataFolder(), "text/" + charName + ".schematic"))))).read(new BukkitWorld(Config.world).getWorldData());
+ }
+
+ @Override
+ public void saveSchem(SchematicNode schem, Region region, int minY) throws WorldEditException {
+ World w = new BukkitWorld(Config.world);
+ Vector min = new Vector(region.getMinX(), minY, region.getMinZ());
+ CuboidRegion cuboidRegion = new CuboidRegion(w, min, new Vector(region.getMaxX(), region.getMaxY(), region.getMaxZ()).subtract(Vector.ONE));
+ BlockArrayClipboard clipboard = new BlockArrayClipboard(cuboidRegion);
+
+ ForwardExtentCopy forwardExtentCopy = new ForwardExtentCopy(
+ WorldEdit.getInstance().getEditSessionFactory().getEditSession(w, -1), cuboidRegion, clipboard, min
+ );
+ Operations.complete(forwardExtentCopy);
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ try {
+ ClipboardWriter writer = ClipboardFormat.SCHEMATIC.getWriter(outputStream);
+ writer.write(clipboard, w.getWorldData());
+ writer.close();
+ } catch (IOException e) {
+ throw new SecurityException(e);
+ }
+
+ new SchematicData(schem).saveFromBytes(outputStream.toByteArray(), false);
+ }
+}
diff --git a/FightSystem/FightSystem_9/build.gradle.kts b/FightSystem/FightSystem_9/build.gradle.kts
new file mode 100644
index 00000000..8e018b08
--- /dev/null
+++ b/FightSystem/FightSystem_9/build.gradle.kts
@@ -0,0 +1,59 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":FightSystem:FightSystem_Core"))
+ compileOnly(project(":FightSystem:FightSystem_8"))
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:spigot:1.9")
+ compileOnly("de.steamwar:worldedit:1.12")
+}
diff --git a/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/BountifulWrapper9.java b/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/BountifulWrapper9.java
new file mode 100644
index 00000000..96cc9436
--- /dev/null
+++ b/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/BountifulWrapper9.java
@@ -0,0 +1,170 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.listener.Recording;
+import de.steamwar.fightsystem.record.GlobalRecorder;
+import org.bukkit.*;
+import org.bukkit.attribute.Attribute;
+import org.bukkit.attribute.AttributeInstance;
+import org.bukkit.boss.BarColor;
+import org.bukkit.boss.BarStyle;
+import org.bukkit.boss.BossBar;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerPickupArrowEvent;
+import org.bukkit.event.player.PlayerSwapHandItemsEvent;
+import org.bukkit.scoreboard.Team;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class BountifulWrapper9 implements BountifulWrapper {
+
+ private static final Class> enumHand = Reflection.getClass("{nms.world}.EnumHand");
+ private static final Object mainHand = enumHand.getEnumConstants()[0];
+ private static final Reflection.FieldAccessor> blockPlaceHand = Reflection.getField(Recording.blockPlacePacket, enumHand, 0);
+
+ @Override
+ public boolean mainHand(Object packet) {
+ return blockPlaceHand.get(packet) == mainHand;
+ }
+
+ @Override
+ public boolean bowInHand(boolean mainHand, Player p) {
+ return (mainHand ? p.getInventory().getItemInMainHand() : p.getInventory().getItemInOffHand()).getType() == Material.BOW;
+ }
+
+ @Override
+ public void setAttackSpeed(Player player) {
+ AttributeInstance attribute = player.getAttribute(Attribute.GENERIC_ATTACK_SPEED);
+ attribute.setBaseValue(16);
+ }
+
+ @Override
+ public void setNametagVisibility(Team team) {
+ team.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.FOR_OWN_TEAM);
+ }
+
+ @Override
+ public Listener newDenyArrowPickupListener() {
+ return new Listener() {
+ @EventHandler
+ public void onArrowPickup(PlayerPickupArrowEvent e){
+ if(Fight.fighting(e.getPlayer()))
+ e.setCancelled(true);
+ }
+ };
+ }
+
+ @Override
+ public Listener newDenyHandSwapListener() {
+ return new Listener() {
+ @EventHandler
+ public void onSwapItems(PlayerSwapHandItemsEvent event) {
+ if(Fight.fighting(event.getPlayer()))
+ event.setCancelled(true);
+ }
+ };
+ }
+
+ @Override
+ public void recordHandItems(Player player) {
+ GlobalRecorder.getInstance().item(player, Recording.disarmNull(player.getInventory().getItemInMainHand()), "MAINHAND");
+ GlobalRecorder.getInstance().item(player, Recording.disarmNull(player.getInventory().getItemInOffHand()), "OFFHAND");
+ }
+
+ @Override
+ public Listener newHandSwapRecorder() {
+ return new Listener() {
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void onItemSwap(PlayerSwapHandItemsEvent e){
+ if(Recording.isNotSent(e.getPlayer()))
+ return;
+
+ Player player = e.getPlayer();
+ GlobalRecorder.getInstance().item(player, Recording.disarmNull(e.getMainHandItem()), "MAINHAND");
+ GlobalRecorder.getInstance().item(player, Recording.disarmNull(e.getOffHandItem()), "OFFHAND");
+ }
+ };
+ }
+
+ @Override
+ public void spawnParticle(World world, String particleName, double x, double y, double z) {
+ world.spawnParticle(Particle.valueOf(particleName), x, y, z, 1);
+ }
+
+ private final Map barMap = new HashMap<>();
+ @Override
+ public void sendBar(Player player, FightTeam team, double progress, String text) {
+ barMap.keySet().removeIf(p -> !p.isOnline());
+
+ if(!barMap.containsKey(player)) {
+ BossBar bar = Bukkit.createBossBar(player.getName(), BarColor.WHITE, BarStyle.SOLID);
+ barMap.put(player, bar);
+ bar.addPlayer(player);
+ }
+
+ BossBar bar = barMap.get(player);
+ BarColor color = chat2bar(team.getColor());
+ if(bar.getColor() != color)
+ bar.setColor(color);
+
+ if(bar.getProgress() != progress)
+ bar.setProgress(progress);
+
+ if(!bar.getTitle().equals(text))
+ bar.setTitle(text);
+ }
+
+ private BarColor chat2bar(ChatColor color) {
+ switch(color) {
+ case DARK_BLUE:
+ case DARK_AQUA:
+ case BLUE:
+ case AQUA:
+ return BarColor.BLUE;
+ case GREEN:
+ case DARK_GREEN:
+ return BarColor.GREEN;
+ case DARK_RED:
+ case RED:
+ return BarColor.RED;
+ case DARK_PURPLE:
+ return BarColor.PURPLE;
+ case GOLD:
+ case YELLOW:
+ return BarColor.YELLOW;
+ case LIGHT_PURPLE:
+ return BarColor.PINK;
+ case BLACK:
+ case WHITE:
+ case GRAY:
+ case DARK_GRAY:
+ default:
+ return BarColor.WHITE;
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper9.java b/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper9.java
new file mode 100644
index 00000000..54281ed3
--- /dev/null
+++ b/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper9.java
@@ -0,0 +1,58 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.FightWorld;
+import net.minecraft.server.v1_9_R2.Chunk;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_9_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_9_R2.entity.CraftEntity;
+import org.bukkit.entity.Entity;
+
+import java.util.stream.Stream;
+
+public class CraftbukkitWrapper9 implements CraftbukkitWrapper {
+ @Override
+ public void resetChunk(World world, World backup, int x, int z) {
+ net.minecraft.server.v1_9_R2.World w = ((CraftWorld) world).getHandle();
+ Chunk chunk = w.getChunkAt(x, z);
+ Chunk backupChunk = ((CraftWorld) backup).getHandle().getChunkAt(x, z);
+
+ System.arraycopy(backupChunk.getSections(), 0, chunk.getSections(), 0, chunk.getSections().length);
+ System.arraycopy(backupChunk.heightMap, 0, chunk.heightMap, 0, chunk.heightMap.length);
+ w.tileEntityListTick.removeAll(chunk.tileEntities.values());
+ if (!FightWorld.isPAPER()) {
+ w.tileEntityList.removeAll(chunk.tileEntities.values());
+ }
+ chunk.tileEntities.clear();
+ chunk.tileEntities.putAll(backupChunk.tileEntities);
+ }
+
+ @Override
+ public float headRotation(Entity e) {
+ return ((CraftEntity)e).getHandle().getHeadRotation();
+ }
+
+ @Override
+ public Stream> entityIterator() {
+ return ((CraftWorld) Config.world).getHandle().entityList.stream();
+ }
+}
diff --git a/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/SWSound9.java b/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/SWSound9.java
new file mode 100644
index 00000000..3589c09d
--- /dev/null
+++ b/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/SWSound9.java
@@ -0,0 +1,40 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import org.bukkit.Sound;
+
+public class SWSound9 implements SWSound.ISWSound {
+ @Override
+ public Sound getSound(SWSound sound){
+ switch(sound){
+ case ENTITY_WITHER_DEATH:
+ return Sound.ENTITY_WITHER_DEATH;
+ case BLOCK_NOTE_BASS:
+ return Sound.BLOCK_NOTE_BASS;
+ case BLOCK_NOTE_PLING:
+ return Sound.BLOCK_NOTE_PLING;
+ case ENTITY_GENERIC_EXPLODE:
+ return Sound.ENTITY_GENERIC_EXPLODE;
+ default:
+ return null;
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/build.gradle.kts b/FightSystem/FightSystem_Core/build.gradle.kts
new file mode 100644
index 00000000..188af1d3
--- /dev/null
+++ b/FightSystem/FightSystem_Core/build.gradle.kts
@@ -0,0 +1,61 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs("src/")
+ }
+ resources {
+ srcDirs("src/")
+ exclude("**/*.java", "**/*.kt")
+ }
+ }
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ compileOnly(project(":SpigotCore"))
+
+ compileOnly("de.steamwar:worldedit:1.15")
+
+ compileOnly("org.spigotmc:spigot-api:1.19-R0.1-SNAPSHOT")
+ compileOnly("it.unimi.dsi:fastutil:8.5.6")
+ compileOnly("io.netty:netty-all:4.1.68.Final")
+ compileOnly("com.mojang:authlib:1.5.25")
+}
diff --git a/FightSystem/FightSystem_Core/src/config.yml b/FightSystem/FightSystem_Core/src/config.yml
new file mode 100644
index 00000000..ca3645d6
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/config.yml
@@ -0,0 +1,180 @@
+Server:
+ # Base server folder
+ Folder: ""
+ # Server java archive
+ ServerJar: ""
+ # Available arenas
+ Maps: []
+ # Names to address the game mode in the chat interface
+ ChatNames: []
+ # If the game mode should be marked as a historic game mode
+ Historic: false # defaults to false if missing
+ # If ranked matches should be available for the game mode
+ Ranked: false # defaults to false if missing
+
+# The questions that have to be answered to accept the schematic
+CheckQuestions: [] # Disables check schem type if missing
+
+# The available schematic ranks
+Ranks: [] # Disables ranks for this schematic type if missing
+
+Times:
+ # Time in seconds the server stops after starting if nobody joins
+ NoPlayersOnlineDuration: 300 # defaults to 300 if missing
+ # Time in seconds the team leaders have to choose their schematic
+ PreSchemPasteDuration: 120 # defaults to 120 if missing
+ # Time in seconds for preparing
+ SetupDuration: 300 # defaults to 300 if missing
+ # Time in seconds the final countdown is long
+ PreFightDuration: 30 # defaults to 30 if missing
+ # Time in seconds to spectate the arena after the fight
+ SpectatorDuration: 30 # defaults to 30 if missing
+
+Arena:
+ # The amount of blocks the schematics should be pasted under the surface
+ WaterDepth: 0 # defaults to 0 if missing
+ # The outer border of the arena, measured in blocks around the schematic areas
+ Schem2Border:
+ x: 24 # defaults to 24 if missing
+ z: 24 # defaults to 24 if missing
+ # The offset the teams spawn relative to the center of their area
+ SpawnOffset:
+ x: 0 # defaults to 0 if missing
+ y: 0 # defaults to Schematic.Size.y if missing
+ z: 0 # defaults to 0 if missing
+ # The size of the team areas are expanded around the schematics
+ BorderFromSchematic: 12 # defaults to 12 if missing
+ # If ground walkable, teams can walk below the lower arena border during setup
+ GroundWalkable: true # defaults to true if missing
+ # Disable snow and ice melting
+ DisableSnowMelt: false # defaults to false if missing
+ # Allow leaving the arena area as spectator
+ Leaveable: false # defaults to false if missing
+ # Allow missiles to fly to the enemy and not stop at the schem border.
+ AllowMissiles: false # defaults to true if EnterStages are present otherwise 'false'
+
+Schematic:
+ # The size of the schematics
+ Size:
+ x: 0
+ y: 0
+ z: 0
+ # The schematic type that can be chosen in this arena
+ Type: Normal # defaults to Normal if missing
+ # The schematic types that are also allowed to be chosen in this arena
+ SubTypes: [] # defaults to empty List
+ # Shortcut of the schematic type
+ Shortcut: "" # defaults to "" if missing
+ # Spigot (1.8) material for GUIs
+ Material: STONE_BUTTON # defaults to STONE_BUTTON if missing
+ # Manual check of schematic necessary
+ ManualCheck: true # defaults to true if missing
+ # If the schematics should be rotated during pasting
+ Rotate: true # defaults to true if missing
+ # If the schematics should be pasted aligned to the borders instead of centered
+ PasteAligned: false # defaults to false if missing
+ # If only public schematics are allowed
+ OnlyPublicSchematics: false # defaults to false if missing
+ # If the public only force should be completely disabled
+ IgnorePublicOnly: false # defaults to false if missing
+ # If obsidian and bedrock should be replaced during PRE_RUNNING
+ ReplaceObsidianBedrock: false # defaults to false if missing
+ # If the replacement should happen with block updates
+ ReplaceWithBlockupdates: false # defaults to false if missing
+ # If the schematic perparation arena mode is time limited
+ UnlimitedPrepare: false # defaults to false if missing
+ # Maximal amount of blocks allowed in the schematic
+ MaxBlocks: 0 # defaults to 0 (ignored) if missing
+ # Maximal amount of items per dispenser
+ MaxDispenserItems: 128 # defaults to 128 if missing
+ # List of limited material (combinations)
+ # List contains tags Amount (integer) and Materials (List of material names in Spigot 1.12 AND Spigot 1.15 format)
+ Limited:
+ - Materials: []
+ Amount: 0
+
+# The name of the game mode presented to the players
+GameName: WarGear # defaults to WarGear if missing
+# The prefix used for team chats
+TeamChatPrefix: + # defaults to + if missing
+Blue:
+ Name: Blau # defaults to Blue if missing
+ Prefix: '§3' # defaults to '§3' if missing
+Red:
+ Name: Rot # defaults to Red if missing
+ Prefix: '§c' # defaults to '§c' if missing
+
+# The list of active win conditions
+WinConditions: # defaults to none if missing
+ - TIMEOUT
+ # - HEART_RATIO_TIMEOUT
+ # - PERCENT_TIMEOUT
+
+ - ALL_DEAD
+ # - CAPTAIN_DEAD
+
+ # - PERCENT_SYSTEM
+ # - POINTS
+ # - POINTS_AIRSHIP
+
+ # - WATER_TECH_KO
+ # - TIME_TECH_KO
+ # - PUMPKIN_TECH_KO
+
+ # - HELLS_BELLS
+ # - METEOR
+ # - PERSISTENT_DAMAGE
+ # - TNT_DISTRIBUTION
+
+WinConditionParams:
+ # The time of any of the timeout win conditions in seconds
+ TimeoutTime: 1200 # defaults to 1200 if missing
+ # The percentage when any of the percent win conditions limits or triggers a win
+ PercentWin: 7.0 # defaults to 7.0 if missing
+ # Does the percentage still change after the start of the enter phase
+ PercentEntern: true # defaults to true if missing
+ # Is Blocks a whitelist (true) or blacklist (false)
+ BlocksWhitelist: false # defaults to false if missing
+ # Special Blocks (Valid spigot material values) used by the percent win conditions
+ Blocks: [] # defaults to none if missing
+
+Kits:
+ # The kit file for this configuration
+ File: kits.yml # defaults to kits.yml if missing
+ # The default kit for team members
+ MemberDefault: default # defaults to default if missing
+ # The default kit for team leaders
+ LeaderDefault: default # defaults to default if missing
+ # If the personal kit system is active
+ PersonalKits: false # defaults to false if missing
+ # Items (Valid spigot material values) that are not allowed in the personal kit
+ ForbiddenItems: [] # defaults to none if missing
+
+# A list of integers containing the waiting time of this enter stage in the fight
+EnterStages: [] # defaults to no enter stages (empty list) if missing
+
+Techhider:
+ # The tech hider blocks/materials have to be valid lowercase minecraft material names
+ # Activates the tech hider
+ Active: false # defaults to false if missing
+ # Which block the tech hider replaces to.
+ ObfuscateWith: end_stone # defaults to end_stone if missing
+ # A list of all hidden blocks. "water" results in the hiding of all waterlogged blocks as well.
+ HiddenBlocks: [] # defaults to none if missing
+ # The block entity contents that are hidden (here with minecraft:nametag)
+ HiddenBlockEntites: [] # defaults to none if missing
+
+# The following configuration must be in the world folder/config.yml
+#
+# # The lower arena border under which players get damage
+# UnderBorder: 0 # defaults to BlueCorner.y if missing
+# # The lowest corner in all axis of the blue team schematic area
+# BlueCorner:
+# x: 0
+# y: 0
+# z: 0
+# # The offset between the lowest corner of the blue area and the lowest corner of the red area
+# BlueToRed:
+# x: 0 # defaults to 0 if missing
+# y: 0 # defaults to 0 if missing
+# z: 0 # defaults to Schematic.Size.z + 50 if missing
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ArenaMode.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ArenaMode.java
new file mode 100644
index 00000000..e96e0204
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ArenaMode.java
@@ -0,0 +1,56 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+public enum ArenaMode {
+ NORMAL,
+ EVENT,
+ TEST,
+ CHECK,
+ PREPARE,
+ REPLAY;
+
+ public static final Set All = Collections.unmodifiableSet(EnumSet.allOf(ArenaMode.class));
+
+ public static final Set Normal = Collections.unmodifiableSet(EnumSet.of(NORMAL));
+ public static final Set Check = Collections.unmodifiableSet(EnumSet.of(CHECK));
+ public static final Set Event = Collections.unmodifiableSet(EnumSet.of(EVENT));
+ public static final Set Test = Collections.unmodifiableSet(EnumSet.of(TEST, CHECK));
+ public static final Set Prepare = Collections.unmodifiableSet(EnumSet.of(PREPARE));
+ public static final Set Replay = Collections.unmodifiableSet(EnumSet.of(REPLAY, TEST));
+
+ public static final Set AntiReplay = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(REPLAY)));
+ public static final Set AntiTest = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(TEST, CHECK)));
+ public static final Set AntiEvent = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(EVENT)));
+ public static final Set AntiTestCheckPrepare = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(TEST, CHECK, PREPARE)));
+ public static final Set AntiPrepare = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(PREPARE)));
+ public static final Set VariableTeams = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(EVENT, REPLAY, CHECK)));
+ public static final Set ManualTeams = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(EVENT, REPLAY)));
+ public static final Set RankedEvent = Collections.unmodifiableSet(EnumSet.of(EVENT, REPLAY));
+ public static final Set Restartable = Collections.unmodifiableSet(EnumSet.of(NORMAL, TEST));
+ public static final Set NotRestartable = Collections.unmodifiableSet(EnumSet.of(EVENT, REPLAY));
+ public static final Set SoloLeader = Collections.unmodifiableSet(EnumSet.of(TEST, CHECK, PREPARE));
+ public static final Set NotOnBau = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(TEST, CHECK, PREPARE, REPLAY)));
+ public static final Set SeriousFight = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(TEST, CHECK, REPLAY)));
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/Config.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/Config.java
new file mode 100644
index 00000000..d58ebbb7
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/Config.java
@@ -0,0 +1,415 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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;
+
+import de.steamwar.fightsystem.utils.Region;
+import de.steamwar.fightsystem.winconditions.Winconditions;
+import de.steamwar.sql.*;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.Player;
+import org.bukkit.util.Vector;
+
+import java.io.File;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+public class Config {
+
+ private Config(){}
+
+ public static final World world = Bukkit.getWorlds().get(0);
+
+ //Fight sequence
+ public static final int NoPlayerOnlineDuration;
+ public static final int PreSchemPasteDuration;
+ public static final int SetupDuration;
+ public static final int PreFightDuration;
+ public static final int SpectatorDuration;
+
+ // entern parameter
+ public static final List EnterStages;
+
+ //arena parameter
+ public static final Region BluePasteRegion;
+ public static final Region RedPasteRegion;
+ public static final Region BlueExtendRegion;
+ public static final Region RedExtendRegion;
+ public static final Region ArenaRegion;
+ public static final Region PlayerRegion;
+
+ public static final Location TeamBlueSpawn;
+ public static final Location TeamRedSpawn;
+ public static final Location SpecSpawn;
+
+ public static final int BlueToRedX;
+ private static final int BlueToRedY;
+ public static final int BlueToRedZ;
+
+ public static final int PreperationArea;
+ public static final int WaterDepth;
+ public static final boolean GroundWalkable;
+ public static final boolean DisableSnowMelt;
+ public static final boolean ArenaLeaveable;
+ public static final boolean AllowMissiles;
+
+ //schematic parameter
+ public static final boolean RanksEnabled;
+ public static final boolean OnlyPublicSchematics;
+ public static final boolean IgnorePublicOnly;
+ public static final de.steamwar.sql.SchematicType SchematicType;
+ public static final List SubTypes;
+ public static final boolean RedRotate;
+ public static final boolean BlueRotate;
+ public static final boolean PasteAligned;
+ public static final boolean ReplaceObsidianBedrock;
+ public static final boolean ReplaceWithBlockupdates;
+ public static final boolean UnlimitedPrepare;
+
+ //team parameter
+ public static final String TeamRedName;
+ public static final String TeamBlueName;
+ public static final String TeamRedColor;
+ public static final String TeamBlueColor;
+ public static final String GameName;
+ public static final String TeamChatDetection;
+ public static final UUID BlueLeader;
+ public static final UUID RedLeader;
+
+ //Active win conditions
+ public static final Set ActiveWinconditions;
+
+ //win condition parameters
+ public static final int TimeoutTime;
+ public static final double PercentWin;
+ public static final boolean PercentEntern;
+ public static final boolean PercentBlocksWhitelist;
+ public static final Set PercentBlocks;
+
+ //default kits
+ public static final String MemberDefault;
+ public static final String LeaderDefault;
+ public static final boolean PersonalKits;
+ public static final Set ForbiddenItems;
+ public static final String KitFile;
+
+ //tech hider parameter
+ public static final boolean TechhiderActive;
+ public static final Set HiddenBlocks;
+ public static final Set HiddenBlockEntities;
+ public static final Material ObfuscateWith;
+
+ //event parameter
+ public static final EventFight EventKampf;
+ private static final Set Referees;
+ public static final int EventTeamBlueID;
+ public static final int EventTeamRedID;
+ public static final boolean BothTeamsPublic;
+ public static final int MaximumTeamMembers;
+ public static final boolean LiveReplay;
+
+ //check parameter
+ public static final int CheckSchemID;
+ public static final int PrepareSchemID;
+
+ public static final ArenaMode mode;
+
+ //replay system parameter
+ public static final String spectateIP = "127.0.0.1";
+ public static final int SpectatePort;
+ public static final int ReplayID;
+
+ static{
+ CheckSchemID = Integer.parseInt(System.getProperty("checkSchemID", "0"));
+ PrepareSchemID = Integer.parseInt(System.getProperty("prepareSchemID", "0"));
+ ReplayID = Integer.parseInt(System.getProperty("replay", "0"));
+
+ String configFile = System.getProperty("config", "config.yml");
+ if(!new File(FightSystem.getPlugin().getDataFolder(), configFile).exists()) {
+ FightSystem.getPlugin().saveDefaultConfig();
+ Bukkit.getLogger().log(Level.SEVERE, "Arenaconfig fehlt!");
+ Bukkit.shutdown();
+ }
+ FileConfiguration config = YamlConfiguration.loadConfiguration(new File(FightSystem.getPlugin().getDataFolder(), configFile));
+
+ File worldConfigFile = new File(world.getWorldFolder(), "config.yml");
+ if(!worldConfigFile.exists()) {
+ Bukkit.getLogger().log(Level.SEVERE, "Weltconfig fehlt!");
+ Bukkit.shutdown();
+ }
+ FileConfiguration worldconfig = YamlConfiguration.loadConfiguration(worldConfigFile);
+
+ NoPlayerOnlineDuration = config.getInt("Times.NoPlayersOnlineDuration", 300);
+ PreSchemPasteDuration = config.getInt("Times.PreSchemPasteDuration", 120);
+ SetupDuration = config.getInt("Times.SetupDuration", 300);
+ PreFightDuration = config.getInt("Times.PreFightDuration", 30);
+ SpectatorDuration = config.getInt("Times.SpectatorDuration", 30);
+
+ int blueCornerX = worldconfig.getInt("BlueCorner.x");
+ int blueCornerY = worldconfig.getInt("BlueCorner.y");
+ int blueCornerZ = worldconfig.getInt("BlueCorner.z");
+ int underBorder = worldconfig.getInt("UnderBorder", blueCornerY);
+
+ WaterDepth = config.getInt("Arena.WaterDepth", 0);
+ int schem2BorderX = config.getInt("Arena.Schem2Border.x", 24);
+ int schem2BorderZ = config.getInt("Arena.Schem2Border.z", 24);
+ PreperationArea = config.getInt("Arena.BorderFromSchematic", 12);
+ GroundWalkable = config.getBoolean("Arena.GroundWalkable", true);
+ DisableSnowMelt = config.getBoolean("Arena.DisableSnowMelt", false);
+ ArenaLeaveable = config.getBoolean("Arena.Leaveable", false);
+
+ int schemsizeX = config.getInt("Schematic.Size.x");
+ int schemsizeY = config.getInt("Schematic.Size.y");
+ int schemsizeZ = config.getInt("Schematic.Size.z");
+ RanksEnabled = !config.getStringList("Ranks").isEmpty();
+ SchematicType = de.steamwar.sql.SchematicType.fromDB(Objects.requireNonNull(config.getString("Schematic.Type", "normal")));
+ SubTypes = config.getStringList("Schematic.SubTypes").stream().map(de.steamwar.sql.SchematicType::fromDB).collect(Collectors.toList());
+ IgnorePublicOnly = config.getBoolean("Schematic.IgnorePublicOnly", false);
+ boolean rotate = config.getBoolean("Schematic.Rotate", true);
+ PasteAligned = config.getBoolean("Schematic.PasteAligned", false);
+ ReplaceObsidianBedrock = config.getBoolean("Schematic.ReplaceObsidianBedrock", false);
+ ReplaceWithBlockupdates = config.getBoolean("Schematic.ReplaceWithBlockupdates", false);
+ UnlimitedPrepare = config.getBoolean("Schematic.UnlimitedPrepare", false);
+
+ GameName = config.getString("GameName", "WarGear");
+ TeamChatDetection = config.getString("TeamChatPrefix", "+");
+
+ ActiveWinconditions = Collections.unmodifiableSet(config.getStringList("WinConditions").stream().map(Winconditions::valueOf).collect(Collectors.toSet()));
+
+ TimeoutTime = config.getInt("WinConditionParams.TimeoutTime", 1200);
+ PercentWin = config.getDouble("WinConditionParams.PercentWin", 7.0);
+ PercentEntern = config.getBoolean("WinConditionParams.PercentEntern", true);
+ PercentBlocksWhitelist = config.getBoolean("WinConditionParams.BlocksWhitelist", false);
+ PercentBlocks = Collections.unmodifiableSet(config.getStringList("WinConditionParams.Blocks").stream().map(Material::valueOf).collect(Collectors.toSet()));
+
+ EnterStages = Collections.unmodifiableList(config.getIntegerList("EnterStages"));
+ AllowMissiles = config.getBoolean("Arena.AllowMissiles", !EnterStages.isEmpty());
+
+ KitFile = config.getString("Kits.File", "kits.yml");
+ MemberDefault = config.getString("Kits.MemberDefault", "default");
+ LeaderDefault = config.getString("Kits.LeaderDefault", "default");
+ PersonalKits = config.getBoolean("Kits.PersonalKits", false);
+ ForbiddenItems = Collections.unmodifiableSet(config.getStringList("Kits.ForbiddenItems").stream().map(Material::valueOf).collect(Collectors.toSet()));
+
+ TechhiderActive = config.getBoolean("Techhider.Active", false);
+ ObfuscateWith = Material.getMaterial(config.getString("Techhider.ObfuscateWith", "end_stone").toUpperCase());
+ HiddenBlocks = config.getStringList("Techhider.HiddenBlocks").stream().map(String::toUpperCase).map(Material::getMaterial).collect(Collectors.toSet());
+ HiddenBlockEntities = Collections.unmodifiableSet(new HashSet<>(config.getStringList("Techhider.HiddenBlockEntities")));
+
+ if(schemsizeX < 0){
+ schemsizeX = -schemsizeX;
+ blueCornerX = blueCornerX - schemsizeX;
+ }
+ if(schemsizeY < 0){
+ schemsizeY = -schemsizeY;
+ blueCornerY = blueCornerY - schemsizeY;
+ }
+ if(schemsizeZ < 0){
+ schemsizeZ = -schemsizeZ;
+ blueCornerZ = blueCornerZ - schemsizeZ;
+ }
+
+ BlueToRedX = worldconfig.getInt("BlueToRed.x", 0);
+ BlueToRedY = worldconfig.getInt("BlueToRed.y", 0);
+ BlueToRedZ = worldconfig.getInt("BlueToRed.z", schemsizeZ + 50);
+ double teamBlueSpawnOffsetX = config.getDouble("Arena.SpawnOffset.x", 0);
+ double teamBlueSpawnOffsetY = config.getDouble("Arena.SpawnOffset.y", schemsizeY);
+ double teamBlueSpawnOffsetZ = config.getDouble("Arena.SpawnOffset.z", 0);
+
+ int teamRedCornerX = BlueToRedX + blueCornerX;
+ int teamRedCornerY = BlueToRedY + blueCornerY;
+ int teamRedCornerZ = BlueToRedZ + blueCornerZ;
+
+ int teamBluePasteX = blueCornerX + schemsizeX / 2;
+ int teamBluePasteZ = blueCornerZ + schemsizeZ / 2;
+ int teamRedPasteX = teamBluePasteX + BlueToRedX;
+ int teamRedPasteZ = teamBluePasteZ + BlueToRedZ;
+
+ TeamBlueSpawn = new Location(world,
+ teamBluePasteX + 0.5 + teamBlueSpawnOffsetX,
+ blueCornerY + 0.5 + teamBlueSpawnOffsetY,
+ teamBluePasteZ + 0.5 + teamBlueSpawnOffsetZ);
+
+ TeamRedSpawn = new Location(world,
+ teamRedPasteX + 0.5 - teamBlueSpawnOffsetX,
+ teamRedCornerY + 0.5 + teamBlueSpawnOffsetY,
+ teamRedPasteZ + 0.5 - teamBlueSpawnOffsetZ);
+
+ SpecSpawn = new Location(world,
+ teamBluePasteX + BlueToRedX /2.0,
+ blueCornerY + BlueToRedY /2.0 + schemsizeY/2.0,
+ teamBluePasteZ + BlueToRedZ /2.0);
+
+ Vector v1 = TeamBlueSpawn.toVector().subtract(TeamRedSpawn.toVector());
+ double pitch = Math.toDegrees(v1.angle(v1.clone().setY(0)));
+ double yaw = Math.toDegrees(v1.clone().setY(0).angle(new Vector(0, 0, 1)));
+
+ TeamBlueSpawn.setYaw((float) yaw + 180);
+ TeamBlueSpawn.setPitch((float) pitch);
+
+ TeamRedSpawn.setYaw((float) yaw);
+ TeamRedSpawn.setPitch((float) pitch * -1);
+
+
+ boolean teamRedRotate;
+ boolean teamBlueRotate;
+ int arenaMinX;
+ int arenaMaxX;
+ int arenaMinZ;
+ int arenaMaxZ;
+ if(BlueToRedX > 0){
+ arenaMinX = blueCornerX - schem2BorderX;
+ arenaMaxX = teamRedCornerX + schemsizeX + schem2BorderX;
+ teamRedRotate = true;
+ teamBlueRotate = false;
+ }else{
+ arenaMinX = teamRedCornerX - schem2BorderX;
+ arenaMaxX = blueCornerX + schemsizeX + schem2BorderX;
+ teamRedRotate = false;
+ teamBlueRotate = true;
+ }
+ if(BlueToRedZ > 0){
+ arenaMinZ = blueCornerZ - schem2BorderZ;
+ arenaMaxZ = teamRedCornerZ + schemsizeZ + schem2BorderZ;
+ teamRedRotate = true;
+ teamBlueRotate = false;
+ }else{
+ arenaMinZ = teamRedCornerZ - schem2BorderZ;
+ arenaMaxZ = blueCornerZ + schemsizeZ + schem2BorderZ;
+ if(BlueToRedZ != 0){
+ teamRedRotate = false;
+ teamBlueRotate = true;
+ }
+ }
+ if(!rotate){
+ teamRedRotate = false;
+ teamBlueRotate = false;
+ }
+ RedRotate = teamRedRotate;
+ BlueRotate = teamBlueRotate;
+
+ RedPasteRegion = Region.fromSize(teamRedCornerX, teamRedCornerY, teamRedCornerZ, schemsizeX, schemsizeY, schemsizeZ);
+ BluePasteRegion = Region.fromSize(blueCornerX, blueCornerY, blueCornerZ, schemsizeX, schemsizeY, schemsizeZ);
+
+ RedExtendRegion = Region.withExtension(teamRedCornerX, teamRedCornerY, teamRedCornerZ, schemsizeX, schemsizeY, schemsizeZ, PreperationArea, PreperationArea, PreperationArea);
+ BlueExtendRegion = Region.withExtension(blueCornerX, blueCornerY, blueCornerZ, schemsizeX, schemsizeY, schemsizeZ, PreperationArea, PreperationArea, PreperationArea);
+ ArenaRegion = Region.withExtension(arenaMinX, blueCornerY, arenaMinZ, arenaMaxX - arenaMinX, schemsizeY, arenaMaxZ - arenaMinZ, 0, PreperationArea, 0);
+ PlayerRegion = new Region(arenaMinX, underBorder, arenaMinZ, arenaMaxX, world.getMaxHeight(), arenaMaxZ);
+
+ int eventKampfID = Integer.parseInt(System.getProperty("fightID", "0"));
+ if(eventKampfID >= 1){
+ EventKampf = EventFight.get(eventKampfID);
+ if(EventKampf == null){
+ Bukkit.getLogger().log(Level.SEVERE, "Failed to load EventFight");
+ Bukkit.shutdown();
+ }
+
+ assert EventKampf != null;
+ Team team1 = Team.get(EventKampf.getTeamBlue());
+ Team team2 = Team.get(EventKampf.getTeamRed());
+
+ if(team1 == null || team2 == null){
+ Bukkit.getLogger().log(Level.SEVERE, "Failed to load Team");
+ Bukkit.shutdown();
+ }
+
+ assert team1 != null;
+ assert team2 != null;
+ TeamBlueName = team1.getTeamKuerzel();
+ TeamRedName = team2.getTeamKuerzel();
+ TeamBlueColor = "§" + team1.getTeamColor();
+ TeamRedColor = "§" + team2.getTeamColor();
+ EventTeamBlueID = team1.getTeamId();
+ EventTeamRedID = team2.getTeamId();
+ BothTeamsPublic = EventTeamRedID == 0 && EventTeamBlueID == 0;
+ SpectatePort = EventKampf.getSpectatePort();
+ LiveReplay = SpectatePort != 0;
+ Referees = Referee.get(Config.EventKampf.getEventID());
+
+ Event event = Event.get(EventKampf.getEventID());
+ if(BothTeamsPublic) {
+ OnlyPublicSchematics = true;
+ MaximumTeamMembers = Integer.MAX_VALUE;
+ }else{
+ OnlyPublicSchematics = event.publicSchemsOnly();
+ MaximumTeamMembers = event.getMaximumTeamMembers();
+ }
+ }else{
+ //No event
+ TeamRedColor = config.getString("Red.Prefix", "§c");
+ TeamBlueColor = config.getString("Blue.Prefix", "§3");
+ TeamRedName = config.getString("Red.Name", "Rot");
+ TeamBlueName = config.getString("Blue.Name", "Blau");
+ OnlyPublicSchematics = config.getBoolean("Schematic.OnlyPublicSchematics", false);
+ EventTeamBlueID = 0;
+ EventTeamRedID = 0;
+ EventKampf = null;
+ BothTeamsPublic = true;
+ Referees = Collections.emptySet();
+ MaximumTeamMembers = Integer.MAX_VALUE;
+ LiveReplay = false;
+ SpectatePort = -ReplayID;
+ }
+
+ String blueLeader = System.getProperty("blueLeader", null);
+ String redLeader = System.getProperty("redLeader", null);
+ if(blueLeader != null)
+ BlueLeader = UUID.fromString(blueLeader);
+ else
+ BlueLeader = null;
+ if(redLeader != null)
+ RedLeader = UUID.fromString(redLeader);
+ else
+ RedLeader = null;
+
+ if(CheckSchemID != 0){
+ mode = ArenaMode.CHECK;
+ }else if(PrepareSchemID != 0){
+ mode = ArenaMode.PREPARE;
+ }else if(eventKampfID >= 1){
+ mode = ArenaMode.EVENT;
+ }else if(eventKampfID == -1){
+ mode = ArenaMode.TEST;
+ }else if(ReplayID != 0){
+ mode = ArenaMode.REPLAY;
+ }else{
+ mode = ArenaMode.NORMAL;
+ }
+ }
+
+ public static boolean test(){
+ return ArenaMode.Test.contains(mode);
+ }
+ public static boolean replayserver(){
+ return ReplayID < 0;
+ }
+ public static boolean blueNegZ(){
+ return BlueToRedZ > 0;
+ }
+
+ public static boolean isReferee(Player player) {
+ return Referees.contains(SteamwarUser.get(player.getUniqueId()).getId());
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java
new file mode 100644
index 00000000..6e6b1c40
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java
@@ -0,0 +1,230 @@
+/*
+ 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;
+
+import com.comphenix.tinyprotocol.TinyProtocol;
+import de.steamwar.core.Core;
+import de.steamwar.fightsystem.commands.*;
+import de.steamwar.fightsystem.countdown.*;
+import de.steamwar.fightsystem.event.HellsBells;
+import de.steamwar.fightsystem.event.Meteor;
+import de.steamwar.fightsystem.event.PersistentDamage;
+import de.steamwar.fightsystem.event.TNTDistributor;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.fight.FightWorld;
+import de.steamwar.fightsystem.fight.HotbarKit;
+import de.steamwar.fightsystem.listener.Shutdown;
+import de.steamwar.fightsystem.listener.*;
+import de.steamwar.fightsystem.record.FileRecorder;
+import de.steamwar.fightsystem.record.FileSource;
+import de.steamwar.fightsystem.record.GlobalRecorder;
+import de.steamwar.fightsystem.record.LiveRecorder;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.OneShotStateDependent;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.utils.*;
+import de.steamwar.fightsystem.winconditions.*;
+import de.steamwar.message.Message;
+import de.steamwar.sql.SchematicNode;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.plugin.java.JavaPlugin;
+
+public class FightSystem extends JavaPlugin {
+
+ @Getter
+ private static FightSystem plugin;
+
+ private Message message;
+ private FightTeam lastWinner;
+ private String lastWinreason;
+ private TechHiderWrapper techHider;
+ private HullHider hullHider;
+
+ @Override
+ public void onLoad() {
+ plugin = this;
+ }
+
+ @Override
+ public void onEnable() {
+ if(Core.getInstance() == null) {
+ Core.setInstance(this);
+ TinyProtocol.init();
+ }
+
+ message = new Message("de.steamwar.fightsystem.FightSystem", FightSystem.class.getClassLoader());
+
+ new EntityDamage();
+ new WaterRemover();
+ new Permanent();
+ new PistonListener(ArenaMode.AntiTestCheckPrepare, e -> e.setCancelled(true));
+ new PistonListener(ArenaMode.Test, e -> getMessage().broadcastActionbar("PISTON_PUSHED_OUTSIDE"));
+ new PistonListener(ArenaMode.Prepare, e -> {
+ getMessage().broadcast("PISTON_PUSHED_OUTSIDE");
+ shutdown();
+ });
+ new Chat();
+ new ArenaBorder();
+ new IngameDeath();
+ new InFightDamage();
+ new InFightInventory();
+ new DenyWorldInteraction();
+ new DenyInventoryMovement();
+ new EventJoin();
+ new Recording();
+ new Check();
+ new Shutdown();
+ new SetupQuit();
+ new PrepareSchem();
+ new TestJoin();
+ new NormalJoin();
+ new RunningWorldInteraction();
+ new PersonalKitCreator();
+ new ArrowStopper();
+ new StateDependentListener(ArenaMode.All, FightState.All, BountifulWrapper.impl.newDenyArrowPickupListener());
+ new BlockFadeListener();
+ new LeaveableArena();
+ new ClickAnalyzer();
+ new BlockPlaceCollision();
+ new HotbarKit.HotbarKitListener();
+ new JoinRequestListener();
+ new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f));
+
+ new EnterHandler();
+ techHider = new TechHiderWrapper();
+ hullHider = new HullHider();
+ new FightWorld();
+ new FightUI();
+ new FightStatistics();
+ new BungeeFightInfo();
+
+ new WinconditionAllDead();
+ new WinconditionCaptainDead();
+ new WinconditionBlocks(Winconditions.WATER_TECH_KO, "WaterTechKO", "BAR_WATER", FlatteningWrapper.impl::isWater);
+ new WinconditionBlocks(Winconditions.PUMPKIN_TECH_KO, "PumpkinTechKO", "BAR_CANNONS", block -> block.getType() == WinconditionBlocks.PUMPKIN_LANTERN);
+ new WinconditionPercent(Winconditions.PERCENT_SYSTEM, "Percent");
+ new WinconditionPoints();
+ new WinconditionPointsAirShip();
+ new WinconditionTimeout();
+ new WinconditionTimeTechKO();
+ new EventTeamOffWincondition();
+ new WinconditionComparisonTimeout(Winconditions.HEART_RATIO_TIMEOUT, "HeartTimeout", "WIN_MORE_HEALTH", FightTeam::getHeartRatio);
+ new WinconditionComparisonTimeout(Winconditions.PERCENT_TIMEOUT, "PercentTimeout", "WIN_LESS_DAMAGE", team -> -Wincondition.getPercentWincondition().getPercent(team));
+
+ new HellsBells();
+ new Meteor();
+ new PersistentDamage();
+ new TNTDistributor();
+ new WinconditionAmongUs();
+
+ new NoPlayersOnlineCountdown();
+ new PreSchemCountdown();
+ new PostSchemCountdown();
+ new PreRunningCountdown();
+ new SpectateOverCountdown();
+ new EventSpectateCountdown();
+
+ new LeaveCommand();
+ new KitCommand();
+ new RemoveCommand();
+ new RequestsCommand();
+ new InfoCommand();
+ new WGCommand();
+ new TBCommand();
+ new GamemodeCommand();
+ new ReadyCommand();
+ new AkCommand();
+ new LeaderCommand();
+ new LockschemCommand();
+ new StateCommand();
+ new SkipCommand();
+ new TPSWarpCommand();
+ new UnrankCommand();
+ new WinCommand();
+
+ new LiveRecorder();
+ new FileRecorder();
+
+ FileSource.startReplay();
+
+ if(Config.mode == ArenaMode.EVENT) {
+ FightState.setFightState(FightState.PRE_SCHEM_SETUP);
+ }else if(Config.mode == ArenaMode.CHECK){
+ SchematicNode checkSchematicNode = SchematicNode.getSchematicNode(Config.CheckSchemID);
+ Fight.getBlueTeam().setSchem(checkSchematicNode);
+
+ if (checkSchematicNode.getName().endsWith("-prepared")) {
+ SchematicNode unpreparedSchematicNode = SchematicNode.getSchematicNode(checkSchematicNode.getOwner(), checkSchematicNode.getName().substring(0, checkSchematicNode.getName().length() - 9), checkSchematicNode.getParent());
+ if (unpreparedSchematicNode != null) {
+ Fight.getRedTeam().setSchem(unpreparedSchematicNode);
+ }
+ }
+ }else if(Config.mode == ArenaMode.PREPARE) {
+ Fight.getUnrotated().setSchem(SchematicNode.getSchematicNode(Config.PrepareSchemID));
+ }
+ }
+
+ @Override
+ public void onDisable() {
+ GlobalRecorder.getInstance().close();
+ ClickAnalyzer.close();
+ }
+
+ public static void setSpectateState(FightTeam winFightTeam, String winreason, String subtitle, Object... params) {
+ plugin.lastWinner = winFightTeam;
+ plugin.lastWinreason = winreason;
+ FightUI.printWin(winFightTeam, subtitle, params);
+
+ FightState.setFightState(FightState.SPECTATE);
+ }
+
+ public static Message getMessage() {
+ return plugin.message;
+ }
+
+ public static FightTeam getLastWinner() {
+ return plugin.lastWinner;
+ }
+
+ public static String getLastWinreason() {
+ return plugin.lastWinreason;
+ }
+
+ public static TechHiderWrapper getTechHider() {
+ return plugin.techHider;
+ }
+
+ public static HullHider getHullHider() {
+ return plugin.hullHider;
+ }
+
+ public static void shutdown() {
+ //Staggered kick to prevent lobby overloading
+ if(Bukkit.getOnlinePlayers().isEmpty()){
+ Bukkit.shutdown();
+ return;
+ }
+
+ Bukkit.getOnlinePlayers().iterator().next().kickPlayer(null);
+ Bukkit.getScheduler().runTaskLater(plugin, FightSystem::shutdown, 10);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.properties b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.properties
new file mode 100644
index 00000000..3c220d64
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.properties
@@ -0,0 +1,256 @@
+#
+# This file is a part of the SteamWar software.
+#
+# Copyright (C) 2022 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 .
+#
+
+PREFIX=§eArena§8»
+
+# Commands
+NO_TEAM=§cYou are in no team
+FIGHT_ALREADY_STARTED=§cThe fight already started
+NOT_LEADER=§cYou aren't a leader
+PLAYER_UNAVAILABLE=§cThe player is not in an arena
+
+NOT_IN_TEAM=§e{0} §cis not in your team
+
+KIT_UNAVAILABLE=§cThis kit does not exist
+KIT_CHOSEN=§7Selected kit §e{0}
+
+GAMEMODE_NOT_ALLOWED=§cChanging gamemode is not permitted
+GAMEMODE_UNKNOWN=§cUnknown gamemode {0}
+GAMEMODE_HELP=§8/§7gm §8[§egamemode§8]
+
+LEADER_FULL=§cAll teams already have a leader
+ALREADY_IN_TEAM=§cYou are already in a team
+
+LOCKSCHEM_HELP=§8/§7lockschem §8[§eteam§8]
+UNKNOWN_TEAM=§cThis team does not exist
+LOCKSCHEM_LOCKED=§7Schematic locked
+LOCKSCHEM_LOCKED_BY=§cThe schematic was locked by §e{0}
+
+REMOVE_HELP=§8/§eremove §8[§eplayer§8]
+
+NOT_FIGHTLEADER=§cYou are not the fight leader
+WIN_HELP=§8/§7win §8[§eteam §8or §etie§8]
+
+INFO_RANKED=§7Ranked§8: §e{0}
+INFO_LEADER=§7Leader {0}§8: {1}
+INFO_SCHEMATIC=§7Schematic {0}§8: §e{1} §7from {2}, Rank: {3}
+
+TPSWARP_HELP=§8/§7tpswarp §8[§eticks per second§8]
+TPSWARP_SET=§7TPS set to §e{0}
+
+
+# GUI
+STATE_TITLE=Fight state
+STATE_PRE_LEADER_SETUP=§7Team leader waiting phase
+STATE_PRE_SCHEM_SETUP=§7Schematic selection phase
+STATE_POST_SCHEM_SETUP=§7Preparation phase
+STATE_PRE_RUNNING=§eKit distribution
+STATE_RUNNING=§eFighting phase
+STATE_SPECTATE_WIN=§7Victory {0}
+STATE_SPECTATE_TIE=§7Draw
+
+REMOVE_TITLE=Kick player
+
+KIT_SELECTION_TITLE=Kit selection
+KIT_NO_KITS=§cNo kits found
+KIT_CREATE=§eNew kit
+KITNAME_TITLE=Input kit name
+KITNAME_IN_USE=§cThis kit name is already in use
+KIT_SEARCH=§eSearch
+KITSEARCH_TITLE=Search for kit
+
+SCHEM_NO_ENEMY=§cNo schematic selection without an opponent
+SCHEM_TITLE={0} selection
+SCHEM_PUBLIC=§ePublic {0}
+SCHEM_UNCHECKED=§eUnchecked {0}
+SCHEM_PRIVATE=§ePrivate {0}
+SCHEM_NO_PRIVATE=§7No private {0} present
+SCHEM_PRIVATE_FORBIDDEN=§7No private {0} allowed
+
+
+# Countdowns
+COUNTDOWN_MINUTES=§e{0} §7Minutes {1}
+COUNTDOWN_SECONDS=§e{0} §7Seconds {1}
+COUNTDOWN_SECOND=§eOne §7second {1}
+
+ENTERN_COUNTDOWN=until boarding is allowed
+ENTERN_ALLOWED=§eBoarding §7is now allowed
+
+SHUTDOWN_COUNTDOWN=until the server is stopped
+PRE_SCHEM_COUNTDOWN=until a public schematic is chosen
+POST_SCHEM_COUNTDOWN=until the kits are distributed
+PRE_RUNNING_COUNTDOWN=until the fight starts
+RUNNING_COUNTDOWN=until the fight ends
+SPECTATE_COUNTDOWN=until the arena is reset
+
+
+# Fight
+SCHEMATIC_UNLOADABLE=§cUnable to load schematic
+SCHEMATIC_CHOSEN=§7{0} §e{1} §7chosen
+SCHEMATIC_UNCHECKED=§7Team {0} §7has chosen an §eunchecked §7schematic§8!
+TEAM_READY=§aTeam ready
+TEAM_NOT_READY=§c§mTeam ready
+SKIP_READY=§aSkipping to next event
+SKIP_NOT_READY=§c§mSkipping to next event
+TEAM_CHAT={0}{1}§8» {0}{2}
+CHOOSE_KIT=§eChoose kit
+RESPAWN=§eRespawn
+REMOVE_PLAYERS=§cKick player
+CHOOSE_SCHEMATIC=§eChoose {0}
+SCHEMATIC_REQUIRED=§cChoose a schematic first
+
+KIT_PREVIEW_EDIT=§7Edit kit
+KIT_PREVIEW_CHOOSE=§aSelect kit
+KIT_PREVIEW_BACK=§cBack
+KIT_PREVIEW_DELETE=§cDelete kit
+KIT_DELETION_CONFIRMATION=Are you sure you want to delete the kit?
+KIT_DELETION_ABORT=§cCancel
+KIT_DELETION_DELETE=§aDelete
+
+
+# Listener
+NO_ARENA_LEAVING=§cYou may not leave the arena
+CHECK_JOIN_DENIED=§cThere is a schematic in review on this server!
+CHECK_COMMAND_LOCKED=§cThis command is locked during review! Admin will be notified.
+NO_BLOCK_BREAK=§cYou are currently not allowed to break blocks here
+NO_BLOCK_PLACE=§cYou are currently not allowed to place blocks here
+NO_BOW_USAGE=§cYou can only use your bow after the fight has started
+NO_PARTICIPANT=§cYou are no fight participant
+NO_FRIENDLY_FIRE=§cNo friendly fire allowed
+NO_TNT_PLACE=§cYou are not allowed to place tnt
+NO_TELEPORT=§cYou are not allowed to use this teleport function
+OPEN_INVENTORY_TO_CUSTOMIZE=§eOpen inventory to customize your kit
+NO_ENTERN=§cYou may not board
+NO_TEAMAREA=§cYou are not allowed in the team area
+TEST_BECOME_LEADER=§7Become a team leader with §8/§eleader
+PREPARE_SCHEM_DELETED=§cApparently the schematic to be submitted was deleted. submission aborted.
+PREPARE_SCHEM_EXISTS=§cThere is already a schematic with the suffix -prepared, please rename or delete it, submission aborted.
+PREPARE_ACTIVE_PISTON=§cMoving pistons were found in the team area, submission aborted.
+PREPARE_FAILED_SAVING=§cThe schematic could not be saved, submission aborted.
+PREPARE_SENT_IN=§aA team member will review the schematic soon
+PARTICIPANT_CHAT={0} {1}§8» §7{2}
+FIGHTLEADER_CHAT=§e{0}§8» §e{1}
+SPECTATOR_CHAT=§7{0}§8» §7{1}
+PISTON_PUSHED_OUTSIDE=§cA piston pushed a block outside the allowed area!
+
+
+# Replay
+REPLAY_ENDS=§cReplay ended
+OLD_STRING={0}
+
+
+# States
+COMMAND_CURRENTLY_UNAVAILABLE=§cThis command is unavailable at this time of the fight
+
+
+# Utils
+TPS_WARNING=§c{0} §7TPS
+
+UI_PRE_RUNNING=§7Kits distributed
+UI_RUNNING=§aFight started
+UI_SKIP=§7Skipping to next event
+UI_UNRANKED=§7Unranked match
+UI_PLAYER_JOINS=§a§l» {0}{1}
+UI_PLAYER_LEAVES=§c§l« {0}{1}
+UI_LEADER_JOINS=§a§l» {0}Leader {1}
+UI_PLAYER_DEATH={0}{1} §7died
+UI_PLAYER_LEAVE={0}{1} §7left the fight
+UI_WIN={0}Victory {1}
+UI_DRAW=§7Draw
+
+BAR_PRE_LEADER=§7Waiting for team leader
+BAR_PRE_SCHEM={1} §7Schematic selection {0} {2}
+BAR_PREPARE={1} {3} §7Preparation {0} {4} {2}
+BAR_PRE_RUNNING={1} {3} §7Start of fight in {0} {4} {2}
+BAR_RUNNING0={1} {3} {0} {4} {2}
+BAR_RUNNING1={1} {3} {5} {0} {6} {4} {2}
+BAR_RUNNING2={1} {3} {5} {7} {0} {6} {8} {4} {2}
+BAR_RUNNING3={1} {3} {5} {7} {9} {0} {6} {8} {10} {4} {2}
+BAR_TIE={1} §7Draw {0} {2}
+BAR_WIN={1} §7Victory {3} {0} {2}
+BAR_POINTS={0} §8Points
+BAR_POINTS_OF={0}§8/§7{1} §8Points
+BAR_PERCENT={0}§8%
+BAR_CANNONS={0} §8Cannons
+BAR_WATER={0} §8Water
+
+
+# Winconditions
+HELLS_BELLS_COUNTDOWN=until the bombs start dropping
+HELLS_BELLS_START_1=§c!!Careful!! Bombers arriving in about a minute.
+HELLS_BELLS_START_2=§cBombers approaching, arrival in about a minute.
+HELLS_BELLS_START_3=§cBombers spotted on the radar, ETA: one minute.
+HELLS_BELLS_START_4=§cUnknown flying object spotted, arriving in about a minute.
+HELLS_BELLS_START_5=§cFlying object spotted. ETA: one minute.
+HELLS_BELLS_START_6=§cWild bombers appeared, arrival in about a minute.
+HELLS_BELLS_SWAP_1=§aThe bombs are starting to fall more quickly.
+HELLS_BELLS_SWAP_2=§aMore bombers arriving.
+HELLS_BELLS_SWAP_3=§aAdditional bombers sighted.
+HELLS_BELLS_SWAP_4=§aThe bombardement is increasing.
+
+METEOR_COUNTDOWN=until meteors start falling
+METEOR_START_1=§cA meteor shower was detected
+METEOR_START_2=§cShooting starts are 100% more likely to appear during this fight
+METEOR_START_3=§cMeteors sighted, seek shelter immediately!
+METEOR_START_4=§cThe end is near! The meteors will hit us in about a minute.
+METEOR_START_5=§fNEWS §cThere will be multiple meteor showers this afternoon!
+METEOR_START_6=§cAirships still flying are doomed to crash.
+METEOR_SWAP_1=§aIt doesn't stop, the meteors appear to be increasing.
+METEOR_SWAP_2=§aThis was only the beginning, the meteors are arriving faster and more powerful.
+METEOR_SWAP_3=§aIt seems like it won't stop, the meteors are starting to arrive more quickly!
+METEOR_SWAP_4=§aAnother shower has been detected, get to safety!
+
+TECHKO_COUNTDOWN=must have fired a shot by {0}
+
+WIN_FIGHTLEADER=§7Referee decision
+WIN_PERCENT={0} §7is too badly damaged
+WIN_OFFLINE_BOTH=§7Both teams offline
+WIN_OFFLINE={0} §7offline
+WIN_ALL_DEAD={0}All players neutralised
+WIN_LEADER_DEAD={0} neutralised
+WIN_TIME_OVER=§7Time is up!
+WIN_MORE_HEALTH={0} with more remaining health
+WIN_LESS_DAMAGE={0} §7less damaged
+WIN_POINTS={0} has more points
+WIN_POINTS_EQUAL=§7Equal points
+WIN_TECHKO={0} §7is tech K.O.
+WIN_IMPOSTER_DEAD={0} §7killed the imposter
+WIN_CREWMATE_DEAD={0} §7killed all team mates
+
+AMONG_US_IMPOSTER_MESSAGE = §4You are the Imposter§8! §7Kill all your team mates to win the game!
+AMONG_US_IMPOSTER_AMONG_MESSAGE = §4There is an Imposter among us§8! §7Kill him to win the game!
+
+
+# Invites
+JOIN_REQUEST=§7Request join
+JOIN_REQUEST_TITLE=Request join
+JOIN_REQUEST_ALREADY=§cYou have already sent a join request
+JOIN_REQUEST_TEAM=§7Join {0}
+JOIN_REQUEST_CONFIRMATION=§7Join request submitted
+JOIN_REQUEST_NOTIFICATION=§e{0} §7requests joining team {1}§8. §7Accept or decline using §8/§erequests
+
+REQUESTS=§7Open join requests
+REQUESTS_TITLE=Open join requests
+REQUEST_DECLINED=§cJoin of {0} declined
+REQUEST_YOUR_DECLINED=§cYour join request was declined
+REQUESTS_LEFT_CLICK=§eLeft click §7to §eaccept§8!
+REQUESTS_RIGHT_CLICK=§eRight click §7to §edecline§8!
+
+NO_JOIN_REQUEST=§cThe player did not request joining
+NO_CONFIRMATION=§cNo confirmation necessary
\ No newline at end of file
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem_de.properties b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem_de.properties
new file mode 100644
index 00000000..06b07d18
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem_de.properties
@@ -0,0 +1,236 @@
+#
+# This file is a part of the SteamWar software.
+#
+# Copyright (C) 2022 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 .
+#
+
+# Commands
+NO_TEAM=§cDu bist in keinem Team
+FIGHT_ALREADY_STARTED=§cDer Kampf hat bereits begonnen
+NOT_LEADER=§cDu bist kein Leader
+PLAYER_UNAVAILABLE=§cDer Spieler ist nicht in der Arena
+
+NOT_IN_TEAM=§e{0} §cist nicht in deinem Team
+
+KIT_UNAVAILABLE=§cDieses Kit gibt es nicht
+KIT_CHOSEN=§7Kit §e{0} §7gew§hlt
+
+GAMEMODE_NOT_ALLOWED=§cSpielmodusänderung verboten
+GAMEMODE_UNKNOWN=§cUnbekannter Spielmodus {0}
+GAMEMODE_HELP=§8/§7gm §8[§eSpielmodus§8]
+
+LEADER_FULL=§cAlle Teams haben bereits einen Leader
+ALREADY_IN_TEAM=§cDu bist bereits in einem Team
+
+LOCKSCHEM_HELP=§8/§7lockschem §8[§eTeam§8]
+UNKNOWN_TEAM=§cDieses Team existiert nicht
+LOCKSCHEM_LOCKED=§7Schematic gesperrt
+LOCKSCHEM_LOCKED_BY=§cDie Schematic wurde von §e{0} §cgesperrt
+
+REMOVE_HELP=§8/§eremove §8[§eSpieler§8]
+
+NOT_FIGHTLEADER=§cDu bist nicht Kampfleiter
+WIN_HELP=§8/§7win §8[§eTeam §8oder §etie§8]
+
+TPSWARP_HELP=§8/§7tpswarp §8[§eTicks pro Sekunde§8]
+TPSWARP_SET=§7TPS auf §e{0} §7gesetzt
+
+
+# GUI
+STATE_TITLE=Kampfstatus
+STATE_PRE_LEADER_SETUP=§7Teamleaderwartephase
+STATE_PRE_SCHEM_SETUP=§7Schemauswahlphase
+STATE_POST_SCHEM_SETUP=§7Vorbereitungsphase
+STATE_PRE_RUNNING=§eKitausgabe
+STATE_RUNNING=§eKampfphase
+STATE_SPECTATE_WIN=§7Sieg {0}
+STATE_SPECTATE_TIE=§7Unentschieden
+
+REMOVE_TITLE=Spieler rauswerfen
+
+KIT_SELECTION_TITLE=Kitauswahl
+KIT_NO_KITS=§cKeine Kits gefunden
+KIT_CREATE=§eNeues Kit
+KITNAME_TITLE=Kitname eingeben
+KITNAME_IN_USE=§cDieser Kitname wird bereits genutzt
+KIT_SEARCH=§eSuchen
+KITSEARCH_TITLE=Nach Kit suchen
+
+SCHEM_NO_ENEMY=§cKeine Schematicwahl ohne Gegner
+SCHEM_TITLE={0}-Auswahl
+SCHEM_PUBLIC=§eÖffentliches {0}
+SCHEM_UNCHECKED=§eUngeprüftes {0}
+SCHEM_PRIVATE=§ePrivates {0}
+SCHEM_NO_PRIVATE=§7Kein privates {0} vorhanden
+SCHEM_PRIVATE_FORBIDDEN=§7Kein privates {0} erlaubt
+
+
+# Countdowns
+COUNTDOWN_MINUTES=§e{0} §7Minuten {1}
+COUNTDOWN_SECONDS=§e{0} §7Sekunden {1}
+COUNTDOWN_SECOND=§eEine §7Sekunde {1}
+
+ENTERN_COUNTDOWN=bis Entern erlaubt ist
+ENTERN_ALLOWED=§eEntern §7ist nun erlaubt
+
+SHUTDOWN_COUNTDOWN=bis der Server gestoppt wird
+PRE_SCHEM_COUNTDOWN=bis eine Public-Schematic gewählt wird
+POST_SCHEM_COUNTDOWN=bis die Kits verteilt werden
+PRE_RUNNING_COUNTDOWN=bis die Arena freigegeben ist
+RUNNING_COUNTDOWN=bis der Kampf vorbei ist
+SPECTATE_COUNTDOWN=bis die Arena zurückgesetzt wird
+
+
+# Fight
+SCHEMATIC_UNLOADABLE=§cSchematic konnte nicht geladen werden
+SCHEMATIC_CHOSEN=§7{0} §e{1} §7gewählt
+SCHEMATIC_UNCHECKED=§7Team {0} §7hat eine §eungeprüfte §7Schematic gewählt§8!
+TEAM_READY=§aTeam bereit
+TEAM_NOT_READY=§c§mTeam bereit
+SKIP_READY=§aBeschleunigung zum nächsten Event
+SKIP_NOT_READY=§c§mBeschleunigung zum nächsten Event
+CHOOSE_KIT=§eKit wählen
+RESPAWN=§eRespawn
+REMOVE_PLAYERS=§cSpieler rauswerfen
+CHOOSE_SCHEMATIC=§e{0} wählen
+SCHEMATIC_REQUIRED=§cZuerst muss eine Schematic gewählt sein
+
+KIT_PREVIEW_EDIT=§7Kit bearbeiten
+KIT_PREVIEW_CHOOSE=§aKit wählen
+KIT_PREVIEW_BACK=§cZurück
+KIT_PREVIEW_DELETE=§cKit löschen
+KIT_DELETION_CONFIRMATION=Kit wirklich löschen?
+KIT_DELETION_ABORT=§cAbbrechen
+KIT_DELETION_DELETE=§aLöschen
+
+
+# Listener
+NO_ARENA_LEAVING=§cDu darfst die Arena nicht verlassen
+CHECK_JOIN_DENIED=§cAuf diesem Server wird momentan eine Schematic geprüft!
+CHECK_COMMAND_LOCKED=§cDieser Befehl ist beim Prüfen gesperrt! Admin wird benachrichtigt.
+NO_BLOCK_BREAK=§cDu darfst hier derzeit keine Blöcke abbauen
+NO_BLOCK_PLACE=§cDu darfst hier derzeit keine Blöcke setzen
+NO_BOW_USAGE=§cDu darfst den Bogen erst nach Kampfbeginn nutzen
+NO_PARTICIPANT=§cDu bist kein Kampfteilnehmer
+NO_FRIENDLY_FIRE=§cDu darfst deinen Teamkollegen keinen Schaden zufügen
+NO_TNT_PLACE=§cDu darfst kein TNT setzen
+NO_TELEPORT=§cDu darfst diese Teleportfunktion nicht benutzen
+OPEN_INVENTORY_TO_CUSTOMIZE=§eInventar zum Anpassen des Kits öffnen
+NO_ENTERN=§cDu darfst nicht entern
+NO_TEAMAREA=§cDu darfst nicht zu den Teams
+TEST_BECOME_LEADER=§7Werde zum Teamleader mit §8/§eleader
+PREPARE_SCHEM_DELETED=§cAnscheinend wurde die auszufahrende Schematic gelöscht, Einsenden wird abgebrochen.
+PREPARE_SCHEM_EXISTS=§cEs existiert bereits eine Schem mit Namenszusatz -prepared, diese bitte umbenennen oder löschen, Einsenden wird abgebrochen.
+PREPARE_ACTIVE_PISTON=§cIm Teambereich wurden sich noch bewegende Pistons gefunden, Einsenden wird abgebrochen.
+PREPARE_FAILED_SAVING=§cDie Schematic konnte nicht gespeichert werden, Einsenden wird abgebrochen.
+PREPARE_SENT_IN=§aDie Schematic wird nun zeitnah von einem Teammitglied überprüft
+PISTON_PUSHED_OUTSIDE=§cEin Kolben hat einen Block aus dem erlaubten Bereich geschoben!
+
+
+# Replay
+REPLAY_ENDS=§cReplay beendet
+
+
+# States
+COMMAND_CURRENTLY_UNAVAILABLE=§cDieser Befehl ist zu diesem Kampfzeitpunkt nicht verfügbar
+
+
+# Utils
+UI_PRE_RUNNING=§7Kits verteilt
+UI_RUNNING=§aArena freigegeben
+UI_SKIP=§7Sprung zum nächsten Ereignis
+UI_UNRANKED=§7Ungewerteter Kampf
+UI_LEADER_JOINS=§a§l» {0}Leader {1}
+UI_PLAYER_DEATH={0}{1} §7ist gestorben
+UI_PLAYER_LEAVE={0}{1} §7hat den Kampf verlassen
+UI_WIN={0}Sieg {1}
+UI_DRAW=§7Unentschieden
+
+BAR_PRE_LEADER=§7Warten auf Teamleader
+BAR_PRE_SCHEM={1} §7Schemauswahl {0} {2}
+BAR_PREPARE={1} {3} §7Vorbereitung {0} {4} {2}
+BAR_PRE_RUNNING={1} {3} §7Kampfbeginn in {0} {4} {2}
+BAR_TIE={1} §7Unentschieden {0} {2}
+BAR_WIN={1} §7Sieg {3} {0} {2}
+BAR_POINTS={0} §8Punkte
+BAR_POINTS_OF={0}§8/§7{1} §8Punkte
+BAR_PERCENT={0}§8%
+BAR_CANNONS={0} §8Kanonen
+BAR_WATER={0} §8Wasser
+
+
+# Winconditions
+HELLS_BELLS_COUNTDOWN=bis die Bomben fallen
+HELLS_BELLS_START_1=§c!!Achtung!! Bomber im Anflug, noch ca. eine Minute bis zur Ankunft.
+HELLS_BELLS_START_2=§cBomber im Anflug, ca. eine Minute bis zur Ankunft.
+HELLS_BELLS_START_3=§cBomber auf dem Radar gesichtet, geschätzte Ankunftszeit: eine Minute.
+HELLS_BELLS_START_4=§cUnbekanntes Flugobjekt gesichtet, trifft in ca. einer Minute ein.
+HELLS_BELLS_START_5=§cFlugobjekt gesichtet. Ankunft in ca. einer Minute.
+HELLS_BELLS_START_6=§cBomber erschienen, Ankunft ca. eine Minute.
+HELLS_BELLS_SWAP_1=§aDie Bomben fallen nun schneller.
+HELLS_BELLS_SWAP_2=§aMehr Bomber im Anflug.
+HELLS_BELLS_SWAP_3=§aZusätzliche Bomber gesichtet.
+HELLS_BELLS_SWAP_4=§aDas Bombardement scheint sich zu erhöhen.
+
+METEOR_COUNTDOWN=bis es Meteore regnet
+METEOR_START_1=§cEin Meteorschauer wurden am Himmel entdeckt
+METEOR_START_2=§cSternschnuppen sind diesen Fight 100% wahrscheinlicher
+METEOR_START_3=§cEs wurden Meteoriten am Himmel entdeckt, begeben sie sich umgehend in Sicherheit!
+METEOR_START_4=§cDer Untergang steht nahe! Die Meteoriten werden uns in etwa einer Minute treffen.
+METEOR_START_5=§fNEWS §cHeute Nachmittag wird es größere Meteoriten Schauer geben!
+METEOR_START_6=§cNoch fliegende Airships sind dem Absturz geweiht.
+METEOR_SWAP_1=§aEs hört nicht auf, die Meteoriten scheinen mehr zu werden.
+METEOR_SWAP_2=§aDas war erst der Anfang, die Meteoriten kommen immer schneller und machen mehr Schaden.
+METEOR_SWAP_3=§aEs scheint als würde es nicht aufhören, die Meteoriten kommen nur schneller!
+METEOR_SWAP_4=§aEin weiterer Schauer ist entdeckt worden, begebt euch in Sicherheit!
+
+TECHKO_COUNTDOWN=bis {0} §7einen Schuss abgegeben haben muss
+
+WIN_FIGHTLEADER=§7Kampfleiterentscheidung
+WIN_PERCENT={0} §7zu beschädigt
+WIN_OFFLINE_BOTH=§7Beide Teams offline
+WIN_OFFLINE={0} §7offline
+WIN_ALL_DEAD={0}Alle Spieler kampfunfähig
+WIN_LEADER_DEAD={0} kampfunfähig
+WIN_TIME_OVER=§7Zeit abgelaufen
+WIN_MORE_HEALTH={0} mit mehr verbleibenden Leben
+WIN_LESS_DAMAGE={0} §7weniger beschädigt
+WIN_POINTS={0} hat mehr Punkte
+WIN_POINTS_EQUAL=§7Gleicher Punktestand
+WIN_TECHKO={0} §7ist Tech K.O.
+WIN_IMPOSTER_DEAD={0} §7 hat den Imposter getötet
+WIN_CREWMATE_DEAD={0} §7 hat alle Kameraden getötet
+
+AMONG_US_IMPOSTER_MESSAGE = §4Du bist ein Imposter§8! §7Du musst alle Kameraden töten, um zu gewinnen.
+AMONG_US_IMPOSTER_AMONG_MESSAGE = §4Es ist ein Imposter unter uns§8! §7Tötet ihn, um das Spiel zu gewinnen!
+
+# Invites
+JOIN_REQUEST=§7Teambeitritt anfragen
+JOIN_REQUEST_TITLE=Teambeitritt anfragen
+JOIN_REQUEST_ALREADY=§cDu hast bereits ein Team um Beitritt angefragt
+JOIN_REQUEST_TEAM={0} §7beitreten
+JOIN_REQUEST_CONFIRMATION=§7Teambeitritt angefragt
+JOIN_REQUEST_NOTIFICATION=§e{0} §7möchte Team {1} §7beitreten§8. §7Akzeptiere oder lehne ab mit §8/§erequests
+
+REQUESTS=§7Offene Beitrittsanfragen
+REQUESTS_TITLE=Offene Beitrittsanfragen
+REQUEST_DECLINED=§cBeitritt von {0} abgelehnt
+REQUEST_YOUR_DECLINED=§cDeine Betrittsanfrage wurde abgelehnt
+REQUESTS_LEFT_CLICK=§eLinksklick §7um §eanzunehmen§8!
+REQUESTS_RIGHT_CLICK=§eRechtsklick §7um §eabzulehnen§8!
+
+NO_JOIN_REQUEST=§cDer Spieler hat noch keinen Beitritt angefragt
+NO_CONFIRMATION=§cKeine Zustimmung nötig
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java
new file mode 100644
index 00000000..6f5cb005
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/AI.java
@@ -0,0 +1,294 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2023 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.ai;
+
+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.listener.Chat;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.OneShotStateDependent;
+import de.steamwar.fightsystem.utils.Region;
+import de.steamwar.sql.SchematicNode;
+import de.steamwar.sql.SteamwarUser;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.Note;
+import org.bukkit.block.Block;
+import org.bukkit.block.Lectern;
+import org.bukkit.block.data.BlockData;
+import org.bukkit.block.data.Openable;
+import org.bukkit.block.data.Powerable;
+import org.bukkit.block.data.type.Comparator;
+import org.bukkit.block.data.type.NoteBlock;
+import org.bukkit.block.data.type.Repeater;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Villager;
+import org.bukkit.event.player.PlayerTeleportEvent;
+import org.bukkit.scheduler.BukkitTask;
+import org.bukkit.util.Vector;
+
+import java.util.*;
+import java.util.logging.Level;
+
+public abstract class AI {
+
+ private static final Map ais = new HashMap<>();
+
+ static {
+ new OneShotStateDependent(ArenaMode.All, FightState.Spectate, () -> {
+ ais.values().forEach(AI::stop);
+ ais.clear();
+ });
+ }
+
+ public static AI getAI(UUID uuid) {
+ return ais.get(uuid);
+ }
+
+ private final FightTeam team;
+ private final LivingEntity entity;
+ private final BukkitTask task;
+ private final Queue queue = new ArrayDeque<>();
+
+ protected AI(FightTeam team, SteamwarUser user) {
+ this.team = team;
+
+ entity = (LivingEntity) Config.world.spawnEntity(Config.SpecSpawn, EntityType.VILLAGER);
+ entity.setCustomName(user.getUserName());
+ ((Villager)entity).setAware(false);
+
+ task = Bukkit.getScheduler().runTaskTimer(FightSystem.getPlugin(), this::run, 1, 1);
+ ais.put(entity.getUniqueId(), this);
+ team.addMember(entity, user);
+ }
+
+ public abstract SchematicNode chooseSchematic();
+
+ public boolean acceptJoinRequest(Player player, FightTeam team) {
+ return true;
+ }
+
+ protected abstract void plan();
+
+ public void stop() {
+ if(!entity.isDead())
+ entity.remove();
+
+ if(!task.isCancelled())
+ task.cancel();
+ }
+
+ public LivingEntity getEntity() {
+ return entity;
+ }
+
+ protected void setReady() {
+ if(FightState.getFightState() != FightState.POST_SCHEM_SETUP)
+ return;
+
+ if(team.getLeader().getEntity() != entity)
+ return;
+
+ team.setReady(true);
+ }
+
+ protected void chat(String message) {
+ FightSystem.getPlugin().getLogger().log(Level.INFO, () -> entity.getName() + "» " + message);
+ Chat.broadcastChat("PARTICIPANT_CHAT", team.getColoredName(), entity.getName(), message);
+ }
+
+ protected Vector getPosition() {
+ Location location = entity.getLocation();
+ Region extend = team.getExtendRegion();
+ if(Fight.getUnrotated() == team)
+ return new Vector(
+ location.getX() - extend.getMinX(),
+ location.getY() - team.getSchemRegion().getMinY(),
+ location.getZ() - extend.getMinZ()
+ );
+ else
+ return new Vector(
+ extend.getMaxX() - location.getX(),
+ location.getY() - team.getSchemRegion().getMinY(),
+ extend.getMaxZ() - location.getZ()
+ );
+ }
+
+ protected Material getBlock(Vector pos) {
+ queue.add(new Action(1));
+ return translate(pos, true).getBlock().getType();
+ }
+
+ protected boolean isPowered(Vector pos) {
+ queue.add(new Action(1));
+ return translate(pos, true).getBlock().isBlockPowered();
+ }
+
+ protected void setTNT(Vector pos) {
+ queue.add(new Action(1) {
+ @Override
+ public void run() {
+ if(FightState.getFightState() != FightState.RUNNING)
+ return;
+
+ Location location = translate(pos, true);
+ if(interactionDistanceViolation(location))
+ return;
+
+ Block block = location.getBlock();
+ if(block.getType() == Material.AIR)
+ block.setType(Material.TNT);
+ }
+ });
+ }
+
+ protected void interact(Vector pos) {
+ queue.add(new Action(1) {
+ @Override
+ public void run() {
+ Location location = translate(pos, true);
+ if(interactionDistanceViolation(location))
+ return;
+
+ interact(location.getBlock());
+ }
+ });
+ }
+
+ protected void interact(Vector pos, int n) {
+ queue.add(new Action(1) {
+ @Override
+ public void run() {
+ Location location = translate(pos, true);
+ if (interactionDistanceViolation(location))
+ return;
+ Block block = location.getBlock();
+ BlockData data = block.getBlockData();
+ if (data instanceof Repeater) {
+ Repeater repeater = (Repeater) data;
+ repeater.setDelay(n);
+ } else if (data instanceof Lectern) {
+ Lectern lectern = (Lectern) data;
+ lectern.setPage(n);
+ }
+ block.setBlockData(data);
+ }
+ });
+ }
+
+ protected void move(Vector pos) {
+ queue.add(new Action(2) {
+ @Override
+ public void run() {
+ Location location = entity.getLocation();
+ Location target = translate(pos, false);
+ if(Math.abs(location.getX() - target.getX()) > 1 || Math.abs(location.getY() - target.getY()) > 1.2 || Math.abs(location.getZ() - target.getZ()) > 1) {
+ FightSystem.getPlugin().getLogger().log(Level.INFO, () -> entity.getName() + ": Overdistance movement " + location.toVector() + " " + target.toVector());
+ return;
+ }
+
+ if(!team.getFightPlayer(entity).canEntern() && !team.getExtendRegion().inRegion(target))
+ return;
+
+ entity.teleport(target, PlayerTeleportEvent.TeleportCause.COMMAND);
+ }
+ });
+ }
+
+ private boolean interactionDistanceViolation(Location location) {
+ return location.distance(entity.getEyeLocation()) > 5;
+ }
+
+ private void interact(Block block) {
+ BlockData data = block.getBlockData(); //TODO only 1.14+ compatible at the moment
+ if (data instanceof NoteBlock) {
+ NoteBlock noteBlock = (NoteBlock) data;
+ Note note = noteBlock.getNote();
+ noteBlock.setNote(note.isSharped() ? note.flattened() : note.sharped());
+ } else if (data instanceof Openable) {
+ Openable openable = (Openable) data;
+ openable.setOpen(!openable.isOpen());
+ } else if (data instanceof Comparator) {
+ Comparator comparator = (Comparator) data;
+ comparator.setMode(Comparator.Mode.values()[1 - comparator.getMode().ordinal()]);
+ } else if (data instanceof Powerable) {
+ Material type = block.getType();
+ Powerable powerable = (Powerable) data;
+ boolean isPowered = powerable.isPowered();
+
+ if(type.name().endsWith("BUTTON")) {
+ if(isPowered)
+ return;
+
+ Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> {
+ if(!block.getType().name().endsWith("BUTTON"))
+ return;
+
+ powerable.setPowered(false);
+ block.setBlockData(powerable);
+ }, type.name().endsWith("STONE_BUTTON") ? 20 : 30);
+ }
+
+ powerable.setPowered(!isPowered);
+ }
+ block.setBlockData(data);
+ }
+
+ private void run() {
+ if(queue.isEmpty())
+ plan();
+
+ if(!queue.isEmpty() && --queue.peek().delay == 0)
+ queue.poll().run();
+ }
+
+ private Location translate(Vector pos, boolean blockPos) {
+ Region extend = team.getExtendRegion();
+ if(Fight.getUnrotated() == team)
+ return new Location(
+ Config.world,
+ pos.getX() + extend.getMinX(),
+ pos.getY() + team.getSchemRegion().getMinY(),
+ pos.getZ() + extend.getMinZ()
+ );
+ else
+ return new Location(
+ Config.world,
+ extend.getMaxX() - pos.getX() - (blockPos ? 1 : 0),
+ pos.getY() + team.getSchemRegion().getMinY(),
+ extend.getMaxZ() - pos.getZ() - (blockPos ? 1 : 0)
+ );
+ }
+
+ private static class Action {
+ private int delay;
+ public Action(int delay) {
+ this.delay = delay;
+ }
+
+ public void run() {}
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java
new file mode 100644
index 00000000..2a89353c
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelAI.java
@@ -0,0 +1,60 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2023 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.ai;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.sql.SchematicNode;
+import de.steamwar.sql.SteamwarUser;
+import org.bukkit.util.Vector;
+
+import java.util.List;
+import java.util.Random;
+
+public class LixfelAI extends AI {
+
+ private final Random random = new Random();
+ private LixfelPathplanner pathplanner;
+
+ public LixfelAI(FightTeam team, String user) {
+ super(team, SteamwarUser.get(user));
+ }
+
+
+ @Override
+ public SchematicNode chooseSchematic() {
+ List publics = SchematicNode.getAllSchematicsOfType(0, Config.SchematicType.toDB());
+ SchematicNode schem = publics.get(new Random().nextInt(publics.size()));
+ pathplanner = new LixfelPathplanner(schem);
+ return schem;
+ }
+
+ @Override
+ protected void plan() {
+ setReady();
+ Vector destination = pathplanner.getWalkable().get(random.nextInt(pathplanner.getWalkable().size()));
+ List path = pathplanner.plan(getPosition(), destination);
+ if(!path.isEmpty())
+ chat("Path size: " + path.size());
+ for(Vector p : path) {
+ move(p);
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java
new file mode 100644
index 00000000..b2e593e8
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/ai/LixfelPathplanner.java
@@ -0,0 +1,141 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2023 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.ai;
+
+import com.sk89q.worldedit.extent.clipboard.Clipboard;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.regions.Region;
+import com.sk89q.worldedit.world.block.BlockType;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.sql.SchematicData;
+import de.steamwar.sql.SchematicNode;
+import org.bukkit.util.Vector;
+
+import java.io.IOException;
+import java.util.*;
+
+public class LixfelPathplanner {
+
+ private static BlockType getBlockType(Clipboard clipboard, BlockVector3 vector) {
+ return clipboard.getBlock(vector).getBlockType();
+ }
+
+ private static boolean nonsolid(Clipboard clipboard, BlockVector3 vector) {
+ return !getBlockType(clipboard, vector).getMaterial().isSolid();
+ }
+
+ private static Vector toBukkit(BlockVector3 vector) {
+ return new Vector(vector.getX() + 0.5, vector.getY(), vector.getZ() + 0.5);
+ }
+
+ private final List walkable = new ArrayList<>();
+ private final Map neighbours = new HashMap<>();
+
+ public LixfelPathplanner(SchematicNode schem) {
+ try {
+ fillWalkable(new SchematicData(schem).load());
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public List getWalkable() {
+ return walkable;
+ }
+
+ private void fillWalkable(Clipboard clipboard) {
+ BlockVector3 min = clipboard.getRegion().getMinimumPoint().subtract(Config.PreperationArea, 0, Config.PreperationArea); //TODO assumes nonextended Schematic with maximal size
+ Region region = clipboard.getRegion();
+ clipboard.getRegion().forEach(vector -> {
+ BlockVector3 below = vector.subtract(0, 1, 0);
+ if(!region.contains(below))
+ return;
+
+ BlockType belowMaterial = getBlockType(clipboard, below);
+ BlockVector3 above = vector.add(0, 1, 0);
+ if(nonsolid(clipboard, vector)) {
+ if(
+ (belowMaterial.getMaterial().isSolid() || belowMaterial.getId().equals("minecraft:ladder")) &&
+ (!region.contains(above) || nonsolid(clipboard, above))
+ )
+ walkable.add(toBukkit(vector.subtract(min)));
+ } else {
+ if(!region.contains(above))
+ walkable.add(toBukkit(above.subtract(min)));
+ }
+ });
+
+ for(Vector vector : walkable) {
+ neighbours.put(vector, walkable.stream().filter(neighbour -> neighbouring(neighbour, vector)).filter(neighbour -> neighbour != vector).toArray(Vector[]::new));
+ }
+ }
+
+ public List planToAnywhere(Vector start, Vector destination) {
+ Vector intermediate = walkable.stream().filter(vector -> neighbouring(vector, destination)).findAny().orElse(null);
+
+ if(intermediate == null)
+ return Collections.emptyList();
+
+ List plan = plan(start, intermediate);
+ plan.add(destination);
+
+ return plan;
+ }
+
+ public List plan(Vector start, Vector destination) {
+ if(neighbouring(start, destination))
+ return Collections.singletonList(destination);
+
+ Map approach = new HashMap<>();
+ Set checking = Collections.singleton(destination);
+
+ while(!checking.isEmpty()) {
+ Set toCheck = new HashSet<>();
+ for(Vector current : checking) {
+ Vector firstStep = Arrays.stream(neighbours.get(current))
+ .filter(vector -> !approach.containsKey(vector))
+ .filter(next -> {
+ approach.put(next, current);
+ toCheck.add(next);
+ return neighbouring(next, start);
+ })
+ .findAny().orElse(null);
+
+ if(firstStep != null) {
+ List path = new ArrayList<>();
+ path.add(firstStep);
+
+ while(path.get(path.size()-1) != destination) {
+ path.add(approach.get(path.get(path.size()-1)));
+ }
+
+ return path;
+ }
+ }
+ checking = toCheck;
+ }
+
+ return Collections.emptyList();
+ }
+
+ private boolean neighbouring(Vector a, Vector b) {
+ return Math.abs(a.getX() - b.getX()) <= 1 && Math.abs(a.getY() - b.getY()) <= 1 && Math.abs(a.getZ() - b.getZ()) <= 1;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/AkCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/AkCommand.java
new file mode 100644
index 00000000..27e6b797
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/AkCommand.java
@@ -0,0 +1,57 @@
+/*
+ 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.commands;
+
+import de.steamwar.core.Core;
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.fight.Kit;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCommand;
+import de.steamwar.sql.SteamwarUser;
+import de.steamwar.sql.UserPerm;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class AkCommand implements CommandExecutor {
+
+ public AkCommand() {
+ new StateDependentCommand(ArenaMode.Test, FightState.All, "ak", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player)) {
+ return false;
+ }
+ Player player = (Player) sender;
+ if(!player.isOp())
+ return false;
+
+ if(!SteamwarUser.get(player.getUniqueId()).hasPerm(UserPerm.ADMINISTRATION) && Core.getInstance() != FightSystem.getPlugin()){
+ return false;
+ }
+
+ Kit.createKit(args[0], player);
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/Commands.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/Commands.java
new file mode 100644
index 00000000..b880fa6d
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/Commands.java
@@ -0,0 +1,170 @@
+/*
+ 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.commands;
+
+import com.comphenix.tinyprotocol.Reflection;
+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.fight.Kit;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.sql.PersonalKit;
+import de.steamwar.sql.SteamwarUser;
+import lombok.experimental.UtilityClass;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.Bukkit;
+import org.bukkit.command.Command;
+import org.bukkit.command.SimpleCommandMap;
+import org.bukkit.entity.Player;
+
+@UtilityClass
+public class Commands {
+
+ private static final Reflection.FieldAccessor commandMap = Reflection.getField("{obc}.CraftServer", "commandMap", SimpleCommandMap.class);
+ public static void injectCommand(Command cmd) {
+ commandMap.get(Bukkit.getServer()).register("FightSystem", cmd);
+ }
+
+ private static void errNoTeam(Player p){
+ FightSystem.getMessage().sendPrefixless("NO_TEAM", p, ChatMessageType.ACTION_BAR);
+ }
+
+ static boolean checkSetup(Player p){
+ if(!FightState.setup()){
+ FightSystem.getMessage().sendPrefixless("FIGHT_ALREADY_STARTED", p, ChatMessageType.ACTION_BAR);
+ return true;
+ }
+ return false;
+ }
+
+ private static FightPlayer checkGetPlayer(Player p){
+ FightPlayer fightPlayer = Fight.getFightPlayer(p);
+ if(fightPlayer == null){
+ errNoTeam(p);
+ }
+ return fightPlayer;
+ }
+
+ public static FightPlayer checkGetLeader(Player p){
+ FightPlayer fightPlayer = checkGetPlayer(p);
+ if(fightPlayer != null && !fightPlayer.isLeader()){
+ FightSystem.getMessage().sendPrefixless("NOT_LEADER", p, ChatMessageType.ACTION_BAR);
+ fightPlayer = null;
+ }
+ return fightPlayer;
+ }
+
+ public static FightTeam checkGetTeam(Player p){
+ FightTeam fightTeam = Fight.getPlayerTeam(p);
+ if(fightTeam == null){
+ errNoTeam(p);
+ }
+ return fightTeam;
+ }
+
+ private static Player checkGetPlayer(Player p, String t){
+ Player target = Bukkit.getPlayer(t);
+ if(target == null) {
+ FightSystem.getMessage().sendPrefixless("PLAYER_UNAVAILABLE", p, ChatMessageType.ACTION_BAR);
+ }
+ return target;
+ }
+
+ static void toggleReady(Player p){
+ FightTeam fightTeam = checkGetTeam(p);
+ if(fightTeam == null || checkGetLeader(p) == null)
+ return;
+
+ fightTeam.setReady(!fightTeam.isReady());
+ }
+
+ static void toggleSkip(Player p){
+ FightTeam fightTeam = checkGetTeam(p);
+ if(fightTeam == null || checkGetLeader(p) == null)
+ return;
+
+ fightTeam.skip();
+ }
+
+ static void leaveTeam(Player p){
+ if(checkSetup(p))
+ return;
+
+ FightTeam fightTeam = checkGetTeam(p);
+ if(fightTeam == null)
+ return;
+
+ fightTeam.removePlayer(p);
+ }
+
+ static void kick(Player p, String kicked){
+ if(checkSetup(p))
+ return;
+
+ FightTeam fightTeam = checkGetTeam(p);
+ if(fightTeam == null)
+ return;
+
+ FightPlayer fightPlayer = checkGetLeader(p);
+ if(fightPlayer == null)
+ return;
+
+ Player target = checkGetPlayer(p, kicked);
+ if(target == null)
+ return;
+
+ if(!fightTeam.isPlayerInTeam(target)){
+ FightSystem.getMessage().sendPrefixless("NOT_IN_TEAM", p, ChatMessageType.ACTION_BAR, target.getName());
+ return;
+ }
+
+ fightTeam.removePlayer(target);
+ }
+
+ public static void kit(Player p, String kitName){
+ if(checkSetup(p))
+ return;
+
+ FightPlayer fightPlayer = checkGetPlayer(p);
+ if(fightPlayer == null)
+ return;
+
+ Kit k = null;
+ if(Config.PersonalKits){
+ PersonalKit kit = PersonalKit.get(SteamwarUser.get(p.getUniqueId()).getId(), Config.SchematicType.toDB(), kitName);
+ if(kit != null){
+ kit.setInUse();
+ k = new Kit(kit);
+ }
+ }else{
+ k = Kit.getKitByName(kitName);
+ }
+
+ if(k == null || !k.canUseKit(fightPlayer.isLeader())){
+ FightSystem.getMessage().sendPrefixless("KIT_UNAVAILABLE", p, ChatMessageType.ACTION_BAR);
+ return;
+ }
+
+ FightSystem.getMessage().sendPrefixless("KIT_CHOSEN", p, ChatMessageType.ACTION_BAR, kitName);
+ fightPlayer.setKit(k);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/GUI.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/GUI.java
new file mode 100644
index 00000000..0fba5388
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/GUI.java
@@ -0,0 +1,224 @@
+/*
+ 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.fight.*;
+import de.steamwar.fightsystem.listener.PersonalKitCreator;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.utils.ColorConverter;
+import de.steamwar.inventory.*;
+import de.steamwar.message.Message;
+import de.steamwar.sql.PersonalKit;
+import de.steamwar.sql.SchematicNode;
+import de.steamwar.sql.SchematicType;
+import de.steamwar.sql.SteamwarUser;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.ClickType;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class GUI {
+ private GUI(){}
+
+ private static final Message msg = FightSystem.getMessage();
+
+ @SuppressWarnings("deprecation")
+ private static void addTeamRequest(Player p, SWInventory inv, int pos, FightTeam team) {
+ byte colorCode = ColorConverter.chat2dye(team.getColor()).getDyeData();
+ String name = team.getLeader() != null ? team.getLeader().getEntity().getName() : team.getName();
+ inv.setItem(pos, SWItem.getDye(colorCode), colorCode, msg.parse("JOIN_REQUEST_TEAM", p, team.getColor() + name), click -> {
+ p.closeInventory();
+ new JoinRequest(p, team);
+ });
+ }
+
+ public static void joinRequest(Player p) {
+ if(JoinRequest.get(p) != null) {
+ msg.sendPrefixless("JOIN_REQUEST_ALREADY", p, ChatMessageType.ACTION_BAR);
+ return;
+ }
+
+ SWInventory inv = new SWInventory(p, 9, msg.parse("JOIN_REQUEST_TITLE", p));
+ FightTeam team = Fight.getPlayerTeam(p);
+ if(team != Fight.getRedTeam())
+ addTeamRequest(p, inv, team == null ? 0 : 4, Fight.getBlueTeam());
+ if(team != Fight.getBlueTeam())
+ addTeamRequest(p, inv, team == null ? 8 : 4, Fight.getRedTeam());
+ inv.open();
+ }
+
+ public static void state(Player p){
+ SWInventory inv = new SWInventory(p, 9, msg.parse("STATE_TITLE", p));
+ int i = 0;
+ for(FightState state : FightState.values()) {
+ if(state == FightState.SPECTATE)
+ continue;
+
+ inv.setItem(i++, Material.GLASS, msg.parse("STATE_" + state.name(), p), click -> FightState.setFightState(state));
+ }
+ for(FightTeam team : Fight.teams()) {
+ inv.setItem(i++, Material.GLASS, msg.parse("STATE_SPECTATE_WIN", p, team.getColoredName()), click -> FightSystem.setSpectateState(team, "operator", "WIN_FIGHTLEADER"));
+ }
+ inv.setItem(i, Material.GLASS, msg.parse("STATE_SPECTATE_TIE", p), click -> FightSystem.setSpectateState(null, "operator", "WIN_FIGHTLEADER"));
+ inv.setCallback(-999, (ClickType click) -> p.closeInventory());
+ inv.open();
+ }
+
+ public static void chooseJoinRequests(Player p){
+ List> players = JoinRequest.openRequests(p, Fight.getPlayerTeam(p));
+ SWListInv inv = new SWListInv<>(p, msg.parse("REQUESTS_TITLE", p), players, (ClickType click, Player player) -> {
+ p.closeInventory();
+ RequestsCommand.onJoinRequest(p, player, click.isLeftClick() ? JoinRequest::accept : JoinRequest::decline);
+ });
+ inv.setCallback(-999, (ClickType click) -> p.closeInventory());
+ inv.open();
+ }
+
+ public static void chooseRemove(Player p){
+ List> players = SWListInv.createPlayerList(p.getUniqueId());
+ FightTeam team = Fight.getPlayerTeam(p);
+ if(team == null)
+ return;
+ players.removeIf(swItemUUIDPair -> !team.equals(Fight.getPlayerTeam(Bukkit.getPlayer(swItemUUIDPair.getObject()))));
+ SWListInv inv = new SWListInv<>(p, msg.parse("REMOVE_TITLE", p), players, (ClickType click, UUID player) -> {
+ Commands.kick(p, SteamwarUser.get(player).getUserName());
+ p.closeInventory();
+ });
+ inv.setCallback(-999, (ClickType click) -> p.closeInventory());
+ inv.open();
+ }
+
+ public static void kitSelection(Player p, String query){
+ FightPlayer fightPlayer = Fight.getFightPlayer(p);
+ if(fightPlayer == null)
+ return;
+
+ List> entries = new ArrayList<>();
+
+ if(Config.PersonalKits){
+ List kits = PersonalKit.get(SteamwarUser.get(p.getUniqueId()).getId(), Config.SchematicType.toDB());
+ kits.forEach(kit -> entries.add(new SWListInv.SWListEntry<>(new SWItem(Material.LEATHER_CHESTPLATE, "§e" + kit.getName(), new ArrayList<>(), kit.isInUse(), clickType -> {}), new Kit(kit))));
+ }else{
+ List kitList = Kit.getAvailableKits(fightPlayer.isLeader());
+ for(Kit k : kitList){
+ entries.add(new SWListInv.SWListEntry<>(new SWItem(Material.LEATHER_CHESTPLATE, k.getName(), null, k.leaderExclusive(), null), k));
+ }
+ }
+
+ entries.removeIf(entry -> !entry.getObject().getName().toLowerCase().contains(query.toLowerCase()));
+
+ SWListInv inv = new SWListInv<>(p, msg.parse("KIT_SELECTION_TITLE", p), false, entries, (clickType, kit) -> kit.preview(p));
+ inv.setCallback(-999, (ClickType click) -> p.closeInventory());
+ if(entries.isEmpty()) {
+ inv.setItem(22, new SWItem(Material.BARRIER, msg.parse("KIT_NO_KITS", p)));
+ }
+ if(Config.PersonalKits){
+ inv.setItem(48, Material.NETHER_STAR, msg.parse("KIT_CREATE", p), clickType -> {
+ SWAnvilInv anvilInv = new SWAnvilInv(p, msg.parse("KITNAME_TITLE", p));
+ anvilInv.setItem(Material.LEATHER_CHESTPLATE);
+ anvilInv.setCallback(s -> {
+ SteamwarUser user = SteamwarUser.get(p.getUniqueId());
+ if(PersonalKit.get(user.getId(), Config.SchematicType.toDB(), s) != null) {
+ msg.sendPrefixless("KITNAME_IN_USE", p, ChatMessageType.ACTION_BAR);
+ p.closeInventory();
+ return;
+ }
+ Kit prototype = Kit.getAvailableKits(Fight.getFightPlayer(p).isLeader()).get(0);
+ PersonalKit kit = PersonalKit.create(user.getId(), Config.SchematicType.toDB(), s, prototype.getInventory(), prototype.getArmor());
+ PersonalKitCreator.openKitCreator(p, kit);
+ });
+ anvilInv.open();
+ });
+ }
+ inv.setItem(50, Material.NAME_TAG, msg.parse("KIT_SEARCH", p), clickType -> {
+ SWAnvilInv anvilInv = new SWAnvilInv(p, msg.parse("KITSEARCH_TITLE", p));
+ anvilInv.setItem(Material.PAPER);
+ anvilInv.setCallback(s -> kitSelection(p, s));
+ anvilInv.open();
+ });
+ inv.open();
+ }
+
+ public static void preSchemDialog(Player p){
+ if(!Config.test() && FightState.getFightState() != FightState.PRE_SCHEM_SETUP){
+ msg.sendPrefixless("SCHEM_NO_ENEMY", p, ChatMessageType.ACTION_BAR);
+ return;
+ }
+
+ int invSize = (Config.SubTypes.size() + 1) * 9;
+ SWInventory inv = new SWInventory(p, invSize, msg.parse("SCHEM_TITLE", p, Config.GameName));
+ setupSchemTypeRow(p, inv, Config.SchematicType, 0);
+ for (int i = 0; i < Config.SubTypes.size(); i++) {
+ setupSchemTypeRow(p, inv, Config.SubTypes.get(i), i + 1);
+ }
+ inv.setCallback(-999, (ClickType click) -> p.closeInventory());
+ inv.open();
+ }
+
+ private static void setupSchemTypeRow(Player p, SWInventory inv, SchematicType type, int row) {
+ inv.setItem(row * 9 + 8, Material.REDSTONE, msg.parse("SCHEM_PUBLIC", p, type.name()), (ClickType click) -> {
+ p.closeInventory();
+ schemDialog(p, type, true, false);
+ });
+
+ if (Fight.publicOnly()) {
+ inv.setItem(row * 9, SWItem.getDye(8), (byte)8, msg.parse("SCHEM_PRIVATE_FORBIDDEN", p, type.name()), (ClickType click)->{});
+ return;
+ }
+
+ if (type.checkType() != null && type.checkType() != type && ArenaMode.AntiEvent.contains(Config.mode) && !SchematicNode.getAllAccessibleSchematicsOfType(SteamwarUser.get(p.getUniqueId()).getId(), type.checkType().toDB()).isEmpty()) {
+ inv.setItem(row * 9 + 4, Material.ANVIL, msg.parse("SCHEM_UNCHECKED", p, type.name()), (ClickType click) -> {
+ p.closeInventory();
+ schemDialog(p, type, false, true);
+ });
+ }
+
+ if (SchematicNode.getAllAccessibleSchematicsOfType(SteamwarUser.get(p.getUniqueId()).getId(), type.toDB()).isEmpty() && !Config.test()) {
+ inv.setItem(row * 9, SWItem.getDye(8), (byte)8, msg.parse("SCHEM_NO_PRIVATE", p, type.name()), (ClickType click)->{});
+ return;
+ }
+
+ inv.setItem(row * 9, SWItem.getMaterial("CAULDRON_ITEM"), msg.parse("SCHEM_PRIVATE", p, type.name()), (ClickType click) -> {
+ p.closeInventory();
+ schemDialog(p, type, false, false);
+ });
+ }
+
+ private static void schemDialog(Player p, SchematicType type, boolean publicSchems, boolean unchecked){
+ SchematicSelector selector = new SchematicSelector(p, Config.test() ? SchematicSelector.selectSchematic() : SchematicSelector.selectSchematicType(unchecked ? type.checkType() : type), node -> {
+ FightTeam fightTeam = Fight.getPlayerTeam(p);
+ if(fightTeam == null)
+ return;
+ if(Config.test() || FightState.getFightState() != FightState.POST_SCHEM_SETUP)
+ fightTeam.pasteSchem(node);
+ p.closeInventory();
+ });
+ selector.setPublicMode(publicSchems?SchematicSelector.PublicMode.PUBLIC_ONLY:SchematicSelector.PublicMode.PRIVATE_ONLY);
+ selector.open();
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/GamemodeCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/GamemodeCommand.java
new file mode 100644
index 00000000..5c566261
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/GamemodeCommand.java
@@ -0,0 +1,107 @@
+/*
+ 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.commands;
+
+import com.google.common.collect.ImmutableList;
+import de.steamwar.core.CommandRemover;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.GameMode;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.defaults.BukkitCommand;
+import org.bukkit.entity.Player;
+import org.bukkit.util.StringUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+
+public class GamemodeCommand extends BukkitCommand {
+
+ private static final List GAMEMODE_NAMES = ImmutableList.of("adventure", "creative", "survival",
+ "spectator");
+
+ public GamemodeCommand() {
+ super("gamemode");
+ List aliases = new ArrayList<>();
+ aliases.add("gm");
+ this.setAliases(aliases);
+
+ try {
+ CommandRemover.removeAll("gamemode");
+ } catch (Exception e) {
+ FightSystem.getPlugin().getLogger().log(Level.SEVERE, "Failed to replace commands", e);
+ }
+ Commands.injectCommand(this);
+ }
+
+ @Override
+ public boolean execute(CommandSender sender, String currentAlias, String[] args) {
+ if (!(sender instanceof Player)) {
+ return false;
+ }else if (args.length == 0) {
+ FightSystem.getMessage().sendPrefixless("GAMEMODE_HELP", sender);
+ return false;
+ }
+
+ Player p = (Player) sender;
+
+ if (!(Config.test() || Config.isReferee(p))) {
+ FightSystem.getMessage().sendPrefixless("GAMEMODE_NOT_ALLOWED", p, ChatMessageType.ACTION_BAR);
+ return false;
+ }
+
+ GameMode mode = createMode(args[0]);
+
+ if(mode == null){
+ FightSystem.getMessage().sendPrefixless("GAMEMODE_UNKNOWN", p, ChatMessageType.ACTION_BAR, args[0]);
+ return false;
+ }
+
+ p.setGameMode(mode);
+ return true;
+ }
+
+ @SuppressWarnings("deprecation")
+ private GameMode createMode(String modeArg){
+ try {
+ return GameMode.getByValue(Integer.parseInt(modeArg));
+ } catch (NumberFormatException ignored) {
+ if ((modeArg.equalsIgnoreCase("creative")) || (modeArg.equalsIgnoreCase("c")))
+ return GameMode.CREATIVE;
+ else if ((modeArg.equalsIgnoreCase("adventure")) || (modeArg.equalsIgnoreCase("a")))
+ return GameMode.ADVENTURE;
+ else if ((modeArg.equalsIgnoreCase("spectator")) || (modeArg.equalsIgnoreCase("sp")))
+ return GameMode.SPECTATOR;
+ else if ((modeArg.equalsIgnoreCase("survival")) || (modeArg.equalsIgnoreCase("s")))
+ return GameMode.SURVIVAL;
+ }
+ return null;
+ }
+
+ @Override
+ public List tabComplete(CommandSender sender, String alias, String[] args) {
+ if (args.length == 1)
+ return StringUtil.copyPartialMatches(args[0], GAMEMODE_NAMES, new ArrayList<>(GAMEMODE_NAMES.size()));
+ return ImmutableList.of();
+ }
+
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/InfoCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/InfoCommand.java
new file mode 100644
index 00000000..4cd6c19c
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/InfoCommand.java
@@ -0,0 +1,64 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2023 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+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.StateDependentCommand;
+import de.steamwar.fightsystem.utils.FightStatistics;
+import de.steamwar.sql.SchematicNode;
+import de.steamwar.sql.SteamwarUser;
+import de.steamwar.sql.UserPerm;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class InfoCommand implements CommandExecutor {
+
+ public InfoCommand() {
+ new StateDependentCommand(ArenaMode.All, FightState.All, "fightinfo", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player))
+ return false;
+
+ Player player = (Player) sender;
+ if(!SteamwarUser.get(player.getUniqueId()).hasPerm(UserPerm.CHECK))
+ return false;
+
+ FightSystem.getMessage().send("INFO_RANKED", player, !FightStatistics.isUnranked());
+ for(FightTeam team : Fight.teams()) {
+ if(!team.isLeaderless())
+ FightSystem.getMessage().send("INFO_LEADER", player, team.getColoredName(), team.getLeader().getEntity().getName());
+
+ if(team.getSchematic() != 0) {
+ SchematicNode schematic = SchematicNode.getSchematicNode(team.getSchematic());
+ FightSystem.getMessage().send("INFO_SCHEMATIC", player, team.getColoredName(), schematic.getName(), SteamwarUser.get(schematic.getOwner()).getUserName(), schematic.getRank());
+ }
+ }
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/KitCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/KitCommand.java
new file mode 100644
index 00000000..000711d8
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/KitCommand.java
@@ -0,0 +1,50 @@
+/*
+ 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class KitCommand implements CommandExecutor {
+
+ public KitCommand() {
+ new StateDependentCommand(ArenaMode.AntiReplay, FightState.Setup, "kit", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player)) {
+ return false;
+ }
+ Player player = (Player) sender;
+
+ if(args.length != 1)
+ GUI.kitSelection(player, "");
+ else{
+ Commands.kit(player, args[0]);
+ }
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/LeaderCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/LeaderCommand.java
new file mode 100644
index 00000000..e2151956
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/LeaderCommand.java
@@ -0,0 +1,60 @@
+/*
+ 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+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.StateDependentCommand;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class LeaderCommand implements CommandExecutor {
+
+ public LeaderCommand() {
+ new StateDependentCommand(ArenaMode.ManualTeams, FightState.Setup, "leader", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player))
+ return false;
+ Player player = (Player) sender;
+
+ if(Fight.getFightPlayer(player) != null) {
+ FightSystem.getMessage().sendPrefixless("ALREADY_IN_TEAM", player, ChatMessageType.ACTION_BAR);
+ return false;
+ }
+
+ for(FightTeam team : Fight.teams()) {
+ if(team.canbeLeader(player)) {
+ team.addMember(player);
+ return false;
+ }
+ }
+ FightSystem.getMessage().sendPrefixless("LEADER_FULL", player, ChatMessageType.ACTION_BAR);
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/LeaveCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/LeaveCommand.java
new file mode 100644
index 00000000..902f7796
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/LeaveCommand.java
@@ -0,0 +1,46 @@
+/*
+ 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class LeaveCommand implements CommandExecutor {
+
+ public LeaveCommand() {
+ new StateDependentCommand(ArenaMode.AntiReplay, FightState.Setup, "leave", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player)) {
+ return false;
+ }
+ Player player = (Player) sender;
+
+ Commands.leaveTeam(player);
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/LockschemCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/LockschemCommand.java
new file mode 100644
index 00000000..9ea0c135
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/LockschemCommand.java
@@ -0,0 +1,68 @@
+/*
+ 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+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.StateDependentCommand;
+import de.steamwar.sql.*;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class LockschemCommand implements CommandExecutor {
+
+ public LockschemCommand() {
+ new StateDependentCommand(ArenaMode.All, FightState.Schem, "lockschem", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player))
+ return false;
+ Player player = (Player) sender;
+
+ if(!SteamwarUser.get(player.getUniqueId()).hasPerm(UserPerm.CHECK))
+ return false;
+
+ if(args.length != 1) {
+ FightSystem.getMessage().sendPrefixless("LOCKSCHEM_HELP", player);
+ return false;
+ }
+
+ String teamName = args[0];
+ FightTeam fightTeam = Fight.getTeamByName(teamName);
+
+ if(fightTeam == null) {
+ FightSystem.getMessage().sendPrefixless("UNKNOWN_TEAM", player, ChatMessageType.ACTION_BAR);
+ return false;
+ }
+
+ SchematicNode.getSchematicNode(fightTeam.getSchematic()).setSchemtype(SchematicType.Normal);
+ FightSystem.getMessage().sendPrefixless("LOCKSCHEM_LOCKED", player, ChatMessageType.ACTION_BAR);
+ fightTeam.broadcastSystem("LOCKSCHEM_LOCKED_BY", player.getName());
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/ReadyCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/ReadyCommand.java
new file mode 100644
index 00000000..4761ca57
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/ReadyCommand.java
@@ -0,0 +1,46 @@
+/*
+ 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class ReadyCommand implements CommandExecutor {
+
+ public ReadyCommand() {
+ new StateDependentCommand(ArenaMode.AntiPrepare, FightState.PostSchemSetup, "ready", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player)) {
+ return false;
+ }
+ Player player = (Player) sender;
+
+ Commands.toggleReady(player);
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/RemoveCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/RemoveCommand.java
new file mode 100644
index 00000000..f7c93537
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/RemoveCommand.java
@@ -0,0 +1,52 @@
+/*
+ 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class RemoveCommand implements CommandExecutor {
+
+ public RemoveCommand() {
+ new StateDependentCommand(ArenaMode.VariableTeams, FightState.Setup, "remove", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player)) {
+ return false;
+ }
+ Player player = (Player) sender;
+
+ if(args.length != 1){
+ FightSystem.getMessage().sendPrefixless("REMOVE_HELP", player);
+ return false;
+ }
+
+ Commands.kick(player, args[0]);
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/RequestsCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/RequestsCommand.java
new file mode 100644
index 00000000..36c2a1e3
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/RequestsCommand.java
@@ -0,0 +1,77 @@
+/*
+ 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+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.fight.JoinRequest;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+import java.util.function.BiConsumer;
+
+public class RequestsCommand implements CommandExecutor {
+
+ public RequestsCommand() {
+ new StateDependentCommand(ArenaMode.VariableTeams, FightState.AntiSpectate, "request", this);
+ new StateDependentCommand(ArenaMode.VariableTeams, FightState.AntiSpectate, "requests", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player))
+ return false;
+ Player player = (Player) sender;
+ FightPlayer fp = Fight.getFightPlayer(player);
+ if(fp == null || !(fp.isLeader() || fp.isLiving())) {
+ GUI.joinRequest(player);
+ return false;
+ }
+
+ if(Commands.checkGetLeader(player) == null)
+ return false;
+
+ GUI.chooseJoinRequests(player);
+ return false;
+ }
+
+ public static void onJoinRequest(Player player, Player target, BiConsumer handleJoinRequest) {
+ JoinRequest request = JoinRequest.get(target);
+ if(request == null) {
+ FightSystem.getMessage().send("NO_JOIN_REQUEST", player);
+ return;
+ }
+
+ FightTeam team = Fight.getPlayerTeam(player);
+ if(!request.required(team)) {
+ FightSystem.getMessage().send("NO_CONFIRMATION", player);
+ return;
+ }
+
+ handleJoinRequest.accept(request, team);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/SkipCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/SkipCommand.java
new file mode 100644
index 00000000..a1bedda9
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/SkipCommand.java
@@ -0,0 +1,53 @@
+/*
+ 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.record.PacketProcessor;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class SkipCommand implements CommandExecutor {
+
+ public SkipCommand() {
+ new StateDependentCommand(ArenaMode.AntiPrepare, ArenaMode.Replay.contains(Config.mode) ? FightState.All : FightState.TeamFix, "skip", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player)) {
+ return false;
+ }
+ Player player = (Player) sender;
+
+ if(PacketProcessor.isReplaying() && player.getUniqueId().equals(Fight.getBlueTeam().getDesignatedLeader())) {
+ PacketProcessor.currentReplay().skipToSubtitle();
+ } else {
+ Commands.toggleSkip(player);
+ }
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/StateCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/StateCommand.java
new file mode 100644
index 00000000..8299a8c6
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/StateCommand.java
@@ -0,0 +1,45 @@
+/*
+ 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class StateCommand implements CommandExecutor {
+
+ public StateCommand() {
+ new StateDependentCommand(ArenaMode.Test, FightState.All, "state", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player)) {
+ return false;
+ }
+
+ GUI.state((Player) sender);
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TBCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TBCommand.java
new file mode 100644
index 00000000..fa18262c
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TBCommand.java
@@ -0,0 +1,45 @@
+/*
+ * 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class TBCommand implements CommandExecutor {
+
+ public TBCommand() {
+ new StateDependentCommand(ArenaMode.Check, FightState.All, "resettb", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player)) {
+ return false;
+ }
+ Fight.getRedTeam().pasteSchem();
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TPSWarpCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TPSWarpCommand.java
new file mode 100644
index 00000000..27927f72
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TPSWarpCommand.java
@@ -0,0 +1,52 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2023 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.commands;
+
+import de.steamwar.core.TPSWarpUtils;
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+
+public class TPSWarpCommand implements CommandExecutor {
+
+ public TPSWarpCommand() {
+ new StateDependentCommand(ArenaMode.Prepare, FightState.PostSchemSetup, "tpswarp", this);
+ new StateDependentCommand(ArenaMode.Prepare, FightState.PostSchemSetup, "tpslimit", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ double tps;
+ try {
+ tps = Double.parseDouble(args[0]);
+ } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
+ FightSystem.getMessage().send("TPSWARP_HELP", sender);
+ return false;
+ }
+
+ TPSWarpUtils.warp(tps);
+ FightSystem.getMessage().broadcastActionbar("TPSWARP_SET", tps);
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/UnrankCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/UnrankCommand.java
new file mode 100644
index 00000000..25bdf35b
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/UnrankCommand.java
@@ -0,0 +1,49 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2022 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCommand;
+import de.steamwar.fightsystem.utils.FightStatistics;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class UnrankCommand implements CommandExecutor {
+
+ public UnrankCommand () {
+ new StateDependentCommand(ArenaMode.VariableTeams, FightState.Setup, "unrank", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player))
+ return false;
+ Player player = (Player) sender;
+
+ if(Commands.checkGetLeader(player) == null)
+ return false;
+
+ FightStatistics.unrank();
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/WGCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/WGCommand.java
new file mode 100644
index 00000000..edb891fc
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/WGCommand.java
@@ -0,0 +1,45 @@
+/*
+ * 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.commands;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCommand;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class WGCommand implements CommandExecutor {
+
+ public WGCommand() {
+ new StateDependentCommand(ArenaMode.Check, FightState.All, "resetwg", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player)) {
+ return false;
+ }
+ Fight.getBlueTeam().pasteSchem();
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/WinCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/WinCommand.java
new file mode 100644
index 00000000..d522a8e2
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/WinCommand.java
@@ -0,0 +1,52 @@
+package de.steamwar.fightsystem.commands;
+
+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.StateDependentCommand;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class WinCommand implements CommandExecutor {
+
+ public WinCommand() {
+ new StateDependentCommand(ArenaMode.Event, FightState.Ingame, "win", this);
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if(!(sender instanceof Player)) {
+ return false;
+ }
+ Player p = (Player) sender;
+
+ if (!Config.isReferee(p)) {
+ FightSystem.getMessage().sendPrefixless("NOT_FIGHTLEADER", p, ChatMessageType.ACTION_BAR);
+ return false;
+ }
+
+ if(args.length == 0){
+ FightSystem.getMessage().sendPrefixless("WIN_HELP", p);
+ return false;
+ }
+
+ if(args[0].equalsIgnoreCase("tie")){
+ FightSystem.setSpectateState(null, "Referee", "WIN_FIGHTLEADER");
+ return false;
+ }
+
+ for(FightTeam team : Fight.teams()) {
+ if(args[0].equalsIgnoreCase(team.getName())){
+ FightSystem.setSpectateState(team, "Referee", "WIN_FIGHTLEADER");
+ return false;
+ }
+ }
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/Countdown.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/Countdown.java
new file mode 100644
index 00000000..e96c6b16
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/Countdown.java
@@ -0,0 +1,138 @@
+/*
+ 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.countdown;
+
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.record.GlobalRecorder;
+import de.steamwar.fightsystem.utils.FightUI;
+import de.steamwar.fightsystem.utils.Message;
+import de.steamwar.fightsystem.utils.SWSound;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.Bukkit;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitTask;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class Countdown {
+
+ private static final List currentCountdowns = new ArrayList<>();
+
+ protected final Message appendix;
+
+ private final int totalTime;
+ protected final Sound sound;
+ private final boolean level;
+
+ protected int time;
+ private BukkitTask task = null;
+
+ public abstract void countdownFinished();
+
+ protected Countdown(int time, Message appendix, SWSound sound, boolean level) {
+ this.totalTime = time;
+ this.time = time;
+ this.appendix = appendix;
+ this.sound = sound != null ? sound.getSound() : null;
+ this.level = level;
+ }
+
+ public void enable() {
+ time = totalTime;
+ task = Bukkit.getScheduler().runTaskTimer(FightSystem.getPlugin(), this::count, 20, 20);
+ currentCountdowns.add(this);
+ show();
+ }
+
+ public void disable() {
+ if(task != null){
+ task.cancel();
+ currentCountdowns.remove(this);
+ task = null;
+ }
+ }
+
+ public static void skip(){
+ if(currentCountdowns.isEmpty())
+ return;
+
+ int smallestTime = currentCountdowns.get(0).time;
+ for(Countdown countdown : currentCountdowns){
+ if(countdown.time < smallestTime)
+ smallestTime = countdown.time;
+ }
+
+ for(Countdown countdown : new ArrayList<>(currentCountdowns)) {
+ countdown.time -= smallestTime;
+ countdown.show();
+ }
+
+ FightUI.addSubtitle("UI_SKIP");
+ }
+
+ public static void sendCountdownMessage(Player p, String message, int displaytime, Message appendix) {
+ FightSystem.getMessage().sendPrefixless(message, p, ChatMessageType.ACTION_BAR, displaytime, FightSystem.getMessage().parse(appendix.getMsg(), p, appendix.getParams()));
+ }
+
+ protected void broadcast(String message, int divisor){
+ if(this.sound != null && divisor == 1)
+ Fight.playSound(this.sound, 100.0F, 1.0F);
+
+ GlobalRecorder.getInstance().countdown(message, time / divisor, appendix);
+ Bukkit.getOnlinePlayers().forEach(p -> sendCountdownMessage(p, message, time / divisor, appendix));
+ }
+
+ public int getTimeLeft(){
+ return time;
+ }
+
+ private void count() {
+ time--;
+ show();
+ }
+
+ private void show(){
+ switch (time) {
+ case 900: case 600: case 300: case 180: case 120:
+ broadcast("COUNTDOWN_MINUTES", 60);
+ break;
+ case 60: case 30: case 20: case 15: case 10: case 5: case 4: case 3: case 2:
+ broadcast("COUNTDOWN_SECONDS", 1);
+ break;
+ case 1:
+ broadcast("COUNTDOWN_SECOND", 1);
+ break;
+ case 0:
+ if(this.sound != null)
+ Fight.playSound(this.sound, 100.0F, 2.0F);
+
+ disable();
+ countdownFinished();
+ break;
+ default:
+ }
+
+ if(this.level)
+ Bukkit.getServer().getOnlinePlayers().forEach(player -> player.setLevel(time));
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/EnternCountdown.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/EnternCountdown.java
new file mode 100644
index 00000000..55bcbe38
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/EnternCountdown.java
@@ -0,0 +1,77 @@
+/*
+ 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.countdown;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.events.BoardingEvent;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightPlayer;
+import de.steamwar.fightsystem.utils.Message;
+import de.steamwar.fightsystem.utils.Region;
+import de.steamwar.fightsystem.utils.SWSound;
+import de.steamwar.techhider.ProtocolUtils;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.Bukkit;
+
+import java.util.List;
+
+public class EnternCountdown extends Countdown {
+
+ private static int calcTime(FightPlayer fp, Countdown countdown) {
+ int time = Config.EnterStages.get(fp.getKit().getEnterStage());
+
+ if(countdown != null) {
+ time -= Config.TimeoutTime - countdown.getTimeLeft();
+
+ if(time < 0)
+ time = 0;
+ }
+
+ return time;
+ }
+
+ private final FightPlayer fightPlayer;
+ private List chunkPos;
+
+ public EnternCountdown(FightPlayer fp, Countdown countdown) {
+ super(calcTime(fp, countdown), new Message("ENTERN_COUNTDOWN"), SWSound.BLOCK_NOTE_PLING, false);
+ fightPlayer = fp;
+ enable();
+ }
+
+ @Override
+ public void countdownFinished() {
+ Bukkit.getPluginManager().callEvent(new BoardingEvent(fightPlayer));
+ FightSystem.getMessage().sendPrefixless("ENTERN_ALLOWED", fightPlayer.getEntity(), ChatMessageType.ACTION_BAR);
+ fightPlayer.ifPlayer(player -> {
+ FightSystem.getHullHider().updatePlayer(player);
+ FightSystem.getTechHider().reloadChunks(player, Fight.getOpposite(fightPlayer.getTeam()).getExtendRegion(), Region.EMPTY);
+ });
+ }
+
+ @Override
+ protected void broadcast(String message, int divisor) {
+ fightPlayer.ifPlayer(player -> {
+ player.playSound(player.getLocation(), sound, 100.0f, 1.0f);
+ sendCountdownMessage(player, message, time / divisor, appendix);
+ });
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/EventSpectateCountdown.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/EventSpectateCountdown.java
new file mode 100644
index 00000000..e8772398
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/EventSpectateCountdown.java
@@ -0,0 +1,41 @@
+/*
+ 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.countdown;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCountdown;
+import de.steamwar.fightsystem.utils.Message;
+import de.steamwar.fightsystem.utils.SWSound;
+
+public class EventSpectateCountdown extends Countdown {
+
+ public EventSpectateCountdown() {
+ super(Config.SpectatorDuration, new Message("SHUTDOWN_COUNTDOWN"), SWSound.BLOCK_NOTE_PLING, false);
+ new StateDependentCountdown(ArenaMode.NotRestartable.contains(Config.mode) && !Config.replayserver(), FightState.Spectate, this);
+ }
+
+ @Override
+ public void countdownFinished() {
+ FightSystem.shutdown();
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/NoPlayersOnlineCountdown.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/NoPlayersOnlineCountdown.java
new file mode 100644
index 00000000..94e54e28
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/NoPlayersOnlineCountdown.java
@@ -0,0 +1,42 @@
+/*
+ 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.countdown;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCountdown;
+import de.steamwar.fightsystem.utils.Message;
+import org.bukkit.Bukkit;
+
+public class NoPlayersOnlineCountdown extends Countdown {
+
+ public NoPlayersOnlineCountdown() {
+ super(Config.NoPlayerOnlineDuration, new Message("SHUTDOWN_COUNTDOWN"), null, false);
+
+ if (!Config.ArenaLeaveable)
+ new StateDependentCountdown(ArenaMode.AntiReplay, FightState.PreLeaderSetup, this);
+ }
+
+ @Override
+ public void countdownFinished() {
+ Bukkit.getServer().shutdown();
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/PostSchemCountdown.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/PostSchemCountdown.java
new file mode 100644
index 00000000..cb5f2db2
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/PostSchemCountdown.java
@@ -0,0 +1,42 @@
+/*
+ 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.countdown;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCountdown;
+import de.steamwar.fightsystem.utils.Message;
+
+public class PostSchemCountdown extends Countdown {
+
+ public PostSchemCountdown() {
+ super(Config.SetupDuration, new Message("POST_SCHEM_COUNTDOWN"), null, false);
+ if(Config.mode == ArenaMode.PREPARE && Config.UnlimitedPrepare)
+ return;
+
+ new StateDependentCountdown(ArenaMode.SeriousFight, FightState.PostSchemSetup, this);
+ }
+
+ @Override
+ public void countdownFinished() {
+ FightState.setFightState(FightState.PRE_RUNNING);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/PreRunningCountdown.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/PreRunningCountdown.java
new file mode 100644
index 00000000..17684a70
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/PreRunningCountdown.java
@@ -0,0 +1,40 @@
+/*
+ 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.countdown;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCountdown;
+import de.steamwar.fightsystem.utils.Message;
+import de.steamwar.fightsystem.utils.SWSound;
+
+public class PreRunningCountdown extends Countdown {
+
+ public PreRunningCountdown() {
+ super(Config.PreFightDuration, new Message("PRE_RUNNING_COUNTDOWN"), SWSound.BLOCK_NOTE_PLING, true);
+ new StateDependentCountdown(ArenaMode.AntiReplay, FightState.PreRunning, this);
+ }
+
+ @Override
+ public void countdownFinished() {
+ FightState.setFightState(FightState.RUNNING);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/PreSchemCountdown.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/PreSchemCountdown.java
new file mode 100644
index 00000000..4200eb65
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/PreSchemCountdown.java
@@ -0,0 +1,40 @@
+/*
+ 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.countdown;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCountdown;
+import de.steamwar.fightsystem.utils.Message;
+import de.steamwar.fightsystem.utils.SWSound;
+
+public class PreSchemCountdown extends Countdown {
+
+ public PreSchemCountdown() {
+ super(Config.PreSchemPasteDuration, new Message("PRE_SCHEM_COUNTDOWN"), SWSound.BLOCK_NOTE_PLING, false);
+ new StateDependentCountdown(ArenaMode.AntiReplay, FightState.PreSchemSetup, this);
+ }
+
+ @Override
+ public void countdownFinished() {
+ FightState.setFightState(FightState.POST_SCHEM_SETUP);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/SpectateOverCountdown.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/SpectateOverCountdown.java
new file mode 100644
index 00000000..9f3cb10d
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/SpectateOverCountdown.java
@@ -0,0 +1,40 @@
+/*
+ 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.countdown;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCountdown;
+import de.steamwar.fightsystem.utils.Message;
+import de.steamwar.fightsystem.utils.SWSound;
+
+public class SpectateOverCountdown extends Countdown {
+
+ public SpectateOverCountdown() {
+ super(Config.test() ? 3600 : Config.SpectatorDuration, new Message("SPECTATE_COUNTDOWN"), SWSound.BLOCK_NOTE_PLING, false);
+ new StateDependentCountdown(ArenaMode.Restartable.contains(Config.mode) || Config.replayserver(), FightState.Spectate, this);
+ }
+
+ @Override
+ public void countdownFinished() {
+ FightState.setFightState(FightState.PRE_LEADER_SETUP);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/TimeOverCountdown.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/TimeOverCountdown.java
new file mode 100644
index 00000000..3e2dde85
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/TimeOverCountdown.java
@@ -0,0 +1,39 @@
+/*
+ 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.countdown;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.utils.Message;
+import de.steamwar.fightsystem.utils.SWSound;
+
+public class TimeOverCountdown extends Countdown {
+
+ private final Runnable timeOver;
+
+ public TimeOverCountdown(Runnable timeOver) {
+ super(Config.TimeoutTime, new Message("RUNNING_COUNTDOWN"), SWSound.BLOCK_NOTE_BASS, false);
+ this.timeOver = timeOver;
+ }
+
+ @Override
+ public void countdownFinished() {
+ timeOver.run();
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/HellsBells.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/HellsBells.java
new file mode 100644
index 00000000..7fe7e4b2
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/HellsBells.java
@@ -0,0 +1,242 @@
+/*
+ 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.event;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.countdown.Countdown;
+import de.steamwar.fightsystem.record.GlobalRecorder;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependent;
+import de.steamwar.fightsystem.utils.Message;
+import de.steamwar.fightsystem.utils.SWSound;
+import de.steamwar.fightsystem.winconditions.Winconditions;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.EntityType;
+import org.bukkit.scheduler.BukkitTask;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+
+
+public class HellsBells {
+
+ public static final Random random = new Random();
+
+ private final int xLength = Config.BlueExtendRegion.getSizeX();
+ private final int zLength = Config.BlueExtendRegion.getSizeZ();
+ private State current = State.PRE;
+ private int currentDrops = 0;
+ private HellsBellsCountdown currentCountdown;
+ private BukkitTask currentDropping;
+
+ private final List startMessages = Arrays.asList("HELLS_BELLS_START_1", "HELLS_BELLS_START_2",
+ "HELLS_BELLS_START_3", "HELLS_BELLS_START_4", "HELLS_BELLS_START_5", "HELLS_BELLS_START_6");
+ private final List stateSwapMessages = Arrays.asList("HELLS_BELLS_SWAP_1", "HELLS_BELLS_SWAP_2",
+ "HELLS_BELLS_SWAP_3", "HELLS_BELLS_SWAP_4");
+
+ public void startCountdown() {
+ if (current == HellsBells.State.PRE || current == HellsBells.State.FIRST) {
+ String startMessage = startMessages.get(random.nextInt(startMessages.size()));
+ GlobalRecorder.getInstance().system(startMessage);
+ FightSystem.getMessage().broadcast(startMessage);
+ current = current.getNext();
+ } else if (current != HellsBells.State.LAST && currentDrops >= current.SWITCH_AFTER) {
+ String stateSwapMessage = stateSwapMessages.get(random.nextInt(stateSwapMessages.size()));
+ GlobalRecorder.getInstance().system(stateSwapMessage);
+ FightSystem.getMessage().broadcast(stateSwapMessage);
+ currentDrops = 0;
+ current = current.getNext();
+ }
+
+ currentDrops++;
+ currentCountdown = new HellsBellsCountdown(current.MIN_TIME + random.nextInt(current.MAX_TIME - current.MIN_TIME));
+ currentCountdown.enable();
+ }
+
+ public void drop() {
+ Direction direction = Direction.getRandom();
+
+ AtomicInteger length = new AtomicInteger(20 + random.nextInt(direction.getLength(zLength, xLength) - 20));
+ int width = 5 + random.nextInt(5);
+ int xOffset = getStart(direction.getLength(xLength, zLength), direction.getLength(length.get(), width));
+ int zOffset = getStart(direction.getLength(zLength, xLength), direction.getLength(width, length.get()));
+ int yOffset = getHeightStart();
+
+ Point redStart;
+ Point blueStart;
+
+ if (direction.isNorthOrWest()) {
+ redStart = new Point(Config.RedExtendRegion.getMaxX() - xOffset, Config.RedExtendRegion.getMaxY() + yOffset, Config.RedExtendRegion.getMaxZ() - zOffset);
+ blueStart = new Point(Config.BlueExtendRegion.getMinX() + xOffset, Config.BlueExtendRegion.getMaxY() + yOffset, Config.BlueExtendRegion.getMinZ() + zOffset);
+ } else {
+ redStart = new Point(Config.RedExtendRegion.getMinX() + xOffset, Config.RedExtendRegion.getMaxY() + yOffset, Config.RedExtendRegion.getMinZ() + zOffset);
+ blueStart = new Point(Config.BlueExtendRegion.getMaxX() - xOffset, Config.BlueExtendRegion.getMaxY() + yOffset, Config.BlueExtendRegion.getMaxZ() - zOffset);
+ }
+
+ currentDropping = Bukkit.getScheduler().runTaskTimer(FightSystem.getPlugin(), () -> {
+ for (int w = 0; w < width; w++) {
+ if (direction.isNorthOrWest()) {
+ Config.world.spawnEntity(redStart.addAndToLocation(Config.world, -1 * (direction.dx * length.get() + w * direction.other().dx), 0, -1 * (direction.dz * length.get() + w * direction.other().dz)), EntityType.PRIMED_TNT);
+
+ Config.world.spawnEntity(blueStart.addAndToLocation(Config.world, direction.dx * length.get() + w * direction.other().dx, 0, direction.dz * length.get() + w * direction.other().dz), EntityType.PRIMED_TNT);
+ } else {
+ Config.world.spawnEntity(redStart.addAndToLocation(Config.world, direction.dx * length.get() + w * direction.other().dx, 0, direction.dz * length.get() + w * direction.other().dz), EntityType.PRIMED_TNT);
+
+ Config.world.spawnEntity(blueStart.addAndToLocation(Config.world, -1 * (direction.dx * length.get() + w * direction.other().dx), 0, -1 * (direction.dz * length.get() + w * direction.other().dz)), EntityType.PRIMED_TNT);
+ }
+ }
+ if (length.addAndGet(-2) <= 0) {
+ currentDropping.cancel();
+ }
+ }, 0L, 4L);
+ }
+
+ private int getStart(int regionSize, int length) {
+ double randomNumber = (Math.max(Math.min(random.nextGaussian(), -2), 2) + 2) / 4;
+ Bukkit.getLogger().log(Level.INFO, "Calculated Start: " + (int) (randomNumber * (regionSize - length)));
+ return Math.max(Math.min((int) (randomNumber * (regionSize - length)), regionSize - length), 0);
+ }
+
+ private int getHeightStart() {
+ return 5 + random.nextInt(15);
+ }
+
+ public HellsBells() {
+ new StateDependent(Winconditions.HELLS_BELLS, FightState.Running) {
+ @Override
+ public void enable() {
+ current = State.PRE;
+ currentDrops = 0;
+ startCountdown();
+ }
+
+ @Override
+ public void disable() {
+ currentCountdown.disable();
+ }
+ }.register();
+ }
+
+ private class HellsBellsCountdown extends Countdown {
+
+ public HellsBellsCountdown(int time) {
+ super(time, new Message("HELLS_BELLS_COUNTDOWN"), SWSound.BLOCK_NOTE_BASS, true);
+ }
+
+ @Override
+ public void countdownFinished() {
+ drop();
+ startCountdown();
+ }
+ }
+
+ private enum State {
+
+ PRE(60, 80, 1),
+ FIRST(40, 60, 3),
+ SECOND(30, 40, 4),
+ THIRD(20, 30, 4),
+ FOURTH(10, 20, 5),
+ LAST(5, 10, 0);
+
+
+ State(int minTime, int maxTime, int switchAfter) {
+ this.MIN_TIME = minTime;
+ this.MAX_TIME = maxTime;
+ this.SWITCH_AFTER = switchAfter;
+ }
+
+ private final int MIN_TIME; //NOSONAR
+ private final int MAX_TIME; //NOSONAR
+ private final int SWITCH_AFTER; //NOSONAR
+
+
+ public State getNext() {
+ switch (this) {
+ case PRE:
+ return FIRST;
+ case FIRST:
+ return SECOND;
+ case SECOND:
+ return THIRD;
+ case THIRD:
+ return FOURTH;
+ case FOURTH:
+ case LAST:
+ return LAST;
+ default:
+ return PRE;
+ }
+ }
+ }
+
+ private enum Direction {
+ NORTH(0, 0, 1),
+ SOUTH(0, 0, -1),
+ EAST(1, 0, 0),
+ WEST(-1, 0, 0);
+
+
+ int dx;
+ int dy;
+ int dz;
+
+ Direction(int dx, int dy, int dz) {
+ this.dx = dx;
+ this.dy = dy;
+ this.dz = dz;
+ }
+
+ public static Direction getRandom() {
+ return Direction.values()[random.nextInt(Direction.values().length)];
+ }
+
+ Direction other() {
+ switch (this) {
+ case NORTH:
+ return EAST;
+ case SOUTH:
+ return WEST;
+ case EAST:
+ return NORTH;
+ case WEST:
+ return SOUTH;
+ default:
+ return this;
+ }
+ }
+
+ public int getLength(int length1, int length2) {
+ return isNorthOrSouth() ? length1 : length2;
+ }
+
+ public boolean isNorthOrSouth() {
+ return this == NORTH || this == SOUTH;
+ }
+
+ public boolean isNorthOrWest() {
+ return this == NORTH || this == WEST;
+ }
+ }
+}
\ No newline at end of file
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/Meteor.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/Meteor.java
new file mode 100644
index 00000000..3f3115d6
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/Meteor.java
@@ -0,0 +1,216 @@
+/*
+ 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.event;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.countdown.Countdown;
+import de.steamwar.fightsystem.record.GlobalRecorder;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependent;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.utils.Message;
+import de.steamwar.fightsystem.utils.SWSound;
+import de.steamwar.fightsystem.winconditions.Winconditions;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Fireball;
+import org.bukkit.entity.LargeFireball;
+import org.bukkit.entity.TNTPrimed;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.ProjectileHitEvent;
+import org.bukkit.scheduler.BukkitTask;
+import org.bukkit.util.Vector;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+
+
+public class Meteor implements Listener {
+
+ public static final Random random = new Random();
+
+ private final Vector vector = new Vector(0, -1, 0);
+ private final int xLength = Config.RedExtendRegion.getMaxX() - Config.RedExtendRegion.getMinX();
+ private final int zLength = Config.RedExtendRegion.getMaxZ() - Config.RedExtendRegion.getMinZ();
+ private Meteor.State current = Meteor.State.PRE;
+ private int currentDrops = 0;
+ private Meteor.MeteorCountdown currentCountdown;
+ private final AtomicInteger amount = new AtomicInteger(0);
+ private BukkitTask currentDropping;
+
+ private final List startMessages = Arrays.asList("METEOR_START_1", "METEOR_START_2", "METEOR_START_3", "METEOR_START_4", "METEOR_START_5", "METEOR_START_6");
+ private final List stateSwapMessages = Arrays.asList("METEOR_SWAP_1", "METEOR_SWAP_2", "METEOR_SWAP_3", "METEOR_SWAP_4");
+
+ public void startCountdown() {
+ if (current == Meteor.State.PRE || current == Meteor.State.FIRST) {
+ String startMessage = startMessages.get(random.nextInt(startMessages.size()));
+ GlobalRecorder.getInstance().system(startMessage);
+ FightSystem.getMessage().broadcast(startMessage);
+ current = current.getNext();
+ } else if (current != Meteor.State.LAST && currentDrops >= current.SWITCH_AFTER) {
+ String stateSwapMessage = stateSwapMessages.get(random.nextInt(stateSwapMessages.size()));
+ GlobalRecorder.getInstance().system(stateSwapMessage);
+ FightSystem.getMessage().broadcast(stateSwapMessage);
+ currentDrops = 0;
+ current = current.getNext();
+ }
+
+ currentDrops++;
+ currentCountdown = new Meteor.MeteorCountdown(current.MIN_TIME + random.nextInt(current.MAX_TIME - current.MIN_TIME));
+ currentCountdown.enable();
+ }
+
+ //@EventHandler
+ public void explode(ProjectileHitEvent event) {
+ if (event.getEntity() instanceof Fireball) {
+ TNTPrimed tnt = Config.world.spawn(event.getEntity().getLocation(), TNTPrimed.class);
+ tnt.setVelocity(new Vector(0, 0, 0));
+ tnt.setFuseTicks(0);
+ tnt.setYield(((Fireball) event.getEntity()).getYield());
+ event.getEntity().remove();
+ }
+ }
+
+ public void drop() {
+ int randomAmount = current.minAmount + random.nextInt(current.maxAmount - current.minAmount);
+ if (amount.get() > 0) {
+ amount.set(amount.get() + randomAmount);
+ return;
+ }
+ amount.set(randomAmount);
+
+ currentDropping = Bukkit.getScheduler().runTaskTimer(FightSystem.getPlugin(), () -> {
+ int xOffset = getStart(xLength);
+ int zOffset = getStart(zLength);
+ int yOffset = getHeightStart();
+
+ Point redStart = new Point(Config.RedExtendRegion.getMinX() + xOffset, Config.RedExtendRegion.getMaxY() + yOffset, Config.RedExtendRegion.getMaxZ() - zOffset);
+ Point blueStart = new Point(Config.BlueExtendRegion.getMinX() + xOffset, Config.BlueExtendRegion.getMaxY() + yOffset, Config.BlueExtendRegion.getMinZ() + zOffset);
+
+ vector.setX(random.nextDouble() - 0.5);
+ vector.setZ(random.nextDouble() - 0.5);
+
+ LargeFireball fireballRed = Config.world.spawn(redStart.toLocation(Config.world), LargeFireball.class);
+ fireballRed.setDirection(vector);
+ fireballRed.setBounce(false);
+ fireballRed.setIsIncendiary(false);
+ fireballRed.setYield(current.explosionSize);
+
+ LargeFireball fireballBlue = Config.world.spawn(blueStart.toLocation(Config.world), LargeFireball.class);
+ vector.setZ(vector.getZ() * -1);
+ fireballBlue.setDirection(vector);
+ fireballBlue.setBounce(false);
+ fireballBlue.setIsIncendiary(false);
+ fireballBlue.setYield(current.explosionSize);
+
+ if (amount.decrementAndGet() <= 0) {
+ currentDropping.cancel();
+ }
+ }, 0L, 4L);
+ }
+
+ private int getStart(int regionSize) {
+ double randomNumber = (random.nextDouble() - random.nextDouble()) / 2 + 0.5;
+ Bukkit.getLogger().log(Level.INFO, "Calculated Start: " + (int) (randomNumber * (regionSize - 1)));
+ return Math.max(Math.min((int) (randomNumber * (regionSize - 1)), regionSize - 1), 0);
+ }
+
+ private int getHeightStart() {
+ return 5 + random.nextInt(15);
+ }
+
+ public Meteor() {
+ new StateDependentListener(Winconditions.METEOR, FightState.Running, this);
+
+ new StateDependent(Winconditions.METEOR, FightState.Running) {
+ @Override
+ public void enable() {
+ startCountdown();
+ }
+
+ @Override
+ public void disable() {
+ currentCountdown.disable();
+ }
+ }.register();
+ }
+
+ private class MeteorCountdown extends Countdown {
+
+ public MeteorCountdown(int time) {
+ super(time, new Message("METEOR_COUNTDOWN"), SWSound.BLOCK_NOTE_BASS, true);
+ }
+
+ @Override
+ public void countdownFinished() {
+ drop();
+ startCountdown();
+ }
+ }
+
+ private enum State {
+
+ PRE(60, 80, 1, 0, 0, 0),
+ FIRST(25, 35, 6, 2, 3, 6),
+ SECOND(20, 30, 7, 4, 4, 8),
+ THIRD(15, 25, 7, 4, 5, 10),
+ FOURTH(10, 20, 8, 6, 7, 14),
+ LAST(5, 10, 0, 6, 9, 18);
+
+
+ State(int minTime, int maxTime, int switchAfter, int explosionSize, int minAmount, int maxAmount) {
+ this.MIN_TIME = minTime;
+ this.MAX_TIME = maxTime;
+ this.SWITCH_AFTER = switchAfter;
+ this.explosionSize = explosionSize;
+ this.minAmount = minAmount;
+ this.maxAmount = maxAmount;
+ }
+
+ private final int MIN_TIME; //NOSONAR
+ private final int MAX_TIME; //NOSONAR
+ private final int SWITCH_AFTER; //NOSONAR
+ private final int explosionSize; //NOSONAR
+ private final int minAmount;
+ private final int maxAmount;
+
+
+ public Meteor.State getNext() {
+ switch (this) {
+ case PRE:
+ return FIRST;
+ case FIRST:
+ return SECOND;
+ case SECOND:
+ return THIRD;
+ case THIRD:
+ return FOURTH;
+ case FOURTH:
+ case LAST:
+ return LAST;
+ default:
+ return PRE;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/PersistentDamage.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/PersistentDamage.java
new file mode 100644
index 00000000..543eff9e
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/PersistentDamage.java
@@ -0,0 +1,49 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2023 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.event;
+
+import com.sk89q.worldedit.WorldEditException;
+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.states.FightState;
+import de.steamwar.fightsystem.states.OneShotStateDependent;
+import de.steamwar.fightsystem.utils.WorldeditWrapper;
+import de.steamwar.fightsystem.winconditions.Winconditions;
+import de.steamwar.sql.SchematicNode;
+
+import java.util.logging.Level;
+
+public class PersistentDamage {
+
+ public PersistentDamage() {
+ if(!ArenaMode.SeriousFight.contains(Config.mode))
+ return;
+
+ new OneShotStateDependent(Winconditions.PERSISTENT_DAMAGE, FightState.Spectate, () -> Fight.teams().forEach(team -> {
+ try{
+ WorldeditWrapper.impl.saveSchem(SchematicNode.getSchematicNode(team.getSchematic()), team.getExtendRegion(), team.getSchemRegion().getMinY());
+ }catch(WorldEditException e){
+ FightSystem.getPlugin().getLogger().log(Level.SEVERE, "Could not persist schematic state", e);
+ }
+ }));
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/Point.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/Point.java
new file mode 100644
index 00000000..13dc087f
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/Point.java
@@ -0,0 +1,44 @@
+/*
+ * 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.event;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+
+class Point {
+
+ private final int x;
+ private final int y;
+ private final int z;
+
+ public Point(int x, int y, int z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ public Location toLocation(World world) {
+ return new Location(world, this.x, this.y, this.z); //NOSONAR
+ }
+
+ public Location addAndToLocation(World world, int x, int y, int z) {
+ return new Location(world, this.x + x, this.y + y, this.z + z); //NOSONAR
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/TNTDistributor.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/TNTDistributor.java
new file mode 100644
index 00000000..8982bb3d
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/event/TNTDistributor.java
@@ -0,0 +1,39 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2023 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.event;
+
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentTask;
+import de.steamwar.fightsystem.winconditions.Winconditions;
+import org.bukkit.Material;
+import org.bukkit.inventory.ItemStack;
+
+public class TNTDistributor {
+
+ public TNTDistributor() {
+ new StateDependentTask(Winconditions.TNT_DISTRIBUTION, FightState.Running, () -> Fight.teams().forEach(team -> team.getPlayers().forEach(fp -> {
+ if(!fp.isLiving())
+ return;
+
+ fp.ifPlayer(player -> player.getInventory().addItem(new ItemStack(Material.TNT, 20)));
+ })), 300, 300);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/events/BoardingEvent.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/events/BoardingEvent.java
new file mode 100644
index 00000000..373d91b4
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/events/BoardingEvent.java
@@ -0,0 +1,40 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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.events;
+
+import de.steamwar.fightsystem.fight.FightPlayer;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+@Getter
+@AllArgsConstructor
+public class BoardingEvent extends Event {
+ @Getter
+ private static final HandlerList handlerList = new HandlerList();
+
+ private final FightPlayer fightPlayer;
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlerList;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/events/TeamDeathEvent.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/events/TeamDeathEvent.java
new file mode 100644
index 00000000..6cbdd059
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/events/TeamDeathEvent.java
@@ -0,0 +1,40 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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.events;
+
+import de.steamwar.fightsystem.fight.FightPlayer;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+@Getter
+@AllArgsConstructor
+public class TeamDeathEvent extends Event {
+ @Getter
+ private static final HandlerList handlerList = new HandlerList();
+
+ private final FightPlayer fightPlayer;
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlerList;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/events/TeamLeaveEvent.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/events/TeamLeaveEvent.java
new file mode 100644
index 00000000..36d96b0e
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/events/TeamLeaveEvent.java
@@ -0,0 +1,40 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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.events;
+
+import de.steamwar.fightsystem.fight.FightPlayer;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+@Getter
+@AllArgsConstructor
+public class TeamLeaveEvent extends Event {
+ @Getter
+ private static final HandlerList handlerList = new HandlerList();
+
+ private final FightPlayer fightPlayer;
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlerList;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/events/TeamSpawnEvent.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/events/TeamSpawnEvent.java
new file mode 100644
index 00000000..7ca62be4
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/events/TeamSpawnEvent.java
@@ -0,0 +1,40 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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.events;
+
+import de.steamwar.fightsystem.fight.FightPlayer;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+
+@Getter
+@AllArgsConstructor
+public class TeamSpawnEvent extends Event {
+ @Getter
+ private static final HandlerList handlerList = new HandlerList();
+
+ private final FightPlayer fightPlayer;
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlerList;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/Fight.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/Fight.java
new file mode 100644
index 00000000..5b8f62f9
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/Fight.java
@@ -0,0 +1,153 @@
+/*
+ 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.fight;
+
+import com.comphenix.tinyprotocol.TinyProtocol;
+import com.mojang.authlib.GameProfile;
+import de.steamwar.core.Core;
+import de.steamwar.core.ProtocolWrapper;
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.record.GlobalRecorder;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.GameMode;
+import org.bukkit.Sound;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+public class Fight {
+ private Fight(){}
+
+ @Getter
+ private static final FightTeam redTeam = new FightTeam(Config.TeamRedName, Config.TeamRedColor, Config.TeamRedSpawn, Config.RedPasteRegion, Config.RedExtendRegion, Config.RedRotate, false, Config.RedLeader);
+ @Getter
+ private static final FightTeam blueTeam = new FightTeam(Config.TeamBlueName, Config.TeamBlueColor, Config.TeamBlueSpawn, Config.BluePasteRegion, Config.BlueExtendRegion, Config.BlueRotate, true, Config.BlueLeader);
+ private static final Collection teams = new HashSet<>();
+ static {
+ teams.add(redTeam);
+ teams.add(blueTeam);
+ }
+
+ public static FightTeam getPlayerTeam(LivingEntity player) {
+ if(redTeam.isPlayerInTeam(player))
+ return redTeam;
+ if(blueTeam.isPlayerInTeam(player))
+ return blueTeam;
+ return null;
+ }
+
+ public static FightTeam getOpposite(FightTeam fightTeam) {
+ if(fightTeam == redTeam)
+ return blueTeam;
+ else if(fightTeam == blueTeam)
+ return redTeam;
+
+ throw new IllegalArgumentException();
+ }
+
+ public static FightPlayer getFightPlayer(LivingEntity player) {
+ if(redTeam.isPlayerInTeam(player))
+ return redTeam.getFightPlayer(player);
+ if(blueTeam.isPlayerInTeam(player))
+ return blueTeam.getFightPlayer(player);
+ return null;
+ }
+
+ public static boolean fighting(LivingEntity player) {
+ return getPlayerTeam(player) != null;
+ }
+
+ public static Collection teams() {
+ return teams;
+ }
+
+ public static FightTeam getUnrotated() {
+ return Config.blueNegZ() ? Fight.getBlueTeam() : Fight.getRedTeam();
+ }
+
+ public static void playSound(Sound sound, float volume, float pitch) {
+ GlobalRecorder.getInstance().soundAtPlayer(sound.name(), volume, pitch);
+ //volume: max. 100, pitch: max. 2
+ if(Core.getVersion() >= 18)
+ Bukkit.getServer().getOnlinePlayers().forEach(player -> player.playSound(player, sound, volume, pitch));
+ else
+ Bukkit.getServer().getOnlinePlayers().forEach(player -> player.playSound(player.getLocation(), sound, volume, pitch));
+ }
+
+ public static FightTeam getTeamByName(String name) {
+ if(redTeam.getName().equalsIgnoreCase(name))
+ return redTeam;
+ if(blueTeam.getName().equalsIgnoreCase(name))
+ return blueTeam;
+ return null;
+ }
+
+ @SuppressWarnings("deprecation")
+ public static void setPlayerGamemode(Player player, GameMode gameMode) {
+ player.setGameMode(gameMode);
+
+ if(gameMode == GameMode.SPECTATOR) {
+ for(Player currentPlayer : Bukkit.getServer().getOnlinePlayers()) {
+ if(currentPlayer.getUniqueId() != player.getUniqueId() && currentPlayer.getGameMode() == GameMode.SPECTATOR) {
+ currentPlayer.hidePlayer(player);
+ player.hidePlayer(currentPlayer);
+ }
+ }
+
+ if(Config.test() || Config.isReferee(player))
+ return;
+
+ Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> {
+ if(!player.isOnline())
+ return;
+ pseudoSpectator(player, true);
+ }, 1);
+ }else if(gameMode == GameMode.SURVIVAL) {
+ for(Player currentPlayer : Bukkit.getServer().getOnlinePlayers()) {
+ if(currentPlayer.getUniqueId() != player.getUniqueId() && currentPlayer.getGameMode() == GameMode.SPECTATOR) {
+ currentPlayer.showPlayer(player);
+ player.showPlayer(currentPlayer);
+ }
+ }
+ }
+ }
+
+ public static void pseudoSpectator(Player player, boolean enable) {
+ TinyProtocol.instance.sendPacket(player, ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.GAMEMODE, new GameProfile(player.getUniqueId(), player.getName()), enable ? GameMode.CREATIVE : GameMode.SPECTATOR));
+ }
+
+ public static boolean publicOnly() {
+ if (Config.OnlyPublicSchematics) {
+ return true;
+ }
+ if (Config.IgnorePublicOnly || ArenaMode.RankedEvent.contains(Config.mode)) {
+ return false;
+ }
+ if (redTeam.getLeader() == null || blueTeam.getLeader() == null) {
+ return false;
+ }
+ return redTeam.isPublicsOnly() || blueTeam.isPublicsOnly();
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightPlayer.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightPlayer.java
new file mode 100644
index 00000000..763bd6fd
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightPlayer.java
@@ -0,0 +1,131 @@
+/*
+ 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.fight;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.ai.AI;
+import de.steamwar.fightsystem.countdown.Countdown;
+import de.steamwar.fightsystem.countdown.EnternCountdown;
+import de.steamwar.fightsystem.events.TeamDeathEvent;
+import de.steamwar.sql.PersonalKit;
+import de.steamwar.sql.SteamwarUser;
+import lombok.Getter;
+import lombok.Setter;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
+
+import java.util.function.Consumer;
+
+public class FightPlayer {
+
+ private final int id;
+ private LivingEntity entity;
+ @Getter
+ private final FightTeam team;
+ private boolean isOut;
+ @Setter
+ @Getter
+ private Kit kit;
+ @Getter
+ private int kills;
+ private EnternCountdown enternCountdown = null;
+
+ FightPlayer(LivingEntity entity, SteamwarUser user, FightTeam team) {
+ this.id = user.getId();
+ this.entity = entity;
+ this.team = team;
+ this.isOut = false;
+ kit = Kit.getKitByName(Config.MemberDefault);
+ if(Config.PersonalKits){
+ PersonalKit personalKit = PersonalKit.getKitInUse(user.getId(), Config.SchematicType.toDB());
+ if(personalKit != null){
+ kit = new Kit(personalKit);
+ }
+ }
+ kills = 0;
+ }
+
+ public void revive() {
+ isOut = false;
+ }
+
+ public void setOut() {
+ isOut = true;
+ Bukkit.getPluginManager().callEvent(new TeamDeathEvent(this));
+ stopEnternCountdown();
+ ifAI(AI::stop);
+ }
+
+ public void startEnternCountdown(Countdown countdown) {
+ if(Config.EnterStages.size() > kit.getEnterStage() && kit.getEnterStage() >= 0)
+ enternCountdown = new EnternCountdown(this, countdown);
+ }
+
+ public void stopEnternCountdown(){
+ if(enternCountdown != null){
+ enternCountdown.disable();
+ }
+ enternCountdown = null;
+ }
+
+ public LivingEntity getEntity() {
+ LivingEntity bukkit = Bukkit.getPlayer(entity.getUniqueId());
+ if(bukkit != null)
+ entity = bukkit;
+ return entity;
+ }
+
+ public SteamwarUser getUser() {
+ return SteamwarUser.get(id);
+ }
+
+ public void ifAI(Consumer function) {
+ if(entity instanceof Player)
+ return;
+ AI ai = AI.getAI(entity.getUniqueId());
+ if(ai != null)
+ function.accept(ai);
+ }
+
+ public void ifPlayer(Consumer function) {
+ if(entity instanceof Player)
+ function.accept((Player) entity);
+ }
+
+ public boolean isLiving() {
+ return !this.isOut;
+ }
+
+ public boolean isLeader() {
+ FightPlayer leader = team.getLeader();
+ return leader != null && leader.getEntity() == entity;
+ }
+
+ public void addKill(){
+ kills++;
+ }
+
+ public boolean canEntern(){
+ if(enternCountdown == null)
+ return false;
+ return enternCountdown.getTimeLeft() == 0;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java
new file mode 100644
index 00000000..20c304d7
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java
@@ -0,0 +1,217 @@
+/*
+ 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.fight;
+
+import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.extent.clipboard.Clipboard;
+import com.sk89q.worldedit.math.transform.AffineTransform;
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.record.GlobalRecorder;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependent;
+import de.steamwar.fightsystem.utils.ColorConverter;
+import de.steamwar.fightsystem.utils.Region;
+import de.steamwar.fightsystem.utils.WorldeditWrapper;
+import de.steamwar.sql.SchematicData;
+import de.steamwar.sql.SchematicNode;
+import de.steamwar.sql.SchematicType;
+import org.bukkit.Bukkit;
+import org.bukkit.DyeColor;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.util.Vector;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Random;
+import java.util.logging.Level;
+
+public class FightSchematic extends StateDependent {
+
+ private final FightTeam team;
+ private final Region region;
+ private final boolean rotate;
+
+ private Clipboard clipboard = null;
+ private int schematic = 0;
+
+ public FightSchematic(FightTeam team, boolean rotate) {
+ super(ArenaMode.All, FightState.PostSchemSetup);
+ this.team = team;
+ this.region = team.getSchemRegion();
+ this.rotate = rotate;
+ register();
+ }
+
+ public boolean hasSchematic() {
+ return clipboard != null;
+ }
+
+ public int getId(){
+ return schematic;
+ }
+
+ public void setSchematic(SchematicNode schem) {
+ schematic = schem.getId();
+ try {
+ clipboard = new SchematicData(schem).load();
+
+ if(schem.replaceColor())
+ replaceTeamColor(clipboard);
+ } catch (IOException e) {
+ team.broadcastSystem("SCHEMATIC_UNLOADABLE");
+ Bukkit.getLogger().log(Level.SEVERE, e, () -> "Couldn't load Schematic " + schem.getName());
+ }
+ }
+
+ public void setSchematic(int schemId, Clipboard clipboard) {
+ this.schematic = schemId;
+ this.clipboard = clipboard;
+ }
+
+ public void reset(){
+ schematic = 0;
+ clipboard = null;
+ }
+
+ @Override
+ public void enable() {
+ if(FightState.getFightState() == FightState.SPECTATE)
+ return;
+
+ if(clipboard == null){
+ List publics = SchematicNode.getAllSchematicsOfType(0, Config.SchematicType.toDB());
+ if(publics.isEmpty()) {
+ for (SchematicType type : Config.SubTypes) {
+ publics = SchematicNode.getAllSchematicsOfType(0, type.toDB());
+ if (!publics.isEmpty()) {
+ break;
+ }
+ }
+ if (publics.isEmpty()) {
+ return;
+ }
+ }
+
+ setSchematic(publics.get(new Random().nextInt(publics.size())));
+ }
+
+ if(ArenaMode.AntiReplay.contains(Config.mode)) {
+ if(team.isBlue())
+ GlobalRecorder.getInstance().blueSchem(schematic);
+ else
+ GlobalRecorder.getInstance().redSchem(schematic);
+ }
+
+ Bukkit.getScheduler().runTask(FightSystem.getPlugin(), this::paste);
+ }
+
+ private void replaceTeamColor(Clipboard clipboard) {
+ try {
+ WorldeditWrapper.impl.replaceTeamColor(clipboard, ArenaMode.AntiPrepare.contains(Config.mode) ? ColorConverter.chat2dye(team.getColor()) : DyeColor.PINK);
+ } catch (WorldEditException e) {
+ Bukkit.getLogger().log(Level.SEVERE, "Could not recolor schematic", e);
+ }
+ }
+
+ private void paste(){
+ FreezeWorld freezer = new FreezeWorld();
+
+ Vector dims = WorldeditWrapper.impl.getDimensions(clipboard);
+ WorldeditWrapper.impl.pasteClipboard(
+ clipboard,
+ new Location(Config.world, region.centerX(), region.getMinY(), region.centerZ()),
+ new Vector(
+ Config.PasteAligned && Config.BlueToRedX != 0 ? region.getSizeX()/2.0 - dims.getBlockX() : -dims.getBlockX()/2.0,
+ Config.WaterDepth != 0 ? Config.WaterDepth - WorldeditWrapper.impl.getWaterDepth(clipboard) : 0,
+ Config.PasteAligned && Config.BlueToRedZ != 0 ? region.getSizeZ()/2.0 - dims.getBlockZ() : -dims.getBlockZ()/2.0
+ ).add(new Vector(rotate ? 1 : 0, 0, rotate ? 1 : 0)),
+ new AffineTransform().rotateY(rotate ? 180 : 0)
+ );
+ FightSystem.getHullHider().initialize(team);
+ if(ArenaMode.Check.contains(Config.mode) && !team.isBlue())
+ replaceSync(Material.TNT, Material.OBSIDIAN);
+
+ Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), freezer::disable, 3);
+ Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), team::teleportToSpawn, 40);
+ }
+
+ @Override
+ public void disable() {
+ if(!Config.ReplaceObsidianBedrock || Config.mode == ArenaMode.PREPARE)
+ return;
+
+ FreezeWorld freezer = null;
+ if(!Config.ReplaceWithBlockupdates)
+ freezer = new FreezeWorld();
+
+ replaceSync(Material.OBSIDIAN, Material.TNT);
+ replaceSync(Material.BEDROCK, Material.SLIME_BLOCK);
+
+ if(!Config.ReplaceWithBlockupdates)
+ freezer.disable();
+ }
+
+ public void pasteTeamName(){
+ char[] chars = team.getName().toCharArray();
+ Clipboard[] characters = new Clipboard[chars.length];
+
+ int length = 0;
+ int[] offsets = new int[chars.length];
+
+ for(int i = 0; i < chars.length; i++){
+ Clipboard character;
+ try {
+ character = WorldeditWrapper.impl.loadChar(chars[i] == '/' ? "slash" : String.valueOf(chars[i]));
+ } catch (IOException e) {
+ Bukkit.getLogger().log(Level.WARNING, "Could not display character {} due to missing file!", chars[i]);
+ try {
+ character = WorldeditWrapper.impl.loadChar("");
+ }catch (IOException ex) {
+ throw new SecurityException("Could not load text", ex);
+ }
+ }
+
+ replaceTeamColor(character);
+
+ characters[i] = character;
+ offsets[i] = length;
+ length += WorldeditWrapper.impl.getDimensions(character).getBlockX() + 1; // 1 is the distance between characters
+ }
+
+ length -= 1;
+ AffineTransform aT = new AffineTransform().rotateY(((team == Fight.getRedTeam()) == (Config.BlueToRedZ > 0)) ? 180 : 0);
+ Location base = new Location(Config.world, region.centerX(), team.getExtendRegion().getMaxY(), region.centerZ());
+ for(int i = 0; i < characters.length; i++){
+ WorldeditWrapper.impl.pasteClipboard(characters[i], base, new Vector(offsets[i] - length/2, 0, -region.getSizeZ()/2), aT);
+ }
+ }
+
+ private void replaceSync(Material target, Material replacement){
+ region.forEach((x, y, z) -> {
+ Block block = Config.world.getBlockAt(x, y, z);
+ if(block.getType() == target)
+ block.setType(replacement);
+ });
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java
new file mode 100644
index 00000000..853646fc
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java
@@ -0,0 +1,520 @@
+/*
+ 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.fight;
+
+import com.sk89q.worldedit.extent.clipboard.Clipboard;
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.commands.GUI;
+import de.steamwar.fightsystem.countdown.Countdown;
+import de.steamwar.fightsystem.events.TeamLeaveEvent;
+import de.steamwar.fightsystem.events.TeamSpawnEvent;
+import de.steamwar.fightsystem.listener.FightScoreboard;
+import de.steamwar.fightsystem.listener.Permanent;
+import de.steamwar.fightsystem.listener.PersonalKitCreator;
+import de.steamwar.fightsystem.listener.TeamArea;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.OneShotStateDependent;
+import de.steamwar.fightsystem.states.StateDependent;
+import de.steamwar.fightsystem.utils.*;
+import de.steamwar.fightsystem.winconditions.Wincondition;
+import de.steamwar.fightsystem.winconditions.Winconditions;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.sql.SchematicNode;
+import de.steamwar.sql.SteamwarUser;
+import lombok.Getter;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.*;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.scoreboard.NameTagVisibility;
+import org.bukkit.scoreboard.Team;
+
+import java.util.*;
+import java.util.function.Consumer;
+
+
+public class FightTeam {
+
+ private static void setKitButton(HotbarKit kit, boolean leader) {
+ if (Kit.getAvailableKits(leader).size() > 1 || Config.PersonalKits)
+ kit.setItem(1, "CHOOSE_KIT", new ItemBuilder(Material.LEATHER_CHESTPLATE).enchant().build(), player -> GUI.kitSelection(player, ""));
+ else
+ kit.setItem(1, null, null, null);
+ }
+
+ private static final HotbarKit memberKit = new HotbarKit();
+ static {
+ setKitButton(memberKit, false);
+ memberKit.setItem(7, "RESPAWN", new ItemBuilder(Material.BEACON).build(), player -> player.teleport(Objects.requireNonNull(Fight.getPlayerTeam(player)).getSpawn()));
+ }
+ private static final HotbarKit notReadyKit = new HotbarKit(memberKit);
+ static {
+ setKitButton(notReadyKit, true);
+
+ if(!ArenaMode.RankedEvent.contains(Config.mode)){
+ notReadyKit.setItem(2, "REQUESTS", new ItemBuilder(Material.PAPER).build(), GUI::chooseJoinRequests);
+ notReadyKit.setItem(3, "REMOVE_PLAYERS", new ItemBuilder(SWItem.getMaterial("FIREWORK_CHARGE")).build(), GUI::chooseRemove);
+ }
+
+ if(Config.test())
+ notReadyKit.setItem(5, "CHOOSE_SCHEMATIC", new ItemBuilder(SWItem.getMaterial("CAULDRON_ITEM")).enchant().build(), GUI::preSchemDialog);
+
+ notReadyKit.setItem(4, "TEAM_NOT_READY", new ItemBuilder(SWItem.getDye(10), (short) 10).enchant().build(), player -> Objects.requireNonNull(Fight.getPlayerTeam(player)).setReady(true));
+ }
+ private static final HotbarKit chooseSchemKit = new HotbarKit(notReadyKit);
+ static {
+ chooseSchemKit.setItem(4, "CHOOSE_SCHEMATIC", new ItemBuilder(SWItem.getMaterial("CAULDRON_ITEM")).enchant().build(), GUI::preSchemDialog);
+ }
+ private static final HotbarKit readyKit = new HotbarKit(memberKit);
+ static {
+ readyKit.setItem(1, null, null, null);
+ readyKit.setItem(4, "TEAM_READY", new ItemBuilder(SWItem.getDye(8), (short) 8).enchant().build(), player -> Objects.requireNonNull(Fight.getPlayerTeam(player)).setReady(false));
+ }
+
+ @Getter
+ private UUID designatedLeader;
+ @Getter
+ private FightPlayer leader;
+ @Getter
+ private boolean publicsOnly;
+
+ private final Map players = new HashMap<>();
+
+ @Getter
+ private String name;
+ @Getter
+ private String prefix;
+ @Getter
+ private ChatColor color;
+ private final FightSchematic schematic;
+ private final Team team;
+ @Getter
+ private final boolean blue;
+
+ @Getter
+ private boolean ready;
+ private boolean skip;
+
+ @Getter
+ private final Location spawn;
+ @Getter
+ private final Region schemRegion;
+ @Getter
+ private final Region extendRegion;
+
+ @SuppressWarnings("deprecation")
+ public FightTeam(String name, String prefix, Location spawn, Region schemRegion, Region extendRegion, boolean rotate, boolean blue, UUID designatedLeader) { //TODO: Static TeamConfig object
+ this.spawn = spawn;
+ this.schemRegion = schemRegion;
+ this.extendRegion = extendRegion;
+ this.publicsOnly = false;
+ this.ready = false;
+ this.skip = false;
+ this.blue = blue;
+ this.designatedLeader = designatedLeader;
+ setPrefixAndName(prefix, name);
+ this.schematic = new FightSchematic(this, rotate);
+ new KitLoader();
+ new SpectateHandler();
+ new TeamArea(this);
+
+ team = FightScoreboard.getBukkitTeam(name);
+ WorldOfColorWrapper.impl.setTeamColor(team, color);
+ BountifulWrapper.impl.setNametagVisibility(team);
+ team.setNameTagVisibility(NameTagVisibility.HIDE_FOR_OTHER_TEAMS);
+ if (!Config.ActiveWinconditions.contains(Winconditions.AMONG_US)) {
+ team.setAllowFriendlyFire(false);
+ }
+
+ new OneShotStateDependent(ArenaMode.Restartable, FightState.PreLeaderSetup, () -> Bukkit.getScheduler().runTask(FightSystem.getPlugin(), this::reset));
+ new OneShotStateDependent(Config.replayserver(), FightState.PreLeaderSetup, () -> Bukkit.getScheduler().runTask(FightSystem.getPlugin(), this::reset));
+ new OneShotStateDependent(ArenaMode.All, FightState.PostSchemSetup, () -> {
+ if(leader != null)
+ leader.ifPlayer(notReadyKit::loadToPlayer);
+ });
+ }
+
+ public void setPrefixAndName(String prefix, String name){
+ this.name = name;
+ this.prefix = prefix;
+ this.color = ChatColor.getByChar(ChatColor.getLastColors(prefix).replace("§", ""));
+ }
+
+ public boolean canbeLeader(Player p){
+ return isLeaderless() && (designatedLeader == null || designatedLeader.equals(p.getUniqueId()));
+ }
+
+ public void teleportToSpawn(){
+ players.forEach((player, fp) -> fp.getEntity().teleport(spawn));
+ }
+
+ public FightPlayer getFightPlayer(LivingEntity player) {
+ return players.get(player.getUniqueId());
+ }
+
+ public boolean allPlayersOut() {
+ for(FightPlayer fightPlayer : players.values()) {
+ if(fightPlayer.isLiving())
+ return false;
+ }
+ return true;
+ }
+
+ public boolean isPlayerInTeam(LivingEntity player) {
+ return players.containsKey(player.getUniqueId());
+ }
+
+ public boolean canPlayerEntern(LivingEntity player) {
+ return getFightPlayer(player).canEntern();
+ }
+
+ public boolean isPlayerLeader(LivingEntity player) {
+ if(leader != null)
+ return leader.getEntity().equals(player);
+ else
+ return false;
+ }
+
+ public void reset() {
+ skip = false;
+ ready = false;
+ schematic.reset();
+
+ Set playerSet = new HashSet<>(players.keySet());
+ playerSet.removeIf(uuid -> {
+ Player player = Bukkit.getPlayer(uuid);
+ if(player == null) {
+ removePlayer(players.get(uuid).getEntity());
+ return true;
+ }
+
+ return false;
+ });
+ FightPlayer leaderBackup = leader;
+ players.clear();
+ leader = null;
+
+ if(leaderBackup != null){
+ playerSet.remove(leaderBackup.getEntity().getUniqueId());
+ addMember(leaderBackup.getEntity(), leaderBackup.getUser(), true);
+ }
+
+ playerSet.forEach(uuid -> addMember(Bukkit.getPlayer(uuid), SteamwarUser.get(uuid), true));
+
+ if(ArenaMode.VariableTeams.contains(Config.mode) && isLeaderless()){
+ List onlinePlayers = new ArrayList<>(Bukkit.getOnlinePlayers());
+ Collections.shuffle(onlinePlayers);
+ for(Player player : onlinePlayers) {
+ if(Fight.getPlayerTeam(player) == null && canbeLeader(player)){
+ addMember(player);
+ break;
+ }
+ }
+ }
+ }
+
+ public void broadcast(String message, Object... params) {
+ broadcast(player -> FightSystem.getMessage().sendPrefixless(message, player, ChatMessageType.ACTION_BAR, params));
+ }
+
+ public void broadcastSystem(String message, Object... params) {
+ broadcast(player -> FightSystem.getMessage().send(message, player, params));
+ }
+
+ public void broadcastChat(Player sender, String message) {
+ broadcast(player -> FightSystem.getMessage().sendPrefixless("TEAM_CHAT", player, ChatMessageType.CHAT, prefix, sender.getName(), message));
+ }
+
+ private void broadcast(Consumer f) {
+ players.forEach((uuid, fightPlayer) -> {
+ Player player = Bukkit.getPlayer(uuid);
+ if(player != null)
+ f.accept(player);
+ });
+ }
+
+ public void addMember(Player player) {
+ addMember(player, SteamwarUser.get(player.getUniqueId()), false);
+ }
+
+ public void addMember(LivingEntity entity, SteamwarUser user) {
+ addMember(entity, user, false);
+ }
+
+ public void addMember(LivingEntity entity, SteamwarUser user, boolean silent) {
+ FightPlayer fightPlayer = getFightPlayer(entity) != null ? getFightPlayer(entity) : new FightPlayer(entity, user, this);
+ fightPlayer.revive();
+ players.put(entity.getUniqueId(), fightPlayer);
+ Bukkit.getPluginManager().callEvent(new TeamSpawnEvent(fightPlayer));
+
+ Permanent.getSpectatorTeam().removeEntry(entity.getName());
+ team.addEntry(entity.getName());
+
+ entity.setHealth(20);
+ entity.teleport(spawn);
+
+ fightPlayer.ifPlayer(player -> {
+ BountifulWrapper.impl.setAttackSpeed(player);
+ player.setFoodLevel(20);
+ player.getInventory().clear();
+ FightSystem.getHullHider().updatePlayer(player);
+
+ if(FightState.Spectate.contains(FightState.getFightState())) {
+ Fight.setPlayerGamemode(player, GameMode.SPECTATOR);
+ } else {
+ Fight.setPlayerGamemode(player, GameMode.SURVIVAL);
+ (FightState.ingame() ? fightPlayer.getKit() : memberKit).loadToPlayer(player);
+ }
+ });
+
+ if(FightState.Running.contains(FightState.getFightState()))
+ fightPlayer.startEnternCountdown(Wincondition.getTimeOverCountdown());
+
+ fightPlayer.ifPlayer(player -> FightSystem.getTechHider().reloadChunks(player, Config.ArenaRegion, fightPlayer.canEntern() ? Region.EMPTY : Fight.getOpposite(this).getExtendRegion()));
+
+ if(isLeaderless())
+ setLeader(fightPlayer, silent);
+ else if(!silent)
+ FightUI.addSubtitle("UI_PLAYER_JOINS", prefix, entity.getName());
+ }
+
+ public void removePlayer(LivingEntity entity) {
+ FightPlayer fightPlayer = getFightPlayer(entity);
+ Bukkit.getPluginManager().callEvent(new TeamLeaveEvent(fightPlayer));
+
+ fightPlayer.ifPlayer(PersonalKitCreator::closeIfInKitCreator);
+ players.remove(entity.getUniqueId());
+ team.removeEntry(entity.getName());
+ Permanent.getSpectatorTeam().addEntry(entity.getName());
+
+ FightUI.addSubtitle("UI_PLAYER_LEAVES", prefix, entity.getName());
+
+ if(fightPlayer.equals(leader))
+ removeLeader();
+
+ entity.teleport(Config.SpecSpawn);
+
+ fightPlayer.ifPlayer(player -> {
+ Fight.setPlayerGamemode(player, GameMode.SPECTATOR);
+ player.getInventory().clear();
+
+ if(player.isOnline()){
+ FightSystem.getHullHider().updatePlayer(player);
+ FightSystem.getTechHider().reloadChunks(player, Config.ArenaRegion, Fight.getOpposite(this).getExtendRegion());
+
+ if(ArenaMode.VariableTeams.contains(Config.mode))
+ HotbarKit.SPECTATOR_KIT.loadToPlayer(player);
+ }
+ });
+ }
+
+ public boolean isLeaderless() {
+ return leader == null;
+ }
+
+ private void removeLeader() {
+ this.leader = null;
+ if(!players.isEmpty()) {
+ setLeader(players.values().iterator().next(), false);
+ }else if(FightState.getFightState() != FightState.PRE_LEADER_SETUP && !ArenaMode.RankedEvent.contains(Config.mode)){
+ Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> FightState.setFightState(FightState.PRE_LEADER_SETUP), 1);
+ }
+ }
+
+ private void setLeader(FightPlayer leader, boolean silent) {
+ leader.ifPlayer(PersonalKitCreator::closeIfInKitCreator);
+
+ this.leader = leader;
+ designatedLeader = null;
+ if(ready)
+ setReady(false);
+
+ if(!silent)
+ FightUI.addSubtitle("UI_LEADER_JOINS", prefix, leader.getEntity().getName());
+
+ publicsOnly = SchematicNode.getAllAccessibleSchematicsOfType(leader.getUser().getId(), Config.SchematicType.toDB()).isEmpty();
+
+ if(!Config.PersonalKits)
+ leader.setKit(Kit.getKitByName(Config.LeaderDefault));
+
+ leader.ifPlayer(player -> {
+ if(FightState.getFightState() != FightState.POST_SCHEM_SETUP)
+ chooseSchemKit.loadToPlayer(player);
+ else
+ notReadyKit.loadToPlayer(player);
+ });
+
+ if(FightState.getFightState() == FightState.PRE_LEADER_SETUP && !Fight.getOpposite(this).isLeaderless()){
+ FightState.setFightState(FightState.PRE_SCHEM_SETUP);
+ }
+
+ leader.ifAI(ai -> pasteSchem(ai.chooseSchematic()));
+ }
+
+ public Collection getPlayers() {
+ return players.values();
+ }
+
+ public void pasteSchem() {
+ testPasteAction();
+ }
+
+ public void pasteSchem(SchematicNode schematic){
+ if(schematic.getSchemtype().check()) {
+ FightStatistics.unrank();
+ FightSystem.getMessage().broadcast("SCHEMATIC_UNCHECKED", getColoredName());
+ }
+
+ setSchem(schematic);
+ testPasteAction();
+ }
+
+ public void pasteSchem(int schemId, Clipboard clipboard){
+ this.schematic.setSchematic(schemId, clipboard);
+ testPasteAction();
+ }
+
+ private void testPasteAction(){
+ if(Config.test())
+ this.schematic.enable();
+ else if(Fight.getOpposite(this).schematic.hasSchematic()){
+ FightState.setFightState(FightState.POST_SCHEM_SETUP);
+ }
+ }
+
+ public void pasteTeamName(){
+ schematic.pasteTeamName();
+ }
+
+ public void setSchem(SchematicNode schematic){
+ this.schematic.setSchematic(schematic);
+ broadcast("SCHEMATIC_CHOSEN", Config.GameName, schematic.getName());
+ }
+
+ public void setReady(boolean ready) {
+ LivingEntity l = leader.getEntity();
+
+ if(!schematic.hasSchematic()){
+ FightSystem.getMessage().sendPrefixless("SCHEMATIC_REQUIRED", l, ChatMessageType.ACTION_BAR);
+ return;
+ }
+
+ this.ready = ready;
+ if(ready) {
+ broadcast("TEAM_READY");
+ leader.ifPlayer(readyKit::loadToPlayer);
+ if(Fight.getOpposite(this).isReady() || ArenaMode.SoloLeader.contains(Config.mode))
+ FightState.setFightState(FightState.PRE_RUNNING);
+ } else {
+ broadcast("TEAM_NOT_READY");
+ leader.ifPlayer(notReadyKit::loadToPlayer);
+ }
+ }
+
+ public void skip(){
+ this.skip = !skip;
+ if(skip){
+ broadcast("SKIP_READY");
+ if(Fight.getOpposite(this).skip || Config.test()){
+ skip = false;
+ Fight.getOpposite(this).skip = false;
+ Countdown.skip();
+ }
+ }else{
+ broadcast("SKIP_NOT_READY");
+ }
+ }
+
+ public String getColoredName() {
+ return prefix + name;
+ }
+
+ public int getSchematic() {
+ return schematic.getId();
+ }
+
+ public double getCurrentHearts() {
+ return players.values().stream().filter(FightPlayer::isLiving).mapToDouble(fp -> fp.getEntity().getHealth()).sum();
+ }
+
+ public double getHeartRatio(){
+ int maximumHearts = players.size() * 20;
+ return maximumHearts != 0 ? getCurrentHearts() / maximumHearts : 0;
+ }
+
+ public int getPlayerCount() {
+ return players.size();
+ }
+
+ public int getAlivePlayers() {
+ return (int) players.values().stream().filter(FightPlayer::isLiving).count();
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ private class KitLoader extends StateDependent {
+ private KitLoader() {
+ super(ArenaMode.AntiReplay, FightState.Ingame);
+ register();
+ }
+
+ @Override
+ public void enable() {
+ for(FightPlayer fightPlayer : players.values()) {
+ fightPlayer.ifPlayer(player -> {
+ PersonalKitCreator.closeIfInKitCreator(player);
+
+ player.closeInventory();
+ fightPlayer.getKit().loadToPlayer(player);
+ });
+ }
+ }
+
+ @Override
+ public void disable() {
+ players.values().forEach(fightPlayer -> fightPlayer.ifPlayer(p -> p.getInventory().clear()));
+ }
+ }
+
+ private class SpectateHandler extends StateDependent {
+ private SpectateHandler() {
+ super(ArenaMode.AntiReplay, FightState.Spectate);
+ register();
+ }
+
+ @Override
+ public void enable() {
+ players.values().forEach(fightPlayer -> {
+ fightPlayer.ifPlayer(player -> Fight.setPlayerGamemode(player, GameMode.SPECTATOR));
+ fightPlayer.getEntity().teleport(FightTeam.this.spawn);
+ });
+ }
+
+ @Override
+ public void disable() {
+ players.values().forEach(fightPlayer -> fightPlayer.ifPlayer(player -> Fight.setPlayerGamemode(player, GameMode.SURVIVAL)));
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightWorld.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightWorld.java
new file mode 100644
index 00000000..efcc16a3
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightWorld.java
@@ -0,0 +1,88 @@
+/*
+ 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.fight;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.listener.Recording;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependent;
+import de.steamwar.fightsystem.utils.CraftbukkitWrapper;
+import de.steamwar.fightsystem.utils.FlatteningWrapper;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.WorldCreator;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class FightWorld extends StateDependent {
+
+ @Getter
+ private static final boolean PAPER = Bukkit.getVersion().contains("git-Paper");
+
+ public FightWorld() {
+ super(ArenaMode.Restartable.contains(Config.mode) || Config.replayserver(), FightState.Schem);
+ register();
+ }
+
+ @Override
+ public void enable() {
+ //unused
+ }
+
+ @Override
+ public void disable() {
+ Bukkit.getScheduler().runTask(FightSystem.getPlugin(), FightWorld::resetWorld); //Delay to prevent raceconditions with techhider and hullhider
+ }
+
+ public static void forceLoad(){
+ Config.ArenaRegion.forEachChunk((cX, cZ) -> {
+ Config.world.loadChunk(cX, cZ);
+ FlatteningWrapper.impl.forceLoadChunk(Config.world, cX, cZ);
+ });
+ }
+
+ public static void resetWorld(){
+ List entities = new ArrayList<>();
+ Recording.iterateOverEntities(Objects::nonNull, entity -> {
+ if(entity.getType() != EntityType.PLAYER && (!Config.ArenaLeaveable || Config.ArenaRegion.inRegion(entity.getLocation())))
+ entities.add(entity);
+ });
+ entities.forEach(Entity::remove);
+ entities.clear();
+
+ World backup = new WorldCreator(Config.world.getName() + "/backup").createWorld();
+ assert backup != null;
+ Config.ArenaRegion.forEachChunk((x, z) -> {
+ CraftbukkitWrapper.impl.resetChunk(Config.world, backup, x, z);
+ for(Player p : Bukkit.getOnlinePlayers()) {
+ de.steamwar.core.CraftbukkitWrapper.impl.sendChunk(p, x, z);
+ }
+ });
+ Bukkit.unloadWorld(backup, false);
+ }
+}
\ No newline at end of file
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FreezeWorld.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FreezeWorld.java
new file mode 100644
index 00000000..7b58543f
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FreezeWorld.java
@@ -0,0 +1,96 @@
+/*
+ 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.fight;
+
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.utils.BountifulWrapper;
+import org.bukkit.Bukkit;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.*;
+import org.bukkit.event.entity.ItemSpawnEvent;
+import org.bukkit.event.inventory.InventoryMoveItemEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+
+public class FreezeWorld implements Listener {
+
+ private final Listener denyHandSwap = BountifulWrapper.impl.newDenyHandSwapListener();
+
+ public FreezeWorld(){
+ Bukkit.getPluginManager().registerEvents(this, FightSystem.getPlugin());
+ Bukkit.getPluginManager().registerEvents(denyHandSwap, FightSystem.getPlugin());
+ }
+
+ public void disable(){
+ HandlerList.unregisterAll(this);
+ HandlerList.unregisterAll(denyHandSwap);
+ }
+
+ @EventHandler
+ public void onBlockPhysicsEvent(BlockPhysicsEvent e){
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onPistonExtend(BlockPistonExtendEvent e){
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onPistonRetract(BlockPistonRetractEvent e){
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onBlockGrow(BlockGrowEvent e){
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onRedstoneEvent(BlockRedstoneEvent e){
+ e.setNewCurrent(e.getOldCurrent());
+ }
+
+ @EventHandler
+ public void onBlockDispense(BlockDispenseEvent e){
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onInventoryMoveEvent(InventoryMoveItemEvent e){
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onBlockExplosion(BlockExplodeEvent e){
+ e.setCancelled(true);
+ }
+ @EventHandler
+ public void onBlockExplosion(ItemSpawnEvent e){
+ e.setCancelled(true);
+ }
+
+ @EventHandler(priority = EventPriority.LOW)
+ public void handlePlayerInteract(PlayerInteractEvent event) {
+ event.setCancelled(true);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/HotbarKit.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/HotbarKit.java
new file mode 100644
index 00000000..5bc1d844
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/HotbarKit.java
@@ -0,0 +1,121 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2022 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.fight;
+
+import de.steamwar.core.Core;
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.commands.GUI;
+import de.steamwar.fightsystem.listener.PersonalKitCreator;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.states.StateDependentTask;
+import de.steamwar.fightsystem.utils.ItemBuilder;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.Action;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.inventory.EquipmentSlot;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.potion.PotionEffect;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public class HotbarKit extends Kit {
+
+ public static final HotbarKit SPECTATOR_KIT = new HotbarKit();
+ static {
+ for(int i = 0; i < 9; i++)
+ SPECTATOR_KIT.setItem(i, "JOIN_REQUEST", new ItemBuilder(Material.PAPER).build(), GUI::joinRequest);
+ }
+
+ private static final int HOTBAR_SIZE = 9;
+
+ private final String[] nameTags;
+ private final Consumer[] onClicks;
+
+ protected HotbarKit(String name, ItemStack[] inventory, ItemStack[] armor, Collection effects, String[] nameTags, Consumer[] onClicks) {
+ super(name, inventory, armor, effects);
+ this.nameTags = nameTags;
+ this.onClicks = onClicks;
+ }
+
+ public HotbarKit() {
+ this(null, new ItemStack[HOTBAR_SIZE], null, null, new String[HOTBAR_SIZE], new Consumer[HOTBAR_SIZE]);
+ }
+
+ public HotbarKit(HotbarKit kit) {
+ this(kit.getName(), kit.getInventory().clone(), kit.getArmor() != null ? kit.getArmor().clone() : null, kit.getEffects(), kit.nameTags.clone(), kit.onClicks.clone());
+ }
+
+ public void setItem(int id, String nameTag, ItemStack stack, Consumer onClick) {
+ super.setItem(id, stack);
+ nameTags[id] = nameTag;
+ onClicks[id] = onClick;
+ }
+
+ @Override
+ public synchronized void loadToPlayer(Player player) {
+ for(int i = 0; i < HOTBAR_SIZE; i++) {
+ if(nameTags[i] != null) {
+ ItemMeta meta = Objects.requireNonNull(getInventory()[i].getItemMeta());
+ meta.setDisplayName(FightSystem.getMessage().parse(nameTags[i], player, Config.GameName));
+ getInventory()[i].setItemMeta(meta);
+ }
+ }
+ super.loadToPlayer(player);
+ }
+
+ public static class HotbarKitListener implements Listener {
+
+ private static final Set clicked = new HashSet<>();
+
+ public HotbarKitListener() {
+ new StateDependentListener(ArenaMode.AntiReplay, FightState.All, this);
+ new StateDependentTask(ArenaMode.AntiReplay, FightState.All, clicked::clear, 10, 10);
+ }
+
+ @EventHandler
+ public void handlePlayerInteract(PlayerInteractEvent event) {
+ if(event.getAction() == Action.PHYSICAL || (Core.getVersion() > 8 && event.getHand() != EquipmentSlot.HAND))
+ return;
+
+ Player player = event.getPlayer();
+ int slot = player.getInventory().getHeldItemSlot();
+ Kit activeKit = activeKits.get(player);
+ if(!(activeKit instanceof HotbarKit) || PersonalKitCreator.inKitCreator(player) || activeKit.getInventory()[slot] == null)
+ return;
+
+ event.setCancelled(true);
+ if(!clicked.add(player))
+ return;
+
+ ((HotbarKit)activeKit).onClicks[slot].accept(player);
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/JoinRequest.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/JoinRequest.java
new file mode 100644
index 00000000..a7756e4c
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/JoinRequest.java
@@ -0,0 +1,110 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2022 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.fight;
+
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.inventory.SWListInv;
+import net.md_5.bungee.api.ChatMessageType;
+import net.md_5.bungee.api.chat.ClickEvent;
+import org.bukkit.entity.Player;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class JoinRequest {
+
+ private static final Map activeRequests = new HashMap<>();
+
+ public static List> openRequests(Player p, FightTeam team) {
+ return activeRequests.values().stream().filter(
+ request -> request.waitOnApproval.contains(team)
+ ).map(request -> {
+ SWItem item = SWItem.getPlayerSkull(request.player);
+ item.setLore(Arrays.asList(
+ FightSystem.getMessage().parse("REQUESTS_LEFT_CLICK", p),
+ FightSystem.getMessage().parse("REQUESTS_RIGHT_CLICK", p)
+ ));
+ return new SWListInv.SWListEntry<>(item, request.player);
+ }).collect(Collectors.toList());
+ }
+
+ public static void clearRequests() {
+ activeRequests.clear();
+ }
+
+ public static JoinRequest get(Player player) {
+ return activeRequests.get(player);
+ }
+
+ private final Player player;
+ private final FightTeam team;
+ private final Set waitOnApproval;
+
+ public JoinRequest(Player player, FightTeam team) {
+ this.player = player;
+ this.team = team;
+ this.waitOnApproval = new HashSet<>(FightState.ingame() ? Fight.teams() : Collections.singleton(team));
+ Set alreadyAccepted = new HashSet<>();
+
+ activeRequests.put(player, this);
+ for(FightTeam t : waitOnApproval) {
+ FightPlayer leader = t.getLeader();
+ if(leader == null)
+ continue;
+
+ if(leader.getEntity() == null)
+ continue;
+
+ leader.ifPlayer(leaderPlayer -> FightSystem.getMessage().sendPrefixless("JOIN_REQUEST_NOTIFICATION", leaderPlayer, "REQUESTS", new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/requests"), player.getName(), team.getColoredName()));
+ leader.ifAI(ai -> {
+ if(ai.acceptJoinRequest(player, team))
+ alreadyAccepted.add(t);
+ });
+ }
+
+ FightSystem.getMessage().sendPrefixless("JOIN_REQUEST_CONFIRMATION", player, ChatMessageType.ACTION_BAR);
+ alreadyAccepted.forEach(this::accept);
+ }
+
+ public boolean required(FightTeam decider) {
+ return waitOnApproval.contains(decider);
+ }
+
+ public void accept(FightTeam acceptor) {
+ waitOnApproval.remove(acceptor);
+
+ if(waitOnApproval.isEmpty()) {
+ team.addMember(player);
+ activeRequests.remove(player);
+ }
+ }
+
+ public void decline(FightTeam declinor) {
+ FightSystem.getMessage().sendPrefixless("REQUEST_YOUR_DECLINED", player, ChatMessageType.ACTION_BAR);
+ waitOnApproval.forEach(t -> t.broadcast("REQUEST_DECLINED", player.getName()));
+ silentDecline();
+ }
+
+ public void silentDecline() {
+ activeRequests.remove(player);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/Kit.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/Kit.java
new file mode 100644
index 00000000..e3829099
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/Kit.java
@@ -0,0 +1,368 @@
+/*
+ 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.fight;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.commands.Commands;
+import de.steamwar.fightsystem.commands.GUI;
+import de.steamwar.fightsystem.listener.PersonalKitCreator;
+import de.steamwar.fightsystem.utils.FlatteningWrapper;
+import de.steamwar.inventory.SWInventory;
+import de.steamwar.inventory.SWItem;
+import de.steamwar.sql.PersonalKit;
+import de.steamwar.sql.SteamwarUser;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.FileConfiguration;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.potion.PotionEffect;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.logging.Level;
+
+public class Kit {
+ private static final File kits = new File(FightSystem.getPlugin().getDataFolder(), Config.KitFile);
+ private static final ArrayList loadedKits = new ArrayList<>();
+
+ protected static final Map activeKits = new HashMap<>();
+
+ public static Kit getActiveKit(Player player) {
+ return activeKits.get(player);
+ }
+
+ static {
+ if(!kits.exists()) {
+ Bukkit.getLogger().log(Level.SEVERE, "Kitconfig fehlend!" + kits.getAbsolutePath());
+ }
+
+ FileConfiguration kitData = YamlConfiguration.loadConfiguration(kits);
+ ConfigurationSection kitSection = kitData.getConfigurationSection("Kits");
+
+ for(String key : Objects.requireNonNull(kitSection).getKeys(false)) {
+ loadedKits.add(new Kit(Objects.requireNonNull(kitSection.getConfigurationSection(key))));
+ }
+ }
+
+ @Getter
+ private final String name;
+ @Getter
+ private final ItemStack[] inventory;
+ @Getter
+ private final ItemStack[] armor;
+ @Getter
+ private final Collection effects;
+ /* In which stage is entern allowed? */
+ @Getter
+ private final int enterStage;
+ /* Is this kit allowed to set/handle tnt? */
+ @Getter
+ private final boolean tnt;
+ private final boolean leaderAllowed;
+ private final boolean memberAllowed;
+
+ protected Kit(String name, ItemStack[] inventory, ItemStack[] armor, Collection effects) {
+ this.name = name;
+ this.inventory = inventory;
+ this.armor = armor;
+ this.effects = effects;
+ this.leaderAllowed = true;
+ this.memberAllowed = true;
+ this.enterStage = 0;
+ this.tnt = true;
+ }
+
+ protected Kit(Kit kit) {
+ this(kit.name, kit.inventory.clone(), kit.armor, kit.effects);
+ }
+
+ public Kit(String name, Player player) {
+ this(name, player.getInventory().getContents(), player.getInventory().getArmorContents(), player.getActivePotionEffects());
+ }
+
+ public Kit(PersonalKit kit){
+ this(kit.getName(), kit.getInventory(), kit.getArmor(), Collections.emptyList());
+ }
+
+ public Kit(ConfigurationSection kit){
+ name = kit.getName();
+ inventory = Objects.requireNonNull(kit.getList("Items")).toArray(new ItemStack[0]);
+ if(kit.isList("Armor"))
+ armor = Objects.requireNonNull(kit.getList("Armor")).toArray(new ItemStack[0]);
+ else
+ armor = null;
+ leaderAllowed = kit.getBoolean("LeaderAllowed");
+ memberAllowed = kit.getBoolean("MemberAllowed");
+ if(kit.isList("Effects"))
+ effects = (List) kit.getList("Effects");
+ else
+ effects = null;
+ enterStage = kit.getInt("EnterStage", 0);
+ tnt = kit.getBoolean("TNT", true);
+ }
+
+ protected void setItem(int id, ItemStack stack) {
+ inventory[id] = stack;
+ }
+
+ public static Kit getKitByName(String kitName) {
+ for(Kit kit : loadedKits) {
+ if(kit.getName().equalsIgnoreCase(kitName))
+ return kit;
+ }
+ return null;
+ }
+
+ public static List getAvailableKits(boolean leader){
+ List kits = new ArrayList<>();
+ for (Kit k : loadedKits) {
+ if (k.canUseKit(leader)){
+ kits.add(k);
+ }
+ }
+ return kits;
+ }
+
+ public boolean canUseKit(boolean leader){
+ if (leader) {
+ return leaderAllowed;
+ } else {
+ return memberAllowed;
+ }
+ }
+
+ public boolean leaderExclusive() {
+ return !memberAllowed;
+ }
+
+ public boolean contains(ItemStack stack) {
+ for(ItemStack i : inventory) {
+ if(similar(i, stack))
+ return true;
+ }
+ for(ItemStack i : armor) {
+ if(similar(i, stack))
+ return true;
+ }
+ return false;
+ }
+
+ public void toPersonalKit(PersonalKit kit) {
+ kit.setContainer(inventory, armor);
+ }
+
+ public void removeBadItems(){
+ Kit normal = Kit.getKitByName(Config.MemberDefault);
+ assert normal != null;
+
+ for(int i = 0; i < inventory.length; i++){
+ if(isBadItem(inventory[i]))
+ inventory[i] = null;
+ }
+
+ }
+
+ public static boolean isBadItem(ItemStack stack){
+ if(stack == null)
+ return false;
+
+ //Check for forbidden item
+ if(Config.ForbiddenItems.contains(stack.getType()))
+ return true;
+
+ //Check for attribute modifiers
+ if(FlatteningWrapper.impl.hasAttributeModifier(stack)){
+ return true;
+ }
+
+ if(stack.hasItemMeta()){
+ ItemMeta meta = stack.getItemMeta();
+ if(FlatteningWrapper.impl.containsBlockMeta(meta))
+ return true; //Blocks always upwards slabs etc.
+
+ if(hasItems(stack))
+ return true; //Blocks prefilled inventories
+ }
+
+ Kit normal = Kit.getKitByName(Config.MemberDefault);
+ assert normal != null;
+ return !normal.isEnchantmentInKit(stack) && !stack.getEnchantments().isEmpty();
+ }
+
+ private static final Class> itemStack = Reflection.getClass("{nms.world.item}.ItemStack");
+ private static final Reflection.MethodInvoker asNMSCopy = Reflection.getTypedMethod(Reflection.getClass("{obc}.inventory.CraftItemStack"), "asNMSCopy", itemStack, ItemStack.class);
+ private static final Class> nbtTagCompound = Reflection.getClass("{nms.nbt}.NBTTagCompound");
+ private static final Reflection.MethodInvoker getTag = Reflection.getTypedMethod(itemStack, null, nbtTagCompound);
+ private static final Reflection.MethodInvoker getKeys = Reflection.getTypedMethod(nbtTagCompound, null, Set.class);
+ public static boolean hasItems(ItemStack stack) {
+ Set keys = new HashSet<>((Set) getKeys.invoke(getTag.invoke(asNMSCopy.invoke(null, stack))));
+ keys.remove("Enchantments");
+ keys.remove("Damage");
+ return !keys.isEmpty();
+ }
+
+ private boolean isEnchantmentInKit(ItemStack stack){
+ for(ItemStack is : inventory){
+ if(similar(stack, is))
+ return true;
+ }
+ if(armor != null){
+ for(ItemStack is : armor){
+ if(similar(stack, is))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean similar(ItemStack stack, ItemStack stack2){
+ if(stack == null || stack2 == null)
+ return false;
+ if(stack.getType() != stack2.getType())
+ return false;
+ if(stack.hasItemMeta() != stack2.hasItemMeta())
+ return false;
+ if(stack.getItemMeta() == null || stack2.getItemMeta() == null)
+ return true;
+
+ //Enchantment Map comparison used for default similarity check does not work
+ Map en = stack.getItemMeta().getEnchants();
+ Map en2 = new HashMap<>(stack.getItemMeta().getEnchants());
+
+ for(Map.Entry e : en.entrySet()){
+ if(!en2.remove(e.getKey(), e.getValue()))
+ return false;
+ }
+ return en2.isEmpty();
+ }
+
+ public void loadToPlayer(Player player) {
+ activeKits.put(player, this);
+
+ player.getInventory().clear();
+ player.getInventory().setContents(inventory);
+ if(armor != null)
+ player.getInventory().setArmorContents(armor);
+ player.updateInventory();
+ if(effects != null)
+ player.addPotionEffects(effects);
+ }
+
+ /**
+ * Opens a kit preview with the options to go back to kit selection or to select the kit.
+ */
+ public void preview(Player player){
+ SWInventory inv = new SWInventory(player, 54, name);
+
+ //36 = Inventargröße
+ for(int i = 0; i < 36; i++){
+ if(inventory[i] == null)
+ continue;
+ SWItem item = new SWItem();
+ item.setItemStack(inventory[i]);
+ inv.setItem(i, item);
+ }
+
+ if(armor != null){
+ for(int i = 0; i < 4; i++){
+ if(armor[i] == null)
+ continue;
+ SWItem item = new SWItem();
+ item.setItemStack(armor[i]);
+ inv.setItem(36 + i, item);
+ }
+ }
+
+ if(effects != null){
+ Iterator it = effects.iterator();
+ int pos = 44;
+ while(it.hasNext()){
+ PotionEffect effect = it.next();
+ SWItem item = new SWItem(SWItem.getMaterial("POTION"), effect.getType().getName());
+ inv.setItem(pos, item);
+ pos--;
+ }
+ }
+
+ inv.setCallback(-999, click -> player.closeInventory());
+ if(Config.PersonalKits){
+ inv.setItem(49, SWItem.getMaterial("WOOD_AXE"), FightSystem.getMessage().parse("KIT_PREVIEW_EDIT", player), clickType -> PersonalKitCreator.openKitCreator(player, PersonalKit.get(SteamwarUser.get(player.getUniqueId()).getId(), Config.SchematicType.toDB(), name)));
+ inv.setItem(53, Material.BARRIER, FightSystem.getMessage().parse("KIT_PREVIEW_DELETE", player), clickType -> {
+ player.closeInventory();
+ SWInventory conf = new SWInventory(player, 9, FightSystem.getMessage().parse("KIT_DELETION_CONFIRMATION", player));
+ conf.setItem(8, SWItem.getDye(1), FightSystem.getMessage().parse("KIT_DELETION_ABORT", player), click -> player.closeInventory());
+ conf.setItem(0, SWItem.getDye(10), FightSystem.getMessage().parse("KIT_DELETION_DELETE", player), click -> {
+ player.closeInventory();
+ SteamwarUser user = SteamwarUser.get(player.getUniqueId());
+ PersonalKit kit = PersonalKit.get(user.getId(), Config.SchematicType.toDB(), name);
+ if(kit.isInUse()) {
+ List kits = PersonalKit.get(user.getId(), Config.SchematicType.toDB());
+ if(!kits.isEmpty()){
+ PersonalKit kit1 = kits.get(0);
+ kit1.setInUse();
+ FightPlayer fightPlayer = Fight.getFightPlayer(player);
+ assert fightPlayer != null;
+ fightPlayer.setKit(new Kit(kit1));
+ }
+ }
+ kit.delete();
+ });
+ conf.open();
+ });
+ }
+ inv.setItem(45, SWItem.getDye(10), (byte)10, FightSystem.getMessage().parse("KIT_PREVIEW_CHOOSE", player), click -> {
+ Commands.kit(player, name);
+ player.closeInventory();
+ });
+ inv.setItem(53, SWItem.getDye(1), (byte)1, FightSystem.getMessage().parse("KIT_PREVIEW_BACK", player), click -> GUI.kitSelection(player, ""));
+ inv.open();
+ }
+
+ public static void createKit(String kitName, Player player){
+ loadedKits.add(new Kit(kitName, player));
+ YamlConfiguration yamlConfiguration = new YamlConfiguration();
+ for(Kit k : loadedKits){
+ ConfigurationSection section = yamlConfiguration.createSection("Kits." + k.getName());
+ section.set("Items", k.inventory);
+ if(k.armor != null)
+ section.set("Armor", k.armor);
+ section.set("LeaderAllowed", k.leaderAllowed);
+ section.set("MemberAllowed", k.memberAllowed);
+ section.set("Effects", k.effects);
+ section.set("EnterStage", k.enterStage);
+ section.set("TNT", k.tnt);
+ }
+
+ try {
+ yamlConfiguration.save(kits);
+ }catch(IOException e){
+ throw new SecurityException("Failed to save kits.data", e);
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArenaBorder.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArenaBorder.java
new file mode 100644
index 00000000..4c0daa45
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArenaBorder.java
@@ -0,0 +1,108 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.events.TeamDeathEvent;
+import de.steamwar.fightsystem.events.TeamLeaveEvent;
+import de.steamwar.fightsystem.events.TeamSpawnEvent;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightPlayer;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.states.StateDependentTask;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+public class ArenaBorder implements Listener {
+
+ private final Border spectatorBorder = new Border(Config.PlayerRegion, true, 5, "NO_ARENA_LEAVING", "ArenaBorder.spectatorBorder");
+ private final Border playerBorder = new Border(Config.PlayerRegion.to2d(), true, 5, "NO_ARENA_LEAVING", "ArenaBorder.playerBorder");
+
+ public ArenaBorder() {
+ new StateDependentListener(ArenaMode.All, FightState.All, this);
+ new StateDependentTask(ArenaMode.All, FightState.Running, this::damage, 2, 2);
+ new StateDependentListener(!Config.GroundWalkable, FightState.AntiRunning, new Listener() {
+ @EventHandler
+ public void onMove(PlayerMoveEvent e) {
+ Player player = e.getPlayer();
+ if(e.getTo().getY() <= Config.PlayerRegion.getMinY() && playerBorder.contains(player))
+ player.teleport(Fight.getPlayerTeam(player).getSpawn());
+ }
+ });
+ }
+
+ @EventHandler
+ public void onPlayerJoin(PlayerJoinEvent e) {
+ addToSpectator(e.getPlayer());
+ }
+
+ @EventHandler
+ public void onTeamJoin(TeamSpawnEvent e) {
+ e.getFightPlayer().ifPlayer(player -> {
+ spectatorBorder.removePlayer(player);
+ playerBorder.addPlayer(player);
+ });
+ }
+
+ @EventHandler
+ public void onTeamDeath(TeamDeathEvent e) {
+ e.getFightPlayer().ifPlayer(player -> {
+ playerBorder.removePlayer(player);
+ addToSpectator(player);
+ });
+ }
+
+ @EventHandler
+ public void onTeamLeave(TeamLeaveEvent e) {
+ e.getFightPlayer().ifPlayer(player -> {
+ playerBorder.removePlayer(player);
+ addToSpectator(player);
+ });
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent e) {
+ spectatorBorder.removePlayer(e.getPlayer());
+ }
+
+ private void damage() {
+ Fight.teams().forEach(team -> {
+ for(FightPlayer fp : team.getPlayers()) {
+ LivingEntity entity = fp.getEntity();
+ if(fp.isLiving() && entity.getLocation().getY() <= Config.PlayerRegion.getMinY())
+ entity.damage(1);
+ }
+ });
+ }
+
+ private void addToSpectator(Player player) {
+ if(Config.ArenaLeaveable || !player.isOnline() || playerBorder.contains(player))
+ return;
+
+ spectatorBorder.addPlayer(player);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArrowStopper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArrowStopper.java
new file mode 100644
index 00000000..3fd7f7dc
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArrowStopper.java
@@ -0,0 +1,100 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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 com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentTask;
+import de.steamwar.fightsystem.utils.WorldOfColorWrapper;
+import org.bukkit.Location;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Projectile;
+import org.bukkit.projectiles.ProjectileSource;
+import org.bukkit.util.Vector;
+
+public class ArrowStopper {
+
+ private static final Vector NULL_VECTOR = new Vector(0, 0, 0);
+ private static final BlockFace[] BLOCK_FACES = {BlockFace.UP, BlockFace.DOWN, BlockFace.EAST, BlockFace.WEST, BlockFace.NORTH, BlockFace.SOUTH};
+
+ public ArrowStopper() {
+ new StateDependentTask(Config.TechhiderActive, FightState.Running, this::run, 1, 1);
+ }
+
+ private static final Class> entityArrow = Reflection.getClass("{nms.world.entity.projectile}.EntityArrow");
+ private void run() {
+ Recording.iterateOverEntities(entityArrow::isInstance, entity -> {
+ Projectile arrow = (Projectile) entity;
+ if (invalidEntity(arrow))
+ return;
+
+ Location prevLocation = arrow.getLocation().toVector().subtract(arrow.getVelocity()).toLocation(arrow.getWorld());
+ if (arrow.getTicksLived() == 0){
+ ProjectileSource projSource = arrow.getShooter();
+ if(projSource instanceof Player)
+ prevLocation = ((Player) arrow.getShooter()).getEyeLocation();
+ else
+ return;
+ }
+ if (checkBlocks(arrow.getLocation().getBlock(), prevLocation.getBlock())) {
+ arrow.remove();
+ }
+ });
+ }
+
+ private boolean checkBlocks(Block start, Block end) {
+ Block cursor = start;
+
+ while (!cursor.getLocation().equals(end.getLocation())) {
+ BlockFace nearest = BlockFace.SELF;
+ double nearestDistance = cursor.getLocation().distance(end.getLocation());
+ for (BlockFace face : BLOCK_FACES) {
+ Block relative = cursor.getRelative(face);
+ double distance = relative.getLocation().distance(end.getLocation());
+ if(distance < nearestDistance) {
+ nearestDistance = distance;
+ nearest = face;
+ }
+ }
+ cursor = cursor.getRelative(nearest);
+ if(checkBlock(cursor))
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean checkBlock(Block block) {
+ return Config.HiddenBlocks.contains(block.getType());
+ }
+
+ private boolean invalidEntity(Projectile entity) {
+ Location location = entity.getLocation();
+ boolean teamFrom = entity.getVelocity().getZ() > 0;
+ boolean overMid = location.getZ() > Config.SpecSpawn.getZ();
+ boolean otherSide = teamFrom == overMid;
+ return otherSide || !Config.ArenaRegion.inRegion(location) ||
+ WorldOfColorWrapper.impl.isInBlock(entity) ||
+ entity.getVelocity().equals(NULL_VECTOR);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/BlockFadeListener.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/BlockFadeListener.java
new file mode 100644
index 00000000..51b5b949
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/BlockFadeListener.java
@@ -0,0 +1,24 @@
+package de.steamwar.fightsystem.listener;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.Material;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockFadeEvent;
+
+public class BlockFadeListener implements Listener {
+
+ public BlockFadeListener() {
+ new StateDependentListener(Config.DisableSnowMelt, FightState.All, this);
+ }
+
+ @EventHandler
+ public void onBlockFade(BlockFadeEvent event) {
+ if (Config.ArenaRegion.inRegion(event.getBlock()) && (event.getBlock().getType() == Material.SNOW_BLOCK || event.getBlock().getType() == Material.SNOW || event.getBlock().getType() == Material.ICE)) {
+ event.setCancelled(true);
+ }
+ }
+
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/BlockPlaceCollision.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/BlockPlaceCollision.java
new file mode 100644
index 00000000..7b3bf541
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/BlockPlaceCollision.java
@@ -0,0 +1,61 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2023 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.ArenaMode;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.utils.FlatteningWrapper;
+import org.bukkit.Location;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockPlaceEvent;
+
+public class BlockPlaceCollision implements Listener {
+
+ public BlockPlaceCollision() {
+ new StateDependentListener(ArenaMode.All, FightState.All, this);
+ }
+
+ @EventHandler
+ public void onBlockPlace(BlockPlaceEvent event) {
+ Block block = event.getBlock();
+ if(!block.getType().isSolid())
+ return;
+
+ // Hitbox size: 0.6xz, 1.8y, 1.5y when sneaking
+ Player player = event.getPlayer();
+ Location min = player.getLocation().add(-0.3, 0, -0.3);
+ Location max = player.getLocation().add(0.3, FlatteningWrapper.impl.isCrouching(player) ? 0.6 : (player.isSneaking() ? 1.5 : 1.8), 0.3);
+
+ Location blockmin = block.getLocation();
+ Location blockmax = block.getLocation().add(1.0, 1.0, 1.0);
+ if(
+ max.getX() <= blockmin.getX() || min.getX() >= blockmax.getX() ||
+ max.getY() <= blockmin.getY() || min.getY() >= blockmax.getY() ||
+ max.getZ() <= blockmin.getZ() || min.getZ() >= blockmax.getZ()
+ )
+ return;
+
+ event.setCancelled(true);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Border.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Border.java
new file mode 100644
index 00000000..6ab6605e
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Border.java
@@ -0,0 +1,148 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentTask;
+import de.steamwar.fightsystem.utils.FlatteningWrapper;
+import de.steamwar.fightsystem.utils.Region;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+
+import java.util.*;
+import java.util.logging.Level;
+
+public class Border {
+
+ private final Map> ghostBarriers = new HashMap<>();
+ private final Map lastLocation = new HashMap<>();
+ private final boolean contain;
+ private final String resetMessage;
+ private final String name;
+ private final Region region;
+ private final int ghostRange;
+ private final int ghostSize;
+
+ public Border(Region region, boolean contain, int ghostRange, String resetMessage, String name) {
+ this.contain = contain;
+ this.resetMessage = resetMessage;
+ this.name = name;
+ this.region = region;
+ this.ghostRange = ghostRange;
+ this.ghostSize = 2*ghostRange + 1;
+
+ new StateDependentTask(ArenaMode.All, FightState.All, this::run, 1, 1);
+ }
+
+ public void addPlayer(Player player) {
+ if(ghostBarriers.containsKey(player.getUniqueId()))
+ return;
+
+ ghostBarriers.put(player.getUniqueId(), new HashSet<>());
+ lastLocation.put(player.getUniqueId(), player.getLocation());
+ FightSystem.getPlugin().getLogger().log(Level.INFO, () -> player.getName() + " was added to border " + name);
+ }
+
+ public boolean contains(Player player) {
+ return ghostBarriers.containsKey(player.getUniqueId());
+ }
+
+ public void removePlayer(Player player) {
+ FightSystem.getPlugin().getLogger().log(Level.INFO, () -> player.getName() + " was removed from border " + name);
+ lastLocation.remove(player.getUniqueId());
+ Set blocks = ghostBarriers.remove(player.getUniqueId());
+ if(blocks == null || !player.isOnline())
+ return;
+
+ for(Block block : blocks)
+ sendChange(player, block, Material.AIR);
+ }
+
+ private void run() {
+ List offline = new ArrayList<>();
+ for(Map.Entry> entry : ghostBarriers.entrySet()) {
+ UUID uuid = entry.getKey();
+ Player player = Bukkit.getPlayer(uuid);
+ if(player == null) {
+ offline.add(uuid);
+ continue;
+ }
+
+ Location location = player.getLocation();
+ if(region.playerInRegion(location) != contain) {
+ player.teleport(lastLocation.get(uuid));
+ FightSystem.getMessage().sendPrefixless(resetMessage, player, ChatMessageType.ACTION_BAR);
+ return;
+ }
+
+ Set ghostBlocks = entry.getValue();
+ Region ghostRegion = Region.fromSize(location.getBlockX() - ghostRange, location.getBlockY() - ghostRange, location.getBlockZ() - ghostRange, ghostSize, ghostSize, ghostSize);
+ ghostBlocks.removeIf(block -> {
+ if(!ghostRegion.inRegion(block)) {
+ sendChange(player, block, Material.AIR);
+ return true;
+ }
+ return false;
+ });
+
+ if(contain) {
+ borderIteration(player, ghostBlocks, Region.fromSize(region.getMinX()-1, region.getMinY(), region.getMinZ(), 1, region.getSizeY(), region.getSizeZ()).intersection(ghostRegion));
+ borderIteration(player, ghostBlocks, Region.fromSize(region.getMinX(), region.getMinY()-1, region.getMinZ(), region.getSizeX(), 1, region.getSizeZ()).intersection(ghostRegion));
+ borderIteration(player, ghostBlocks, Region.fromSize(region.getMinX(), region.getMinY(), region.getMinZ()-1, region.getSizeX(), region.getSizeY(), 1).intersection(ghostRegion));
+ borderIteration(player, ghostBlocks, Region.fromSize(region.getMaxX(), region.getMinY(), region.getMinZ(), 1, region.getSizeY(), region.getSizeZ()).intersection(ghostRegion));
+ borderIteration(player, ghostBlocks, Region.fromSize(region.getMinX(), region.getMaxY(), region.getMinZ(), region.getSizeX(), 1, region.getSizeZ()).intersection(ghostRegion));
+ borderIteration(player, ghostBlocks, Region.fromSize(region.getMinX(), region.getMinY(), region.getMaxZ(), region.getSizeX(), region.getSizeY(), 1).intersection(ghostRegion));
+ } else {
+ borderIteration(player, ghostBlocks, Region.fromSize(region.getMinX(), region.getMinY(), region.getMinZ(), 1, region.getSizeY(), region.getSizeZ()).intersection(ghostRegion));
+ borderIteration(player, ghostBlocks, Region.fromSize(region.getMinX(), region.getMinY(), region.getMinZ(), region.getSizeX(), 1, region.getSizeZ()).intersection(ghostRegion));
+ borderIteration(player, ghostBlocks, Region.fromSize(region.getMinX(), region.getMinY(), region.getMinZ(), region.getSizeX(), region.getSizeY(), 1).intersection(ghostRegion));
+ borderIteration(player, ghostBlocks, Region.fromSize(region.getMaxX()-1, region.getMinY(), region.getMinZ(), 1, region.getSizeY(), region.getSizeZ()).intersection(ghostRegion));
+ borderIteration(player, ghostBlocks, Region.fromSize(region.getMinX(), region.getMaxY()-1, region.getMinZ(), region.getSizeX(), 1, region.getSizeZ()).intersection(ghostRegion));
+ borderIteration(player, ghostBlocks, Region.fromSize(region.getMinX(), region.getMinY(), region.getMaxZ()-1, region.getSizeX(), region.getSizeY(), 1).intersection(ghostRegion));
+ }
+
+ lastLocation.put(entry.getKey(), location);
+ }
+
+ offline.forEach(uuid -> FightSystem.getPlugin().getLogger().log(Level.INFO, () -> uuid + " was removed from border " + name));
+ offline.forEach(ghostBarriers::remove);
+ offline.forEach(lastLocation::remove);
+ }
+
+ private void borderIteration(Player player, Set ghostBlocks, Region border) {
+ border.forEach((x, y, z) -> {
+ Block block = Config.world.getBlockAt(x, y, z);
+ if(ghostBlocks.add(block))
+ sendChange(player, block, Material.BARRIER);
+ });
+ }
+
+ private void sendChange(Player player, Block block, Material type) {
+ if(block.getType() == Material.AIR)
+ FlatteningWrapper.impl.sendBlockChange(player, block, type);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Chat.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Chat.java
new file mode 100644
index 00000000..827f46c9
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Chat.java
@@ -0,0 +1,70 @@
+/*
+ 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.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.record.GlobalRecorder;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.AsyncPlayerChatEvent;
+
+import java.util.logging.Level;
+
+public class Chat implements Listener {
+
+ public Chat(){
+ new StateDependentListener(ArenaMode.All, FightState.All, this);
+ }
+
+ @EventHandler
+ public void handlePlayerChat(AsyncPlayerChatEvent event) {
+ Player player = event.getPlayer();
+ String message = event.getMessage();
+
+ FightSystem.getPlugin().getLogger().log(Level.INFO, player.getName() + "» " + message);
+ FightTeam fightTeam = Fight.getPlayerTeam(player);
+ if(fightTeam != null) {
+ String teamName = fightTeam.getColoredName();
+ if(message.startsWith(Config.TeamChatDetection)) {
+ fightTeam.broadcastChat(player, message.substring(1));
+ } else {
+ broadcastChat("PARTICIPANT_CHAT", teamName, player.getName(), message);
+ }
+ }else if(Config.isReferee(player)){
+ broadcastChat("FIGHTLEADER_CHAT", player.getName(), message);
+ }else{
+ broadcastChat("SPECTATOR_CHAT", player.getName(), message);
+ }
+
+ event.setCancelled(true);
+ }
+
+ public static void broadcastChat(String message, Object... params) {
+ GlobalRecorder.getInstance().chat(message, params);
+ FightSystem.getMessage().chat(message, params);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Check.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Check.java
new file mode 100644
index 00000000..f6efd8cc
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Check.java
@@ -0,0 +1,71 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.sql.SchematicNode;
+import de.steamwar.sql.SteamwarUser;
+import de.steamwar.sql.UserPerm;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+
+import java.util.logging.Level;
+
+public class Check implements Listener {
+
+ public Check() {
+ new StateDependentListener(ArenaMode.Check, FightState.All, this);
+ }
+
+ @EventHandler
+ public void onJoin(PlayerJoinEvent e){
+ Player player = e.getPlayer();
+ SteamwarUser user = SteamwarUser.get(player.getUniqueId());
+
+ if(user.hasPerm(UserPerm.CHECK))
+ return;
+
+ SchematicNode schem = SchematicNode.getSchematicNode(Config.CheckSchemID);
+ if(user.getId() == schem.getOwner())
+ return;
+
+ FightSystem.getMessage().send("CHECK_JOIN_DENIED", player);
+ player.kickPlayer("");
+ }
+
+ @EventHandler
+ public void handlePlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
+ Player player = event.getPlayer();
+ if(!event.getMessage().contains("copy") && !event.getMessage().contains("cut"))
+ return;
+
+ event.setCancelled(true);
+ FightSystem.getMessage().send("CHECK_COMMAND_LOCKED", player);
+ Bukkit.getLogger().log(Level.SEVERE, player.getName() + " tried to use a copy command!");
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ClickAnalyzer.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ClickAnalyzer.java
new file mode 100644
index 00000000..73e2afa9
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ClickAnalyzer.java
@@ -0,0 +1,59 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2022 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 com.comphenix.tinyprotocol.Reflection;
+import com.comphenix.tinyprotocol.TinyProtocol;
+import de.steamwar.core.Core;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.utils.CraftbukkitWrapper;
+import org.bukkit.entity.Player;
+
+import java.io.*;
+
+public class ClickAnalyzer {
+
+ private static final PrintStream output;
+
+ static {
+ try {
+ output = new PrintStream(new BufferedOutputStream(new FileOutputStream(new File(Config.world.getWorldFolder(), "clicks.csv"))));
+ } catch (FileNotFoundException e) {
+ throw new SecurityException(e);
+ }
+ }
+
+ public ClickAnalyzer() {
+ TinyProtocol.instance.addFilter(Recording.blockPlacePacket, this::onBlockPlace);
+ if(Core.getVersion() > 8)
+ TinyProtocol.instance.addFilter(Reflection.getClass("{nms.network.protocol.game}.PacketPlayInUseItem"), this::onBlockPlace);
+ }
+
+ public Object onBlockPlace(Player player, Object packet) {
+ synchronized(output) {
+ output.println(player.getName() + "," + System.nanoTime() + "," + CraftbukkitWrapper.impl.headRotation(player) + "," + player.getLocation().getPitch());
+ }
+ return packet;
+ }
+
+ public static void close() {
+ output.close();
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/DenyInventoryMovement.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/DenyInventoryMovement.java
new file mode 100644
index 00000000..6c7f71f8
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/DenyInventoryMovement.java
@@ -0,0 +1,57 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.ArenaMode;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.utils.BountifulWrapper;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryDragEvent;
+import org.bukkit.event.player.PlayerPickupItemEvent;
+
+public class DenyInventoryMovement implements Listener {
+
+ public DenyInventoryMovement() {
+ new StateDependentListener(ArenaMode.AntiTest, FightState.AntiIngame, this);
+
+ Listener listener = BountifulWrapper.impl.newDenyHandSwapListener();
+ new StateDependentListener(ArenaMode.AntiTest, FightState.AntiIngame, listener);
+ }
+
+ @EventHandler
+ public void onInventoryClick(InventoryClickEvent event) {
+ if(!PersonalKitCreator.inKitCreator(event.getWhoClicked()))
+ event.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onInventoryDrag(InventoryDragEvent event) {
+ if(!PersonalKitCreator.inKitCreator(event.getWhoClicked()))
+ event.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onItemPickup(PlayerPickupItemEvent event) {
+ event.setCancelled(true);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/DenyWorldInteraction.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/DenyWorldInteraction.java
new file mode 100644
index 00000000..f494d0bc
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/DenyWorldInteraction.java
@@ -0,0 +1,109 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.block.BlockIgniteEvent;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.entity.EntityExplodeEvent;
+import org.bukkit.event.entity.ProjectileLaunchEvent;
+import org.bukkit.event.hanging.HangingBreakEvent;
+import org.bukkit.event.player.PlayerDropItemEvent;
+import org.bukkit.event.player.PlayerKickEvent;
+
+public class DenyWorldInteraction implements Listener {
+
+ public DenyWorldInteraction() {
+ new StateDependentListener(ArenaMode.Test, FightState.PreRunning, this);
+ new StateDependentListener(ArenaMode.AntiTest, FightState.AntiRunning, this);
+ }
+
+ @EventHandler
+ public void handleBlockBreak(BlockBreakEvent event) {
+ Player player = event.getPlayer();
+ if(Fight.fighting(player)) {
+ event.setCancelled(true);
+ FightSystem.getMessage().sendPrefixless("NO_BLOCK_BREAK", player, ChatMessageType.ACTION_BAR);
+ }
+ }
+
+ @EventHandler
+ public void handleItemDrop(PlayerDropItemEvent event) {
+ Player player = event.getPlayer();
+ if(Fight.fighting(player)) {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler
+ public void handleHangingBreak(HangingBreakEvent event) {
+ if(Config.ArenaRegion.inRegion(event.getEntity().getLocation())) {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler
+ public void handleBlockPlace(BlockPlaceEvent event) {
+ Player player = event.getPlayer();
+ if(Fight.fighting(player)) {
+ event.setCancelled(true);
+ FightSystem.getMessage().sendPrefixless("NO_BLOCK_PLACE", player, ChatMessageType.ACTION_BAR);
+ }
+ }
+
+ @EventHandler
+ public void handleEntityExplode(EntityExplodeEvent event) {
+ if(!Config.ArenaLeaveable || Config.ArenaRegion.inRegion(event.getLocation()))
+ event.setCancelled(true);
+ }
+
+ @EventHandler
+ public void handleBlockBurn(BlockIgniteEvent event) {
+ if(!Config.ArenaLeaveable || Config.ArenaRegion.inRegion(event.getBlock()))
+ event.setCancelled(true);
+ }
+
+ @EventHandler
+ public void handlePlayerKickEvent(PlayerKickEvent e){
+ if(e.getReason().contains("Flying is not enabled"))
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void handleProjectileLaunch(ProjectileLaunchEvent event) {
+ if(event.getEntity().getShooter() instanceof Player) {
+ Player player = (Player) event.getEntity().getShooter();
+ if(Fight.fighting(player)) {
+ event.setCancelled(true);
+ FightSystem.getMessage().sendPrefixless("NO_BOW_USAGE", player, ChatMessageType.ACTION_BAR);
+ }
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/EntityDamage.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/EntityDamage.java
new file mode 100644
index 00000000..18959b68
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/EntityDamage.java
@@ -0,0 +1,48 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+import org.bukkit.event.entity.EntityDamageEvent;
+
+public class EntityDamage implements Listener {
+
+ public EntityDamage() {
+ new StateDependentListener(ArenaMode.All, FightState.AntiRunning, this);
+ }
+
+ @EventHandler
+ public void handleEntityDamage(EntityDamageEvent event) {
+ if(Config.ArenaRegion.in2dRegion(event.getEntity().getLocation()))
+ event.setCancelled(true);
+ }
+
+ @EventHandler
+ public void handleEntityDamageByEntity(EntityDamageByEntityEvent event) {
+ if(Config.ArenaRegion.in2dRegion(event.getEntity().getLocation()))
+ event.setCancelled(true);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/EventJoin.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/EventJoin.java
new file mode 100644
index 00000000..6630becb
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/EventJoin.java
@@ -0,0 +1,93 @@
+/*
+ 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.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.StateDependentListener;
+import de.steamwar.sql.SteamwarUser;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerLoginEvent;
+
+public class EventJoin implements Listener {
+
+ public EventJoin() {
+ new StateDependentListener(ArenaMode.Event, FightState.All, this);
+ }
+
+ @EventHandler
+ public void playerLogin(PlayerLoginEvent event) {
+ if(!Config.LiveReplay)
+ return;
+
+ Player player = event.getPlayer();
+ SteamwarUser user = SteamwarUser.get(player.getUniqueId());
+ if(user.getTeam() == Config.EventTeamBlueID ||
+ user.getTeam() == Config.EventTeamRedID ||
+ Config.isReferee(player))
+ return;
+
+ event.disallow(PlayerLoginEvent.Result.KICK_OTHER, FightSystem.getMessage().parse("NO_PARTICIPANT", player));
+ }
+
+ @EventHandler
+ public void handlePlayerJoin(PlayerJoinEvent event) {
+ Player player = event.getPlayer();
+ SteamwarUser user = SteamwarUser.get(player.getUniqueId());
+
+ if(Config.isReferee(player))
+ return;
+
+ if(FightState.Setup.contains(FightState.getFightState())){
+ FightTeam team = null;
+ if(user.getTeam() == Config.EventTeamBlueID)
+ team = Fight.getBlueTeam();
+ else if(user.getTeam() == Config.EventTeamRedID)
+ team = Fight.getRedTeam();
+
+ if(Config.BothTeamsPublic){
+ if(Fight.getRedTeam().getPlayers().size() < Fight.getBlueTeam().getPlayers().size())
+ team = Fight.getRedTeam();
+ else
+ team = Fight.getBlueTeam();
+ }else if(team == null){
+ if(Config.EventTeamRedID == 0)
+ team = Fight.getRedTeam();
+ else if(Config.EventTeamBlueID == 0)
+ team = Fight.getBlueTeam();
+ }
+
+ if(team != null && team.getPlayers().size() < Config.MaximumTeamMembers){
+ team.addMember(player);
+ return;
+ }
+ }
+
+ if(Config.LiveReplay)
+ player.kickPlayer(FightSystem.getMessage().parse("NO_PARTICIPANT", player));
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/FightScoreboard.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/FightScoreboard.java
new file mode 100644
index 00000000..63ca402f
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/FightScoreboard.java
@@ -0,0 +1,94 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.scoreboard.SWScoreboard;
+import de.steamwar.scoreboard.ScoreboardCallback;
+import org.bukkit.Bukkit;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.scoreboard.Scoreboard;
+import org.bukkit.scoreboard.Team;
+
+import java.util.HashMap;
+import java.util.Objects;
+
+public class FightScoreboard implements Listener, ScoreboardCallback {
+
+ public static Team getBukkitTeam(String name) {
+ Scoreboard scoreboard = Objects.requireNonNull(Bukkit.getScoreboardManager()).getMainScoreboard();
+ Team team = scoreboard.getTeam(name);
+ if(team != null)
+ return team;
+
+ return scoreboard.registerNewTeam(name);
+ }
+
+ private static FightScoreboard scoreboard;
+
+ public static FightScoreboard getScoreboard(){
+ if(scoreboard == null)
+ scoreboard = new FightScoreboard();
+ return scoreboard;
+ }
+
+ private String title = "";
+ private final HashMap scores = new HashMap<>();
+
+
+ private FightScoreboard(){
+ new StateDependentListener(ArenaMode.Replay, FightState.All, this);
+ Bukkit.getOnlinePlayers().forEach(player -> SWScoreboard.createScoreboard(player, this));
+ }
+
+ @EventHandler
+ public void onPlayerJoin(PlayerJoinEvent event) {
+ SWScoreboard.createScoreboard(event.getPlayer(), this);
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ SWScoreboard.removeScoreboard(event.getPlayer());
+ }
+
+ public void setTitle(String t) {
+ scores.clear();
+ title = t;
+ }
+
+ public void addScore(String string, int i) {
+ scores.put(string, i);
+ }
+
+ @Override
+ public String getTitle() {
+ return title;
+ }
+
+ @Override
+ public HashMap getData() {
+ return scores;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/InFightDamage.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/InFightDamage.java
new file mode 100644
index 00000000..7194fdd6
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/InFightDamage.java
@@ -0,0 +1,88 @@
+/*
+ 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.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.winconditions.Winconditions;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.entity.Arrow;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+
+import java.util.Objects;
+
+public class InFightDamage implements Listener {
+
+ public InFightDamage() {
+ new StateDependentListener(!Config.ActiveWinconditions.contains(Winconditions.AMONG_US), FightState.Running, this);
+ }
+
+ @EventHandler
+ public void handleEntityDamageByEntity(EntityDamageByEntityEvent event) {
+ if(!(event.getEntity() instanceof LivingEntity))
+ //Target is not a player
+ return;
+
+ LivingEntity player = ((LivingEntity) event.getEntity());
+ if(!Fight.fighting(player))
+ return;
+
+ LivingEntity damager;
+
+ if(event.getDamager() instanceof LivingEntity) {
+ damager = (LivingEntity) event.getDamager();
+ }else if(event.getDamager() instanceof Arrow) {
+ Arrow damagerArrow = (Arrow) event.getDamager();
+ if(!(damagerArrow.getShooter() instanceof LivingEntity))
+ //Shooter is not a player
+ return;
+
+ damager = (LivingEntity) damagerArrow.getShooter();
+ }else{
+ //Damager is not a player
+ return;
+ }
+
+ if(!Fight.fighting(damager)){
+ event.setCancelled(true);
+ //Damager is not fighting
+ return;
+ }
+
+ if(Fight.getPlayerTeam(player) == Fight.getPlayerTeam(damager)) {
+ event.setCancelled(true);
+ if(event.getDamager() instanceof Arrow){
+ event.getDamager().setFireTicks(0);
+ player.setFireTicks(0);
+ }
+ FightSystem.getMessage().sendPrefixless("NO_FRIENDLY_FIRE", damager, ChatMessageType.ACTION_BAR);
+ }
+
+ if(player.getHealth() - event.getFinalDamage() <= 0){
+ Objects.requireNonNull(Fight.getFightPlayer(damager)).addKill();
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/InFightInventory.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/InFightInventory.java
new file mode 100644
index 00000000..ad41d0a0
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/InFightInventory.java
@@ -0,0 +1,75 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryDragEvent;
+import org.bukkit.event.inventory.InventoryType;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+public class InFightInventory implements Listener {
+
+ private static final Set allowed = Collections.unmodifiableSet(EnumSet.of(Material.TNT, Material.AIR));
+
+ public InFightInventory() {
+ new StateDependentListener(ArenaMode.AntiReplay, FightState.Ingame, this);
+ }
+
+ @EventHandler
+ public void onInventoryClick(InventoryClickEvent event) {
+ if(!Fight.fighting((Player) event.getWhoClicked()))
+ return;
+
+ InventoryType top = event.getView().getTopInventory().getType();
+ if(top == InventoryType.CRAFTING)
+ return;
+
+ if ((event.getCursor() != null && !allowed.contains(event.getCursor().getType())) || (event.getCurrentItem() != null && !allowed.contains(event.getCurrentItem().getType())))
+ event.setCancelled(true); // Deny if transferred item is not TNT
+ }
+
+ @EventHandler
+ public void onInventoryDrag(InventoryDragEvent event) {
+ if(!Fight.fighting((Player) event.getWhoClicked()))
+ return;
+
+ if (event.getInventory().getType() != InventoryType.PLAYER) {
+ int inventorySize = event.getInventory().getSize();
+
+ for (int i : event.getRawSlots()) {
+ if (i < inventorySize) {
+ event.setCancelled(true);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/IngameDeath.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/IngameDeath.java
new file mode 100644
index 00000000..6ff538f4
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/IngameDeath.java
@@ -0,0 +1,78 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightPlayer;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.utils.FightUI;
+import de.steamwar.fightsystem.utils.SWSound;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDeathEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import java.util.function.Consumer;
+
+public class IngameDeath implements Listener {
+
+ public IngameDeath() {
+ new StateDependentListener(ArenaMode.AntiReplay, FightState.Ingame, this);
+ }
+
+ @EventHandler
+ public void broadcastDeath(EntityDeathEvent event) {
+ onPlayerEnd(event.getEntity(), fightPlayer -> {
+ FightUI.addSubtitle("UI_PLAYER_DEATH", fightPlayer.getTeam().getPrefix(), fightPlayer.getEntity().getName());
+ Fight.playSound(SWSound.ENTITY_WITHER_DEATH.getSound(), 100.0F, 1.0F);
+ });
+ }
+
+ @EventHandler
+ public void broadcastQuit(PlayerQuitEvent event) {
+ onPlayerEnd(event.getPlayer(), fightPlayer -> FightUI.addSubtitle("UI_PLAYER_LEAVE", fightPlayer.getTeam().getPrefix(), fightPlayer.getEntity().getName()));
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void handlePlayerDeath(EntityDeathEvent event) {
+ onPlayerEnd(event.getEntity(), FightPlayer::setOut);
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void handlePlayerQuit(PlayerQuitEvent event) {
+ onPlayerEnd(event.getPlayer(), FightPlayer::setOut);
+ }
+
+ private void onPlayerEnd(Entity player, Consumer withLivingPlayer) {
+ if(!(player instanceof LivingEntity))
+ return;
+
+ FightPlayer fightPlayer = Fight.getFightPlayer((LivingEntity) player);
+ if(fightPlayer == null || !fightPlayer.isLiving())
+ return;
+
+ withLivingPlayer.accept(fightPlayer);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/JoinRequestListener.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/JoinRequestListener.java
new file mode 100644
index 00000000..1ed3fe8b
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/JoinRequestListener.java
@@ -0,0 +1,74 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2022 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.ArenaMode;
+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.HotbarKit;
+import de.steamwar.fightsystem.fight.JoinRequest;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.OneShotStateDependent;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.event.player.PlayerRespawnEvent;
+
+public class JoinRequestListener implements Listener {
+
+ public JoinRequestListener() {
+ new OneShotStateDependent(ArenaMode.VariableTeams, FightState.PreLeaderSetup, () -> Bukkit.getScheduler().runTask(FightSystem.getPlugin(), JoinRequest::clearRequests));
+ new OneShotStateDependent(ArenaMode.VariableTeams, FightState.PreRunning, () -> Bukkit.getScheduler().runTask(FightSystem.getPlugin(), JoinRequest::clearRequests));
+
+ new StateDependentListener(ArenaMode.VariableTeams, FightState.All, this);
+ }
+
+ @EventHandler(priority = EventPriority.HIGH)
+ public void onJoin(PlayerJoinEvent event) {
+ Player player = event.getPlayer();
+ FightPlayer fp = Fight.getFightPlayer(player);
+
+ if (!Config.ArenaLeaveable && (fp == null || !fp.isLiving())) {
+ HotbarKit.SPECTATOR_KIT.loadToPlayer(player);
+ }
+ }
+
+ @EventHandler
+ public void handlePlayerRespawn(PlayerRespawnEvent event) {
+ Player player = event.getPlayer();
+ if(Fight.fighting(player)) {
+ HotbarKit.SPECTATOR_KIT.loadToPlayer(player);
+ }
+ }
+
+ @EventHandler
+ public void onLeave(PlayerQuitEvent event) {
+ JoinRequest request = JoinRequest.get(event.getPlayer());
+ if(request != null)
+ request.silentDecline();
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/LeaveableArena.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/LeaveableArena.java
new file mode 100644
index 00000000..9d0bb4e2
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/LeaveableArena.java
@@ -0,0 +1,89 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2022 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.fight.Fight;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.GameMode;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.event.player.PlayerRespawnEvent;
+import org.spigotmc.event.player.PlayerSpawnLocationEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class LeaveableArena implements Listener {
+
+ private final Map spectatorsInArena = new HashMap<>();
+
+ public LeaveableArena() {
+ new StateDependentListener(Config.ArenaLeaveable, FightState.All, this);
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onJoin(PlayerSpawnLocationEvent event) {
+ Player player = event.getPlayer();
+ if(Fight.fighting(player))
+ return;
+
+ if(Config.ArenaRegion.in2dRegion(event.getSpawnLocation())) {
+ markInArena(player);
+ }
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onRespawn(PlayerRespawnEvent event) {
+ Player player = event.getPlayer();
+ if(Config.ArenaRegion.in2dRegion(event.getRespawnLocation())) {
+ markInArena(player);
+ } else {
+ spectatorsInArena.remove(player);
+ }
+ }
+
+ @EventHandler
+ public void arenaBorder(PlayerMoveEvent event){
+ Player player = event.getPlayer();
+ if(Fight.fighting(player))
+ return;
+
+ boolean inArena = Config.ArenaRegion.in2dRegion(event.getTo());
+ boolean spectator = spectatorsInArena.containsKey(player);
+
+ if(inArena && !spectator) {
+ markInArena(player);
+ } else if(!inArena && spectator) {
+ GameMode mode = spectatorsInArena.remove(player);
+ if(mode != null)
+ player.setGameMode(mode);
+ }
+ }
+
+ private void markInArena(Player player) {
+ spectatorsInArena.put(player, player.getGameMode());
+ Fight.setPlayerGamemode(player, GameMode.SPECTATOR);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/NormalJoin.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/NormalJoin.java
new file mode 100644
index 00000000..371ae75c
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/NormalJoin.java
@@ -0,0 +1,49 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+
+public class NormalJoin implements Listener {
+
+ public NormalJoin() {
+ new StateDependentListener(ArenaMode.Normal, FightState.PreLeaderSetup, this);
+ }
+
+ @EventHandler
+ public void handlePlayerJoin(PlayerJoinEvent event) {
+ Player player = event.getPlayer();
+ if (Fight.getPlayerTeam(player) != null)
+ return;
+
+ FightTeam team = Fight.teams().stream().filter(t -> player.getUniqueId().equals(t.getDesignatedLeader())).findAny( // Player is designated leader of a team
+ ).orElse(Fight.teams().stream().filter(t -> t.canbeLeader(player)).findAny().orElse(null)); // Else search empty team
+ if(team != null)
+ team.addMember(player);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Permanent.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Permanent.java
new file mode 100644
index 00000000..90342de2
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Permanent.java
@@ -0,0 +1,243 @@
+/*
+ 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.ArenaMode;
+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.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.utils.BountifulWrapper;
+import de.steamwar.fightsystem.utils.FlatteningWrapper;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.GameMode;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.block.BlockDispenseEvent;
+import org.bukkit.event.block.BlockFromToEvent;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.entity.EntityExplodeEvent;
+import org.bukkit.event.entity.FoodLevelChangeEvent;
+import org.bukkit.event.entity.PlayerDeathEvent;
+import org.bukkit.event.entity.SpawnerSpawnEvent;
+import org.bukkit.event.inventory.CraftItemEvent;
+import org.bukkit.event.inventory.FurnaceSmeltEvent;
+import org.bukkit.event.player.*;
+import org.bukkit.event.weather.WeatherChangeEvent;
+import org.bukkit.event.world.WorldLoadEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.scoreboard.NameTagVisibility;
+import org.bukkit.scoreboard.Team;
+
+public class Permanent implements Listener {
+
+ private static final Team spectatorTeam = FightScoreboard.getBukkitTeam("Spectator");
+ static {
+ BountifulWrapper.impl.setNametagVisibility(spectatorTeam);
+ spectatorTeam.setNameTagVisibility(NameTagVisibility.NEVER);
+ }
+
+ public static Team getSpectatorTeam() {
+ return spectatorTeam;
+ }
+
+ public Permanent() {
+ new StateDependentListener(ArenaMode.All, FightState.All, this);
+ }
+
+ @EventHandler
+ public void handlePlayerRespawn(PlayerRespawnEvent event){
+ Player player = event.getPlayer();
+ if(Fight.fighting(player)) {
+ Fight.setPlayerGamemode(player, GameMode.SPECTATOR);
+
+ FightTeam team = Fight.getPlayerTeam(player);
+ event.setRespawnLocation(team == null ? Config.SpecSpawn : team.getSpawn());
+ }
+ }
+
+ @EventHandler
+ public void onTpGM3(PlayerTeleportEvent e) {
+ if (e.getCause() == PlayerTeleportEvent.TeleportCause.SPECTATE) {
+ e.setCancelled(true);
+ FightSystem.getMessage().sendPrefixless("NO_TELEPORT", e.getPlayer(), ChatMessageType.ACTION_BAR);
+ e.getPlayer().kickPlayer(null);
+ }
+ }
+
+ @EventHandler(priority = EventPriority.HIGH)
+ public void handlePlayerJoin(PlayerJoinEvent event) {
+ event.setJoinMessage(null);
+
+ Player player = event.getPlayer();
+ FightPlayer fp = Fight.getFightPlayer(player);
+
+ if (!Config.ArenaLeaveable && fp == null) {
+ Fight.setPlayerGamemode(player, GameMode.SPECTATOR);
+ spectatorTeam.addEntry(player.getName());
+ player.teleport(Config.SpecSpawn);
+ } else if(fp != null && !fp.isLiving()) {
+ Fight.setPlayerGamemode(player, GameMode.SPECTATOR);
+ player.teleport(fp.getTeam().getSpawn());
+ }
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void handlePlayerDeath(PlayerDeathEvent event) {
+ if(!Config.ArenaLeaveable || Fight.fighting(event.getEntity()))
+ event.setDeathMessage(null);
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void handlePlayerLeave(PlayerQuitEvent event) {
+ if(!Config.ArenaLeaveable)
+ event.setQuitMessage(null);
+ }
+
+ @EventHandler
+ public void handleFoodLevelChange(FoodLevelChangeEvent event) {
+ if(Fight.fighting((Player) event.getEntity()))
+ event.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onWeatherChange(WeatherChangeEvent event) {
+ event.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onSpawnerSpawn(SpawnerSpawnEvent e){
+ if(Config.ArenaRegion.inRegion(e.getSpawner().getBlock()))
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onSleep(PlayerBedEnterEvent e) {
+ if(Fight.fighting(e.getPlayer()))
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onCrafting(CraftItemEvent e) {
+ if(Fight.fighting((Player) e.getWhoClicked()))
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onFurnace(FurnaceSmeltEvent e){
+ if(Config.ArenaRegion.inRegion(e.getBlock()))
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onDropPickup(PlayerPickupItemEvent e) {
+ if(!(Config.ArenaRegion.inRegion(e.getItem().getLocation())))
+ return;
+ Player player = e.getPlayer();
+
+ ItemStack stack = e.getItem().getItemStack();
+ if(Config.PersonalKits) {
+ if(Config.ForbiddenItems.contains(stack.getType())) {
+ e.setCancelled(true);
+ }
+ } else {
+ FightPlayer fp = Fight.getFightPlayer(player);
+ if(fp == null)
+ return;
+
+ if(!fp.getKit().contains(stack)) {
+ e.setCancelled(true);
+ }
+ }
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onExplosion(EntityExplodeEvent e) {
+ e.blockList().removeIf(block -> {
+ if(block.getType() == Material.TNT) {
+ return false;
+ } else {
+ block.setType(Material.AIR);
+ return true;
+ }
+ });
+ }
+
+ @EventHandler
+ public void onWorldLoad(WorldLoadEvent e) {
+ if(!Config.ArenaLeaveable)
+ e.getWorld().setAutoSave(false);
+ }
+
+ @EventHandler
+ public void onBlockFromTo(BlockFromToEvent event) {
+ if(!event.getBlock().getType().equals(Material.DRAGON_EGG)) return;
+
+ for (FightTeam team : Fight.teams()) {
+ if(team.getExtendRegion().inRegion(event.getBlock())){
+
+ if(team.getExtendRegion().inRegion(event.getToBlock())) return;
+
+ }
+ }
+
+ event.setCancelled(true);
+ }
+
+ @EventHandler
+ public void onBlockDispense(BlockDispenseEvent e) {
+ Block block = e.getBlock();
+
+ if(!Config.ArenaRegion.inRegion(block))
+ return;
+
+ if(e.getItem().getType() == Material.TNT || FlatteningWrapper.impl.isFacingWater(block))
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void blockPlace(BlockPlaceEvent event) {
+ Player player = event.getPlayer();
+ Block block = event.getBlock();
+ if(Fight.teams().stream().anyMatch(team -> team.getExtendRegion().inRegion(block)))
+ return;
+
+ event.setCancelled(true);
+ FightSystem.getMessage().sendPrefixless("NO_BLOCK_PLACE", player, ChatMessageType.ACTION_BAR);
+ }
+
+ @EventHandler
+ public void blockBreak(BlockBreakEvent event) {
+ Block block = event.getBlock();
+ if(Config.BlueExtendRegion.getMinY() <= block.getY())
+ return;
+
+ event.setCancelled(true);
+ FightSystem.getMessage().sendPrefixless("NO_BLOCK_BREAK", event.getPlayer(), ChatMessageType.ACTION_BAR);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PersonalKitCreator.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PersonalKitCreator.java
new file mode 100644
index 00000000..f9a3b2d8
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PersonalKitCreator.java
@@ -0,0 +1,163 @@
+/*
+ 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.Kit;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.sql.PersonalKit;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.Bukkit;
+import org.bukkit.GameMode;
+import org.bukkit.entity.HumanEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.InventoryAction;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.bukkit.event.inventory.InventoryOpenEvent;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+public class PersonalKitCreator implements Listener {
+
+ private static final Map openKitCreators = new HashMap<>();
+
+ public PersonalKitCreator(){
+ new StateDependentListener(Config.PersonalKits, FightState.Setup, this);
+ }
+
+ public static void openKitCreator(Player player, PersonalKit kit){
+ player.closeInventory();
+ new InventoryBackup(player, kit);
+ new Kit(kit).loadToPlayer(player);
+ player.setGameMode(GameMode.CREATIVE);
+ FightSystem.getMessage().sendPrefixless("OPEN_INVENTORY_TO_CUSTOMIZE", player, ChatMessageType.ACTION_BAR);
+ }
+
+ public static boolean inKitCreator(HumanEntity player){
+ return openKitCreators.containsKey(player);
+ }
+
+ public static void closeIfInKitCreator(HumanEntity player){
+ InventoryBackup backup = openKitCreators.get(player);
+ if(backup == null)
+ return;
+
+ backup.close();
+ }
+
+ @EventHandler
+ public void onInventoryClick(InventoryClickEvent e){
+ if(!openKitCreators.containsKey(e.getWhoClicked()))
+ return;
+
+ Player player = (Player) e.getWhoClicked();
+ //Deny bad items
+ if(Kit.isBadItem(e.getCursor()))
+ e.setCancelled(true);
+
+ /* Should the inventory reset? */
+ if(e.getAction() != InventoryAction.PLACE_ALL)
+ return;
+
+ ItemStack[] items = e.getWhoClicked().getInventory().getContents();
+ for(int i = 0; i < items.length; i++){
+ ItemStack stack = items[i];
+ if(stack != null && i != e.getSlot())
+ return;
+ }
+
+ FightPlayer fightPlayer = Fight.getFightPlayer(player);
+ assert fightPlayer != null;
+ Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> fightPlayer.getKit().loadToPlayer(player), 1);
+ }
+
+ @EventHandler
+ public void onMoveEvent(PlayerMoveEvent e){
+ InventoryBackup backup = openKitCreators.get(e.getPlayer());
+ if(backup == null)
+ return;
+
+ backup.close();
+ }
+
+ @EventHandler
+ public void onOpenEvent(InventoryOpenEvent e){
+ InventoryBackup backup = openKitCreators.get(e.getPlayer());
+ if(backup == null)
+ return;
+
+ backup.close();
+ }
+
+ @EventHandler
+ public void onInventoryClose(InventoryCloseEvent e) {
+ InventoryBackup backup = openKitCreators.get(e.getPlayer());
+ if(backup == null)
+ return;
+
+ backup.close();
+ }
+
+ @EventHandler
+ public void onPlayerExit(PlayerQuitEvent e){
+ InventoryBackup backup = openKitCreators.get(e.getPlayer());
+ if(backup == null)
+ return;
+
+ backup.close();
+ }
+
+ private static class InventoryBackup{
+ private final Player player;
+ private final PersonalKit kit;
+ private final Kit backup;
+
+ private InventoryBackup(Player player, PersonalKit kit){
+ openKitCreators.put(player, this);
+ this.player = player;
+ this.backup = Kit.getActiveKit(player);
+ this.kit = kit;
+ }
+
+ private void close(){
+ openKitCreators.remove(player);
+ Kit kit1 = new Kit(kit.getName(), player);
+ kit1.removeBadItems();
+ kit1.toPersonalKit(kit);
+ backup.loadToPlayer(player);
+ player.setGameMode(GameMode.SURVIVAL);
+ kit.setInUse();
+ player.closeInventory();
+ Objects.requireNonNull(Fight.getFightPlayer(player)).setKit(new Kit(kit));
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PistonListener.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PistonListener.java
new file mode 100644
index 00000000..26c2fb89
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PistonListener.java
@@ -0,0 +1,79 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.PistonMoveReaction;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockPistonEvent;
+import org.bukkit.event.block.BlockPistonExtendEvent;
+import org.bukkit.event.block.BlockPistonRetractEvent;
+
+import java.util.Set;
+import java.util.function.Consumer;
+
+public class PistonListener implements Listener {
+
+ private final Consumer leftAreaHandler;
+
+ public PistonListener(Set condition, Consumer leftAreaHandler) {
+ this.leftAreaHandler = leftAreaHandler;
+ if(!condition.contains(Config.mode))
+ return;
+
+ //Wenn Entern aktiv ist, sollen Raketen etc. entern können
+ new StateDependentListener(!Config.AllowMissiles, FightState.All, this);
+ new StateDependentListener(Config.AllowMissiles, FightState.Setup, this);
+ }
+
+ @EventHandler
+ public void handlePistonExtend(BlockPistonExtendEvent e){
+ if(!Config.ArenaRegion.inRegion(e.getBlock()))
+ return;
+
+ BlockFace face = e.getDirection();
+ for(Block block : e.getBlocks()){
+ Block target = block.getRelative(face);
+ if(!Config.BlueExtendRegion.inRegion(target) && !Config.RedExtendRegion.inRegion(target) && block.getPistonMoveReaction() != PistonMoveReaction.BREAK) {
+ leftAreaHandler.accept(e);
+ return;
+ }
+ }
+ }
+
+ @EventHandler
+ public void handlePistonRetract(BlockPistonRetractEvent e){
+ if(!Config.ArenaRegion.inRegion(e.getBlock()))
+ return;
+
+ for(Block block : e.getBlocks()){
+ if(!Config.BlueExtendRegion.inRegion(block) && !Config.RedExtendRegion.inRegion(block)) {
+ leftAreaHandler.accept(e);
+ return;
+ }
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PrepareSchem.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PrepareSchem.java
new file mode 100644
index 00000000..288eda76
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PrepareSchem.java
@@ -0,0 +1,135 @@
+/*
+ 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 com.sk89q.worldedit.WorldEditException;
+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.fightsystem.utils.FlatteningWrapper;
+import de.steamwar.fightsystem.utils.Region;
+import de.steamwar.fightsystem.utils.WorldeditWrapper;
+import de.steamwar.sql.SchematicNode;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.util.Vector;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class PrepareSchem implements Listener {
+
+ 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)))
+ stationaryMovingPistons.add(new Vector(x, y, z));
+ });
+ }, 1));
+
+ new StateDependentListener(ArenaMode.Prepare, FightState.Setup, this){
+ @Override
+ public void disable() {
+ super.disable();
+ Region region = Fight.getUnrotated().getExtendRegion();
+ int minY = Fight.getUnrotated().getSchemRegion().getMinY();
+
+ SchematicNode schem;
+ try{
+ schem = SchematicNode.getSchematicNode(Config.PrepareSchemID);
+ }catch(SecurityException e){
+ FightSystem.getMessage().broadcast("PREPARE_SCHEM_DELETED");
+ Bukkit.shutdown();
+ return;
+ }
+
+ try{
+ region.forEach((x, y, z) -> {
+ if(FlatteningWrapper.impl.checkPistonMoving(Config.world.getBlockAt(x, y, z)) && !stationaryMovingPistons.contains(new Vector(x, y, z))){
+ FightSystem.getMessage().broadcast("PREPARE_ACTIVE_PISTON");
+ Bukkit.shutdown();
+ throw new IllegalStateException();
+ }
+ });
+ }catch (IllegalStateException e){
+ return;
+ }
+
+ if(schemExists(schem))
+ return;
+
+ schem = SchematicNode.createSchematicNode(schem.getOwner(), preparedName(schem), schem.getParent(), Config.SchematicType.checkType().toDB(), schem.getItem());
+
+ try{
+ WorldeditWrapper.impl.saveSchem(schem, region, minY);
+ }catch(WorldEditException e){
+ FightSystem.getMessage().broadcast("PREPARE_FAILED_SAVING");
+ Bukkit.shutdown();
+ schem.delete();
+ throw new SecurityException("Could not save schem", e);
+ }
+
+ FightSystem.getMessage().broadcast("PREPARE_SENT_IN");
+ Bukkit.shutdown();
+ }
+ };
+ }
+
+ @EventHandler
+ public void handlePlayerJoin(PlayerJoinEvent event) {
+ Player player = event.getPlayer();
+ FightTeam team = Fight.getPlayerTeam(player);
+
+ if (team == null) {
+ Fight.getUnrotated().addMember(player);
+ }
+
+ if(FightState.getFightState() == FightState.PRE_LEADER_SETUP) {
+ FightState.setFightState(FightState.PRE_SCHEM_SETUP);
+ FightState.setFightState(FightState.POST_SCHEM_SETUP);
+ }
+
+ schemExists(SchematicNode.getSchematicNode(Config.PrepareSchemID));
+ }
+
+ private boolean schemExists(SchematicNode schem) {
+ if(SchematicNode.getSchematicNode(schem.getOwner(), preparedName(schem), schem.getParent()) != null) {
+ FightSystem.getMessage().broadcast("PREPARE_SCHEM_EXISTS");
+ Bukkit.shutdown();
+ return true;
+ }
+ return false;
+ }
+
+ private String preparedName(SchematicNode schem) {
+ return schem.getName() + "-prepared";
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java
new file mode 100644
index 00000000..f77c1904
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java
@@ -0,0 +1,309 @@
+/*
+ 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 com.comphenix.tinyprotocol.Reflection;
+import com.comphenix.tinyprotocol.TinyProtocol;
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.events.TeamDeathEvent;
+import de.steamwar.fightsystem.events.TeamLeaveEvent;
+import de.steamwar.fightsystem.events.TeamSpawnEvent;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightPlayer;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.record.GlobalRecorder;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependent;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.states.StateDependentTask;
+import de.steamwar.fightsystem.utils.BountifulWrapper;
+import de.steamwar.fightsystem.utils.CraftbukkitWrapper;
+import de.steamwar.fightsystem.utils.FlatteningWrapper;
+import de.steamwar.fightsystem.utils.SWSound;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockPhysicsEvent;
+import org.bukkit.event.entity.*;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.event.player.*;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.Random;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class Recording implements Listener {
+
+ private static final int AIR = 0;
+ private static final Random random = new Random();
+
+ public static ItemStack disarmNull(ItemStack stack){
+ if(stack == null)
+ return new ItemStack(Material.AIR);
+ return stack;
+ }
+
+ public static boolean isNotSent(Player p){
+ FightPlayer fp = Fight.getFightPlayer(p);
+ return fp == null || !fp.isLiving() || FightState.getFightState() == FightState.SPECTATE;
+ }
+
+ public static final Class> primedTnt = Reflection.getClass("{nms.world.entity.item}.EntityTNTPrimed");
+ private static final Reflection.MethodInvoker getBukkitEntity = Reflection.getTypedMethod(Reflection.getClass("{nms.world.entity}.Entity"), "getBukkitEntity", null);
+ public static void iterateOverEntities(Predicate filter, Consumer consumer) {
+ CraftbukkitWrapper.impl.entityIterator().filter(filter).map(entity -> (Entity) getBukkitEntity.invoke(entity)).forEach(consumer);
+ }
+
+ public Recording() {
+ new StateDependentListener(ArenaMode.AntiReplay, FightState.All, this);
+ new StateDependentListener(ArenaMode.AntiReplay, FightState.All, BountifulWrapper.impl.newHandSwapRecorder());
+ new StateDependent(ArenaMode.AntiReplay, FightState.Ingame){
+ @Override
+ public void enable() {
+ Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> Fight.teams().forEach(Recording.this::setKitItems), 1);
+ }
+
+ @Override
+ public void disable() {
+ Fight.teams().forEach(Recording.this::despawnTeam);
+ despawnTNT();
+ }
+ }.register();
+ new StateDependent(ArenaMode.AntiReplay, FightState.Ingame) {
+ private final BiFunction place = Recording.this::blockPlace;
+ private final BiFunction dig = Recording.this::blockDig;
+
+ @Override
+ public void enable() {
+ TinyProtocol.instance.addFilter(blockPlacePacket, place);
+ TinyProtocol.instance.addFilter(blockDigPacket, dig);
+ }
+
+ @Override
+ public void disable() {
+ TinyProtocol.instance.removeFilter(blockPlacePacket, place);
+ TinyProtocol.instance.removeFilter(blockDigPacket, dig);
+ }
+ }.register();
+ new StateDependentTask(ArenaMode.AntiReplay, FightState.All, () -> {
+ GlobalRecorder.getInstance().tick();
+
+ if(FightState.getFightState() == FightState.SPECTATE || !GlobalRecorder.getInstance().recording())
+ return;
+
+ iterateOverEntities(primedTnt::isInstance, this::trackEntity);
+ }, 1, 1);
+ }
+
+ private void trackEntity(Entity entity) {
+ GlobalRecorder.getInstance().entityMoves(entity);
+ GlobalRecorder.getInstance().entitySpeed(entity);
+ }
+
+ private static final Class> blockDigPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInBlockDig");
+ private static final Class> playerDigType = blockDigPacket.getDeclaredClasses()[0];
+ private static final Reflection.FieldAccessor> blockDigType = Reflection.getField(blockDigPacket, playerDigType, 0);
+ private static final Object releaseUseItem = playerDigType.getEnumConstants()[5];
+ private Object blockDig(Player p, Object packet) {
+ if(!isNotSent(p) && blockDigType.get(packet) == releaseUseItem)
+ GlobalRecorder.getInstance().bowSpan(p, false, false);
+ return packet;
+ }
+
+ public static final Class> blockPlacePacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInBlockPlace");
+ private Object blockPlace(Player p, Object packet) {
+ boolean mainHand = BountifulWrapper.impl.mainHand(packet);
+ if(!isNotSent(p) && BountifulWrapper.impl.bowInHand(mainHand, p))
+ GlobalRecorder.getInstance().bowSpan(p, true, !mainHand);
+ return packet;
+ }
+
+ @EventHandler
+ public void onPlayerSpawn(TeamSpawnEvent e) {
+ GlobalRecorder.getInstance().playerJoins(e.getFightPlayer().getEntity(), e.getFightPlayer().getUser());
+ }
+
+ @EventHandler(priority = EventPriority.HIGH)
+ public void onPlayerMove(PlayerMoveEvent e){
+ if(isNotSent(e.getPlayer()))
+ return;
+
+ GlobalRecorder.getInstance().entityMoves(e.getPlayer());
+ }
+
+ @EventHandler
+ public void onPlayerDeath(TeamDeathEvent e) {
+ GlobalRecorder.getInstance().entityDespawns(e.getFightPlayer().getEntity());
+ }
+
+ @EventHandler
+ public void onPlayerLeave(TeamLeaveEvent e) {
+ GlobalRecorder.getInstance().entityDespawns(e.getFightPlayer().getEntity());
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void onBlockPhysics(BlockPhysicsEvent e){
+ if(FlatteningWrapper.impl.doRecord(e))
+ GlobalRecorder.getInstance().blockChange(e.getBlock());
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void onSneak(PlayerToggleSneakEvent e){
+ if(isNotSent(e.getPlayer()))
+ return;
+
+ GlobalRecorder.getInstance().playerSneak(e.getPlayer(), e.isSneaking());
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void onAnimation(PlayerAnimationEvent e){
+ if(isNotSent(e.getPlayer()))
+ return;
+
+ if(e.getAnimationType() == PlayerAnimationType.ARM_SWING)
+ GlobalRecorder.getInstance().entityAnimation(e.getPlayer(), AIR);
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void onEntityDamage(EntityDamageEvent e) {
+ if(e.getEntityType() != EntityType.PLAYER)
+ return;
+
+ Player p = (Player) e.getEntity();
+ if(isNotSent(p))
+ return;
+
+ GlobalRecorder.getInstance().damageAnimation(p);
+
+ if(e.getCause() == EntityDamageEvent.DamageCause.FIRE_TICK || e.getCause() == EntityDamageEvent.DamageCause.FIRE)
+ GlobalRecorder.getInstance().setOnFire(p, false);
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void onEntityCombust(EntityCombustEvent e) {
+ if(e.getEntityType() != EntityType.PLAYER)
+ return;
+
+ Player p = (Player) e.getEntity();
+ if(isNotSent(p))
+ return;
+
+ GlobalRecorder.getInstance().setOnFire(p, false);
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void onTNTSpawn(EntitySpawnEvent e){
+ if(e.getEntityType() != EntityType.PRIMED_TNT)
+ return;
+
+ GlobalRecorder.getInstance().tntSpawn(e.getEntity());
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void onExplosion(EntityExplodeEvent e){
+ if(e.getEntityType() != EntityType.PRIMED_TNT)
+ return;
+
+ Location loc = e.getLocation();
+ GlobalRecorder.getInstance().entityDespawns(e.getEntity());
+ GlobalRecorder.getInstance().particle(loc.getX(), loc.getY(), loc.getZ(), "EXPLOSION_HUGE");
+ GlobalRecorder.getInstance().sound(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), SWSound.ENTITY_GENERIC_EXPLODE, "BLOCKS", 4.0F, (1.0F + (random.nextFloat() - random.nextFloat()) * 0.2F) * 0.7F);
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void onItem(PlayerItemHeldEvent e){
+ if(isNotSent(e.getPlayer()))
+ return;
+
+ GlobalRecorder.getInstance().item(e.getPlayer(), disarmNull(e.getPlayer().getInventory().getItem(e.getNewSlot())), "MAINHAND");
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
+ public void onProjectileSpawn(ProjectileLaunchEvent e){
+ if(e.getEntityType() == EntityType.FIREBALL)
+ GlobalRecorder.getInstance().fireballSpawn(e.getEntity());
+ else if(e.getEntityType() == EntityType.ARROW)
+ GlobalRecorder.getInstance().arrowSpawn(e.getEntity());
+ }
+
+ @EventHandler(priority = EventPriority.HIGH, 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:
+ GlobalRecorder.getInstance().item(player, disarmNull(e.getCurrentItem()), "HEAD");
+ break;
+ case 102:
+ GlobalRecorder.getInstance().item(player, disarmNull(e.getCurrentItem()), "CHEST");
+ break;
+ case 101:
+ GlobalRecorder.getInstance().item(player, disarmNull(e.getCurrentItem()), "LEGS");
+ break;
+ case 100:
+ default:
+ GlobalRecorder.getInstance().item(player, disarmNull(e.getCurrentItem()), "FEET");
+ }
+ }
+
+ private void setKitItems(FightTeam team){
+ if(FightState.getFightState() != FightState.PRE_RUNNING)
+ return;
+
+ for(FightPlayer fp : team.getPlayers()){
+ if(!fp.isLiving())
+ continue;
+
+ fp.ifPlayer(player -> {
+ BountifulWrapper.impl.recordHandItems(player);
+ GlobalRecorder.getInstance().item(player, disarmNull(player.getInventory().getHelmet()), "HEAD");
+ GlobalRecorder.getInstance().item(player, disarmNull(player.getInventory().getChestplate()), "CHEST");
+ GlobalRecorder.getInstance().item(player, disarmNull(player.getInventory().getLeggings()), "LEGS");
+ GlobalRecorder.getInstance().item(player, disarmNull(player.getInventory().getBoots()), "FEET");
+ });
+ }
+ }
+
+ private void despawnTeam(FightTeam team){
+ for(FightPlayer player : team.getPlayers()){
+ if(player.isLiving())
+ GlobalRecorder.getInstance().entityDespawns(player.getEntity());
+ }
+ }
+
+ private void despawnTNT(){
+ iterateOverEntities(primedTnt::isInstance, GlobalRecorder.getInstance()::entityDespawns);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/RunningWorldInteraction.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/RunningWorldInteraction.java
new file mode 100644
index 00000000..7c00a9f1
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/RunningWorldInteraction.java
@@ -0,0 +1,48 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.ArenaMode;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightPlayer;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.Material;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockPlaceEvent;
+
+public class RunningWorldInteraction implements Listener {
+
+ public RunningWorldInteraction() {
+ new StateDependentListener(ArenaMode.AntiReplay, FightState.Running, this);
+ }
+
+ @EventHandler
+ public void onBlockPlace(BlockPlaceEvent e) {
+ FightPlayer fp = Fight.getFightPlayer(e.getPlayer());
+ if(fp != null && !fp.getKit().isTnt() && e.getBlockPlaced().getType() == Material.TNT){
+ FightSystem.getMessage().sendPrefixless("NO_TNT_PLACE", e.getPlayer(), ChatMessageType.ACTION_BAR);
+ e.setCancelled(true);
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/SetupQuit.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/SetupQuit.java
new file mode 100644
index 00000000..87d4b7e9
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/SetupQuit.java
@@ -0,0 +1,47 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+public class SetupQuit implements Listener {
+
+ public SetupQuit(){
+ new StateDependentListener(ArenaMode.AntiReplay, FightState.Setup, this);
+ }
+
+ @EventHandler
+ public void handlePlayerQuit(PlayerQuitEvent event) {
+ Player player = event.getPlayer();
+ FightTeam team = Fight.getPlayerTeam(player);
+ if(team == null)
+ return;
+
+ team.removePlayer(player);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Shutdown.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Shutdown.java
new file mode 100644
index 00000000..82057e92
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Shutdown.java
@@ -0,0 +1,46 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.Bukkit;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+public class Shutdown implements Listener {
+
+ public Shutdown(){
+ new StateDependentListener(ArenaMode.AntiEvent.contains(Config.mode) && !Config.replayserver(), FightState.All, this);
+ }
+
+ @EventHandler
+ public void handlePlayerQuit(PlayerQuitEvent event) {
+ if(Config.replayserver() || Config.ArenaLeaveable)
+ return;
+
+ //Shutdown server if nobody online
+ if(Bukkit.getOnlinePlayers().isEmpty() || (Bukkit.getOnlinePlayers().size() == 1 && Bukkit.getOnlinePlayers().contains(event.getPlayer())))
+ Bukkit.shutdown();
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/TeamArea.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/TeamArea.java
new file mode 100644
index 00000000..2720b25c
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/TeamArea.java
@@ -0,0 +1,143 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.events.BoardingEvent;
+import de.steamwar.fightsystem.events.TeamDeathEvent;
+import de.steamwar.fightsystem.events.TeamLeaveEvent;
+import de.steamwar.fightsystem.events.TeamSpawnEvent;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightPlayer;
+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.fightsystem.states.StateDependentTask;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class TeamArea implements Listener {
+
+ private final FightTeam team;
+ private final Border spectatorBorder;
+ private final Border bordingBorder;
+ private final Set realSpectator = new HashSet<>();
+
+ public TeamArea(FightTeam team) {
+ this.team = team;
+ this.spectatorBorder = new Border(team.getExtendRegion(), false, 5, "NO_TEAMAREA", team.getName() + ".spectatorBorder");
+ this.bordingBorder = new Border(team.getExtendRegion().to2d(), true, 1, "NO_ENTERN", team.getName() + ".boardingBorder");
+
+ new StateDependentListener(ArenaMode.AntiTest, FightState.All, this);
+ new StateDependentTask(ArenaMode.AntiTest, FightState.TeamFix, this::realSpectatorCheck, 1, 1);
+ new OneShotStateDependent(ArenaMode.AntiTest, FightState.Spectate, () -> Fight.teams().forEach(t -> t.getPlayers().forEach(this::teamSpectator)));
+ }
+
+ @EventHandler
+ public void playerJoin(PlayerJoinEvent e) {
+ Player player = e.getPlayer();
+ if(Fight.getPlayerTeam(player) != team && !Config.isReferee(player))
+ spectatorBorder.addPlayer(player);
+ }
+
+ @EventHandler
+ public void teamJoin(TeamSpawnEvent e) {
+ FightPlayer fightPlayer = e.getFightPlayer();
+ if(fightPlayer.getTeam() == team) {
+ fightPlayer.ifPlayer(player -> {
+ spectatorBorder.removePlayer(player);
+ bordingBorder.addPlayer(player);
+ });
+ }
+ }
+
+ @EventHandler
+ public void teamLeave(TeamLeaveEvent e) {
+ FightPlayer fightPlayer = e.getFightPlayer();
+ fightPlayer.ifPlayer(spectatorBorder::addPlayer);
+ if(fightPlayer.getTeam() == team)
+ fightPlayer.ifPlayer(bordingBorder::removePlayer);
+ }
+
+ @EventHandler
+ public void boarding(BoardingEvent e) {
+ FightPlayer fightPlayer = e.getFightPlayer();
+ if(fightPlayer.getTeam() == team) {
+ fightPlayer.ifPlayer(bordingBorder::removePlayer);
+ } else {
+ fightPlayer.ifPlayer(spectatorBorder::removePlayer);
+ }
+ }
+
+ @EventHandler
+ public void teamDeath(TeamDeathEvent e) {
+ teamSpectator(e.getFightPlayer());
+ }
+
+ private void teamSpectator(FightPlayer fightPlayer) {
+ if(fightPlayer.getTeam() == team) {
+ fightPlayer.ifPlayer(bordingBorder::removePlayer);
+ } else {
+ fightPlayer.ifPlayer(spectatorBorder::addPlayer);
+ }
+ }
+
+ @EventHandler
+ public void playerQuit(PlayerQuitEvent e) {
+ Player player = e.getPlayer();
+ spectatorBorder.removePlayer(player);
+ bordingBorder.removePlayer(player);
+ realSpectator.remove(player);
+ }
+
+ private void realSpectatorCheck() {
+ for(FightPlayer fightPlayer : team.getPlayers()) {
+ if(fightPlayer.isLiving())
+ continue;
+
+ fightPlayer.ifPlayer(player -> {
+ boolean inRegion = team.getExtendRegion().playerInRegion(player.getLocation());
+ if(inRegion && !realSpectator.contains(player)) {
+ realSpectator.add(player);
+
+ //Later to prevent race condition with Fight.setSpecatator() during respawn
+ Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> {
+ if(!player.isOnline())
+ return;
+ Fight.pseudoSpectator(player, false);
+ }, 2);
+ }else if(!inRegion && realSpectator.contains(player)) {
+ Fight.pseudoSpectator(player, true);
+ realSpectator.remove(player);
+ }
+ });
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/TestJoin.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/TestJoin.java
new file mode 100644
index 00000000..77240153
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/TestJoin.java
@@ -0,0 +1,64 @@
+/*
+ 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.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.StateDependentListener;
+import de.steamwar.sql.SteamwarUser;
+import de.steamwar.sql.UserPerm;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+
+public class TestJoin implements Listener {
+
+ public TestJoin() {
+ new StateDependentListener(ArenaMode.Test, FightState.All, this);
+ }
+
+ @EventHandler
+ public void handlePlayerJoin(PlayerJoinEvent event) {
+ Player player = event.getPlayer();
+ FightTeam fightTeam = Fight.getPlayerTeam(player);
+
+ if(Config.ReplayID != 0 && !SteamwarUser.get(player.getUniqueId()).hasPerm(UserPerm.MODERATION)) {
+ FightSystem.getMessage().send("CHECK_JOIN_DENIED", player);
+ player.kickPlayer("");
+ return;
+ }
+
+ if (fightTeam == null && Fight.teams().stream().anyMatch(team -> team.canbeLeader(player))) {
+ FightSystem.getMessage().send("TEST_BECOME_LEADER", player);
+ }
+
+ player.setOp(true);
+
+ if(FightState.getFightState() == FightState.PRE_LEADER_SETUP){
+ FightState.setFightState(FightState.PRE_SCHEM_SETUP);
+ FightState.setFightState(FightState.POST_SCHEM_SETUP);
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/WaterRemover.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/WaterRemover.java
new file mode 100644
index 00000000..c89fcab0
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/WaterRemover.java
@@ -0,0 +1,111 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.utils.FlatteningWrapper;
+import org.bukkit.Location;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.entity.EntityType;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityExplodeEvent;
+import org.bukkit.event.entity.EntitySpawnEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class WaterRemover implements Listener {
+
+ private static final int MIN_Y = Config.BluePasteRegion.getMinY() + Config.WaterDepth;
+
+ private final Map tnt = new HashMap<>();
+
+ public WaterRemover() {
+ new StateDependentListener(ArenaMode.AntiReplay, FightState.Running, this){
+ @Override
+ public void enable() {
+ tnt.clear();
+ super.enable();
+ }
+ };
+ }
+
+ @EventHandler
+ public void handleEntitySpawn(EntitySpawnEvent event) {
+ if(event.getEntityType() != EntityType.PRIMED_TNT)
+ return;
+
+ Location location = event.getLocation();
+ Fight.teams().forEach(team -> {
+ if(team.getExtendRegion().inRegion(location))
+ tnt.put(event.getEntity().getEntityId(), team);
+ });
+ }
+
+ @EventHandler
+ public void handleEntityExplode(EntityExplodeEvent event) {
+ event.setYield(0); //No drops (additionally to world config)
+
+ FightTeam spawn = tnt.remove(event.getEntity().getEntityId());
+ if(spawn != null && !spawn.getExtendRegion().inRegion(event.getLocation())) {
+ Block b = event.getLocation().getBlock();
+ for(int y = -1; y <= 1; y++) {
+ for(int z = -1; z <= 1; z++) {
+ for(int x = -1; x <= 1; x++) {
+ checkBlock(b.getRelative(x, y, z));
+ }
+ }
+ }
+ }
+
+ event.blockList().forEach(this::checkNeighbours);
+ }
+
+ private void checkNeighbours(Block b) {
+ checkBlock(b.getRelative(BlockFace.UP));
+ checkBlock(b.getRelative(BlockFace.EAST));
+ checkBlock(b.getRelative(BlockFace.WEST));
+ checkBlock(b.getRelative(BlockFace.NORTH));
+ checkBlock(b.getRelative(BlockFace.SOUTH));
+ checkBlock(b.getRelative(BlockFace.DOWN));
+ }
+
+ private void checkBlock(Block b) {
+ //do not remove outside teamareas
+ if(!Config.BlueExtendRegion.inRegion(b) && !Config.RedExtendRegion.inRegion(b))
+ return;
+
+ //checks for water and removes it, if present
+ if(!FlatteningWrapper.impl.removeWater(b))
+ return;
+
+ if(b.getY() < MIN_Y)
+ return;
+
+ checkNeighbours(b);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/FileRecorder.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/FileRecorder.java
new file mode 100644
index 00000000..98933dd8
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/FileRecorder.java
@@ -0,0 +1,75 @@
+/*
+ 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 de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependent;
+import lombok.Getter;
+
+import java.io.*;
+import java.util.zip.GZIPOutputStream;
+
+public class FileRecorder extends StateDependent implements Recorder {
+
+ @Getter
+ private static final File file = new File(Config.world.getWorldFolder(), "recording.recording");
+ private DataOutputStream outputStream;
+
+ public FileRecorder(){
+ super(Config.ReplayID == 0, FightState.Recording);
+ register();
+ }
+
+ @Override
+ public DataOutputStream getStream() {
+ return outputStream;
+ }
+
+ @Override
+ public void enable() {
+ try{
+ outputStream = new DataOutputStream(new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream(file), 4096)){
+ @Override
+ public synchronized void flush() {
+ //Don't flush explicitly (performance)
+ }
+
+ @Override
+ public void close() throws IOException {
+ try{
+ super.flush();
+ }catch(IOException e){
+ //do nothing
+ }
+ super.close();
+ }
+ });
+ }catch(IOException e){
+ throw new SecurityException("Could not open file", e);
+ }
+ Recorder.super.enable();
+ }
+
+ @Override
+ public void disable() {
+ Recorder.super.disable();
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/FileSource.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/FileSource.java
new file mode 100644
index 00000000..2fcaa59e
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/FileSource.java
@@ -0,0 +1,69 @@
+/*
+ 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 de.steamwar.sql.Replay;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.zip.GZIPInputStream;
+
+public class FileSource extends PacketSource {
+
+ public static void startReplay() {
+ if(Config.replayserver()) {
+ try {
+ new LiveServer();
+ } catch (IOException e) {
+ throw new SecurityException("Could not start replayserver", e);
+ }
+ return;
+ }
+
+ if(Config.ReplayID > 0) {
+ try {
+ new FileSource(Replay.get(Config.ReplayID).getReplay());
+ } catch (IOException e) {
+ throw new SecurityException("Could not start replay", e);
+ }
+ }
+ }
+
+ public FileSource(File fightFile) throws IOException {
+ super(new GZIPInputStream(new FileInputStream(fightFile)));
+ new PacketProcessor(this);
+ }
+
+ @Override
+ boolean isClosed() {
+ try{
+ return available() == 0;
+ }catch (IOException e){
+ return true;
+ }
+ }
+
+ @Override
+ boolean async() {
+ return false;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/GlobalRecorder.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/GlobalRecorder.java
new file mode 100644
index 00000000..b0e9824b
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/GlobalRecorder.java
@@ -0,0 +1,95 @@
+/*
+ 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 java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+
+public class GlobalRecorder extends OutputStream implements Recorder {
+
+ private static final GlobalRecorder recorder = new GlobalRecorder();
+
+ public static GlobalRecorder getInstance(){
+ return recorder;
+ }
+
+ private final List recorders = new ArrayList<>();
+ private final DataOutputStream outputStream = new DataOutputStream(this);
+
+ public void add(Recorder recorder){
+ recorders.add(recorder);
+ }
+
+ public void remove(Recorder recorder){
+ recorders.remove(recorder);
+ }
+
+ public boolean recording(){
+ return !recorders.isEmpty();
+ }
+
+ @Override
+ public DataOutputStream getStream() {
+ return outputStream;
+ }
+
+ @Override
+ public void write(int i) {
+ foreach(stream -> stream.write(i));
+ }
+
+ @Override
+ public void write(byte[] bytes, int i, int i1) {
+ foreach(stream -> stream.write(bytes, i, i1));
+ }
+
+ @Override
+ public void flush() {
+ foreach(OutputStream::flush);
+ }
+
+ @Override
+ public void close() {
+ foreach(OutputStream::close);
+ }
+
+ private void foreach(IOThrower thrower) {
+ for(int i = 0; i < recorders.size(); i++) {
+ Recorder stream = recorders.get(i);
+ try{
+ thrower.run(stream.getStream());
+ }catch (IOException e){
+ Bukkit.getLogger().log(Level.SEVERE, "Could not operate on OutputStream", e);
+ stream.disable();
+ i--; // Recorder was removed from recorders
+ }
+ }
+ }
+
+ interface IOThrower{
+ void run(OutputStream stream) throws IOException;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/LiveRecorder.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/LiveRecorder.java
new file mode 100644
index 00000000..55fef032
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/LiveRecorder.java
@@ -0,0 +1,70 @@
+/*
+ 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 de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependent;
+import org.bukkit.Bukkit;
+
+import java.io.*;
+import java.net.Socket;
+import java.util.logging.Level;
+
+public class LiveRecorder extends StateDependent implements Recorder {
+
+ private DataOutputStream outputStream;
+ private Socket socket;
+
+ public LiveRecorder(){
+ super(Config.LiveReplay, FightState.AntiSpectate);
+ register();
+ }
+
+ @Override
+ public void enable() {
+ try {
+ 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 disable
+ socket.setTcpNoDelay(true); // Don't wait on ack
+ outputStream = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream(), 1024));
+ } catch (IOException e) {
+ Bukkit.getLogger().log(Level.WARNING, "Could not init connection to spectate server", e);
+ disable();
+ }
+ Recorder.super.enable();
+ }
+
+ @Override
+ public void disable() {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ Bukkit.getLogger().log(Level.WARNING, "IOException on socket close", e);
+ }
+ Recorder.super.disable();
+ }
+
+ @Override
+ public DataOutputStream getStream() {
+ return outputStream;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/LiveServer.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/LiveServer.java
new file mode 100644
index 00000000..5bb63084
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/LiveServer.java
@@ -0,0 +1,62 @@
+/*
+ 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 de.steamwar.fightsystem.FightSystem;
+import org.bukkit.Bukkit;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.logging.Level;
+
+public class LiveServer {
+
+ private final ServerSocket socket;
+
+ public LiveServer() throws IOException {
+ socket = new ServerSocket(Config.SpectatePort);
+ Bukkit.getScheduler().runTaskAsynchronously(FightSystem.getPlugin(), this::acceptConnections);
+ }
+
+ public void close(){
+ try {
+ socket.close();
+ } catch (IOException e) {
+ Bukkit.getLogger().log(Level.SEVERE, "Could not close socket", e);
+ }
+ }
+
+ private void acceptConnections(){
+ try {
+ while(!socket.isClosed()){
+ Socket s = socket.accept();
+ if(PacketProcessor.isReplaying()){
+ s.close();
+ }else{
+ new LiveSource(s);
+ }
+ }
+ } catch (IOException e) {
+ Bukkit.getLogger().log(Level.INFO, "Stopping accepting connections", e);
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/LiveSource.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/LiveSource.java
new file mode 100644
index 00000000..313ff85d
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/LiveSource.java
@@ -0,0 +1,56 @@
+/*
+ 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 java.io.IOException;
+import java.net.Socket;
+import java.util.logging.Level;
+
+public class LiveSource extends PacketSource {
+ private final Socket socket;
+
+ protected LiveSource(Socket socket) throws IOException {
+ super(socket.getInputStream());
+ this.socket = socket;
+ new PacketProcessor(this);
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ try {
+ socket.close();
+ } catch (IOException e) {
+ Bukkit.getLogger().log(Level.SEVERE, "IOException on close", e);
+ }
+ }
+
+ @Override
+ boolean isClosed() {
+ return socket.isClosed();
+ }
+
+ @Override
+ boolean async() {
+ return true;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java
new file mode 100644
index 00000000..6296e374
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java
@@ -0,0 +1,715 @@
+/*
+ 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 com.sk89q.worldedit.extent.clipboard.Clipboard;
+import de.steamwar.core.Core;
+import de.steamwar.entity.REntity;
+import de.steamwar.entity.REntityServer;
+import de.steamwar.entity.RPlayer;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.countdown.Countdown;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.fight.FightWorld;
+import de.steamwar.fightsystem.fight.FreezeWorld;
+import de.steamwar.fightsystem.listener.FightScoreboard;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.utils.*;
+import de.steamwar.sql.SchematicData;
+import de.steamwar.sql.SchematicNode;
+import de.steamwar.sql.SteamwarUser;
+import de.steamwar.sql.Team;
+import de.steamwar.techhider.BlockIds;
+import net.md_5.bungee.api.ChatMessageType;
+import net.md_5.bungee.api.chat.BaseComponent;
+import net.md_5.bungee.api.chat.TextComponent;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.Sound;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.scheduler.BukkitTask;
+import org.bukkit.scoreboard.NameTagVisibility;
+
+import java.io.EOFException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+public class PacketProcessor implements Listener {
+
+ private static final org.bukkit.scoreboard.Team team = FightScoreboard.getBukkitTeam("Replay");
+ static {
+ BountifulWrapper.impl.setNametagVisibility(team);
+ team.setNameTagVisibility(NameTagVisibility.NEVER);
+ }
+
+ private static PacketProcessor currentProcessor = null;
+
+ public static PacketProcessor currentReplay() {
+ return currentProcessor;
+ }
+
+ public static boolean isReplaying(){
+ return currentProcessor != null;
+ }
+
+ private final PacketParser[] packetDecoder = new PacketParser[256];
+
+ private final PacketSource source;
+ private final BukkitTask task;
+ private final LinkedList syncList = new LinkedList<>();
+ private final Set hiddenBlockIds = Config.HiddenBlocks.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet());
+ private final int obfuscateWith = BlockIds.impl.materialToId(Config.ObfuscateWith);
+ private final FreezeWorld freezer = new FreezeWorld();
+ private final REntityServer entityServer = new REntityServer();
+ private final Map entities = new HashMap<>();
+
+ private boolean rotateZ = false;
+ private int arenaMinX = Config.ArenaRegion.getMinX();
+ private int arenaMinY = Config.BluePasteRegion.getMinY();
+ private int arenaMinZ = Config.ArenaRegion.getMinZ();
+
+ private boolean skipToSubtitle = false;
+
+ private boolean tickFinished = false;
+ private final List lastPackets = new LinkedList<>();
+
+ public PacketProcessor(PacketSource source) {
+ this.source = source;
+ currentProcessor = this;
+
+ packetDecoder[0x00] = this::playerJoins;
+ packetDecoder[0x01] = this::entityMoves;
+ packetDecoder[0x02] = this::entityDespawns;
+ packetDecoder[0x03] = this::entitySneak;
+ packetDecoder[0x04] = this::entityAnimation;
+ packetDecoder[0x05] = this::tntSpawn;
+ packetDecoder[0x06] = this::entityVelocity;
+ packetDecoder[0x07] = this::playerItem;
+ packetDecoder[0x08] = this::arrowSpawn;
+ packetDecoder[0x09] = this::fireballSpawn;
+ packetDecoder[0x0a] = this::bow;
+ packetDecoder[0x0b] = this::damage;
+ packetDecoder[0x0c] = this::fireTick;
+ packetDecoder[0x20] = this::oldArenaInfo;
+ packetDecoder[0x21] = this::arenaInfo;
+ packetDecoder[0x30] = this::byteWorldHeightBlock;
+ packetDecoder[0x31] = this::particle;
+ packetDecoder[0x32] = this::sound;
+ packetDecoder[0x33] = this::shortBlock;
+ packetDecoder[0x34] = this::soundAtPlayer;
+ packetDecoder[0x35] = this::shortRelativeBlock;
+ packetDecoder[0x36] = this::block;
+ packetDecoder[0xa0] = () -> send(ChatMessageType.CHAT);
+ packetDecoder[0xa1] = () -> send(ChatMessageType.ACTION_BAR);
+ packetDecoder[0xa2] = () -> send(ChatMessageType.SYSTEM);
+ packetDecoder[0xa3] = this::countdown;
+ packetDecoder[0xa4] = this::chat;
+ packetDecoder[0xa5] = this::system;
+ packetDecoder[0xb0] = () -> pasteSchem(Fight.getBlueTeam());
+ packetDecoder[0xb1] = () -> pasteSchem(Fight.getRedTeam());
+ packetDecoder[0xb2] = this::teams;
+ packetDecoder[0xb3] = () -> pasteEmbeddedSchem(Fight.getBlueTeam());
+ packetDecoder[0xb4] = () -> pasteEmbeddedSchem(Fight.getRedTeam());
+ packetDecoder[0xc0] = this::scoreboardTitle;
+ packetDecoder[0xc1] = this::scoreboardData;
+ packetDecoder[0xc2] = this::bossBar;
+ packetDecoder[0xc3] = this::subtitle;
+ packetDecoder[0xc4] = this::printWin;
+ packetDecoder[0xc5] = this::messageSubtitle;
+ packetDecoder[0xc6] = this::winMessage;
+ packetDecoder[0xc7] = this::bossBarMessage;
+ packetDecoder[0xef] = source::readUTF;
+ packetDecoder[0xff] = this::tick;
+
+ execSync(FightWorld::forceLoad);
+
+ if(source.async()) {
+ Bukkit.getScheduler().runTaskAsynchronously(FightSystem.getPlugin(), this::process);
+ task = Bukkit.getScheduler().runTaskTimer(FightSystem.getPlugin(), this::runSync, 1, 1);
+ }else
+ task = Bukkit.getScheduler().runTaskTimer(FightSystem.getPlugin(), this::process, 1, 1);
+
+ Bukkit.getPluginManager().registerEvents(this, FightSystem.getPlugin());
+ for(Player player : Bukkit.getOnlinePlayers())
+ entityServer.addPlayer(player);
+ }
+
+ private void addREntity(int entityId, REntity entity) {
+ entities.put(entityId, entity);
+ FightSystem.getHullHider().updateREntity(entity);
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onPlayerJoin(PlayerJoinEvent e) {
+ entityServer.addPlayer(e.getPlayer());
+ }
+
+ public void skipToSubtitle() {
+ skipToSubtitle = true;
+ }
+
+ private void winMessage() throws IOException {
+ byte team = source.readByte();
+ Message message = readMessage();
+
+ execSync(() -> {
+ FightTeam winner = null;
+ if(team == 0x01)
+ winner = Fight.getBlueTeam();
+ else if(team == 0x02)
+ winner = Fight.getRedTeam();
+
+ FightSystem.setSpectateState(winner, "Replay ends", message.getMsg(), message.getParams());
+ });
+ }
+
+ private void system() throws IOException {
+ Message message = readMessage();
+ FightSystem.getMessage().broadcast(message.getMsg(), message.getParams());
+ }
+
+ private void messageSubtitle() throws IOException {
+ Message message = readMessage();
+ FightUI.addSubtitle(message.getMsg(), message.getParams());
+ skipToSubtitle = false;
+ }
+
+ private void chat() throws IOException {
+ Message message = readMessage();
+ FightSystem.getMessage().chat(message.getMsg(), message.getParams());
+ }
+
+ private void runSync() {
+ synchronized (syncList) {
+ for(Runnable runnable : syncList) {
+ try{
+ runnable.run();
+ }catch (Exception e) {
+ Bukkit.getLogger().log(Level.WARNING, "Failed to execute packet LastPacket: " + Arrays.toString(lastPackets.toArray()), e);
+ }
+ }
+ syncList.clear();
+ }
+ }
+
+ private void execSync(Runnable runnable){
+ if (!source.async()) {
+ runnable.run();
+ return;
+ }
+
+ synchronized (syncList) {
+ syncList.add(runnable);
+ }
+ }
+
+ private void playerJoins() throws IOException {
+ int entityId = source.readInt();
+ int userId = source.readInt();
+
+ execSync(() -> {
+ SteamwarUser user = SteamwarUser.get(userId);
+ addREntity(entityId, new RPlayer(entityServer, user.getUUID(), user.getUserName(), Config.SpecSpawn));
+ team.addEntry(user.getUserName());
+ });
+ }
+
+ private void entityMoves() throws IOException {
+ int entityId = source.readInt();
+ double x = source.readDouble() - arenaMinX;
+ double locY = source.readDouble() - arenaMinY + Config.BluePasteRegion.getMinY();
+ double z = source.readDouble() - arenaMinZ;
+ if(rotateZ) {
+ x = Config.ArenaRegion.getSizeX() - x;
+ z = Config.ArenaRegion.getSizeZ() - z;
+ }
+ double locX = x + Config.ArenaRegion.getMinX();
+ double locZ = z + Config.ArenaRegion.getMinZ();
+ float pitch = source.readFloat();
+ float yaw = source.readFloat() + (rotateZ ? 180 : 0);
+ byte headYaw = (byte)((source.readByte() + (rotateZ ? 128 : 0)) % 256);
+
+ execSync(() -> {
+ REntity entity = entities.get(entityId);
+ if(entity != null) {
+ entity.move(locX, locY, locZ, pitch, yaw, headYaw);
+ FightSystem.getHullHider().updateREntity(entity);
+ }
+ });
+ }
+
+ private void entityDespawns() throws IOException {
+ int entityId = source.readInt();
+
+ execSync(() -> {
+ REntity entity = entities.remove(entityId);
+ if(entity != null) {
+ FightSystem.getHullHider().despawnREntity(entity);
+ entity.die();
+ }
+ });
+ }
+
+ private void entitySneak() throws IOException {
+ int entityId = source.readInt();
+ boolean sneaking = source.readBoolean();
+
+ execSync(() -> entities.get(entityId).setPose(sneaking ? de.steamwar.core.FlatteningWrapper.EntityPose.SNEAKING : de.steamwar.core.FlatteningWrapper.EntityPose.NORMAL));
+ }
+
+ private void entityAnimation() throws IOException {
+ int entityId = source.readInt();
+ byte animation = source.readByte();
+
+ execSync(() -> entities.get(entityId).showAnimation(animation));
+ }
+
+ private void tntSpawn() throws IOException {
+ int entityId = source.readInt();
+
+ execSync(() -> addREntity(entityId, new REntity(entityServer, EntityType.PRIMED_TNT, Config.SpecSpawn)));
+ }
+
+ private void entityVelocity() throws IOException {
+ int entityId = source.readInt();
+
+ double dX = rotateZ ? -source.readDouble() : source.readDouble();
+ double dY = source.readDouble();
+ double dZ = rotateZ ? -source.readDouble() : source.readDouble();
+
+ execSync(() -> {
+ REntity entity = entities.get(entityId);
+ if(entity != null)
+ entity.setVelocity(dX, dY, dZ);
+ });
+ }
+
+ private void playerItem() throws IOException {
+ int entityId = source.readInt();
+ String item = source.readUTF();
+ boolean enchanted = source.readBoolean();
+ String slotName = source.readUTF();
+
+ ItemStack stack = new ItemStack(Material.valueOf(item.replace("minecraft:", "").toUpperCase()), 1);
+ if(enchanted)
+ stack.addUnsafeEnchantment(Enchantment.DURABILITY, 1);
+
+ Object slot;
+ switch(slotName){
+ case "HEAD":
+ slot = de.steamwar.core.ProtocolWrapper.itemSlots[5];
+ break;
+ case "CHEST":
+ slot = de.steamwar.core.ProtocolWrapper.itemSlots[4];
+ break;
+ case "LEGS":
+ slot = de.steamwar.core.ProtocolWrapper.itemSlots[3];
+ break;
+ case "FEET":
+ slot = de.steamwar.core.ProtocolWrapper.itemSlots[2];
+ break;
+ case "OFFHAND":
+ slot = de.steamwar.core.ProtocolWrapper.itemSlots[1];
+ break;
+ case "MAINHAND":
+ default:
+ slot = de.steamwar.core.ProtocolWrapper.itemSlots[0];
+ }
+
+ execSync(() -> entities.get(entityId).setItem(slot, stack));
+ }
+
+ private void arrowSpawn() throws IOException {
+ int entityId = source.readInt();
+
+ execSync(() -> addREntity(entityId, new REntity(entityServer, EntityType.ARROW, Config.SpecSpawn)));
+ }
+
+ private void fireballSpawn() throws IOException {
+ int entityId = source.readInt();
+
+ execSync(() -> addREntity(entityId, new REntity(entityServer, EntityType.FIREBALL, Config.SpecSpawn)));
+ }
+
+ private void send(ChatMessageType type) throws IOException {
+ String message = source.readUTF();
+
+ BaseComponent[] text = TextComponent.fromLegacyText(message);
+ Bukkit.getOnlinePlayers().forEach(p -> de.steamwar.core.BountifulWrapper.impl.sendMessage(p, type, text));
+ }
+
+ private void countdown() throws IOException {
+ String message = source.readUTF();
+ int displaytime = source.readInt();
+ Message appendix = readMessage();
+
+ Bukkit.getOnlinePlayers().forEach(p -> Countdown.sendCountdownMessage(p, message, displaytime, appendix));
+ }
+
+ private void oldArenaInfo() throws IOException {
+ rotateZ = source.readBoolean() != Config.blueNegZ();
+ arenaMinY = Byte.toUnsignedInt(source.readByte());
+ arenaMinX = source.readInt();
+ arenaMinZ = source.readInt();
+ }
+
+ private void arenaInfo() throws IOException {
+ rotateZ = source.readBoolean() != Config.blueNegZ();
+ arenaMinX = source.readInt();
+ arenaMinY = source.readInt();
+ arenaMinZ = source.readInt();
+ }
+
+ private void shortBlock() throws IOException {
+ int x = Byte.toUnsignedInt(source.readByte()) + Config.ArenaRegion.getMinX();
+ int y = Byte.toUnsignedInt(source.readByte());
+ int z = Byte.toUnsignedInt(source.readByte()) + Config.ArenaRegion.getMinZ();
+ int blockState = source.readShort();
+
+ setBlock(x, y, z, blockState);
+ }
+
+ private void shortRelativeBlock() throws IOException {
+ int x = Byte.toUnsignedInt(source.readByte());
+ int y = Byte.toUnsignedInt(source.readByte());
+ int z = Byte.toUnsignedInt(source.readByte());
+ int blockState = source.readShort();
+
+ if(rotateZ) {
+ x = Config.ArenaRegion.getSizeX() - x;
+ z = Config.ArenaRegion.getSizeZ() - z;
+ }
+
+ setBlock(x + Config.ArenaRegion.getMinX(), y + Config.BluePasteRegion.getMinY(), z + Config.ArenaRegion.getMinZ(), blockState);
+ }
+
+ private void byteWorldHeightBlock() throws IOException {
+ int x = source.readInt() - arenaMinX;
+ int y = Byte.toUnsignedInt(source.readByte()) - arenaMinY;
+ int z = source.readInt() - arenaMinZ;
+ int blockState = source.readInt();
+
+ if(rotateZ) {
+ x = Config.ArenaRegion.getSizeX() - x;
+ z = Config.ArenaRegion.getSizeZ() - z;
+ }
+
+ setBlock(x + Config.ArenaRegion.getMinX(), y + Config.BluePasteRegion.getMinY(), z + Config.ArenaRegion.getMinZ(), blockState);
+ }
+
+ private void block() throws IOException {
+ int x = source.readInt() - arenaMinX;
+ int y = source.readShort() - arenaMinY;
+ int z = source.readInt() - arenaMinZ;
+ int blockState = source.readInt();
+
+ if(rotateZ) {
+ x = Config.ArenaRegion.getSizeX() - x;
+ z = Config.ArenaRegion.getSizeZ() - z;
+ }
+
+ setBlock(x + Config.ArenaRegion.getMinX(), y + Config.BluePasteRegion.getMinY(), z + Config.ArenaRegion.getMinZ(), blockState);
+ }
+
+ private void setBlock(int x, int y, int z, int blockState){
+ if(!Config.ArenaRegion.in2dRegion(x, z))
+ return; //Outside of the arena
+
+ execSync(() -> {
+ BlockIdWrapper.impl.setBlock(Config.world, x, y, z, TechHiderWrapper.ENABLED && hiddenBlockIds.contains(blockState) ? obfuscateWith : blockState);
+ FightSystem.getHullHider().blockUpdate(Config.world.getBlockAt(x, y, z), BlockIdWrapper.impl.idToMaterial(blockState));
+ });
+ }
+
+ private void particle() throws IOException {
+ double x = source.readDouble() - arenaMinX;
+ double y = source.readDouble() - arenaMinY;
+ double z = source.readDouble() - arenaMinZ;
+ String particleName = source.readUTF();
+
+ if(rotateZ) {
+ x = Config.ArenaRegion.getSizeX() - x;
+ z = Config.ArenaRegion.getSizeZ() - z;
+ }
+
+ double finalX = x;
+ double finalZ = z;
+ execSync(() -> BountifulWrapper.impl.spawnParticle(Config.world, particleName, finalX + Config.ArenaRegion.getMinX(), y + Config.BluePasteRegion.getMinY(), finalZ + Config.ArenaRegion.getMinZ()));
+ }
+
+ private void sound() throws IOException {
+ int rawX = source.readInt() - arenaMinX;
+ int y = source.readInt() - arenaMinY + Config.BluePasteRegion.getMinY();
+ int rawZ = source.readInt() - arenaMinZ;
+
+ if(rotateZ) {
+ rawX = Config.ArenaRegion.getSizeX() - rawX;
+ rawZ = Config.ArenaRegion.getSizeZ() - rawZ;
+ }
+
+ int x = rawX + Config.ArenaRegion.getMinX();
+ int z = rawZ + Config.ArenaRegion.getMinZ();
+
+ String soundName = source.readUTF();
+ String soundCategory = source.readUTF();
+
+ float volume = source.readFloat();
+ float pitch = source.readFloat();
+
+ Sound sound = Sound.valueOf(soundName);
+
+ execSync(() -> WorldOfColorWrapper.impl.playSound(new Location(Config.world, x, y, z), sound, soundCategory, volume, pitch));
+ }
+
+ private void soundAtPlayer() throws IOException {
+ String soundName = source.readUTF();
+
+ float volume = source.readFloat();
+ float pitch = source.readFloat();
+
+ Sound sound = Sound.valueOf(soundName);
+
+ execSync(() -> Fight.playSound(sound, volume, pitch));
+ }
+
+ private void pasteSchem(FightTeam team) throws IOException {
+ int schemId = source.readInt();
+ if(schemId == 0)
+ return;
+
+ execSync(() -> team.pasteSchem(SchematicNode.getSchematicNode(schemId)));
+ }
+
+ private void pasteEmbeddedSchem(FightTeam team) throws IOException {
+ int schemId = source.readInt();
+ Clipboard clipboard = SchematicData.clipboardFromStream(new FilterInputStream(source) {
+ @Override
+ public void close() {
+ // FAWE 1.12 calls close...
+ }
+ }, Core.getVersion() > 12);
+
+ execSync(() -> team.pasteSchem(schemId, clipboard));
+ }
+
+ private void teams() throws IOException {
+ int blueId = source.readInt();
+ int redId = source.readInt();
+
+ execSync(() -> {
+ pasteForTeam(blueId, Fight.getBlueTeam());
+ pasteForTeam(redId, Fight.getRedTeam());
+ });
+ }
+
+ private void pasteForTeam(int teamId, FightTeam fightTeam){
+ Team team = Team.get(teamId);
+ fightTeam.setPrefixAndName("§" + team.getTeamColor(), team.getTeamKuerzel());
+ fightTeam.pasteTeamName();
+ }
+
+ private void scoreboardTitle() throws IOException {
+ String title = source.readUTF();
+
+ FightScoreboard.getScoreboard().setTitle(title);
+ }
+
+ private void scoreboardData() throws IOException {
+ String key = source.readUTF();
+ int value = source.readInt();
+
+ FightScoreboard.getScoreboard().addScore(key, value);
+ }
+
+ private void bossBar() throws IOException {
+ double leftBlueProgress = source.readDouble();
+ double leftRedProgress = source.readDouble();
+ String leftBlueText = source.readUTF();
+ String leftRedText = source.readUTF();
+
+ FightUI.getInstance().setBossbar(leftBlueProgress, leftRedProgress, leftBlueText, leftRedText);
+ }
+
+ private void bossBarMessage() throws IOException {
+ double leftBlueProgress = source.readDouble();
+ double leftRedProgress = source.readDouble();
+ Message leftBlueText = readMessage();
+ Message leftRedText = readMessage();
+
+ FightUI.getInstance().setBossbar(leftBlueProgress, leftRedProgress, leftBlueText, leftRedText);
+ }
+
+ private void subtitle() throws IOException {
+ String subtitle = source.readUTF();
+
+ FightUI.addSubtitle("OLD_STRING", subtitle);
+ skipToSubtitle = false;
+ }
+
+ private void printWin() throws IOException {
+ String title = source.readUTF();
+ String subtitle = source.readUTF();
+
+ Bukkit.getOnlinePlayers().forEach(p -> {
+ p.resetTitle();
+ WorldOfColorWrapper.impl.sendTitle(p, title, subtitle, 5, 40, 5);
+ });
+ }
+
+ private void endReplay() {
+ HandlerList.unregisterAll(this);
+ entityServer.close();
+ entities.values().forEach(FightSystem.getHullHider()::despawnREntity);
+ entities.clear();
+
+ freezer.disable();
+ if(!Config.replayserver()) {
+ FightSystem.getMessage().broadcast("REPLAY_ENDS");
+ }
+ if (FightSystem.getLastWinner() != null) {
+ FightUI.getInstance().setBossbar(0.5, 0.5,
+ new Message("BAR_WIN", FightSystem.getLastWinner().getColoredName(), Fight.getBlueTeam().getColoredName(), Fight.getRedTeam().getColoredName(), ""),
+ new Message("BAR_WIN", FightSystem.getLastWinner().getColoredName(), Fight.getRedTeam().getColoredName(), Fight.getBlueTeam().getColoredName(), ""));
+ } else {
+ FightUI.getInstance().setBossbar(0.5, 0.5,
+ new Message("BAR_TIE", "", Fight.getBlueTeam().getColoredName(), Fight.getRedTeam().getColoredName()),
+ new Message("BAR_TIE", "", Fight.getRedTeam().getColoredName(), Fight.getBlueTeam().getColoredName()));
+ }
+ FightState.setFightState(FightState.SPECTATE);
+ currentProcessor = null;
+ task.cancel();
+ }
+
+ private void bow() throws IOException {
+ int entityId = source.readInt();
+ boolean drawn = source.readBoolean();
+ boolean offHand = source.readBoolean();
+
+ execSync(() -> entities.get(entityId).setBowDrawn(drawn, offHand));
+ }
+
+ private void damage() throws IOException {
+ int entityId = source.readInt();
+
+ execSync(() -> entities.get(entityId).showDamage());
+ }
+
+ private void fireTick() throws IOException {
+ int entityId = source.readInt();
+ boolean perma = source.readBoolean();
+
+ execSync(() -> entities.get(entityId).setOnFire(perma));
+ }
+
+ private void tick(){
+ execSync(entityServer::tick);
+
+ if(!source.async() && !skipToSubtitle)
+ tickFinished = true;
+ }
+
+ private void process(){
+ tickFinished = false;
+ try{
+ while(!source.isClosed() && !tickFinished){
+ int packetType = Byte.toUnsignedInt(source.readByte());
+
+ lastPackets.add(packetType);
+ if (lastPackets.size() > 10)
+ lastPackets.remove(0);
+
+ PacketParser parser = packetDecoder[packetType];
+ if(parser != null){
+ parser.process();
+ }else{
+ Bukkit.getLogger().log(Level.SEVERE, "Unknown packet " + packetType + " recieved, closing. LastPacket: " + Arrays.toString(lastPackets.toArray()));
+ source.close();
+ }
+ }
+ } catch (EOFException e) {
+ Bukkit.getLogger().log(Level.INFO, "The FightServer is offline");
+ source.close();
+ } catch(Exception e) {
+ Bukkit.getLogger().log(Level.WARNING, "Could not recieve packet, LastPacket: " + Arrays.toString(lastPackets.toArray()), e);
+ source.close();
+ }
+
+ if(source.isClosed()){
+ execSync(() -> Bukkit.getScheduler().runTask(FightSystem.getPlugin(), this::endReplay));
+ }
+ }
+
+ private Message readMessage() throws IOException {
+ String msg = source.readUTF();
+ List params = new ArrayList<>();
+
+ while(true) {
+ int type = source.readUnsignedByte();
+ switch(type) {
+ case 0x00:
+ return new Message(msg, params.toArray());
+ case 0x01:
+ params.add(source.readBoolean());
+ break;
+ case 0x02:
+ params.add(source.readByte());
+ break;
+ case 0x03:
+ params.add(source.readShort());
+ break;
+ case 0x04:
+ params.add(source.readInt());
+ break;
+ case 0x05:
+ params.add(source.readFloat());
+ break;
+ case 0x06:
+ params.add(source.readDouble());
+ break;
+ case 0x07:
+ params.add(source.readUTF());
+ break;
+ case 0x08:
+ params.add(readMessage());
+ break;
+ default:
+ throw new IOException("Unknown message param type " + type);
+ }
+ }
+ }
+
+ private interface PacketParser{
+ void process() throws IOException;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketSource.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketSource.java
new file mode 100644
index 00000000..e3d994cc
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketSource.java
@@ -0,0 +1,46 @@
+/*
+ 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 java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+
+public abstract class PacketSource extends DataInputStream {
+
+ protected PacketSource(InputStream inputStream){
+ super(inputStream);
+ }
+
+ @Override
+ public void close(){
+ try {
+ super.close();
+ } catch (IOException e) {
+ Bukkit.getLogger().log(Level.SEVERE, "IOException on disable", e);
+ }
+ }
+
+ abstract boolean isClosed();
+ abstract boolean async();
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/Recorder.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/Recorder.java
new file mode 100644
index 00000000..d57a29b9
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/Recorder.java
@@ -0,0 +1,382 @@
+/*
+ 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.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightPlayer;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.utils.BlockIdWrapper;
+import de.steamwar.fightsystem.utils.CraftbukkitWrapper;
+import de.steamwar.fightsystem.utils.Message;
+import de.steamwar.fightsystem.utils.SWSound;
+import de.steamwar.sql.NodeData;
+import de.steamwar.sql.SchematicNode;
+import de.steamwar.sql.SteamwarUser;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.util.Vector;
+
+import java.io.*;
+import java.util.logging.Level;
+
+public interface Recorder {
+ DataOutputStream getStream();
+
+ default void enable() {
+ GlobalRecorder.getInstance().add(this);
+
+ if(ArenaMode.Event.contains(Config.mode)){
+ teamIds(Config.EventTeamBlueID, Config.EventTeamRedID);
+ }
+
+ arenaInfo();
+ Fight.teams().forEach(this::enableTeam);
+ }
+
+ default void enableTeam(FightTeam team){
+ if(FightState.Schem.contains(FightState.getFightState())){
+ if(team.isBlue())
+ blueSchem(team.getSchematic());
+ else
+ redSchem(team.getSchematic());
+ }
+
+ if(FightState.AntiSpectate.contains(FightState.getFightState())){
+ for(FightPlayer player : team.getPlayers()){
+ if(player.isLiving()){
+ playerJoins(player.getEntity(), player.getUser());
+ }
+ }
+ }
+ }
+
+ default void disable() {
+ GlobalRecorder.getInstance().remove(this);
+ try {
+ getStream().close();
+ } catch (IOException e) {
+ Bukkit.getLogger().log(Level.SEVERE, "Could not close OutputStream", e);
+ }
+ }
+
+ /*
+ * 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
+ * BowSpanPacket (0x0a) + int EntityId + boolean start + hand
+ * PlayerDamagePacket (0x0b) + int EntityId
+ * SetOnFire (0x0c) + int EntityId + boolean perma
+ *
+ * DEPRECATED ArenaInfo (0x20) + bool blueNegZ + byte arenaY + int arenaMinX + int arenaMinZ
+ * ArenaInfo (0x21) + bool blueNegZ + int arenaMinX + int arenaMinY + int arenaMinZ
+ *
+ * DEPRECATED 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
+ * DEPRECATED ShortBlockPacket (0x33) + pos relative to ArenaMinX,ArenaMinZ byte, byte, byte + short BlockState
+ * SoundAtPlayerPacket (0x34) + string (soundType, soundCategory) + float volume, pitch
+ * ShortBlockPacket (0x35) + pos relative to ArenaMinX,BluePasteY,ArenaMinZ byte, byte, byte + short BlockState
+ * BlockPacket (0x36) + pos int, short, int + int BlockState
+ *
+ *
+ * DEPRECATED ChatPacket (0xa0) + String message
+ * DEPRECATED ActionBarPacket (0xa1) + String message
+ * DEPRECATED SystemPacket (0xa2) + String message
+ * CountdownPacket (0xa3) + String message, int displaytime, Message appendix
+ * ChatPacket (0xa4) + Message
+ * SystemPacket (0xa5) + Message
+ *
+ * BlueSchemPacket (0xb0) + int blueSchemId
+ * RedSchemPacket (0xb1) + int redSchemId
+ * TeamIDPacket (0xb2) + int blueTeamId, redTeamId
+ * BlueEmbeddedSchemPacket (0xb3) + int blueSchemId + gzipt NBT blob
+ * RedEmbeddedSchemPacket (0xb4) + int redSchemId + gzipt NBT blob
+ *
+ * DEPRECATED ScoreboardTitlePacket (0xc0) + String scoreboardTitle
+ * DEPRECATED ScoreboardDataPacket (0xc1) + String key + int value
+ * DEPRECATED BossBarPacket (0xc2) + double leftBlueProgress, leftRedProgress + String leftBlueText, leftRedText
+ * DEPRECATED SubtitlePacket (0xc3) + String subtitle
+ * DEPRECATED PrintWinPacket (0xc4) + String title, subtitle
+ * SubtitlePacket (0xc5) + Message
+ * WinPacket (0xc6) + byte team + Message subtitle
+ * BossBarPacket (0xc7) + double leftBlueProgress, leftRedProgress + Message leftBlueText, leftRedText
+ *
+ * CommentPacket (0xfe) + String comment
+ * TickPacket (0xff)
+ *
+ * Message-Format
+ * String message + byte prefixed Object-Params + byte 0x00
+ * 0x00: End of message
+ * 0x01: boolean following
+ * 0x02: byte following
+ * 0x03: short following
+ * 0x04: int following
+ * 0x05: float following
+ * 0x06: double following
+ * 0x07: String following
+ * 0x08: Message following
+ * */
+
+ default void playerJoins(LivingEntity p, SteamwarUser user){
+ write(0x00, p.getEntityId(), user.getId());
+ entityMoves(p);
+ }
+
+ default void entityMoves(Entity e){
+ Location location = e.getLocation();
+ write(0x01, e.getEntityId(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(), (byte) (CraftbukkitWrapper.impl.headRotation(e) * 256 / 360));
+ }
+
+ default void entityDespawns(Entity e){
+ write(0x02, e.getEntityId());
+ }
+
+ default void playerSneak(Player p, boolean sneaks){
+ write(0x03, p.getEntityId(), sneaks);
+ }
+
+ default void entityAnimation(Entity e, int animation){
+ write(0x04, e.getEntityId(), (byte)animation);
+ }
+
+ default void tntSpawn(Entity e){
+ write(0x05, e.getEntityId());
+ entityMoves(e);
+ entitySpeed(e);
+ }
+
+ default void entitySpeed(Entity e){
+ Vector velocity = e.getVelocity();
+ write(0x06, e.getEntityId(), velocity.getX(), velocity.getY(), velocity.getZ());
+ }
+
+ default void item(Player p, ItemStack item, String slot){
+ write(0x07, p.getEntityId(), "minecraft:" + item.getType().name().toLowerCase(), !item.getEnchantments().isEmpty(), slot);
+ }
+
+ default void arrowSpawn(Entity e){
+ write(0x08, e.getEntityId());
+ entityMoves(e);
+ entitySpeed(e);
+ if(e.getFireTicks() > 0)
+ setOnFire(e, true);
+ }
+
+ default void fireballSpawn(Entity e){
+ write(0x09, e.getEntityId());
+ entityMoves(e);
+ entitySpeed(e);
+ }
+
+ default void bowSpan(Entity e, boolean start, boolean offHand) {
+ write(0x0a, e.getEntityId(), start, offHand);
+ }
+
+ default void damageAnimation(Player p) {
+ write(0x0b, p.getEntityId());
+ }
+
+ default void setOnFire(Entity e, boolean perma) {
+ write(0x0c, e.getEntityId(), perma);
+ }
+
+ default void arenaInfo(){
+ write(0x21, Config.blueNegZ(), Config.ArenaRegion.getMinX(), Config.BluePasteRegion.getMinY(), Config.ArenaRegion.getMinZ());
+ }
+
+ default void blockChange(Block block){
+ int blockState = BlockIdWrapper.impl.blockToId(block);
+
+ int shortX = block.getX() - Config.ArenaRegion.getMinX();
+ int shortY = block.getY() - Config.BluePasteRegion.getMinY();
+ int shortZ = block.getZ() - Config.ArenaRegion.getMinZ();
+ if((short)blockState == blockState && shortX >= 0 && shortX < 256 && shortY >= 0 && shortZ >= 0 && shortZ < 256){
+ //Short block packet
+ write(0x35, (byte)shortX, (byte)shortY, (byte)shortZ, (short)blockState);
+ }else{
+ //Block packet
+ write(0x36, block.getX(), (short)block.getY(), block.getZ(), blockState);
+ }
+ }
+
+ default void particle(double x, double y, double z, String particleType){
+ write(0x31, x, y, z, particleType);
+ }
+
+ default void sound(int x, int y, int z, SWSound soundType, String soundCategory, float volume, float pitch){
+ write(0x32, x, y, z, soundType.getSound().name(), soundCategory, volume, pitch);
+ }
+
+ default void soundAtPlayer(String soundType, float volume, float pitch){
+ write(0x34, soundType, volume, pitch);
+ }
+
+ default void countdown(String message, int displaytime, Message appendix) {
+ write(0xa3, message, displaytime, appendix);
+ }
+
+ default void chat(String msg, Object... params) {
+ write(0xa4, new Message(msg, params));
+ }
+
+ default void system(String msg, Object... params) {
+ write(0xa5, new Message(msg, params));
+ }
+
+ default void teamIds(int blueTeamId, int redTeamId) {
+ write(0xb2, blueTeamId, redTeamId);
+ }
+
+ default void blueSchem(int schemId) {
+ schem(0xb3, 0xb0, schemId);
+ }
+
+ default void redSchem(int schemId) {
+ schem(0xb4, 0xb1, schemId);
+ }
+
+ default void schem(int embedId, int noEmbedId, int schemId){
+ if(schemId == 0) {
+ write(noEmbedId, schemId);
+ return;
+ }
+
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ try{
+ copy(NodeData.get(SchematicNode.getSchematicNode(schemId)).schemData(), buffer);
+ }catch (EOFException e) {
+ Bukkit.getLogger().log(Level.INFO, "EOFException ignored");
+ } catch (IOException e) {
+ Bukkit.getLogger().log(Level.SEVERE, "Could not embed schematic", e);
+ disable();
+ return;
+ }
+
+ write(embedId, schemId, buffer.toByteArray());
+ }
+
+ static void copy(InputStream input, OutputStream output) throws IOException {
+ int bytes;
+ for(byte[] buffer = new byte[8192]; (bytes = input.read(buffer)) > 0;) {
+ output.write(buffer, 0, bytes);
+ }
+ }
+
+ default void bossBar(double leftBlueProgress, double leftRedProgress, Message leftBlueText, Message leftRedText) {
+ write(0xc7, leftBlueProgress, leftRedProgress, leftBlueText, leftRedText);
+ }
+
+ default void subtitle(Message subtitle) {
+ write(0xc5, subtitle);
+ }
+
+ default void winMessage(FightTeam team, String subtitle, Object... params) {
+ byte bTeam = 0x00;
+ if(team != null) {
+ bTeam = (byte) (team.isBlue() ? 0x01 : 0x02);
+ }
+ write(0xc6, bTeam, new Message(subtitle, params));
+ }
+
+ default void tick(){
+ write(0xff);
+ }
+
+ default void write(int id, Object... objects){
+ DataOutputStream stream = getStream();
+ synchronized (stream) {
+ try {
+ stream.writeByte(id);
+ for(Object o : objects) {
+ writeObject(stream, o);
+ }
+ stream.flush();
+ } catch (IOException e) {
+ Bukkit.getLogger().log(Level.SEVERE, "Could not write", e);
+ disable();
+ }
+ }
+ }
+
+ default void writeObject(DataOutputStream stream, Object o) throws IOException {
+ if(o instanceof Boolean)
+ stream.writeBoolean((Boolean)o);
+ else if(o instanceof Byte)
+ stream.writeByte((Byte)o);
+ else if(o instanceof Short)
+ stream.writeShort((Short)o);
+ else if(o instanceof Integer)
+ stream.writeInt((Integer)o);
+ else if(o instanceof Float)
+ stream.writeFloat((Float)o);
+ else if(o instanceof Double)
+ stream.writeDouble((Double)o);
+ else if(o instanceof String)
+ stream.writeUTF((String)o);
+ else if(o instanceof byte[])
+ stream.write((byte[])o);
+ else if(o instanceof Message)
+ writeMessage(stream, (Message) o);
+ else
+ throw new SecurityException("Undefined write for: " + o.getClass().getName());
+ }
+
+ default void writeMessage(DataOutputStream stream, Message message) throws IOException {
+ stream.writeUTF(message.getMsg());
+ for(Object o : message.getParams()) {
+ if(o instanceof Boolean)
+ stream.writeByte(0x01);
+ else if(o instanceof Byte)
+ stream.writeByte(0x02);
+ else if(o instanceof Short)
+ stream.writeByte(0x03);
+ else if(o instanceof Integer)
+ stream.writeByte(0x04);
+ else if(o instanceof Float)
+ stream.writeByte(0x05);
+ else if(o instanceof Double)
+ stream.writeByte(0x06);
+ else if(o instanceof String)
+ stream.writeByte(0x07);
+ else if(o instanceof Message)
+ stream.writeByte(0x08);
+ else
+ throw new SecurityException("Undefined message serialization for: " + o.getClass().getName());
+ writeObject(stream, o);
+ }
+ stream.writeByte(0x00);
+ }
+
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/FightState.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/FightState.java
new file mode 100644
index 00000000..1d5acf35
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/FightState.java
@@ -0,0 +1,94 @@
+/*
+ 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.states;
+
+import lombok.Getter;
+
+import java.util.*;
+
+public enum FightState {
+ PRE_LEADER_SETUP,
+ PRE_SCHEM_SETUP,
+ POST_SCHEM_SETUP,
+ PRE_RUNNING,
+ RUNNING,
+ SPECTATE;
+
+ public static final Set All = Collections.unmodifiableSet(EnumSet.allOf(FightState.class));
+
+ public static final Set PreLeaderSetup = Collections.unmodifiableSet(EnumSet.of(PRE_LEADER_SETUP));
+ public static final Set PreSchemSetup = Collections.unmodifiableSet(EnumSet.of(PRE_SCHEM_SETUP));
+ public static final Set PostSchemSetup = Collections.unmodifiableSet(EnumSet.of(POST_SCHEM_SETUP));
+ public static final Set PreRunning = Collections.unmodifiableSet(EnumSet.of(PRE_RUNNING));
+ public static final Set Running = Collections.unmodifiableSet(EnumSet.of(RUNNING));
+ public static final Set Spectate = Collections.unmodifiableSet(EnumSet.of(SPECTATE));
+
+ public static final Set Setup = Collections.unmodifiableSet(EnumSet.of(PRE_LEADER_SETUP, PRE_SCHEM_SETUP, POST_SCHEM_SETUP));
+ public static final Set Ingame = Collections.unmodifiableSet(EnumSet.of(PRE_RUNNING, RUNNING));
+ public static final Set TeamFix = Collections.unmodifiableSet(EnumSet.of(PRE_RUNNING, RUNNING, SPECTATE));
+ public static final Set Schem = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(PRE_LEADER_SETUP, PRE_SCHEM_SETUP)));
+ public static final Set Recording = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(PRE_LEADER_SETUP, PRE_SCHEM_SETUP, SPECTATE)));
+ public static final Set AntiRunning = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(RUNNING)));
+ public static final Set AntiIngame = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(PRE_RUNNING, RUNNING)));
+ public static final Set AntiSpectate = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(SPECTATE)));
+
+ private static final Map stateDependentFeatures = new HashMap<>();
+ @Getter
+ private static FightState fightState = PRE_LEADER_SETUP;
+
+ public static void registerStateDependent(IStateDependent stateDependent){
+ if(stateDependent.enabled().isEmpty())
+ return;
+ boolean enabled = stateDependent.enabled().contains(fightState);
+ stateDependentFeatures.put(stateDependent, enabled);
+ if(enabled)
+ stateDependent.enable();
+ }
+
+ public static void setFightState(FightState state){
+ fightState = state;
+
+ for(Map.Entry feature : stateDependentFeatures.entrySet()){
+ //Enable feature if should be enabled and currently disabled
+ if(feature.getKey().enabled().contains(fightState) && !feature.getValue()){
+ feature.getKey().enable();
+ feature.setValue(true);
+ }
+
+ //Disable feature if should be disabled and currently enabled
+ if(!feature.getKey().enabled().contains(fightState) && feature.getValue()){
+ feature.getKey().disable();
+ feature.setValue(false);
+ }
+ }
+ }
+
+ public static boolean setup(){
+ return Setup.contains(fightState);
+ }
+
+ public static boolean ingame(){
+ return Ingame.contains(fightState);
+ }
+
+ public static boolean infight(){
+ return fightState == RUNNING;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/IStateDependent.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/IStateDependent.java
new file mode 100644
index 00000000..1d9f170e
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/IStateDependent.java
@@ -0,0 +1,40 @@
+/*
+ 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.states;
+
+import java.util.Set;
+
+public interface IStateDependent {
+
+ /**
+ * @return returns a set containing
+ */
+ Set enabled();
+
+ /**
+ * Enables the state dependent object
+ */
+ void enable();
+
+ /**
+ * Disables the state dependent object
+ */
+ void disable();
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/OneShotStateDependent.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/OneShotStateDependent.java
new file mode 100644
index 00000000..610168f3
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/OneShotStateDependent.java
@@ -0,0 +1,59 @@
+/*
+ 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.states;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.winconditions.Winconditions;
+
+import java.util.Set;
+
+public class OneShotStateDependent extends StateDependent{
+
+ private final Runnable runnable;
+
+ public OneShotStateDependent(Set mode, Set states, Runnable runnable) {
+ super(mode, states);
+ this.runnable = runnable;
+ register();
+ }
+
+ public OneShotStateDependent(Winconditions wincondition, Set states, Runnable runnable) {
+ super(Config.ActiveWinconditions.contains(wincondition), states);
+ this.runnable = runnable;
+ register();
+ }
+
+ public OneShotStateDependent(boolean active, Set states, Runnable runnable) {
+ super(active, states);
+ this.runnable = runnable;
+ register();
+ }
+
+ @Override
+ public void enable() {
+ runnable.run();
+ }
+
+ @Override
+ public void disable() {
+ //Do nothing, oneshot
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependent.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependent.java
new file mode 100644
index 00000000..e20c4fbe
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependent.java
@@ -0,0 +1,55 @@
+/*
+ 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.states;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.winconditions.Winconditions;
+
+import java.util.Set;
+
+public abstract class StateDependent implements IStateDependent {
+
+ private final Set enabled;
+ private final boolean register;
+
+ protected StateDependent(Winconditions wincondition, Set states){
+ this(Config.ActiveWinconditions.contains(wincondition), states);
+ }
+
+ protected StateDependent(Set mode, Set states){
+ this(mode.contains(Config.mode), states);
+ }
+
+ protected StateDependent(boolean enabled, Set states){
+ this.enabled = states;
+ this.register = enabled;
+ }
+
+ public void register(){
+ if(register)
+ FightState.registerStateDependent(this);
+ }
+
+ @Override
+ public Set enabled() {
+ return enabled;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependentCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependentCommand.java
new file mode 100644
index 00000000..2d25b68d
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependentCommand.java
@@ -0,0 +1,58 @@
+/*
+ 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.states;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.FightSystem;
+import net.md_5.bungee.api.ChatMessageType;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.PluginCommand;
+
+import java.util.Set;
+
+public class StateDependentCommand extends StateDependent {
+
+ private static final CommandExecutor unavailable = (sender, cmd, s, strings) -> {
+ FightSystem.getMessage().sendPrefixless("COMMAND_CURRENTLY_UNAVAILABLE", sender, ChatMessageType.ACTION_BAR);
+ return false;
+ };
+
+ private final PluginCommand command;
+ private final CommandExecutor executor;
+
+ public StateDependentCommand(Set mode, Set states, String name, CommandExecutor executor) {
+ super(mode, states);
+ this.executor = executor;
+ this.command = FightSystem.getPlugin().getCommand(name);
+ assert command != null;
+ disable();
+ register();
+ }
+
+ @Override
+ public void enable() {
+ command.setExecutor(executor);
+ }
+
+ @Override
+ public void disable() {
+ command.setExecutor(unavailable);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependentCountdown.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependentCountdown.java
new file mode 100644
index 00000000..4bdbc093
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependentCountdown.java
@@ -0,0 +1,71 @@
+/*
+ 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.states;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.countdown.Countdown;
+import de.steamwar.fightsystem.winconditions.Winconditions;
+
+import java.util.Set;
+
+public class StateDependentCountdown extends StateDependent {
+
+ private static Countdown mainCountdown = null;
+
+ public static Countdown getMainCountdown() {
+ return mainCountdown;
+ }
+
+ private final Countdown countdown;
+
+ public StateDependentCountdown(Set mode, Set states, Countdown countdown) {
+ super(mode, states);
+ this.countdown = countdown;
+ register();
+ }
+
+ public StateDependentCountdown(Winconditions wincondition, Set states, Countdown countdown) {
+ this(Config.ActiveWinconditions.contains(wincondition), states, countdown);
+ }
+
+ public StateDependentCountdown(boolean active, Set states, Countdown countdown) {
+ super(active, states);
+ this.countdown = countdown;
+ register();
+ }
+
+ public Countdown getCountdown(){
+ return countdown;
+ }
+
+ @Override
+ public void enable() {
+ mainCountdown = countdown;
+ countdown.enable();
+ }
+
+ @Override
+ public void disable() {
+ countdown.disable();
+ if(mainCountdown == countdown)
+ mainCountdown = null;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependentListener.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependentListener.java
new file mode 100644
index 00000000..f5cfffc8
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependentListener.java
@@ -0,0 +1,63 @@
+/*
+ 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.states;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.winconditions.Winconditions;
+import org.bukkit.Bukkit;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.Listener;
+
+import java.util.Set;
+
+public class StateDependentListener extends StateDependent{
+
+ private final Listener listener;
+
+ public StateDependentListener(Winconditions wincondition, Set states, Listener listener){
+ super(Config.ActiveWinconditions.contains(wincondition), states);
+ this.listener = listener;
+ register();
+ }
+
+ public StateDependentListener(boolean enabled, Set states, Listener listener){
+ super(enabled, states);
+ this.listener = listener;
+ register();
+ }
+
+ public StateDependentListener(Set mode, Set states, Listener listener) {
+ super(mode, states);
+ this.listener = listener;
+ register();
+ }
+
+ @Override
+ public void enable() {
+ Bukkit.getPluginManager().registerEvents(listener, FightSystem.getPlugin());
+ }
+
+ @Override
+ public void disable() {
+ HandlerList.unregisterAll(listener);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependentTask.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependentTask.java
new file mode 100644
index 00000000..950fbcb8
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/states/StateDependentTask.java
@@ -0,0 +1,69 @@
+/*
+ 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.states;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.winconditions.Winconditions;
+import org.bukkit.Bukkit;
+import org.bukkit.scheduler.BukkitTask;
+
+import java.util.Set;
+
+public class StateDependentTask extends StateDependent {
+
+ private final Runnable runnable;
+ private final long delay;
+ private final long period;
+
+ private BukkitTask task = null;
+
+ public StateDependentTask(Winconditions wincondition, Set states, Runnable runnable, long delay, long period){
+ this(Config.ActiveWinconditions.contains(wincondition), states, runnable, delay, period);
+ }
+
+ public StateDependentTask(boolean enabled, Set states, Runnable runnable, long delay, long period){
+ super(enabled, states);
+ this.runnable = runnable;
+ this.delay = delay;
+ this.period = period;
+ register();
+ }
+
+ public StateDependentTask(Set mode, Set states, Runnable runnable, long delay, long period) {
+ super(mode, states);
+ this.runnable = runnable;
+ this.delay = delay;
+ this.period = period;
+ register();
+ }
+
+ @Override
+ public void enable() {
+ task = Bukkit.getScheduler().runTaskTimer(FightSystem.getPlugin(), runnable, delay, period);
+ }
+
+ @Override
+ public void disable() {
+ task.cancel();
+ }
+
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BlockIdWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BlockIdWrapper.java
new file mode 100644
index 00000000..a6b825db
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BlockIdWrapper.java
@@ -0,0 +1,46 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.core.VersionDependent;
+import de.steamwar.fightsystem.FightSystem;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+
+public interface BlockIdWrapper {
+ Class> worldServer = Reflection.getClass("{nms.server.level}.WorldServer");
+ Reflection.MethodInvoker getWorldHandle = Reflection.getTypedMethod(Reflection.getClass("{obc}.CraftWorld"), "getHandle", worldServer);
+
+ Class> craftPlayer = Reflection.getClass("{obc}.entity.CraftPlayer");
+ Class> entityPlayer = Reflection.getClass("{nms.server.level}.EntityPlayer");
+ Reflection.MethodInvoker getPlayer = Reflection.getTypedMethod(craftPlayer, "getHandle", entityPlayer);
+
+ BlockIdWrapper impl = VersionDependent.getVersionImpl(FightSystem.getPlugin());
+
+ Material idToMaterial(int blockState);
+ int blockToId(Block block);
+ void setBlock(World world, int x, int y, int z, int blockState);
+
+ void trackEntity(Player player, int entity);
+ void untrackEntity(Player player, int entity);
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BountifulWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BountifulWrapper.java
new file mode 100644
index 00000000..3e96c85a
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BountifulWrapper.java
@@ -0,0 +1,49 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.core.VersionDependent;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.fight.FightTeam;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Listener;
+import org.bukkit.scoreboard.Team;
+
+public interface BountifulWrapper {
+ BountifulWrapper impl = VersionDependent.getVersionImpl(FightSystem.getPlugin());
+
+ boolean mainHand(Object packet);
+ boolean bowInHand(boolean mainHand, Player p);
+
+ void setAttackSpeed(Player player);
+
+ void setNametagVisibility(Team team);
+
+ Listener newDenyArrowPickupListener();
+ Listener newDenyHandSwapListener();
+
+ void recordHandItems(Player player);
+ Listener newHandSwapRecorder();
+
+ void spawnParticle(World world, String particleName, double x, double y, double z);
+
+ void sendBar(Player player, FightTeam team, double progress, String text);
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BungeeFightInfo.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BungeeFightInfo.java
new file mode 100644
index 00000000..6ffa0e98
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BungeeFightInfo.java
@@ -0,0 +1,64 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCountdown;
+import de.steamwar.fightsystem.states.StateDependentTask;
+import de.steamwar.network.NetworkSender;
+import de.steamwar.network.packets.common.FightInfoPacket;
+import de.steamwar.sql.SteamwarUser;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+import java.util.stream.Collectors;
+
+public class BungeeFightInfo {
+
+ public BungeeFightInfo() {
+ new StateDependentTask(ArenaMode.NotOnBau, FightState.All, this::send, 20, 20);
+ }
+
+ private void send() {
+ Player player = Bukkit.getOnlinePlayers().stream().findAny().orElse(null);
+ if(player == null)
+ return;
+
+ NetworkSender.send(new FightInfoPacket(
+ Config.world.getName(),
+ Config.SchematicType.toDB(),
+ "",
+ Fight.getBlueTeam().getColoredName(),
+ Fight.getRedTeam().getColoredName(),
+ FightState.getFightState().name(),
+ StateDependentCountdown.getMainCountdown() != null ? StateDependentCountdown.getMainCountdown().getTimeLeft() : 0,
+ Fight.getBlueTeam().getLeader() != null ? Fight.getBlueTeam().getLeader().getUser().getId() : 0,
+ Fight.getRedTeam().getLeader() != null ? Fight.getRedTeam().getLeader().getUser().getId() : 0,
+ Fight.getBlueTeam().getSchematic(),
+ Fight.getRedTeam().getSchematic(),
+ Fight.getBlueTeam().getPlayers().stream().map(p -> p.getUser().getId()).collect(Collectors.toList()),
+ Fight.getRedTeam().getPlayers().stream().map(p -> p.getUser().getId()).collect(Collectors.toList()),
+ Bukkit.getOnlinePlayers().stream().filter(p -> Fight.getPlayerTeam(p) == null).map(p -> SteamwarUser.get(p.getUniqueId()).getId()).collect(Collectors.toList())
+ ));
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/ColorConverter.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/ColorConverter.java
new file mode 100644
index 00000000..32f44510
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/ColorConverter.java
@@ -0,0 +1,55 @@
+/*
+ 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.utils;
+
+import org.bukkit.ChatColor;
+import org.bukkit.DyeColor;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public class ColorConverter {
+ private ColorConverter(){}
+
+ private static final Map chat2dye = new EnumMap<>(ChatColor.class);
+
+ static{
+ chat2dye.put(ChatColor.WHITE, DyeColor.WHITE);
+ chat2dye.put(ChatColor.GOLD, DyeColor.ORANGE);
+ chat2dye.put(ChatColor.LIGHT_PURPLE, DyeColor.MAGENTA);
+ chat2dye.put(ChatColor.BLUE, DyeColor.LIGHT_BLUE);
+ chat2dye.put(ChatColor.YELLOW, DyeColor.YELLOW);
+ chat2dye.put(ChatColor.GREEN, DyeColor.LIME);
+ chat2dye.put(ChatColor.RED, DyeColor.RED);
+ chat2dye.put(ChatColor.DARK_GRAY, DyeColor.GRAY);
+ chat2dye.put(ChatColor.DARK_AQUA, DyeColor.CYAN);
+ chat2dye.put(ChatColor.DARK_PURPLE, DyeColor.PURPLE);
+ chat2dye.put(ChatColor.DARK_BLUE, DyeColor.BLUE);
+ chat2dye.put(ChatColor.AQUA, DyeColor.CYAN);
+ chat2dye.put(ChatColor.DARK_GREEN, DyeColor.GREEN);
+ chat2dye.put(ChatColor.DARK_RED, DyeColor.RED);
+ chat2dye.put(ChatColor.BLACK, DyeColor.BLACK);
+ chat2dye.put(ChatColor.GRAY, FlatteningWrapper.impl.getSilver());
+ }
+
+ public static DyeColor chat2dye(ChatColor color){
+ return chat2dye.get(color);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper.java
new file mode 100644
index 00000000..242119f5
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper.java
@@ -0,0 +1,36 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.core.VersionDependent;
+import de.steamwar.fightsystem.FightSystem;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+
+import java.util.stream.Stream;
+
+public interface CraftbukkitWrapper {
+ CraftbukkitWrapper impl = VersionDependent.getVersionImpl(FightSystem.getPlugin());
+
+ void resetChunk(World world, World backup, int x, int z);
+ float headRotation(Entity e);
+
+ Stream> entityIterator();
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/EnterHandler.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/EnterHandler.java
new file mode 100644
index 00000000..042efba0
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/EnterHandler.java
@@ -0,0 +1,63 @@
+/*
+ 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.utils;
+
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightPlayer;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.IStateDependent;
+
+import java.util.Set;
+
+public class EnterHandler implements IStateDependent {
+ private static final Set enabled = FightState.Running;
+
+ public EnterHandler(){
+ FightState.registerStateDependent(this);
+ }
+
+ @Override
+ public Set enabled() {
+ return enabled;
+ }
+
+ @Override
+ public void enable() {
+ Fight.teams().forEach(this::registerTeam);
+ }
+
+ @Override
+ public void disable() {
+ Fight.teams().forEach(this::unregisterTeam);
+ }
+
+ private void registerTeam(FightTeam team){
+ for(FightPlayer fp : team.getPlayers()){
+ fp.startEnternCountdown(null);
+ }
+ }
+
+ private void unregisterTeam(FightTeam team){
+ for(FightPlayer fp : team.getPlayers()){
+ fp.stopEnternCountdown();
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FightStatistics.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FightStatistics.java
new file mode 100644
index 00000000..1f46a3a8
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FightStatistics.java
@@ -0,0 +1,154 @@
+/*
+ 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.utils;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.countdown.Countdown;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightPlayer;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.record.FileRecorder;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.OneShotStateDependent;
+import de.steamwar.fightsystem.winconditions.Wincondition;
+import de.steamwar.network.NetworkSender;
+import de.steamwar.network.packets.common.FightEndsPacket;
+import de.steamwar.sql.Replay;
+import de.steamwar.sql.SchematicNode;
+import de.steamwar.sql.SteamwarUser;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+import static de.steamwar.sql.Fight.create;
+
+public class FightStatistics {
+
+ @Getter
+ private static boolean unranked = false;
+
+ public static void unrank() {
+ unranked = true;
+ FightUI.addSubtitle("UI_UNRANKED");
+ }
+
+ private Timestamp starttime = Timestamp.from(Instant.now());
+
+ public FightStatistics() {
+ new OneShotStateDependent(ArenaMode.SeriousFight, FightState.Running, this::enable);
+ new OneShotStateDependent(ArenaMode.Event, FightState.Spectate, this::setEventResult);
+ new OneShotStateDependent(ArenaMode.SeriousFight, FightState.Spectate, () -> Bukkit.getScheduler().runTask(FightSystem.getPlugin(), this::disable));
+ }
+
+ private void enable() {
+ starttime = Timestamp.from(Instant.now());
+ }
+
+ private void setEventResult() {
+ if (FightSystem.getLastWinner() == null)
+ Config.EventKampf.setErgebnis(0);
+ else if (FightSystem.getLastWinner().isBlue())
+ Config.EventKampf.setErgebnis(1);
+ else
+ Config.EventKampf.setErgebnis(2);
+ }
+
+ private void disable() {
+ FightTeam winner = FightSystem.getLastWinner();
+ String windescription = FightSystem.getLastWinreason();
+ String gameMode = Config.SchematicType.toDB();
+ Instant endTime = Instant.now();
+
+ int blueLeader = getLeader(Fight.getBlueTeam());
+ int redLeader = getLeader(Fight.getRedTeam());
+
+ int win = 0;
+ if (winner == Fight.getBlueTeam()) {
+ win = 1;
+ } else if (winner == Fight.getRedTeam()) {
+ win = 2;
+ }
+
+ Integer blueSchem;
+ Integer redSchem;
+ try {
+ blueSchem = SchematicNode.getSchematicNode(Fight.getBlueTeam().getSchematic()).getId();
+ } catch (NullPointerException e) {
+ blueSchem = null;
+ }
+ try {
+ redSchem = SchematicNode.getSchematicNode(Fight.getRedTeam().getSchematic()).getId();
+ } catch (NullPointerException e) {
+ redSchem = null;
+ }
+
+ int remainingTime = 0;
+ Countdown timeOverCountdown = Wincondition.getTimeOverCountdown();
+ if (timeOverCountdown != null) {
+ remainingTime = timeOverCountdown.getTimeLeft();
+ }
+
+ try {
+ int fightId = create(gameMode, Config.world.getName(), starttime, remainingTime,
+ blueLeader, redLeader, blueSchem, redSchem, win, windescription);
+
+ for (FightPlayer fp : Fight.getBlueTeam().getPlayers())
+ savePlayerStats(fp, fightId);
+ for (FightPlayer fp : Fight.getRedTeam().getPlayers())
+ savePlayerStats(fp, fightId);
+
+ if (ArenaMode.Event.contains(Config.mode)) {
+ Config.EventKampf.setFight(fightId);
+ }
+
+ try {
+ Replay.save(fightId, FileRecorder.getFile());
+ } catch (Exception e) {
+ Bukkit.getLogger().log(Level.INFO, "Failed to save replay", e);
+ }
+ } catch (Exception e) {
+ Bukkit.getLogger().log(Level.SEVERE, "Failed to save statistics", e);
+ }
+
+ if (!Bukkit.getOnlinePlayers().isEmpty() && !unranked) {
+ NetworkSender.send(new FightEndsPacket((byte) win, blueSchem == null ? 0 : blueSchem, redSchem == null ? 0 : redSchem, Fight.getBlueTeam().getPlayers().stream().map(FightPlayer::getUser).map(SteamwarUser::getId).collect(Collectors.toList()), Fight.getRedTeam().getPlayers().stream().map(FightPlayer::getUser).map(SteamwarUser::getId).collect(Collectors.toList()), gameMode, (int)(endTime.getEpochSecond() - starttime.toInstant().getEpochSecond())));
+ }
+
+ unranked = false;
+ }
+
+ private int getLeader(FightTeam team) {
+ if (team.getLeader() != null)
+ return team.getLeader().getUser().getId();
+ else if (team.getDesignatedLeader() != null)
+ return SteamwarUser.get(team.getDesignatedLeader()).getId();
+ return 0;
+ }
+
+ private void savePlayerStats(FightPlayer fp, int fightId) {
+ de.steamwar.sql.FightPlayer.create(fightId, fp.getUser().getId(), fp.getTeam().isBlue(), fp.getKit().getName(), fp.getKills(), !fp.isLiving());
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FightUI.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FightUI.java
new file mode 100644
index 00000000..122b6902
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FightUI.java
@@ -0,0 +1,238 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.core.TPSWatcher;
+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.record.GlobalRecorder;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.OneShotStateDependent;
+import de.steamwar.fightsystem.states.StateDependentCountdown;
+import de.steamwar.fightsystem.states.StateDependentTask;
+import de.steamwar.fightsystem.winconditions.Wincondition;
+import lombok.Getter;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.logging.Level;
+import java.util.stream.Collectors;
+
+public class FightUI {
+
+ @Getter
+ private static FightUI instance;
+
+ public FightUI() {
+ new StateDependentTask(ArenaMode.AntiReplay, FightState.All, this::update, 20, 20);
+ new StateDependentTask(ArenaMode.All, FightState.All, this::sendToPlayers, 5, 5);
+ new StateDependentTask(ArenaMode.All, FightState.All, this::tpsWatcher, 20, 20);
+ new OneShotStateDependent(ArenaMode.AntiReplay, FightState.PreRunning, () -> addSubtitle("UI_PRE_RUNNING"));
+ new OneShotStateDependent(ArenaMode.AntiReplay, FightState.Running, () -> addSubtitle("UI_RUNNING"));
+ instance = this;
+ }
+
+ private void update() {
+ final FightTeam blue = Fight.getBlueTeam();
+ final FightTeam red = Fight.getRedTeam();
+
+ double blueHearts = blue.getCurrentHearts();
+ double redHearts = red.getCurrentHearts();
+ double totalHearts = blueHearts + redHearts;
+ if(redHearts == 0.0 || blueHearts == 0.0) {
+ blueHearts = 0.5;
+ redHearts = 0.5;
+ totalHearts = 1.0;
+ }
+
+ int timeLeft = StateDependentCountdown.getMainCountdown() != null ? StateDependentCountdown.getMainCountdown().getTimeLeft() : 0;
+ String time = "§7" + (timeLeft/60) + "§8:§7" + (timeLeft%60<10 ? "0" : "") + (timeLeft%60);
+ String bluePlayers = blue.getPrefix() + blue.getAlivePlayers() + "§8/§7" + blue.getPlayerCount();
+ String redPlayers = red.getPrefix() + red.getAlivePlayers() + "§8/§7" + red.getPlayerCount();
+ List redAdditional = Wincondition.getPrintableWinconditions().stream().map(w -> w.getDisplay(red)).collect(Collectors.toList());
+ List blueAdditional = Wincondition.getPrintableWinconditions().stream().map(w -> w.getDisplay(blue)).collect(Collectors.toList());
+
+ Generator generator;
+ switch(FightState.getFightState()){
+ case PRE_LEADER_SETUP:
+ generator = (l, r, lP, rP, lW, rW) -> new Message("BAR_PRE_LEADER");
+ break;
+ case PRE_SCHEM_SETUP:
+ generator = (l, r, lP, rP, lW, rW) -> new Message("BAR_PRE_SCHEM", time, l.getColoredName(), r.getColoredName());
+ break;
+ case POST_SCHEM_SETUP:
+ generator = (l, r, lP, rP, lW, rW) -> new Message("BAR_PREPARE", time, l.getColoredName(), r.getColoredName(), lP, rP);
+ break;
+ case PRE_RUNNING:
+ generator = (l, r, lP, rP, lW, rW) -> new Message("BAR_PRE_RUNNING", time, l.getColoredName(), r.getColoredName(), lP, rP);
+ break;
+ case RUNNING:
+ generator = (l, r, lP, rP, lW, rW) -> {
+ List params = new ArrayList<>();
+ params.add(time);
+ params.add(l.getColoredName());
+ params.add(r.getColoredName());
+ params.add(lP);
+ params.add(rP);
+ for(int i = 0; i < lW.size(); i++) {
+ params.add(lW.get(i));
+ params.add(rW.get(i));
+ }
+ return new Message("BAR_RUNNING" + lW.size(), params.toArray());
+ };
+ break;
+ case SPECTATE:
+ default:
+ generator = (l, r, lP, rP, lW, rW) -> {
+ if(FightSystem.getLastWinner() == null)
+ return new Message("BAR_TIE", time, l.getColoredName(), r.getColoredName());
+ else
+ return new Message("BAR_WIN", time, l.getColoredName(), r.getColoredName(), FightSystem.getLastWinner().getColoredName());
+ };
+ break;
+ }
+
+ setBossbar(
+ blueHearts / totalHearts, redHearts / totalHearts,
+ generator.gen(blue, red, bluePlayers, redPlayers, blueAdditional, redAdditional),
+ generator.gen(red, blue, redPlayers, bluePlayers, redAdditional, blueAdditional)
+ );
+ }
+
+ private interface Generator {
+ Message gen(FightTeam l, FightTeam r, String lPlayers, String rPlayers, List lWinconditions, List rWinconditions);
+ }
+
+ public void setBossbar(double leftBlueProgress, double leftRedProgress, String leftBlueText, String leftRedText) {
+ setBossbar(leftBlueProgress, leftRedProgress, new Message("OLD_STRING", leftBlueText), new Message("OLD_STRING", leftRedText));
+ }
+
+ public void setBossbar(double leftBlueProgress, double leftRedProgress, Message leftBlueText, Message leftRedText) {
+ GlobalRecorder.getInstance().bossBar(leftBlueProgress, leftRedProgress, leftBlueText, leftRedText);
+ BossBarType.BLUE_LEFT.progress = leftBlueProgress;
+ BossBarType.RED_LEFT.progress = leftRedProgress;
+ BossBarType.BLUE_LEFT.text = leftBlueText;
+ BossBarType.RED_LEFT.text = leftRedText;
+ }
+
+ private void sendToPlayers() {
+ Bukkit.getOnlinePlayers().forEach(player -> {
+ BossBarType bar = BossBarType.byAngle(CraftbukkitWrapper.impl.headRotation(player));
+
+ Object[] params = bar.text.getParams().clone();
+ for(int i = 0; i < params.length; i++) {
+ if(params[i] instanceof Message) {
+ Message msg = (Message) params[i];
+ params[i] = FightSystem.getMessage().parse(msg.getMsg(), player, msg.getParams());
+ }
+ }
+
+ BountifulWrapper.impl.sendBar(player, bar.team, bar.progress, FightSystem.getMessage().parse(bar.text.getMsg(), player, params));
+ });
+ }
+
+ private void tpsWatcher() {
+ double tps = TPSWatcher.getTPS();
+ if(tps < 15.0)
+ FightSystem.getMessage().broadcastActionbar("TPS_WARNING", tps);
+ }
+
+ public enum BossBarType {
+ BLUE_LEFT(Fight.getBlueTeam(), 345, 165),
+ RED_LEFT(Fight.getRedTeam(), 165, 345);
+
+ private static final boolean BLUE_NEG_Z = Config.blueNegZ();
+
+ private final double minAngle;
+ private final double maxAngle;
+
+ private final FightTeam team;
+ private double progress = 0.5;
+ private Message text = new Message("OLD_STRING", "§7");
+
+ BossBarType(FightTeam team, double minAngle, double maxAngle) {
+ this.team = team;
+ this.minAngle = minAngle;
+ this.maxAngle = maxAngle;
+ }
+
+ private static BossBarType byAngle(double angle) {
+ if(BLUE_NEG_Z)
+ angle += 180;
+ angle = ((angle % 360) + 360) % 360;
+
+ for(BossBarType type : values()) {
+ if(type.minAngle < type.maxAngle ? type.minAngle <= angle && angle < type.maxAngle : type.minAngle <= angle || angle < type.maxAngle)
+ return type;
+ }
+
+ throw new SecurityException("No boss bar for angle " + angle + " found");
+ }
+ }
+
+
+ private static final Queue queue = new LinkedList<>();
+ private static boolean subtitleScheduled = false;
+
+ public static void printWin(FightTeam winner, String subtitle, Object... params) {
+ queue.clear();
+
+ FightSystem.getPlugin().getLogger().log(Level.INFO, winner + " " + FightSystem.getMessage().parse(subtitle, null, params));
+ GlobalRecorder.getInstance().winMessage(winner, subtitle, params);
+
+ Bukkit.getOnlinePlayers().forEach(Player::resetTitle);
+ if (winner != null)
+ Bukkit.getOnlinePlayers().forEach(p -> WorldOfColorWrapper.impl.sendTitle(p, FightSystem.getMessage().parse("UI_WIN", p, winner.getColor(), winner.getName()), FightSystem.getMessage().parse(subtitle, p, params), 5, 40, 5));
+ else
+ Bukkit.getOnlinePlayers().forEach(p -> WorldOfColorWrapper.impl.sendTitle(p, FightSystem.getMessage().parse("UI_DRAW", p), FightSystem.getMessage().parse(subtitle, p, params), 5, 40, 5));
+ }
+
+ public static void addSubtitle(String msg, Object... params) {
+ if(FightState.Spectate.contains(FightState.getFightState()))
+ return;
+
+ Message message = new Message(msg, params);
+ queue.add(message);
+ GlobalRecorder.getInstance().subtitle(message);
+ FightSystem.getPlugin().getLogger().log(Level.INFO, FightSystem.getMessage().parse(msg, null, params));
+
+ if(!subtitleScheduled)
+ printSubtitle();
+ }
+
+ private static void printSubtitle() {
+ if(queue.isEmpty()) {
+ subtitleScheduled = false;
+ return;
+ }
+ Message message = queue.poll();
+
+ Bukkit.getOnlinePlayers().forEach(p -> WorldOfColorWrapper.impl.sendTitle(p, " ", FightSystem.getMessage().parse(message.getMsg(), p, message.getParams()), 5, 40, 5));
+ Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), FightUI::printSubtitle, 50);
+ subtitleScheduled = true;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FlatteningWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FlatteningWrapper.java
new file mode 100644
index 00000000..aa2f10e8
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FlatteningWrapper.java
@@ -0,0 +1,54 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.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;
+import org.bukkit.event.block.BlockPhysicsEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+public interface FlatteningWrapper {
+ FlatteningWrapper impl = VersionDependent.getVersionImpl(FightSystem.getPlugin());
+
+ DyeColor getSilver();
+
+ boolean isWater(Block block);
+ boolean removeWater(Block block);
+
+ boolean containsBlockMeta(ItemMeta meta);
+ boolean hasAttributeModifier(ItemStack stack);
+
+ boolean doRecord(BlockPhysicsEvent e);
+
+ void forceLoadChunk(World world, int cX, int cZ);
+
+ boolean checkPistonMoving(Block block);
+
+ boolean isFacingWater(Block dispenser);
+
+ boolean isCrouching(Player player);
+ void sendBlockChange(Player player, Block block, Material type);
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java
new file mode 100644
index 00000000..05ff3fc8
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java
@@ -0,0 +1,379 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2023 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.utils;
+
+import com.comphenix.tinyprotocol.TinyProtocol;
+import de.steamwar.entity.REntity;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.fight.FightTeam;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.logging.Level;
+
+public class Hull {
+
+ private static boolean isOccluding(Material material) {
+ return material.isOccluding() || Config.HiddenBlocks.contains(material);
+ }
+
+ private final Region region;
+ private final boolean groundVisible;
+
+ private final BitSet occluding;
+ private final BitSet visibility;
+ private final Map> blockVisibility = new HashMap<>();
+ private final Set uncoveredSurface = new HashSet<>();
+
+ private final HashSet players = new HashSet<>();
+ private final Set entities = new HashSet<>();
+ private final Set rentities = new HashSet<>();
+
+ 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;
+ if (groundVisible) {
+ directions = new IntVector[]{
+ new IntVector(1, 0, 0),
+ new IntVector(-1, 0, 0),
+ new IntVector(0, 1, 0),
+ new IntVector(0, -1, 0),
+ new IntVector(0, 0, 1),
+ new IntVector(0, 0, -1)
+ };
+ } else {
+ directions = new IntVector[]{
+ new IntVector(1, 0, 0),
+ new IntVector(-1, 0, 0),
+ new IntVector(0, -1, 0),
+ new IntVector(0, 0, 1),
+ new IntVector(0, 0, -1)
+ };
+ }
+
+ // 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) {
+ for (int y = (direction.y == 0 ? -1 : 0); y <= 1; y += 2) {
+ for (int x = (direction.x == 0 ? -1 : 0); x <= 1; x += 2) {
+ map.put(new IntVector(x, y, z), new BitSet(region.volume()));
+ }
+ }
+ }
+ blockVisibility.put(direction, map);
+ }
+ }
+
+ public boolean blockPrecise(Player player, int chunkX, int chunkY, int chunkZ) {
+ return players.contains(player) && !region.chunkSectionOutside(chunkX, chunkY, chunkZ);
+ }
+
+ public boolean isBlockHidden(Player player, int x, int y, int z) {
+ return region.inRegion(x, y, z) && players.contains(player) && !visibility.get(((y - region.getMinY()) * region.getSizeZ() + (z - region.getMinZ())) * region.getSizeX() + (x - region.getMinX()));
+ }
+
+ public boolean isLocationHidden(Player player, Location location) {
+ return players.contains(player) && region.inRegion(location) && !visibility.get(new IntVector(location).toId(region));
+ }
+
+ public void addPlayer(Player player) {
+ if(players.add(player)) {
+ for(Entity entity : entities)
+ BlockIdWrapper.impl.untrackEntity(player, entity.getEntityId());
+ }
+ }
+
+ public void removePlayer(Player player, boolean activeRemoval) {
+ if(players.remove(player) && activeRemoval) {
+ for(Entity entity : entities)
+ BlockIdWrapper.impl.trackEntity(player, entity.getEntityId());
+ // techhider triggers block change sending
+ }
+ }
+
+ public void checkEntity(Entity entity) {
+ Location location = entity.getLocation();
+ if(region.inRegion(location) && !visibility.get(new IntVector(location).toId(region))) { //TODO more precise
+ if(entities.add(entity)) {
+ for(Player player : players)
+ BlockIdWrapper.impl.untrackEntity(player, entity.getEntityId());
+ }
+ } else {
+ if(entities.remove(entity)) {
+ for(Player player : players)
+ BlockIdWrapper.impl.trackEntity(player, entity.getEntityId());
+ }
+ }
+ }
+
+ public void removeEntity(Entity entity) {
+ entities.remove(entity);
+ }
+
+ public void checkREntity(REntity entity) {
+ Location location = new Location(Config.world, entity.getX(), entity.getY(), entity.getZ());
+ if(region.inRegion(location) && !visibility.get(new IntVector(location).toId(region))) { //TODO more precise
+ if(rentities.add(entity))
+ entity.hide(true);
+ } else {
+ if(rentities.remove(entity))
+ entity.hide(false);
+ }
+ }
+
+ public void removeREntity(REntity entity) {
+ rentities.remove(entity);
+ }
+
+ public void initialize() {
+ visibility.clear();
+ occluding.clear();
+ uncoveredSurface.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 (isOccluding(Config.world.getBlockAt(x, y, z).getType()))
+ occluding.set(block.toId(region));
+ });
+ forEachBorder((root, direction) -> {
+ for (Map.Entry quadrant : blockVisibility.get(direction).entrySet()) {
+ checkBlock(new NullList<>(), root, direction, quadrant.getKey(), quadrant.getValue());
+ }
+ });
+ FightSystem.getPlugin().getLogger().log(Level.INFO, () -> "[HullHider] initialisation finished: " + (System.currentTimeMillis() - start) + " ms, visible blocks: " + visibility.cardinality());
+ }
+
+ 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) || isOccluding(changedType))
+ return;
+
+ List uncovered = new ArrayList<>();
+ 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(uncovered, root, direction.getKey(), quadrant.getKey(), quadrant.getValue());
+ }
+ }
+ }
+
+ if(uncovered.isEmpty())
+ return;
+
+ Set uncoveredSet = new HashSet<>(uncovered);
+ Iterator it = entities.iterator();
+ while(it.hasNext()) {
+ Entity entity = it.next();
+ if(uncoveredSet.contains(new IntVector(entity.getLocation()))) { //TODO more precise
+ it.remove();
+ for(Player player : players)
+ BlockIdWrapper.impl.trackEntity(player, entity.getEntityId());
+ }
+ }
+
+ Iterator rit = rentities.iterator();
+ while(rit.hasNext()) {
+ REntity entity = rit.next();
+ if(uncoveredSet.contains(new IntVector(new Location(Config.world, entity.getX(), entity.getY(), entity.getZ())))) { //TODO more precise
+ rit.remove();
+ entity.hide(false);
+ }
+ }
+
+ uncoveredSurface.addAll(uncoveredSet);
+ uncoveredSurface.remove(root);
+ }
+
+ public void sendUncoveredBlocks() {
+ Map> sectionWise = new HashMap<>();
+
+ for(IntVector uncovered : uncoveredSurface) {
+ sectionWise.computeIfAbsent(new IntVector(uncovered.x >> 4, uncovered.y >> 4, uncovered.z >> 4), section -> new ArrayList<>()).add(uncovered);
+ }
+ uncoveredSurface.clear();
+
+ for (Map.Entry> entry : sectionWise.entrySet()) {
+ Object packet = HullHiderWrapper.impl.generateBlockChangePacket(entry.getValue());
+ if(packet == null)
+ continue;
+
+ players.forEach(player -> TinyProtocol.instance.sendPacket(player, packet));
+ }
+ }
+
+ 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)
+ f.accept(new IntVector(x, region.getMinY(), z), new IntVector(0, 1, 0));
+ f.accept(new IntVector(x, region.getMaxY() - 1, z), new IntVector(0, -1, 0));
+ }
+ }
+
+ for (int x = region.getMinX(); x < region.getMaxX(); x++) {
+ for (int y = region.getMinY(); y < region.getMaxY(); y++) {
+ f.accept(new IntVector(x, y, region.getMinZ()), new IntVector(0, 0, 1));
+ f.accept(new IntVector(x, y, region.getMaxZ() - 1), new IntVector(0, 0, -1));
+ }
+ }
+
+ for (int z = region.getMinZ(); z < region.getMaxZ(); z++) {
+ for (int y = region.getMinY(); y < region.getMaxY(); y++) {
+ f.accept(new IntVector(region.getMinX(), y, z), new IntVector(1, 0, 0));
+ f.accept(new IntVector(region.getMaxX() - 1, y, z), new IntVector(-1, 0, 0));
+ }
+ }
+ }
+
+ private void checkBlock(List uncovered, IntVector block, IntVector direction, IntVector quadrant, BitSet quadVisibility) {
+ if (block.notInRegion(region))
+ return;
+
+ int id = block.toId(region);
+ if (quadVisibility.get(id))
+ return;
+
+ quadVisibility.set(id);
+ if (!visibility.get(id)) {
+ visibility.set(id);
+ uncovered.add(block);
+ }
+
+ if (occluding.get(id))
+ return;
+
+ IntVector neighbour = block.add(direction);
+ checkBlock(uncovered, neighbour, direction, quadrant, quadVisibility);
+ boolean neigbourTransparent = boundedNonOccluding(neighbour);
+ boolean diagonalReachable = false;
+ if (direction.x == 0 && (neigbourTransparent || boundedNonOccluding(block.add(quadrant.x, 0, 0)))) {
+ checkBlock(uncovered, 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(uncovered, 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(uncovered, neighbour.add(0, 0, quadrant.z), direction, quadrant, quadVisibility);
+ diagonalReachable = diagonalReachable || boundedNonOccluding(neighbour.add(0, 0, quadrant.z));
+ }
+
+ if (diagonalReachable)
+ checkBlock(uncovered, neighbour.add(quadrant), direction, quadrant, quadVisibility);
+ }
+
+ private boolean boundedNonOccluding(IntVector block) {
+ return !(block.notInRegion(region) || occluding.get(block.toId(region)));
+ }
+
+
+ @Getter
+ @AllArgsConstructor
+ public static class IntVector {
+ private final int x;
+ private final int y;
+ private final int z;
+
+ public IntVector(Location location) {
+ this.x = location.getBlockX();
+ this.y = location.getBlockY();
+ this.z = location.getBlockZ();
+ }
+
+ 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 IntVector add(int x, int y, int z) {
+ return new IntVector(this.x + x, this.y + y, this.z + z);
+ }
+
+ public IntVector add(IntVector v) {
+ return add(v.x, v.y, v.z);
+ }
+
+ @Override
+ public int hashCode() {
+ return y << 24 ^ x << 12 ^ z;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if(o == null || this.getClass() != o.getClass())
+ return false;
+
+ IntVector v = (IntVector) o;
+ return x == v.x && y == v.y && z == v.z;
+ }
+
+ @Override
+ public String toString() {
+ return x + "," + y + "," + z;
+ }
+ }
+
+ private static class NullList extends AbstractList {
+ @Override
+ public void add(int index, E element) {
+ // Straight to /dev/null!
+ }
+
+ @Override
+ public E get(int index) {
+ return null;
+ }
+
+ @Override
+ public int size() {
+ return 0;
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java
new file mode 100644
index 00000000..c613a4ab
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java
@@ -0,0 +1,241 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2022 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.utils;
+
+import com.comphenix.tinyprotocol.Reflection;
+import com.comphenix.tinyprotocol.TinyProtocol;
+import de.steamwar.core.Core;
+import de.steamwar.entity.REntity;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightPlayer;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.listener.Recording;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependent;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.states.StateDependentTask;
+import de.steamwar.techhider.TechHider;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockPhysicsEvent;
+import org.bukkit.event.entity.EntityDeathEvent;
+import org.bukkit.event.entity.EntitySpawnEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+public class HullHider implements Listener {
+
+ private final Map hullMap = new HashMap<>();
+ private final Hull[] hulls;
+ private final Map, BiFunction> packetHiders = new HashMap<>();
+
+ public HullHider() {
+ if(!TechHiderWrapper.ENABLED) {
+ hulls = new Hull[0];
+ return;
+ }
+
+ Fight.teams().forEach(team -> hullMap.put(team, new Hull(team)));
+ hulls = hullMap.values().toArray(new Hull[0]);
+
+ packetHiders.put(packetPlayOutWorldEvent, this::worldEventHider);
+ packetHiders.put(packetPlayOutExplosion, this::explosionHider);
+ posHiderGenerator("{nms.network.protocol.game}.PacketPlayOutWorldParticles", Core.getVersion() >= 18 ? double.class : float.class, 1.0);
+ posHiderGenerator("{nms.network.protocol.game}.PacketPlayOutNamedSoundEffect", int.class, 8.0);
+ if(Core.getVersion() >= 9 && Core.getVersion() < 18)
+ posHiderGenerator("{nms.network.protocol.game}.PacketPlayOutCustomSoundEffect", int.class, 8.0);
+
+ new StateDependentListener(TechHiderWrapper.ENABLED, FightState.Schem, this);
+ new StateDependent(TechHiderWrapper.ENABLED, FightState.Schem) {
+ @Override
+ public void enable() {
+ packetHiders.forEach(TinyProtocol.instance::addFilter);
+ Bukkit.getOnlinePlayers().forEach(HullHider.this::updatePlayer);
+ }
+
+ @Override
+ public void disable() {
+ Bukkit.getOnlinePlayers().forEach(player -> removePlayer(player, true));
+ packetHiders.forEach(TinyProtocol.instance::removeFilter);
+ }
+ }.register();
+ new StateDependentTask(TechHiderWrapper.ENABLED, FightState.Schem, this::onTick, 0, 1);
+ }
+
+ public void initialize(FightTeam team) {
+ if(!TechHiderWrapper.ENABLED)
+ return;
+
+ hullMap.get(team).initialize();
+ }
+
+
+ @EventHandler(priority = EventPriority.HIGH)
+ public void onJoin(PlayerJoinEvent e) {
+ updatePlayer(e.getPlayer());
+ }
+
+ @EventHandler
+ public void onLeave(PlayerQuitEvent e) {
+ removePlayer(e.getPlayer(), false);
+ }
+
+ public void updatePlayer(Player player) {
+ if(!TechHiderWrapper.ENABLED)
+ return;
+
+ FightTeam team = Fight.getPlayerTeam(player);
+ FightPlayer fp = Fight.getFightPlayer(player);
+ for(Map.Entry hull : hullMap.entrySet()) {
+ if(Config.isReferee(player) || hull.getKey() == team || (fp != null && fp.canEntern())) {
+ hull.getValue().removePlayer(player, true);
+ } else {
+ hull.getValue().addPlayer(player);
+ }
+ }
+ }
+
+ private void removePlayer(Player player, boolean activeRemoval) {
+ for (Hull hull : hulls)
+ hull.removePlayer(player, activeRemoval);
+ }
+
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onBlockPhysic(BlockPhysicsEvent e) {
+ if(FlatteningWrapper.impl.doRecord(e))
+ blockUpdate(e.getBlock(), e.getChangedType());
+ }
+
+ public void blockUpdate(Block block, Material changedType) {
+ for (Hull hull : hulls)
+ hull.updateBlockVisibility(block, changedType);
+ }
+
+ public boolean isBlockHidden(Player player, int x, int y, int z) {
+ if(!TechHiderWrapper.ENABLED)
+ return false;
+
+ for (Hull hull : hulls)
+ if(hull.isBlockHidden(player, x, y, z))
+ return true;
+
+ return false;
+ }
+
+ public boolean blockPrecise(Player player, int chunkX, int chunkY, int chunkZ) {
+ if(!TechHiderWrapper.ENABLED)
+ return false;
+
+ for (Hull hull : hulls)
+ if(hull.blockPrecise(player, chunkX, chunkY, chunkZ))
+ return true;
+
+ return false;
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onSpawn(EntitySpawnEvent e) {
+ for (Hull hull : hulls)
+ hull.checkEntity(e.getEntity());
+ }
+
+ private void onTick() {
+ Recording.iterateOverEntities(Objects::nonNull, entity -> {
+ for (Hull hull : hulls)
+ hull.checkEntity(entity);
+ });
+
+ for (Hull hull : hulls)
+ hull.sendUncoveredBlocks();
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onDeath(EntityDeathEvent e) {
+ for(Hull hull : hulls)
+ hull.removeEntity(e.getEntity());
+ }
+
+ public void updateREntity(REntity e) {
+ for(Hull hull : hulls)
+ hull.checkREntity(e);
+ }
+
+ public void despawnREntity(REntity e) {
+ for(Hull hull : hulls)
+ hull.removeREntity(e);
+ }
+
+
+ private static final Class> packetPlayOutWorldEvent = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutWorldEvent");
+ private static final Reflection.FieldAccessor> worldEventPosition = Reflection.getField(packetPlayOutWorldEvent, TechHider.blockPosition, 0);
+ public static final Reflection.FieldAccessor blockPositionY = Reflection.getField("{nms.core}.BaseBlockPosition", int.class, 1);
+ private Object worldEventHider(Player player, Object packet) {
+ Object baseBlock = worldEventPosition.get(packet);
+ return packetHider(player, packet, new Location(Config.world, TechHider.blockPositionX.get(baseBlock), blockPositionY.get(baseBlock), TechHider.blockPositionZ.get(baseBlock)));
+ }
+
+ private static final Class> packetPlayOutExplosion = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutExplosion");
+ private static final Reflection.FieldAccessor explosionBlocks = Reflection.getField(packetPlayOutExplosion, List.class, 0);
+ private static final Function explosionLocation = posPacketToLocation(packetPlayOutExplosion, double.class, 1.0);
+ private Object explosionHider(Player player, Object packet) {
+ if(explosionBlocks.get(packet).isEmpty())
+ return packetHider(player, packet, explosionLocation.apply(packet));
+
+ return packet;
+ }
+
+ private void posHiderGenerator(String typeName, Class extends Number> posType, double factor) {
+ Class> type = Reflection.getClass(typeName);
+ Function location = posPacketToLocation(type, posType, factor);
+ packetHiders.put(type, (player, packet) -> packetHider(player, packet, location.apply(packet)));
+ }
+
+ private static Function posPacketToLocation(Class> type, Class extends Number> posType, double factor) {
+ Reflection.FieldAccessor extends Number> x = Reflection.getField(type, posType, 0);
+ Reflection.FieldAccessor extends Number> y = Reflection.getField(type, posType, 1);
+ Reflection.FieldAccessor extends Number> z = Reflection.getField(type, posType, 2);
+
+ return packet -> new Location(Config.world, x.get(packet).doubleValue()/factor, y.get(packet).doubleValue()/factor, z.get(packet).doubleValue()/factor);
+ }
+
+ private Object packetHider(Player player, Object packet, Location location) {
+ for(Hull hull : hulls) {
+ if(hull.isLocationHidden(player, location))
+ return null;
+ }
+
+ return packet;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHiderWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHiderWrapper.java
new file mode 100644
index 00000000..17a5195b
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHiderWrapper.java
@@ -0,0 +1,31 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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.utils;
+
+import de.steamwar.core.VersionDependent;
+import de.steamwar.fightsystem.FightSystem;
+
+import java.util.List;
+
+public interface HullHiderWrapper {
+ HullHiderWrapper impl = VersionDependent.getVersionImpl(FightSystem.getPlugin());
+
+ Object generateBlockChangePacket(List changes);
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/ItemBuilder.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/ItemBuilder.java
new file mode 100644
index 00000000..728fb924
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/ItemBuilder.java
@@ -0,0 +1,55 @@
+/*
+ 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.utils;
+
+import org.bukkit.Material;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.inventory.ItemFlag;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+public class ItemBuilder {
+
+ private final ItemStack item;
+ private final ItemMeta meta;
+
+ public ItemBuilder(Material material) {
+ item = new ItemStack(material);
+ meta = item.getItemMeta();
+ meta.addItemFlags(ItemFlag.values());
+ }
+
+ @SuppressWarnings("deprecation")
+ public ItemBuilder(Material material, short subid) {
+ item = new ItemStack(material, 1, subid);
+ meta = item.getItemMeta();
+ meta.addItemFlags(ItemFlag.values());
+ }
+
+ public ItemBuilder enchant() {
+ meta.addEnchant(Enchantment.DURABILITY, 1, true);
+ return this;
+ }
+
+ public ItemStack build() {
+ item.setItemMeta(meta);
+ return item;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Message.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Message.java
new file mode 100644
index 00000000..9484b640
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Message.java
@@ -0,0 +1,34 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import lombok.Getter;
+
+@Getter
+public class Message {
+
+ private final String msg;
+ private final Object[] params;
+
+ public Message(String msg, Object... params) {
+ this.msg = msg;
+ this.params = params;
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Region.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Region.java
new file mode 100644
index 00000000..6a559799
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Region.java
@@ -0,0 +1,162 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.techhider.ProtocolUtils;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.bukkit.Location;
+import org.bukkit.block.Block;
+
+import java.util.function.ObjIntConsumer;
+
+@Getter
+@AllArgsConstructor
+public class Region {
+
+ public static final Region EMPTY = Region.fromSize(-10000, -10000, -10000, 0, 0, 0);
+
+ public static Region withExtension(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ, int extendX, int extendY, int extendZ) {
+ return Region.fromSize(minX - extendX, minY - extendY, minZ - extendZ,
+ sizeX + extendX * 2, sizeY + extendY * 2, sizeZ + extendZ * 2);
+ }
+
+ public static Region fromSize(int minX, int minY, int minZ, int sizeX, int sizeY, int sizeZ) {
+ return new Region(minX, minY, minZ, minX+sizeX, minY+sizeY, minZ+sizeZ);
+ }
+
+ private final int minX;
+ private final int minY;
+ private final int minZ;
+ private final int maxX;
+ private final int maxY;
+ private final int maxZ;
+
+ public int getSizeX() {
+ return maxX - minX;
+ }
+
+ public int getSizeY() {
+ return maxY - minY;
+ }
+
+ public int getSizeZ() {
+ return maxZ - minZ;
+ }
+
+ private int getMinChunkX(){
+ return ProtocolUtils.posToChunk(minX);
+ }
+
+ private int getMaxChunkX(){
+ return ProtocolUtils.posToChunk(maxX);
+ }
+
+ private int getMinChunkZ(){
+ return ProtocolUtils.posToChunk(minZ);
+ }
+
+ private int getMaxChunkZ(){
+ return ProtocolUtils.posToChunk(maxZ);
+ }
+
+ public boolean chunkOutside(int cX, int cZ) {
+ return getMinChunkX() > cX || cX > getMaxChunkX() ||
+ getMinChunkZ() > cZ || cZ > getMaxChunkZ();
+ }
+
+ public boolean chunkSectionOutside(int cX, int cY, int cZ) {
+ return getMinChunkX() > cX || cX > getMaxChunkX() ||
+ ProtocolUtils.posToChunk(minY) > cY || cY > ProtocolUtils.posToChunk(maxY) ||
+ getMinChunkZ() > cZ || cZ > getMaxChunkZ();
+ }
+
+ public void forEachChunk(ObjIntConsumer executor) {
+ for(int x = getMinChunkX(); x <= getMaxChunkX(); x++)
+ for(int z = getMinChunkZ(); z <= getMaxChunkZ(); z++)
+ executor.accept(x, z);
+ }
+
+ public void forEach(TriConsumer executor) {
+ for(int x = minX; x < maxX; x++) {
+ for(int y = minY; y < maxY; y++) {
+ for (int z = minZ; z < maxZ; z++) {
+ executor.accept(x, y, z);
+ }
+ }
+ }
+ }
+
+ public int volume(){
+ return (maxX - minX) * (maxY - minY) * (maxZ - minZ);
+ }
+
+ public double centerX() {
+ return (maxX - minX) / 2.0 + minX;
+ }
+
+ public double centerZ() {
+ return (maxZ - minZ) / 2.0 + minZ;
+ }
+
+ public boolean in2dRegion(Location location){
+ return minX <= location.getX() && location.getX() < maxX && minZ <= location.getZ() && location.getZ() <= maxZ;
+ }
+
+ public boolean inRegion(Location location){
+ return in2dRegion(location) && minY < location.getY() && location.getY() < maxY;
+ }
+
+ public boolean playerInRegion(Location location){
+ return in2dRegion(location) && minY <= location.getY() && location.getY() < maxY;
+ }
+
+ public boolean in2dRegion(Block block){
+ return in2dRegion(block.getX(), block.getZ());
+ }
+
+ public boolean in2dRegion(int x, int z) {
+ return minX <= x && x < maxX && minZ <= z && z < maxZ;
+ }
+
+ public boolean inRegion(Block block){
+ return in2dRegion(block) && minY <= block.getY() && block.getY() < maxY;
+ }
+
+ public boolean inRegion(int x, int y, int z) {
+ return in2dRegion(x, z) && minY <= y && y < maxY;
+ }
+
+ public Region intersection(Region other) {
+ int x = Math.max(minX, other.minX);
+ int y = Math.max(minY, other.minY);
+ int z = Math.max(minZ, other.minZ);
+
+ return new Region(x, y, z, Math.min(maxX, other.maxX), Math.min(maxY, other.maxY), Math.min(maxZ, other.maxZ));
+ }
+
+ public Region to2d() {
+ return new Region(minX, Integer.MIN_VALUE/2, minZ, maxX, Integer.MAX_VALUE/2, maxZ);
+ }
+
+ public interface TriConsumer{
+ void accept(T x, V y, U z);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/SWSound.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/SWSound.java
new file mode 100644
index 00000000..9ae178d9
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/SWSound.java
@@ -0,0 +1,41 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.core.VersionDependent;
+import de.steamwar.fightsystem.FightSystem;
+import org.bukkit.Sound;
+
+public enum SWSound {
+ ENTITY_WITHER_DEATH,
+ BLOCK_NOTE_PLING,
+ BLOCK_NOTE_BASS,
+ ENTITY_GENERIC_EXPLODE;
+
+ private static final ISWSound impl = VersionDependent.getVersionImpl(FightSystem.getPlugin());
+
+ public Sound getSound() {
+ return impl.getSound(this);
+ }
+
+ public interface ISWSound {
+ Sound getSound(SWSound sound);
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java
new file mode 100644
index 00000000..9e97afa2
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java
@@ -0,0 +1,142 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.core.CraftbukkitWrapper;
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.events.BoardingEvent;
+import de.steamwar.fightsystem.events.TeamLeaveEvent;
+import de.steamwar.fightsystem.events.TeamSpawnEvent;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependent;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.techhider.TechHider;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+public class TechHiderWrapper extends StateDependent implements TechHider.LocationEvaluator, Listener {
+
+ public static final boolean ENABLED = !Config.OnlyPublicSchematics && !Config.test() && Config.TechhiderActive;
+
+ private final ConcurrentHashMap hiddenRegion = new ConcurrentHashMap<>();
+ private final TechHider techHider;
+
+ public TechHiderWrapper() {
+ super(ENABLED, FightState.Schem);
+ techHider = new TechHider(this, Config.ObfuscateWith, Config.HiddenBlocks, Config.HiddenBlockEntities);
+
+ new StateDependentListener(ENABLED, FightState.Schem, this);
+ register();
+ }
+
+ @Override
+ public void enable() {
+ techHider.enable();
+ }
+
+ @Override
+ public void disable() {
+ techHider.disable();
+ hiddenRegion.clear();
+ }
+
+ @EventHandler
+ public void teamJoin(TeamSpawnEvent e) {
+ e.getFightPlayer().ifPlayer(player -> hiddenRegion.put(player, getHiddenRegion(player)));
+ }
+
+ @EventHandler
+ public void boarding(BoardingEvent e) {
+ e.getFightPlayer().ifPlayer(player -> hiddenRegion.put(player, getHiddenRegion(player)));
+ }
+
+ @EventHandler
+ public void teamLeave(TeamLeaveEvent e) {
+ e.getFightPlayer().ifPlayer(player -> {
+ if(player.isOnline())
+ hiddenRegion.put(player, getHiddenRegion(player));
+ });
+ }
+
+ @EventHandler
+ public void playerQuit(PlayerQuitEvent e) {
+ Player player = e.getPlayer();
+ hiddenRegion.remove(player);
+ }
+
+ public void reloadChunks(Player player, Region region, Region exclusion) {
+ if(!ENABLED || !FightState.Schem.contains(FightState.getFightState()) || !player.isOnline())
+ return;
+
+ region.forEachChunk((chunkX, chunkZ) -> {
+ if(exclusion.chunkOutside(chunkX, chunkZ))
+ CraftbukkitWrapper.impl.sendChunk(player, chunkX, chunkZ);
+ });
+ }
+
+ @Override
+ public boolean skipChunk(Player player, int chunkX, int chunkZ) {
+ return getHiddenRegion(player).chunkOutside(chunkX, chunkZ);
+ }
+
+ @Override
+ public boolean skipChunkSection(Player player, int chunkX, int chunkY, int chunkZ) {
+ return getHiddenRegion(player).chunkSectionOutside(chunkX, chunkY, chunkZ);
+ }
+
+ @Override
+ public TechHider.State check(Player player, int x, int y, int z) {
+ if(hiddenRegion.computeIfAbsent(player, this::getHiddenRegion).inRegion(x, y, z)) {
+ if(FightSystem.getHullHider().isBlockHidden(player, x, y, z)) {
+ return TechHider.State.HIDE;
+ } else {
+ return TechHider.State.CHECK;
+ }
+ } else {
+ return TechHider.State.SKIP;
+ }
+ }
+
+ @Override
+ public boolean blockPrecise(Player player, int chunkX, int chunkY, int chunkZ) {
+ return FightSystem.getHullHider().blockPrecise(player, chunkX, chunkY, chunkZ);
+ }
+
+ private Region getHiddenRegion(Player player) {
+ if(Config.isReferee(player))
+ return Region.EMPTY;
+
+ FightTeam team = Fight.getPlayerTeam(player);
+ if(team == null)
+ return Config.ArenaRegion;
+
+ if(team.canPlayerEntern(player))
+ return Region.EMPTY;
+
+ return Fight.getOpposite(team).getExtendRegion();
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/WorldOfColorWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/WorldOfColorWrapper.java
new file mode 100644
index 00000000..e69b4d29
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/WorldOfColorWrapper.java
@@ -0,0 +1,40 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import de.steamwar.core.VersionDependent;
+import de.steamwar.fightsystem.FightSystem;
+import org.bukkit.ChatColor;
+import org.bukkit.Location;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Projectile;
+import org.bukkit.scoreboard.Team;
+
+public interface WorldOfColorWrapper {
+ WorldOfColorWrapper impl = VersionDependent.getVersionImpl(FightSystem.getPlugin());
+
+ void setTeamColor(Team team, ChatColor color);
+ boolean isInBlock(Projectile e);
+
+ void playSound(Location location, Sound sound, String soundCategory, float volume, float pitch);
+
+ void sendTitle(Player player, String title, String subtitle, int start, int hold, int stop);
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/WorldeditWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/WorldeditWrapper.java
new file mode 100644
index 00000000..83c68803
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/WorldeditWrapper.java
@@ -0,0 +1,43 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.utils;
+
+import com.sk89q.worldedit.WorldEditException;
+import com.sk89q.worldedit.extent.clipboard.Clipboard;
+import com.sk89q.worldedit.math.transform.AffineTransform;
+import de.steamwar.core.VersionDependent;
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.sql.SchematicNode;
+import org.bukkit.DyeColor;
+import org.bukkit.Location;
+import org.bukkit.util.Vector;
+
+import java.io.IOException;
+
+public interface WorldeditWrapper {
+ WorldeditWrapper impl = VersionDependent.getVersionImpl(FightSystem.getPlugin());
+
+ void replaceTeamColor(Clipboard clipboard, DyeColor c) throws WorldEditException;
+ int getWaterDepth(Clipboard clipboard);
+ void pasteClipboard(Clipboard clipboard, Location position, Vector offset, AffineTransform aT);
+ Vector getDimensions(Clipboard clipboard);
+ Clipboard loadChar(String charName) throws IOException;
+ void saveSchem(SchematicNode schem, Region region, int minY) throws WorldEditException;
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/EventTeamOffWincondition.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/EventTeamOffWincondition.java
new file mode 100644
index 00000000..77db2ff4
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/EventTeamOffWincondition.java
@@ -0,0 +1,41 @@
+/*
+ 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.winconditions;
+
+import de.steamwar.fightsystem.ArenaMode;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.OneShotStateDependent;
+
+public class EventTeamOffWincondition extends Wincondition{
+
+ public EventTeamOffWincondition(){
+ super("TeamOffline");
+ new OneShotStateDependent(ArenaMode.Event, FightState.PreRunning, () -> {
+ if(Fight.teams().stream().allMatch(FightTeam::allPlayersOut)){
+ win(null, "WIN_OFFLINE_BOTH");
+ }else{
+ Fight.teams().stream().filter(FightTeam::allPlayersOut).findAny(
+ ).ifPresent(team -> win(Fight.getOpposite(team), "WIN_OFFLINE", team.getColoredName()));
+ }
+ });
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/PrintableWincondition.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/PrintableWincondition.java
new file mode 100644
index 00000000..6af28b0b
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/PrintableWincondition.java
@@ -0,0 +1,27 @@
+/*
+ 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.winconditions;
+
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.utils.Message;
+
+public interface PrintableWincondition {
+ Message getDisplay(FightTeam team);
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/Wincondition.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/Wincondition.java
new file mode 100644
index 00000000..3b62affa
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/Wincondition.java
@@ -0,0 +1,75 @@
+/*
+ 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.winconditions;
+
+import de.steamwar.fightsystem.FightSystem;
+import de.steamwar.fightsystem.countdown.Countdown;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.StateDependentCountdown;
+import lombok.Getter;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.LivingEntity;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.ToDoubleFunction;
+import java.util.stream.Collectors;
+
+public abstract class Wincondition {
+
+ @Getter
+ protected static WinconditionPercent percentWincondition = null;
+ protected static StateDependentCountdown timeOverCountdown = null;
+ @Getter
+ protected static final List printableWinconditions = new ArrayList<>();
+
+ private final String windescription;
+
+ protected Wincondition(String windescription) {
+ this.windescription = windescription;
+ }
+
+ public static Countdown getTimeOverCountdown(){
+ if(timeOverCountdown == null)
+ return null;
+ return timeOverCountdown.getCountdown();
+ }
+
+ protected void win(FightTeam team, String subtitle, Object... params){
+ FightSystem.setSpectateState(team, windescription, subtitle, params);
+ }
+
+ protected FightTeam isTarget(Entity player){
+ if(!(player instanceof LivingEntity))
+ return null;
+
+ return Fight.getPlayerTeam((LivingEntity) player);
+ }
+
+ protected void comparisonWin(ToDoubleFunction evaluate, String winMessage, String tieMessage) {
+ double max = Fight.teams().stream().mapToDouble(evaluate).max().orElseThrow(() -> new SecurityException("No teams present"));
+ List teams = Fight.teams().stream().filter(team -> evaluate.applyAsDouble(team) == max).collect(Collectors.toList());
+ if(teams.size() > 1)
+ win(null, tieMessage);
+ else
+ win(teams.get(0), winMessage, teams.get(0).getColoredName());
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionAllDead.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionAllDead.java
new file mode 100644
index 00000000..8f2d73a3
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionAllDead.java
@@ -0,0 +1,46 @@
+/*
+ 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.winconditions;
+
+import de.steamwar.fightsystem.events.TeamDeathEvent;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+public class WinconditionAllDead extends Wincondition implements Listener {
+
+ public WinconditionAllDead(){
+ super("AllDead");
+ new StateDependentListener(Winconditions.ALL_DEAD, FightState.Ingame, this);
+ }
+
+ @EventHandler
+ public void handlePlayerDeath(TeamDeathEvent event) {
+ FightTeam team = event.getFightPlayer().getTeam();
+
+ if(team.getAlivePlayers() > 0)
+ return;
+
+ win(Fight.getOpposite(team), "WIN_ALL_DEAD", team.getPrefix());
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionAmongUs.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionAmongUs.java
new file mode 100644
index 00000000..80572084
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionAmongUs.java
@@ -0,0 +1,87 @@
+/*
+ * 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.winconditions;
+
+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.states.FightState;
+import de.steamwar.fightsystem.states.OneShotStateDependent;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.PlayerDeathEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import java.util.*;
+
+public class WinconditionAmongUs extends Wincondition implements Listener {
+
+ private Map imposter = new HashMap<>();
+
+ private final Random random = new Random();
+
+ public WinconditionAmongUs() {
+ super("AmongUs");
+ new OneShotStateDependent(Winconditions.AMONG_US, FightState.Ingame, () -> {
+ imposter.put(Fight.getRedTeam(), sendMessageAndReturnImposter(Fight.getRedTeam().getPlayers()));
+ imposter.put(Fight.getBlueTeam(), sendMessageAndReturnImposter(Fight.getBlueTeam().getPlayers()));
+ });
+ new StateDependentListener(Winconditions.AMONG_US, FightState.Ingame, this);
+ }
+
+ private FightPlayer sendMessageAndReturnImposter(Collection fightPlayers) {
+ List fightPlayerList = new ArrayList<>(fightPlayers);
+ FightPlayer imposter = fightPlayerList.get(random.nextInt(fightPlayerList.size()));
+ for (FightPlayer fightPlayer : fightPlayerList) {
+ if (fightPlayer == imposter) {
+ FightSystem.getMessage().send("AMONG_US_IMPOSTER_MESSAGE", fightPlayer.getEntity());
+ } else {
+ FightSystem.getMessage().send("AMONG_US_IMPOSTER_AMONG_MESSAGE", fightPlayer.getEntity());
+ }
+ }
+ return imposter;
+ }
+
+ @EventHandler
+ public void handlePlayerDeath(PlayerDeathEvent event) {
+ handleDeath(event.getEntity().getPlayer());
+ }
+
+ @EventHandler
+ public void handlePlayerQuit(PlayerQuitEvent event) {
+ handleDeath(event.getPlayer());
+ }
+
+ private void handleDeath(Player player){
+ FightTeam team = isTarget(player);
+ if(team == null)
+ return;
+
+ FightPlayer current = team.getFightPlayer(player);
+ if (current == imposter.get(team)) {
+ win(team, "WIN_IMPOSTER_DEAD", team.getPrefix());
+ } else if (team.getAlivePlayers() <= 2 && imposter.get(team).isLiving()) {
+ win(Fight.getOpposite(team), "WIN_CREWMATE_DEAD", Fight.getOpposite(team).getPrefix());
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionBlocks.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionBlocks.java
new file mode 100644
index 00000000..b9b0c9f7
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionBlocks.java
@@ -0,0 +1,92 @@
+/*
+ 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.winconditions;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentTask;
+import de.steamwar.fightsystem.utils.Message;
+import de.steamwar.inventory.SWItem;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+
+public class WinconditionBlocks extends Wincondition implements PrintableWincondition {
+
+ public static final Material PUMPKIN_LANTERN = SWItem.getMaterial("JACK_O_LANTERN");
+
+ private final Map teamMap = new HashMap<>();
+ private final String barMessage;
+ private final Predicate isOfType;
+
+ public WinconditionBlocks(Winconditions wincondition, String windescription, String barMessage, Predicate isOfType) {
+ super(windescription);
+ this.barMessage = barMessage;
+ this.isOfType = isOfType;
+ Fight.teams().forEach(team -> teamMap.put(team, new TeamBlocks(team)));
+
+ new StateDependentTask(wincondition, FightState.Ingame, () -> teamMap.values().forEach(TeamBlocks::check), 200, 200){
+ @Override
+ public void enable() {
+ teamMap.values().forEach(TeamBlocks::find);
+ super.enable();
+ }
+ };
+
+ if(Config.ActiveWinconditions.contains(wincondition))
+ printableWinconditions.add(this);
+ }
+
+ @Override
+ public Message getDisplay(FightTeam team) {
+ return new Message(barMessage, team.getPrefix() + teamMap.get(team).blocks.size());
+ }
+
+ private class TeamBlocks {
+ private final FightTeam team;
+ private final List blocks = new ArrayList<>();
+
+ private TeamBlocks(FightTeam team) {
+ this.team = team;
+ }
+
+ private void find() {
+ blocks.clear();
+ team.getExtendRegion().forEach((x, y, z) -> {
+ Block block = Config.world.getBlockAt(x, y, z);
+ if (isOfType.test(block))
+ blocks.add(block);
+ });
+ }
+
+ private void check() {
+ blocks.removeIf(block -> !isOfType.test(block));
+ if(blocks.isEmpty())
+ win(Fight.getOpposite(team), "WIN_TECHKO", team.getColoredName());
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionCaptainDead.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionCaptainDead.java
new file mode 100644
index 00000000..de0205ab
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionCaptainDead.java
@@ -0,0 +1,47 @@
+/*
+ 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.winconditions;
+
+import de.steamwar.fightsystem.events.TeamDeathEvent;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightPlayer;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+public class WinconditionCaptainDead extends Wincondition implements Listener {
+
+ public WinconditionCaptainDead(){
+ super("LeaderDead");
+ new StateDependentListener(Winconditions.CAPTAIN_DEAD, FightState.Ingame, this);
+ }
+
+ @EventHandler
+ public void handlePlayerDeath(TeamDeathEvent event) {
+ FightPlayer leader = event.getFightPlayer();
+ if(!leader.isLeader())
+ return;
+
+ FightTeam team = leader.getTeam();
+ win(Fight.getOpposite(team), "WIN_LEADER_DEAD", team.getPrefix() + leader.getEntity().getName());
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionComparisonTimeout.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionComparisonTimeout.java
new file mode 100644
index 00000000..d3fa4c3c
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionComparisonTimeout.java
@@ -0,0 +1,38 @@
+/*
+ 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.winconditions;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.countdown.TimeOverCountdown;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCountdown;
+
+import java.util.function.ToDoubleFunction;
+
+public class WinconditionComparisonTimeout extends Wincondition {
+
+ public WinconditionComparisonTimeout(Winconditions wincondition, String windescription, String winMessage, ToDoubleFunction evaluate) {
+ super(windescription);
+ if(Config.ActiveWinconditions.contains(wincondition)) {
+ timeOverCountdown = new StateDependentCountdown(wincondition, FightState.Running, new TimeOverCountdown(() -> comparisonWin(evaluate, winMessage, "WIN_TIME_OVER")));
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionPercent.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionPercent.java
new file mode 100644
index 00000000..37d3fafa
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionPercent.java
@@ -0,0 +1,126 @@
+/*
+ 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.winconditions;
+
+import de.steamwar.fightsystem.Config;
+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.fightsystem.utils.Message;
+import org.bukkit.entity.EntityType;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityExplodeEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public class WinconditionPercent extends Wincondition implements PrintableWincondition {
+
+ private final Map teamMap = new HashMap<>();
+
+ protected Consumer checkWin = team -> {
+ if (getPercent(team) >= Config.PercentWin) {
+ win(Fight.getOpposite(team), "WIN_PERCENT", team.getColoredName());
+ }
+ };
+ protected Consumer postEnable = team -> {};
+
+ public WinconditionPercent(Winconditions wincondition, String windescription) {
+ super(windescription);
+
+ if (Config.ActiveWinconditions.contains(wincondition)) {
+ printableWinconditions.add(this);
+ percentWincondition = this;
+ }
+
+ Fight.teams().forEach(team -> new TeamPercent(team, wincondition));
+ }
+
+ public Message getDisplay(FightTeam team) {
+ return new Message("BAR_PERCENT", team.getPrefix() + (Math.round(10000.0 * (1.0 - getPercent(team) / Config.PercentWin)) / 100.0));
+ }
+
+ public double getPercent(FightTeam team) {
+ return teamMap.get(team).getPercent();
+ }
+
+ protected int getTotalBlocks(FightTeam team) {
+ return teamMap.get(team).totalBlocks;
+ }
+
+ protected int getCurrentBlocks(FightTeam team) {
+ return teamMap.get(team).currentBlocks;
+ }
+
+ private class TeamPercent implements Listener {
+ private final FightTeam team;
+
+ private int totalBlocks = 0;
+ private int currentBlocks = 0;
+
+ private TeamPercent(FightTeam team, Winconditions wincondition) {
+ this.team = team;
+
+ new OneShotStateDependent(wincondition, FightState.Running, this::enable);
+ new StateDependentListener(wincondition, FightState.Running, this).register();
+ teamMap.put(team, this);
+ }
+
+ @EventHandler
+ public void onEntityExplode(EntityExplodeEvent event) {
+ if (
+ event.getEntityType() == EntityType.FIREBALL ||
+ !team.getExtendRegion().inRegion(event.getEntity().getLocation()) ||
+ (!Config.PercentEntern && !Config.EnterStages.isEmpty() && Config.EnterStages.get(0) >= Wincondition.getTimeOverCountdown().getTimeLeft())
+ ) {
+ return;
+ }
+
+ event.blockList().forEach(block -> {
+ if (Config.PercentBlocks.contains(block.getType()) == Config.PercentBlocksWhitelist) {
+ currentBlocks--;
+ }
+ });
+
+ checkWin.accept(team);
+ }
+
+ private void enable() {
+ totalBlocks = 0;
+ team.getSchemRegion().forEach((x, y, z) -> {
+ if (Config.PercentBlocks.contains(Config.world.getBlockAt(x, y, z).getType()) == Config.PercentBlocksWhitelist)
+ totalBlocks++;
+ });
+ currentBlocks = totalBlocks;
+ postEnable.accept(team);
+ }
+
+ private double getPercent() {
+ if (currentBlocks >= totalBlocks) {
+ return 0;
+ }
+ return (totalBlocks - currentBlocks) * 100 / (double) totalBlocks;
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionPoints.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionPoints.java
new file mode 100644
index 00000000..028e893e
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionPoints.java
@@ -0,0 +1,136 @@
+/*
+ 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.winconditions;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.countdown.TimeOverCountdown;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCountdown;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.utils.Message;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.PlayerDeathEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class WinconditionPoints extends WinconditionPercent implements Listener {
+
+ private final Map teamMap = new HashMap<>();
+
+ public WinconditionPoints(){
+ super(Winconditions.POINTS, "Points");
+
+ checkWin = team -> {};
+ postEnable = this::pointInit;
+
+ Fight.teams().forEach(team -> teamMap.put(team, new TeamPoints(team)));
+
+ new StateDependentListener(Winconditions.POINTS, FightState.Ingame, this);
+
+ if(Config.ActiveWinconditions.contains(Winconditions.POINTS)){
+ timeOverCountdown = new StateDependentCountdown(Winconditions.POINTS, FightState.Running, new TimeOverCountdown(this::timeOver));
+ }
+ }
+
+ private void timeOver() {
+ comparisonWin(team -> teamMap.get(team).getPoints(), "WIN_POINTS", "WIN_POINTS_EQUAL");
+ }
+
+ @EventHandler
+ public void handlePlayerDeath(PlayerDeathEvent event) {
+ handleDeath(event.getEntity().getPlayer());
+ }
+
+ @EventHandler
+ public void handlePlayerQuit(PlayerQuitEvent event) {
+ handleDeath(event.getPlayer());
+ }
+
+ private void handleDeath(Player player){
+ FightTeam team = isTarget(player);
+ if(team == null)
+ return;
+
+ TeamPoints enemy = teamMap.get(Fight.getOpposite(team));
+ if(team.isPlayerLeader(player)) {
+ enemy.points += 500;
+ timeOver();
+ }else if(team.getPlayers().size() <= 5)
+ enemy.points += 300;
+ else
+ enemy.points += 200;
+
+ }
+
+ private void pointInit(FightTeam team) {
+ TeamPoints opponent = teamMap.get(Fight.getOpposite(team));
+ if(getTotalBlocks(team) == 0)
+ return;
+
+ teamMap.get(team).setup(getTotalBlocks(Fight.getOpposite(team)));
+ opponent.setup(getTotalBlocks(team));
+ }
+
+ @Override
+ public Message getDisplay(FightTeam team) {
+ return new Message("BAR_POINTS", team.getPrefix() + teamMap.get(team).getPoints());
+ }
+
+ private class TeamPoints {
+ private static final int MAX_POINTS = 2000;
+
+ private final FightTeam team;
+ private double factor;
+ private int points;
+
+ TeamPoints(FightTeam team){
+ this.team = team;
+ }
+
+ private void setup(int enemyBlocks){
+ points = 0;
+ if(enemyBlocks < getTotalBlocks(team)) {
+ this.factor = 100; //Original mit 20 (20% = 0.2 ergeben 2000 Punkte
+ } else {
+ double f = 100.0 * getTotalBlocks(team) / enemyBlocks;
+
+ if(f > 100)
+ f = 100;
+ else if(f < 40)
+ f = 40;
+
+ this.factor = f;
+ }
+ }
+
+ public int getPoints(){
+ int damagePoints = (int)(getPercent(Fight.getOpposite(team)) * factor);
+ if(damagePoints > MAX_POINTS)
+ damagePoints = MAX_POINTS;
+ return points + damagePoints;
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionPointsAirShip.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionPointsAirShip.java
new file mode 100644
index 00000000..4f166a6d
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionPointsAirShip.java
@@ -0,0 +1,153 @@
+/*
+ * 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.winconditions;
+
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.states.StateDependentTask;
+import de.steamwar.fightsystem.utils.Message;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.PlayerDeathEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class WinconditionPointsAirShip extends WinconditionPercent implements Listener {
+
+ private double[] as = new double[] {
+ 0.5,
+ 0.6,
+ 0.68,
+ 0.74,
+ 0.8
+ };
+
+ private double[] es = new double[] {
+ 0.25,
+ 0.3,
+ 0.34,
+ 0.37,
+ 0.4
+ };
+
+ private final Map teamMap = new HashMap<>();
+
+ public WinconditionPointsAirShip(){
+ super(Winconditions.POINTS_AIRSHIP, "Points");
+
+ checkWin = team -> {
+ if (teamMap.get(team).getPoints() > TeamPoints.WIN_POINTS) {
+ end();
+ }
+ };
+ postEnable = this::pointInit;
+
+ Fight.teams().forEach(team -> teamMap.put(team, new TeamPoints(team)));
+
+ new StateDependentListener(Winconditions.POINTS_AIRSHIP, FightState.Ingame, this);
+ new StateDependentTask(Winconditions.POINTS_AIRSHIP, FightState.Running, this::running, 20, 20);
+
+ }
+
+ private void end() {
+ comparisonWin(team -> teamMap.get(team).getPoints(), "WIN_POINTS", "WIN_POINTS_EQUAL");
+ }
+
+ private void running() {
+ AtomicBoolean possibleEnd = new AtomicBoolean(false);
+ Fight.teams().forEach(team -> {
+ teamMap.get(team).points += 1;
+ possibleEnd.compareAndSet(false, teamMap.get(team).getPoints() >= TeamPoints.WIN_POINTS);
+ });
+ if(possibleEnd.get())
+ end();
+ }
+
+ @EventHandler
+ public void handlePlayerDeath(PlayerDeathEvent event) {
+ handleDeath(event.getEntity().getPlayer());
+ }
+
+ @EventHandler
+ public void handlePlayerQuit(PlayerQuitEvent event) {
+ handleDeath(event.getPlayer());
+ }
+
+ private void handleDeath(Player player){
+ FightTeam team = isTarget(player);
+ if(team == null)
+ return;
+
+ TeamPoints enemy = teamMap.get(Fight.getOpposite(team));
+ enemy.points += 200.0 / team.getPlayerCount();
+
+ if (enemy.getPoints() >= TeamPoints.WIN_POINTS)
+ end();
+ }
+
+ private void pointInit(FightTeam team) {
+ TeamPoints opponent = teamMap.get(Fight.getOpposite(team));
+ if(getTotalBlocks(team) == 0)
+ return;
+
+ teamMap.get(team).setup(getTotalBlocks(Fight.getOpposite(team)));
+ opponent.setup(getTotalBlocks(team));
+ }
+
+ @Override
+ public Message getDisplay(FightTeam team) {
+ return new Message("BAR_POINTS_OF", team.getPrefix() + (int) Math.round(teamMap.get(team).getPoints()), TeamPoints.WIN_POINTS);
+ }
+
+ private class TeamPoints {
+ protected static final double WIN_POINTS = 1200;
+
+ private final FightTeam team;
+ private double pointGetPerXBlocksBroken;
+ private double points;
+
+ TeamPoints(FightTeam team){
+ this.team = team;
+ }
+
+ private void setup(int enemyBlocks){
+ points = 0;
+
+ int factorIndex = Math.min(Math.max(team.getPlayerCount(), Fight.getOpposite(team).getPlayerCount()), as.length) - 1;
+ double a = as[factorIndex];
+ double e = es[factorIndex];
+ double volume = team.getSchemRegion().volume();
+
+ double needed = enemyBlocks * (a - (a * enemyBlocks) / volume + (e * enemyBlocks) / volume);
+ pointGetPerXBlocksBroken = needed / WIN_POINTS;
+ }
+
+ public double getPoints(){
+ FightTeam fightTeam = Fight.getOpposite(team);
+ return points + (getPercentWincondition().getTotalBlocks(fightTeam) - getPercentWincondition().getCurrentBlocks(fightTeam)) / pointGetPerXBlocksBroken;
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionTimeTechKO.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionTimeTechKO.java
new file mode 100644
index 00000000..1659edbc
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionTimeTechKO.java
@@ -0,0 +1,127 @@
+/*
+ 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.winconditions;
+
+import de.steamwar.fightsystem.countdown.Countdown;
+import de.steamwar.fightsystem.fight.Fight;
+import de.steamwar.fightsystem.fight.FightTeam;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependent;
+import de.steamwar.fightsystem.states.StateDependentListener;
+import de.steamwar.fightsystem.states.StateDependentTask;
+import de.steamwar.fightsystem.utils.Message;
+import de.steamwar.fightsystem.utils.SWSound;
+import org.bukkit.Location;
+import org.bukkit.entity.EntityType;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityExplodeEvent;
+import org.bukkit.event.entity.EntitySpawnEvent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class WinconditionTimeTechKO extends Wincondition implements Listener {
+
+ private static final int TECH_KO_TIME_IN_S = 90;
+ private static final int TECH_KO_HALF_TIME = TECH_KO_TIME_IN_S/2;
+
+ private final Map spawnLocations = new HashMap<>();
+ private final Map countdowns = new HashMap<>();
+ private final Map currentTime = new HashMap<>();
+
+ public WinconditionTimeTechKO(){
+ super("TechKO");
+
+ new StateDependentListener(Winconditions.TIME_TECH_KO, FightState.Running, this);
+ new StateDependentTask(Winconditions.TIME_TECH_KO, FightState.Running, this::run, 20, 20);
+ new StateDependent(Winconditions.TIME_TECH_KO, FightState.Running) {
+ @Override
+ public void enable() {
+ Fight.teams().forEach(team -> currentTime.put(team, TECH_KO_HALF_TIME));
+ }
+
+ @Override
+ public void disable() {
+ spawnLocations.clear();
+ currentTime.clear();
+ countdowns.values().forEach(Countdown::disable);
+ countdowns.clear();
+ }
+ }.register();
+ }
+
+ @EventHandler
+ public void onSpawn(EntitySpawnEvent e) {
+ if(e.getEntityType() != EntityType.PRIMED_TNT)
+ return;
+
+ Location location = e.getLocation();
+ for(FightTeam team : Fight.teams()) {
+ if(team.getExtendRegion().inRegion(location)) {
+ spawnLocations.put(e.getEntity().getEntityId(), team);
+ break;
+ }
+ }
+ }
+
+ @EventHandler
+ public void onExplode(EntityExplodeEvent e) {
+ if(e.getEntityType() != EntityType.PRIMED_TNT)
+ return;
+
+ FightTeam spawn = spawnLocations.remove(e.getEntity().getEntityId());
+ if(spawn == null)
+ return;
+
+ Location location = e.getLocation();
+ for(FightTeam team : Fight.teams()) {
+ if(team != spawn && team.getExtendRegion().inRegion(location)) {
+ currentTime.put(spawn, TECH_KO_HALF_TIME);
+ TechKOCountdown countdown = countdowns.remove(spawn);
+ if(countdown != null)
+ countdown.disable();
+ }
+ }
+ }
+
+ private void run(){
+ currentTime.entrySet().forEach(entry -> {
+ entry.setValue(entry.getValue() - 1);
+ if(entry.getValue() == 0)
+ countdowns.put(entry.getKey(), new TechKOCountdown(entry.getKey(), TECH_KO_HALF_TIME));
+ });
+ }
+
+ private class TechKOCountdown extends Countdown {
+ private final FightTeam team;
+
+ public TechKOCountdown(FightTeam team, int countdownTime) {
+ super(countdownTime, new Message("TECHKO_COUNTDOWN", team.getColoredName()), SWSound.BLOCK_NOTE_PLING, false);
+ this.team = team;
+ super.enable();
+ }
+
+ @Override
+ public void countdownFinished() {
+ win(Fight.getOpposite(team), "WIN_TECHKO", team.getColoredName());
+ }
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionTimeout.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionTimeout.java
new file mode 100644
index 00000000..3207fee8
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionTimeout.java
@@ -0,0 +1,39 @@
+/*
+ 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.winconditions;
+
+import de.steamwar.fightsystem.Config;
+import de.steamwar.fightsystem.countdown.TimeOverCountdown;
+import de.steamwar.fightsystem.states.FightState;
+import de.steamwar.fightsystem.states.StateDependentCountdown;
+
+public class WinconditionTimeout extends Wincondition {
+
+ public WinconditionTimeout() {
+ super("Timeout");
+ if(Config.ActiveWinconditions.contains(Winconditions.TIMEOUT)){
+ timeOverCountdown = new StateDependentCountdown(Winconditions.TIMEOUT, FightState.Running, new TimeOverCountdown(this::timeOver));
+ }
+ }
+
+ protected void timeOver() {
+ win(null, "WIN_TIME_OVER");
+ }
+}
diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/Winconditions.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/Winconditions.java
new file mode 100644
index 00000000..296cf97e
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/Winconditions.java
@@ -0,0 +1,43 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2021 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.winconditions;
+
+public enum Winconditions {
+ TIMEOUT,
+ HEART_RATIO_TIMEOUT,
+ PERCENT_TIMEOUT,
+
+ ALL_DEAD,
+ CAPTAIN_DEAD,
+
+ PERCENT_SYSTEM,
+ POINTS,
+ POINTS_AIRSHIP,
+
+ TIME_TECH_KO,
+ WATER_TECH_KO,
+ PUMPKIN_TECH_KO,
+
+ HELLS_BELLS,
+ METEOR,
+ AMONG_US,
+ PERSISTENT_DAMAGE,
+ TNT_DISTRIBUTION
+}
diff --git a/FightSystem/FightSystem_Core/src/plugin.yml b/FightSystem/FightSystem_Core/src/plugin.yml
new file mode 100644
index 00000000..d8763954
--- /dev/null
+++ b/FightSystem/FightSystem_Core/src/plugin.yml
@@ -0,0 +1,31 @@
+name: FightSystem
+version: "1.0"
+authors:
+- Yaruma3341
+- Lixfel
+main: de.steamwar.fightsystem.FightSystem
+softdepend:
+ - SpigotCore
+depend:
+ - WorldEdit
+api-version: "1.13"
+
+commands:
+ ak:
+ request:
+ requests:
+ fightinfo:
+ leave:
+ ready:
+ kit:
+ remove:
+ leader:
+ lockschem:
+ state:
+ skip:
+ win:
+ resetwg:
+ resettb:
+ tpslimit:
+ tpswarp:
+ unrank:
\ No newline at end of file
diff --git a/FightSystem/FightSystem_Standalone/build.gradle.kts b/FightSystem/FightSystem_Standalone/build.gradle.kts
new file mode 100644
index 00000000..1ac6fcd6
--- /dev/null
+++ b/FightSystem/FightSystem_Standalone/build.gradle.kts
@@ -0,0 +1,65 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("java")
+ id("base")
+ id("com.github.johnrengelman.shadow")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+tasks.build {
+ finalizedBy(tasks.shadowJar)
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs()
+ }
+ resources {
+ srcDirs()
+ }
+ }
+}
+
+dependencies {
+ implementation(project(":FightSystem:FightSystem_Core"))
+ implementation(project(":FightSystem:FightSystem_8"))
+ implementation(project(":FightSystem:FightSystem_9"))
+ implementation(project(":FightSystem:FightSystem_10"))
+ implementation(project(":FightSystem:FightSystem_12"))
+ implementation(project(":FightSystem:FightSystem_14"))
+ implementation(project(":FightSystem:FightSystem_15"))
+ implementation(project(":FightSystem:FightSystem_18"))
+ implementation(project(":FightSystem:FightSystem_19"))
+ implementation(project(":FightSystem:FightSystem_20"))
+ implementation(project(":SpigotCore"))
+}
diff --git a/FightSystem/build.gradle.kts b/FightSystem/build.gradle.kts
new file mode 100644
index 00000000..016e0832
--- /dev/null
+++ b/FightSystem/build.gradle.kts
@@ -0,0 +1,57 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ id("base")
+ id("java")
+
+ id("com.github.johnrengelman.shadow")
+}
+
+group = "de.steamwar"
+version = ""
+
+tasks.compileJava {
+ options.encoding = "UTF-8"
+}
+
+tasks.build {
+ finalizedBy(tasks.shadowJar)
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+dependencies {
+ compileOnly("org.projectlombok:lombok:1.18.32")
+ annotationProcessor("org.projectlombok:lombok:1.18.32")
+
+ implementation(project(":FightSystem:FightSystem_Core"))
+ implementation(project(":FightSystem:FightSystem_8"))
+ implementation(project(":FightSystem:FightSystem_9"))
+ implementation(project(":FightSystem:FightSystem_10"))
+ implementation(project(":FightSystem:FightSystem_12"))
+ implementation(project(":FightSystem:FightSystem_14"))
+ implementation(project(":FightSystem:FightSystem_15"))
+ implementation(project(":FightSystem:FightSystem_18"))
+ implementation(project(":FightSystem:FightSystem_19"))
+ implementation(project(":FightSystem:FightSystem_20"))
+}
diff --git a/build.gradle.kts b/build.gradle.kts
index 074c8b6a..2ff57269 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -50,7 +50,6 @@ plugins {
group = "de.steamwar"
version = ""
-
private val isInCi by lazy { Os.isFamily(Os.FAMILY_UNIX) && ProcessBuilder("hostname").start().inputStream.bufferedReader().readText().startsWith("steamwar.de") }
allprojects {
@@ -112,3 +111,14 @@ allprojects {
}
}
}
+
+sourceSets {
+ main {
+ java {
+ srcDirs()
+ }
+ resources {
+ srcDirs()
+ }
+ }
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 9e83bc5a..9315a3cf 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -20,8 +20,22 @@
rootProject.name = "SteamWar"
include("CommandFramework")
+
include("CommonCore")
+include("FightSystem")
+include("FightSystem:FightSystem_8")
+include("FightSystem:FightSystem_9")
+include("FightSystem:FightSystem_10")
+include("FightSystem:FightSystem_12")
+include("FightSystem:FightSystem_14")
+include("FightSystem:FightSystem_15")
+include("FightSystem:FightSystem_18")
+include("FightSystem:FightSystem_19")
+include("FightSystem:FightSystem_20")
+include("FightSystem:FightSystem_Core")
+include("FightSystem:FightSystem_Standalone")
+
// include("KotlinCore")
include("LobbySystem_2")