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 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 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 posType, double factor) { + Reflection.FieldAccessor x = Reflection.getField(type, posType, 0); + Reflection.FieldAccessor y = Reflection.getField(type, posType, 1); + Reflection.FieldAccessor 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")