diff --git a/FightSystem/FightSystem_10/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper10.java b/FightSystem/FightSystem_10/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper10.java index 78dd79a5..d5f654ae 100644 --- a/FightSystem/FightSystem_10/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper10.java +++ b/FightSystem/FightSystem_10/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper10.java @@ -55,4 +55,7 @@ public class CraftbukkitWrapper10 implements CraftbukkitWrapper { public Stream entityIterator() { return ((CraftWorld) Config.world).getHandle().entityList.stream(); } + + @Override + public void setupGamerule() { } } diff --git a/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper12.java b/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper12.java index f8e843bc..a134b9fe 100644 --- a/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper12.java +++ b/FightSystem/FightSystem_12/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper12.java @@ -55,4 +55,7 @@ public class CraftbukkitWrapper12 implements CraftbukkitWrapper { public Stream entityIterator() { return ((CraftWorld) Config.world).getHandle().entityList.stream(); } + + @Override + public void setupGamerule() { } } diff --git a/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper14.java b/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper14.java index 07c525d4..8e2f96ab 100644 --- a/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper14.java +++ b/FightSystem/FightSystem_14/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper14.java @@ -56,4 +56,7 @@ public class CraftbukkitWrapper14 implements CraftbukkitWrapper { public Stream entityIterator() { return ((CraftWorld) Config.world).getHandle().entitiesById.values().stream(); } + + @Override + public void setupGamerule() { } } diff --git a/FightSystem/FightSystem_15/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper15.java b/FightSystem/FightSystem_15/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper15.java index 70d9e8c5..64a0f9e1 100644 --- a/FightSystem/FightSystem_15/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper15.java +++ b/FightSystem/FightSystem_15/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper15.java @@ -56,4 +56,7 @@ public class CraftbukkitWrapper15 implements CraftbukkitWrapper { public Stream entityIterator() { return ((CraftWorld) Config.world).getHandle().entitiesById.values().stream(); } + + @Override + public void setupGamerule() { } } diff --git a/FightSystem/FightSystem_18/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper18.java b/FightSystem/FightSystem_18/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper18.java index 8fa8e7de..aafd5c08 100644 --- a/FightSystem/FightSystem_18/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper18.java +++ b/FightSystem/FightSystem_18/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper18.java @@ -62,4 +62,7 @@ public class CraftbukkitWrapper18 implements CraftbukkitWrapper { public Stream entityIterator() { return StreamSupport.stream(((Iterable) getIterable.invoke(getWorldEntities.invoke(getWorld.invoke(Config.world)))).spliterator(), false); } + + @Override + public void setupGamerule() { } } diff --git a/FightSystem/FightSystem_21/build.gradle.kts b/FightSystem/FightSystem_21/build.gradle.kts index a05590d1..2da59fce 100644 --- a/FightSystem/FightSystem_21/build.gradle.kts +++ b/FightSystem/FightSystem_21/build.gradle.kts @@ -24,6 +24,7 @@ plugins { dependencies { compileOnly(project(":FightSystem:FightSystem_Core", "default")) compileOnly(project(":FightSystem:FightSystem_18", "default")) + compileOnly(project(":SpigotCore", "default")) compileOnly(libs.paperapi21) { attributes { @@ -40,4 +41,5 @@ dependencies { } compileOnly(libs.fastutil) + compileOnly(libs.authlib) } diff --git a/FightSystem/FightSystem_21/src/de/steamwar/fightsystem/utils/BlockIdWrapper21.java b/FightSystem/FightSystem_21/src/de/steamwar/fightsystem/utils/BlockIdWrapper21.java new file mode 100644 index 00000000..105bbe36 --- /dev/null +++ b/FightSystem/FightSystem_21/src/de/steamwar/fightsystem/utils/BlockIdWrapper21.java @@ -0,0 +1,77 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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 com.mojang.authlib.GameProfile; +import de.steamwar.core.ProtocolWrapper; +import de.steamwar.fightsystem.FightSystem; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.CraftBlockState; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +public class BlockIdWrapper21 implements BlockIdWrapper { + + @Override + public Material idToMaterial(int blockState) { + return CraftMagicNumbers.getMaterial(net.minecraft.world.level.block.Block.stateById(blockState)).getItemType(); + } + + @Override + public int blockToId(Block block) { + return net.minecraft.world.level.block.Block.getId(((CraftBlockState) block.getState()).getHandle()); + } + + @Override + public void setBlock(World world, int x, int y, int z, int blockState) { + BlockState blockData = net.minecraft.world.level.block.Block.stateById(blockState); + ServerLevel level = ((CraftWorld) world).getHandle(); + BlockPos pos = new BlockPos(x, y, z); + + level.removeBlockEntity(pos); + level.setBlock(pos, blockData, blockState); + level.getChunkSource().blockChanged(pos); + } + + @Override + public void trackEntity(Player player, Entity entity) { + if(entity instanceof Player) + TinyProtocol.instance.sendPacket(player, ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE)); + + player.showEntity(FightSystem.getPlugin(), entity); + } + + @Override + public void untrackEntity(Player player, Entity entity) { + player.hideEntity(FightSystem.getPlugin(), entity); + + if(entity instanceof Player) + TinyProtocol.instance.sendPacket(player, ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE)); + } +} diff --git a/FightSystem/FightSystem_21/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper21.java b/FightSystem/FightSystem_21/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper21.java index b5c920a7..312dce88 100644 --- a/FightSystem/FightSystem_21/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper21.java +++ b/FightSystem/FightSystem_21/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper21.java @@ -19,12 +19,43 @@ package de.steamwar.fightsystem.utils; +import de.steamwar.fightsystem.Config; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import org.bukkit.GameRule; +import org.bukkit.World; +import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.entity.Entity; +import java.util.HashSet; +import java.util.Set; + public class CraftbukkitWrapper21 extends CraftbukkitWrapper18 { @Override public float headRotation(Entity e) { return getEntity(e).getYHeadRot(); } + + @Override + public void setupGamerule() { + Config.world.setGameRule(GameRule.LOCATOR_BAR, false); + } + + private LevelChunk getChunk(World world, int x, int z) { + return ((CraftWorld) world).getHandle().getChunk(x, z); + } + + @Override + public void resetChunk(World world, World backup, int x, int z) { + LevelChunk worldChunk = getChunk(world, x, z); + LevelChunk backupChunk = getChunk(backup, x, z); + LevelChunkSection[] sections = worldChunk.getSections(); + System.arraycopy(backupChunk.getSections(), 0, sections, 0, sections.length); + Set blocks = new HashSet<>(worldChunk.blockEntities.keySet()); + blocks.stream().filter(key -> !backupChunk.blockEntities.containsKey(key)).forEach(worldChunk::removeBlockEntity); + worldChunk.heightmaps.clear(); + worldChunk.heightmaps.putAll(backupChunk.heightmaps); + } } diff --git a/FightSystem/FightSystem_21/src/de/steamwar/fightsystem/utils/ReflectionWrapper21.java b/FightSystem/FightSystem_21/src/de/steamwar/fightsystem/utils/ReflectionWrapper21.java new file mode 100644 index 00000000..118bfba1 --- /dev/null +++ b/FightSystem/FightSystem_21/src/de/steamwar/fightsystem/utils/ReflectionWrapper21.java @@ -0,0 +1,36 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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 io.papermc.paper.datacomponent.DataComponentTypes; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class ReflectionWrapper21 implements ReflectionWrapper { + @Override + public Object explosionHider(Player player, Object packet, PacketHiderFunction packetHiderFunction) { + return packet; + } + + @Override + public boolean hasItems(ItemStack stack) { + return stack.getDataTypes().stream().anyMatch(dataComponentType -> dataComponentType != DataComponentTypes.ENCHANTMENTS || dataComponentType != DataComponentTypes.DAMAGE); + } +} diff --git a/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper8.java b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper8.java index 8d743ae7..e7597dd7 100644 --- a/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper8.java +++ b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper8.java @@ -52,4 +52,7 @@ public class CraftbukkitWrapper8 implements CraftbukkitWrapper { public Stream entityIterator() { return ((CraftWorld) Config.world).getHandle().entityList.stream(); } + + @Override + public void setupGamerule() { } } diff --git a/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/ReflectionWrapper8.java b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/ReflectionWrapper8.java new file mode 100644 index 00000000..6da5308a --- /dev/null +++ b/FightSystem/FightSystem_8/src/de/steamwar/fightsystem/utils/ReflectionWrapper8.java @@ -0,0 +1,59 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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.Reflection; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Function; + +public class ReflectionWrapper8 implements ReflectionWrapper { + + private static final Class packetPlayOutExplosion = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundExplodePacket"); + private static final Reflection.Field explosionBlocks = Reflection.getField(packetPlayOutExplosion, List.class, 0); + private static final Function explosionLocation = HullHider.posPacketToLocation(packetPlayOutExplosion, double.class, 1.0); + + @Override + public Object explosionHider(Player player, Object packet, PacketHiderFunction packetHiderFunction) { + if(explosionBlocks.get(packet).isEmpty()) + return packetHiderFunction.hide(player, packet, explosionLocation.apply(packet)); + + return packet; + } + + + private static final Class itemStack = Reflection.getClass("net.minecraft.world.item.ItemStack"); + private static final Reflection.Method asNMSCopy = Reflection.getTypedMethod(Reflection.getClass("org.bukkit.craftbukkit.inventory.CraftItemStack"), "asNMSCopy", itemStack, ItemStack.class); + private static final Class nbtTagCompound = Reflection.getClass("net.minecraft.nbt.CompoundTag"); + private static final Reflection.Method getTag = Reflection.getTypedMethod(itemStack, null, nbtTagCompound); + private static final Reflection.Method getKeys = Reflection.getTypedMethod(nbtTagCompound, null, Set.class); + @Override + public 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(); + } +} diff --git a/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper9.java b/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper9.java index 54281ed3..c7f393b3 100644 --- a/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper9.java +++ b/FightSystem/FightSystem_9/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper9.java @@ -55,4 +55,7 @@ public class CraftbukkitWrapper9 implements CraftbukkitWrapper { public Stream entityIterator() { return ((CraftWorld) Config.world).getHandle().entityList.stream(); } + + @Override + public void setupGamerule() { } } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java index 1ff85e23..a95670a0 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java @@ -188,6 +188,8 @@ public class FightSystem extends JavaPlugin { }else if(Config.mode == ArenaMode.PREPARE) { Fight.getUnrotated().setSchem(SchematicNode.getSchematicNode(Config.PrepareSchemID)); } + + CraftbukkitWrapper.impl.setupGamerule(); } @Override diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/Kit.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/Kit.java index db97aa27..48a18bde 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/Kit.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/Kit.java @@ -26,6 +26,7 @@ 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.fightsystem.utils.ReflectionWrapper; import de.steamwar.inventory.SWInventory; import de.steamwar.inventory.SWItem; import de.steamwar.sql.PersonalKit; @@ -214,17 +215,8 @@ public class Kit { assert normal != null; return !normal.isEnchantmentInKit(stack) && !stack.getEnchantments().isEmpty(); } - - private static final Class itemStack = Reflection.getClass("net.minecraft.world.item.ItemStack"); - private static final Reflection.Method asNMSCopy = Reflection.getTypedMethod(Reflection.getClass("org.bukkit.craftbukkit.inventory.CraftItemStack"), "asNMSCopy", itemStack, ItemStack.class); - private static final Class nbtTagCompound = Reflection.getClass("net.minecraft.nbt.CompoundTag"); - private static final Reflection.Method getTag = Reflection.getTypedMethod(itemStack, null, nbtTagCompound); - private static final Reflection.Method 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(); + return ReflectionWrapper.impl.hasItems(stack); } private boolean isEnchantmentInKit(ItemStack stack){ diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper.java index 242119f5..e008a3bd 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper.java @@ -33,4 +33,6 @@ public interface CraftbukkitWrapper { float headRotation(Entity e); Stream entityIterator(); + + void setupGamerule(); } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java index c050656d..137e2082 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java @@ -62,6 +62,7 @@ public class HullHider implements Listener { private final Hull[] hulls; private final Map, BiFunction> packetHiders = new HashMap<>(); + private static final Class packetPlayOutExplosion = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundExplodePacket"); public HullHider() { if(!TechHiderWrapper.ENABLED) { hulls = new Hull[0]; @@ -208,14 +209,8 @@ public class HullHider implements Listener { 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("net.minecraft.network.protocol.game.ClientboundExplodePacket"); - private static final Reflection.Field 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; + return ReflectionWrapper.impl.explosionHider(player, packet, this::packetHider); } private void posHiderGenerator(String typeName, Class posType, double factor) { @@ -224,7 +219,7 @@ public class HullHider implements Listener { packetHiders.put(type, (player, packet) -> packetHider(player, packet, location.apply(packet))); } - private static Function posPacketToLocation(Class type, Class posType, double factor) { + public static Function posPacketToLocation(Class type, Class posType, double factor) { Reflection.Field x = Reflection.getField(type, posType, 0); Reflection.Field y = Reflection.getField(type, posType, 1); Reflection.Field z = Reflection.getField(type, posType, 2); diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/ReflectionWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/ReflectionWrapper.java new file mode 100644 index 00000000..cbc9f807 --- /dev/null +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/ReflectionWrapper.java @@ -0,0 +1,37 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * 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.Location; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public interface ReflectionWrapper { + ReflectionWrapper impl = VersionDependent.getVersionImpl(FightSystem.getPlugin()); + + Object explosionHider(Player player, Object packet, PacketHiderFunction packetHiderFunction); + boolean hasItems(ItemStack stack); + + public interface PacketHiderFunction { + Object hide(Player player, Object packet, Location location); + } +} diff --git a/FightSystem/FightSystem_Core/src/plugin.yml b/FightSystem/FightSystem_Core/src/plugin.yml index 6925bf69..09702025 100644 --- a/FightSystem/FightSystem_Core/src/plugin.yml +++ b/FightSystem/FightSystem_Core/src/plugin.yml @@ -8,7 +8,7 @@ softdepend: - SpigotCore depend: - WorldEdit -api-version: "1.13" +api-version: "1.21.6" commands: ak: diff --git a/FightSystem/build.gradle.kts b/FightSystem/build.gradle.kts index 9013824d..6338609a 100644 --- a/FightSystem/build.gradle.kts +++ b/FightSystem/build.gradle.kts @@ -50,6 +50,17 @@ tasks.register("WarGear20") { config = "WarGear20.yml" } +tasks.register("WarGear21") { + group = "run" + description = "Run a WarGear 1.21 Fight Server" + dependsOn(":SpigotCore:shadowJar") + dependsOn(":FightSystem:shadowJar") + template = "WarGear21" + worldName = "arenas/Pentraki" + config = "WarGear20.yml" + jar = "/jars/paper-1.21.6.jar" +} + tasks.register("SpaceCraftDev20") { group = "run" description = "Run a SpaceCraftDev 1.20 Fight Server" diff --git a/SpigotCore/SpigotCore_21/build.gradle.kts b/SpigotCore/SpigotCore_21/build.gradle.kts index 8f2acba4..4b26ba16 100644 --- a/SpigotCore/SpigotCore_21/build.gradle.kts +++ b/SpigotCore/SpigotCore_21/build.gradle.kts @@ -37,4 +37,7 @@ dependencies { compileOnly(libs.paperapi21) compileOnly(libs.nms21) + compileOnly(libs.datafixer) + compileOnly(libs.netty) + compileOnly(libs.authlib) } diff --git a/SpigotCore/SpigotCore_21/src/de/steamwar/core/ProtocolWrapper21.java b/SpigotCore/SpigotCore_21/src/de/steamwar/core/ProtocolWrapper21.java new file mode 100644 index 00000000..7018aa64 --- /dev/null +++ b/SpigotCore/SpigotCore_21/src/de/steamwar/core/ProtocolWrapper21.java @@ -0,0 +1,66 @@ +/* + * 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.core; + +import com.mojang.authlib.GameProfile; +import com.mojang.datafixers.util.Pair; +import de.steamwar.Reflection; +import net.minecraft.Util; +import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; +import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket; +import net.minecraft.world.level.GameType; +import org.bukkit.GameMode; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.function.LongSupplier; + +public class ProtocolWrapper21 implements ProtocolWrapper { + + private static final Reflection.Field equipmentStack = Reflection.getField(equipmentPacket, List.class, 0); + @Override + public void setEquipmentPacketStack(Object packet, Object slot, Object stack) { + equipmentStack.set(packet, Collections.singletonList(new Pair<>(slot, stack))); + } + + private static final Reflection.Constructor removePacketConstructor = Reflection.getConstructor(ClientboundPlayerInfoRemovePacket.class, List.class); + + @Override + @SuppressWarnings("deprecation") + public Object playerInfoPacketConstructor(PlayerInfoAction action, GameProfile profile, GameMode mode) { + if(action == PlayerInfoAction.REMOVE) + return removePacketConstructor.invoke(Collections.singletonList(profile.getId())); + return switch (action) { + case ADD -> new ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE), new ClientboundPlayerInfoUpdatePacket.Entry( + profile.getId(), profile, true, 0, GameType.byId(mode.getValue()), null, true, 0, null + )); + case GAMEMODE -> new ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE), new ClientboundPlayerInfoUpdatePacket.Entry( + profile.getId(), profile, true, 0, GameType.byId(mode.getValue()), null, true, 0, null + )); + default -> null; + }; + } + + @Override + public void initTPSWarp(LongSupplier longSupplier) { + Util.timeSource = () -> System.nanoTime() + longSupplier.getAsLong(); + } +} diff --git a/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ChunkHider21.java b/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ChunkHider21.java new file mode 100644 index 00000000..0d0cb782 --- /dev/null +++ b/SpigotCore/SpigotCore_21/src/de/steamwar/techhider/ChunkHider21.java @@ -0,0 +1,157 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import de.steamwar.Reflection; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.util.SimpleBitStorage; +import net.minecraft.world.level.block.entity.BlockEntityType; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +public class ChunkHider21 implements ChunkHider { + @Override + public Class mapChunkPacket() { + return ClientboundLevelChunkWithLightPacket.class; + } + + private static final UnaryOperator chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); + private static final UnaryOperator chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); + + private static final Reflection.Field chunkXField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0); + private static final Reflection.Field chunkZField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1); + private static final Reflection.Field chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0); + + private static final Reflection.Field dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0); + private static final Reflection.Field tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0); + + @Override + public BiFunction chunkHiderGenerator(TechHider techHider) { + return (p, packet) -> { + int chunkX = chunkXField.get(packet); + int chunkZ = chunkZField.get(packet); + if (techHider.getLocationEvaluator().skipChunk(p, chunkX, chunkZ)) + return packet; + + packet = chunkPacketCloner.apply(packet); + Object dataWrapper = chunkDataCloner.apply(chunkData.get(packet)); + + Set hiddenBlockEntities = techHider.getHiddenBlockEntities(); + tileEntities.set(dataWrapper, ((List)tileEntities.get(dataWrapper)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList())); + + ByteBuf in = Unpooled.wrappedBuffer(dataField.get(dataWrapper)); + ByteBuf out = Unpooled.buffer(in.readableBytes() + 64); + for(int yOffset = p.getWorld().getMinHeight(); yOffset < p.getWorld().getMaxHeight(); yOffset += 16) { + SectionHider section = new SectionHider(p, techHider, in, out, chunkX, yOffset/16, chunkZ); + section.copyBlockCount(); + + blocks(section); + biomes(section); + } + + if (in.readableBytes() != 0) { + throw new IllegalStateException("ChunkHider21: Incomplete chunk data, " + in.readableBytes() + " bytes left"); + } + + byte[] data = new byte[out.readableBytes()]; + out.readBytes(data); + dataField.set(dataWrapper, data); + + chunkData.set(packet, dataWrapper); + return packet; + }; + } + + public static final Class tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo"); + protected static final Reflection.Field entityType = Reflection.getField(tileEntity, BlockEntityType.class, 0); + private static final Class builtInRegestries = Reflection.getClass("net.minecraft.core.registries.BuiltInRegistries"); + private static final Class registry = Reflection.getClass("net.minecraft.core.Registry"); + private static final Reflection.Field nameField = Reflection.getField(builtInRegestries, "BLOCK_ENTITY_TYPE", registry); + private static final Class resourceLocation = Reflection.getClass("net.minecraft.resources.ResourceLocation"); + private static final Reflection.Method getKey = Reflection.getTypedMethod(registry, "getKey", resourceLocation, Object.class); + private static final Reflection.Method getName = Reflection.getTypedMethod(resourceLocation, "getPath", String.class); + protected boolean tileEntityVisible(Set hiddenBlockEntities, Object tile) { + return !hiddenBlockEntities.contains(getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile)))); + } + + private void blocks(SectionHider section) { + section.copyBitsPerBlock(); + + boolean singleValued = section.getBitsPerBlock() == 0; + if (singleValued) { + int value = ProtocolUtils.readVarInt(section.getIn()); + ProtocolUtils.writeVarInt(section.getOut(), !section.isSkipSection() && section.getObfuscate().contains(value) ? section.getTarget() : value); + return; + } else { + section.processPalette(); + } + + if (section.isSkipSection() || (!section.blockPrecise() && section.isPaletted())) { + section.skipNewDataArray(4096); + return; + } + + SimpleBitStorage values = new SimpleBitStorage(section.getBitsPerBlock(), 4096, section.readNewDataArray(4096)); + + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + int pos = (((y * 16) + z) * 16) + x; + + TechHider.State test = section.test(x, y, z); + + switch (test) { + case SKIP: + break; + case CHECK: + if (!section.getObfuscate().contains(values.get(pos))) + break; + case HIDE: + values.set(pos, section.getTarget()); + break; + case HIDE_AIR: + default: + values.set(pos, section.getAir()); + } + } + } + } + + section.writeDataArray(values.getRaw()); + } + + private void biomes(SectionHider section) { + section.copyBitsPerBlock(); + if(section.getBitsPerBlock() == 0) { + section.copyVarInt(); + } else if(section.getBitsPerBlock() < 6) { + section.skipPalette(); + section.skipNewDataArray(64); + } + } +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/Reflection.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/Reflection.java index 86dca0bf..ff298c1a 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/Reflection.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/Reflection.java @@ -157,7 +157,23 @@ public final class Reflection { } else if(MAJOR_VERSION < 21 || MINOR_VERSION < 4) { return Class.forName(spigotClassnames.getOrDefault(name, name)); } else { - return Class.forName(name); + Class clazz = null; + try { + clazz = Class.forName(name); + } catch (ClassNotFoundException e) {} + if (clazz != null && clazz.getName().equals(name)) { + return clazz; + } + + try { + return Core.class.getClassLoader().getParent().loadClass(name); + } catch (ClassNotFoundException e) { + if (clazz == null) { + throw e; + } + + return clazz; + } } } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Cannot find " + name, e); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index a74feb42..e1d2b5e9 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -53,6 +53,7 @@ public interface ChunkHider { private boolean paletted; private int bitsPerBlock; + private int blockCount; private int air; private int target; private Set obfuscate; @@ -86,7 +87,8 @@ public interface ChunkHider { } public void copyBlockCount() { - out.writeShort(in.readShort()); + this.blockCount = in.readShort(); + out.writeShort(blockCount); } public void copyBitsPerBlock() { @@ -140,6 +142,16 @@ public interface ChunkHider { out.writeBytes(in, dataArrayLength*8); } + public void skipNewDataArray(int entries) { + if (bitsPerBlock == 0) { + return; + } + + char valuesPerLong = (char)(64 / bitsPerBlock); + int i1 = (entries + valuesPerLong - 1) / valuesPerLong; + out.writeBytes(in, i1 * Long.BYTES); + } + public long[] readDataArray() { long[] array = new long[copyVarInt()]; for(int i = 0; i < array.length; i++) @@ -148,6 +160,20 @@ public interface ChunkHider { return array; } + public long[] readNewDataArray(int entries) { + if (bitsPerBlock == 0) { + return new long[entries]; + } + + char valuesPerLong = (char) (64 / bitsPerBlock); + int i1 = (entries + valuesPerLong - 1) / valuesPerLong; + long[] array = new long[i1]; + for(int i = 0; i < i1; i++) + array[i] = in.readLong(); + + return array; + } + public void writeDataArray(long[] array) { for(long l : array) out.writeLong(l); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java index 57f27921..3949d7ed 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java @@ -58,6 +58,16 @@ public class ProtocolUtils { }; } + public static UnaryOperator shallowTypedCloneGenerator(Class clazz) { + BiConsumer filler = shallowFill(clazz); + + return source -> { + Object clone = Reflection.newInstance(clazz); + filler.accept(source, clone); + return (T) clone; + }; + } + private static BiConsumer shallowFill(Class clazz) { if(clazz == null) return (source, clone) -> {}; diff --git a/SpigotCore/SpigotCore_Main/src/plugin.yml b/SpigotCore/SpigotCore_Main/src/plugin.yml index a29249d2..cd807cd2 100644 --- a/SpigotCore/SpigotCore_Main/src/plugin.yml +++ b/SpigotCore/SpigotCore_Main/src/plugin.yml @@ -1,7 +1,7 @@ name: SpigotCore version: "2.0" author: Lixfel -api-version: "1.13" +api-version: "1.21" load: STARTUP softdepend: - WorldEdit diff --git a/VelocityCore/src/de/steamwar/command/TypeUtils.java b/VelocityCore/src/de/steamwar/command/TypeUtils.java index a273cda2..db22ec99 100644 --- a/VelocityCore/src/de/steamwar/command/TypeUtils.java +++ b/VelocityCore/src/de/steamwar/command/TypeUtils.java @@ -46,7 +46,9 @@ public class TypeUtils { Player player = sender.getPlayer(); if (player != null && s.isEmpty()) { ProtocolVersion version = player.getProtocolVersion(); - if (version.greaterThan(ProtocolVersion.MINECRAFT_1_19_4)) { + if (version.greaterThan(ProtocolVersion.MINECRAFT_1_20_5)) { + return ServerVersion.PAPER_21; + } else if (version.greaterThan(ProtocolVersion.MINECRAFT_1_19_4)) { return ServerVersion.PAPER_20; } else if (version.greaterThan(ProtocolVersion.MINECRAFT_1_15_2)) { return ServerVersion.PAPER_19; diff --git a/VelocityCore/src/de/steamwar/velocitycore/ServerStarter.java b/VelocityCore/src/de/steamwar/velocitycore/ServerStarter.java index 058771e7..db199b97 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/ServerStarter.java +++ b/VelocityCore/src/de/steamwar/velocitycore/ServerStarter.java @@ -61,7 +61,7 @@ public class ServerStarter { private String worldDir = null; private Node node = null; - private ServerVersion version = ServerVersion.PAPER_20; + private ServerVersion version = ServerVersion.PAPER_21; private Portrange portrange = BAU_PORTS; private Function serverNameProvider = port -> node.getName() + port; private BooleanSupplier startCondition = () -> true; diff --git a/settings.gradle.kts b/settings.gradle.kts index 8ab618ff..6665697f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -126,7 +126,7 @@ dependencyResolutionManagement { library("nms18", "de.steamwar:spigot:1.18") library("nms19", "de.steamwar:spigot:1.19") library("nms20", "de.steamwar:spigot:1.20") - library("nms21", "de.steamwar:spigot:1.21.5") + library("nms21", "de.steamwar:spigot:1.21.6") library("axiom", "de.steamwar:axiompaper:RELEASE") library("worldedit12", "de.steamwar:worldedit:1.12")