diff --git a/.idea/runConfigurations/Remote_JVM_Debugger.xml b/.idea/runConfigurations/Remote_JVM_Debugger.xml new file mode 100644 index 00000000..d7e6e149 --- /dev/null +++ b/.idea/runConfigurations/Remote_JVM_Debugger.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursor.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursor.java index a59a0206..c557f95f 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursor.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursor.java @@ -80,7 +80,7 @@ public class SimulatorCursor implements Listener { } public SimulatorCursor() { - BiFunction function = (player, object) -> { + BiFunction function = (player, object) -> { calcCursor(player); return object; }; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/laufbau/BlockBoundingBox.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/laufbau/BlockBoundingBox.java index 12636ba9..0df055af 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/laufbau/BlockBoundingBox.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/laufbau/BlockBoundingBox.java @@ -21,7 +21,6 @@ package de.steamwar.bausystem.features.slaves.laufbau; import com.sk89q.worldedit.blocks.SkullBlock; import com.sk89q.worldedit.world.block.BaseBlock; -import de.steamwar.bausystem.utils.NMSWrapper; import de.steamwar.inventory.SWItem; import lombok.Getter; import lombok.ToString; @@ -99,7 +98,7 @@ public class BlockBoundingBox { // addPixel(Material.COBWEB.createBlockData(), 0, 0, 0, 0, 0, 0, createItem("LAUFBAU_BLOCK_COBWEB", Material.COBWEB)); addPixel(Material.END_STONE.createBlockData(), 0, 0, 0, 16, 16, 16, null); - addPixel(NMSWrapper.impl.pathMaterial().createBlockData(), 0, 0, 0, 16, 15, 16, createItem("LAUFBAU_BLOCK_GRASS_PATH", NMSWrapper.impl.pathMaterial())); + addPixel(Material.DIRT_PATH.createBlockData(), 0, 0, 0, 16, 15, 16, createItem("LAUFBAU_BLOCK_GRASS_PATH", Material.DIRT_PATH)); addPixel(Material.MUD.createBlockData(), 0, 0, 0, 16, 14, 16, createItem("LAUFBAU_BLOCK_SOUL_SAND", Material.SOUL_SAND)); Cocoa cocoaNorth = (Cocoa) Material.COCOA.createBlockData(); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java index f737af0a..4744e41d 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java @@ -31,7 +31,6 @@ import de.steamwar.command.TypeMapper; import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.linkage.Linked; import de.steamwar.linkage.LinkedInstance; -import de.steamwar.techhider.TechHider; import net.md_5.bungee.api.ChatMessageType; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; @@ -39,6 +38,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; +import de.steamwar.techhider.legacy.TechHider; import java.util.*; import java.util.stream.Collectors; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSSystem.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSSystem.java index a9b36c50..3e1c2772 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSSystem.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tpslimit/TPSSystem.java @@ -19,13 +19,13 @@ package de.steamwar.bausystem.features.tpslimit; +import com.destroystokyo.paper.event.server.ServerTickEndEvent; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.Permission; import de.steamwar.bausystem.SWUtils; import de.steamwar.bausystem.linkage.BauGuiItem; import de.steamwar.bausystem.region.Region; import de.steamwar.bausystem.utils.ScoreboardElement; -import de.steamwar.bausystem.utils.TickEndEvent; import de.steamwar.bausystem.utils.TickManager; import de.steamwar.bausystem.utils.bossbar.BauSystemBossbar; import de.steamwar.bausystem.utils.bossbar.BossBarService; @@ -72,7 +72,7 @@ public class TPSSystem implements Listener { } @EventHandler - public void onTickEnd(TickEndEvent event) { + public void onTickEnd(ServerTickEndEvent event) { bossbar(); } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java index 36829c3b..0b10e163 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java @@ -21,17 +21,22 @@ package de.steamwar.bausystem.features.util; import com.comphenix.tinyprotocol.TinyProtocol; import com.mojang.authlib.GameProfile; +import de.steamwar.Reflection; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.features.tpslimit.TPSUtils; import de.steamwar.bausystem.utils.BauMemberUpdateEvent; -import de.steamwar.bausystem.utils.NMSWrapper; import de.steamwar.command.SWCommand; import de.steamwar.core.ProtocolWrapper; import de.steamwar.core.SWPlayer; import de.steamwar.linkage.Linked; import net.minecraft.network.protocol.game.*; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.entity.player.Abilities; +import net.minecraft.world.level.GameType; import org.bukkit.Bukkit; import org.bukkit.GameMode; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -57,11 +62,11 @@ public class NoClipCommand extends SWCommand implements Listener { public NoClipCommand() { super("noclip", "nc"); - BiFunction first = (player, o) -> { + BiFunction first = (player, o) -> { NoClipData noClipData = SWPlayer.of(player).getComponent(NoClipData.class).orElse(null); if (noClipData == null) return o; if (noClipData.lastTick == TPSUtils.currentTick.get()) return o; - NMSWrapper.impl.setInternalGameMode(player, GameMode.SPECTATOR); + setInternalGameMode(player, GameMode.SPECTATOR); noClipData.lastTick = TPSUtils.currentTick.get(); return o; }; @@ -71,7 +76,7 @@ public class NoClipCommand extends SWCommand implements Listener { BiFunction second = (player, o) -> { NoClipData noClipData = SWPlayer.of(player).getComponent(NoClipData.class).orElse(null); if (noClipData == null) return o; - NMSWrapper.impl.setInternalGameMode(player, GameMode.CREATIVE); + setInternalGameMode(player, GameMode.CREATIVE); noClipData.lastTick = TPSUtils.currentTick.get(); return o; }; @@ -79,15 +84,31 @@ public class NoClipCommand extends SWCommand implements Listener { TinyProtocol.instance.addFilter(ServerboundPlayerActionPacket.class, second); TinyProtocol.instance.addFilter(ServerboundContainerClickPacket.class, second); - BiFunction third = (player, o) -> { + BiFunction third = (player, o) -> { if (SWPlayer.of(player).hasComponent(NoClipData.class)) { - NMSWrapper.impl.setSlotToItemStack(player, o); + int index = o.slotNum(); + if (index >= 36 && index <= 44) { + index -= 36; + } else if (index > 44) { + index -= 5; + } else if (index <= 8) { + index = index - 8 + 36; + } + player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(o.itemStack())); + if (index < 9) player.getInventory().setHeldItemSlot(index); + player.updateInventory(); } return o; }; TinyProtocol.instance.addFilter(ServerboundSetCreativeModeSlotPacket.class, third); } + private static final Reflection.Field playerGameMode = Reflection.getField(ServerPlayerGameMode.class, GameType.class, 0); + + private void setInternalGameMode(Player player, GameMode gameMode) { + playerGameMode.set(((CraftPlayer) player).getHandle().gameMode, GameType.byId(gameMode.getValue())); + } + @Register(help = true) public void genericCommand(@Validator Player player) { SWPlayer swPlayer = SWPlayer.of(player); @@ -95,7 +116,9 @@ public class NoClipCommand extends SWCommand implements Listener { swPlayer.removeComponent(NoClipData.class); } else { player.setGameMode(GameMode.SPECTATOR); - NMSWrapper.impl.setPlayerBuildAbilities(player); + Abilities abilities = (((CraftPlayer) player).getHandle()).getAbilities(); + abilities.mayBuild = true; + abilities.mayfly = true; swPlayer.setComponent(new NoClipData()); BauSystem.MESSAGE.send("OTHER_NOCLIP_SLOT_INFO", player); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java index a3f912ec..ed5b068b 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java @@ -28,6 +28,7 @@ import de.steamwar.bausystem.utils.bossbar.BossBarService; import de.steamwar.linkage.Linked; import de.steamwar.network.packets.PacketHandler; import de.steamwar.network.packets.server.BaumemberUpdatePacket; +import io.papermc.paper.event.player.PlayerPickBlockEvent; import org.bukkit.Bukkit; import org.bukkit.boss.BarColor; import org.bukkit.boss.BarStyle; @@ -35,6 +36,8 @@ 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.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerItemConsumeEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; @@ -127,4 +130,11 @@ public class BauMemberUpdate extends PacketHandler implements Listener { } }, 1); } + + @EventHandler + public void onPlayerClick(PlayerPickBlockEvent event) { + if(SPECTATORS.contains(event.getPlayer())) { + event.setCancelled(true); + } + } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/InventoryListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/InventoryListener.java index edc9f9c2..964f50e6 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/InventoryListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/InventoryListener.java @@ -19,8 +19,9 @@ package de.steamwar.bausystem.features.world; -import de.steamwar.bausystem.utils.NMSWrapper; import de.steamwar.linkage.Linked; +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.ItemContainerContents; import org.bukkit.attribute.Attribute; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; @@ -32,6 +33,8 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import java.util.List; + @Linked public class InventoryListener implements Listener { @@ -57,7 +60,7 @@ public class InventoryListener implements Listener { } stack.setItemMeta(meta); } - if (NMSWrapper.impl.checkItemStack(stack)) { + if (checkItemStack(stack)) { e.setCurrentItem(null); e.setCancelled(true); return; @@ -73,7 +76,7 @@ public class InventoryListener implements Listener { for (int i = 0; i < content.length; i++) { if (content[i] == null) continue; int finalI = i; - if (NMSWrapper.impl.checkItemStack(content[finalI])) { + if (checkItemStack(content[finalI])) { p.getInventory().setItem(i, null); } } @@ -82,11 +85,44 @@ public class InventoryListener implements Listener { @EventHandler(ignoreCancelled = true) public void onBlockPlace(BlockPlaceEvent event) { Player p = event.getPlayer(); - if (NMSWrapper.impl.checkItemStack(event.getItemInHand())) { + if (checkItemStack(event.getItemInHand())) { event.setCancelled(true); event.setBuild(false); p.getInventory().setItemInMainHand(null); p.getInventory().setItemInOffHand(null); } } + + private static final int threshold = 2048; + + private boolean checkItemStack(ItemStack item) { + ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER); + if (data == null) { + return false; + } + + return drillDown(data.contents(), 0, 0) > threshold; + } + + private int drillDown(List items, int layer, int start) { + if (layer > 2) return start + threshold; + int invalid = start; + for (int i = start; i < items.size(); i++) { + ItemStack item = items.get(i); + if (item.isEmpty()) continue; + + invalid += item.getAmount(); + + ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER); + if (data == null) { + continue; + } + + List subItems = data.contents(); + if (subItems.size() > 1) { + invalid = drillDown(subItems, layer + 1, invalid); + } + } + return invalid; + } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/NoCreativeKnockback.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/NoCreativeKnockback.java index 2debf84e..55b7b0f4 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/NoCreativeKnockback.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/NoCreativeKnockback.java @@ -20,18 +20,24 @@ package de.steamwar.bausystem.features.world; import com.comphenix.tinyprotocol.TinyProtocol; -import de.steamwar.bausystem.utils.NMSWrapper; import de.steamwar.linkage.Linked; import net.minecraft.network.protocol.game.ClientboundExplodePacket; import org.bukkit.GameMode; +import java.util.Optional; + @Linked public class NoCreativeKnockback { public NoCreativeKnockback() { TinyProtocol.instance.addFilter(ClientboundExplodePacket.class, (player, o) -> { if (player.getGameMode() != GameMode.CREATIVE) return o; - return NMSWrapper.impl.resetExplosionKnockback(o); + return new ClientboundExplodePacket( + o.center(), + Optional.empty(), + o.explosionParticle(), + o.explosionSound() + ); }); } } diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SignEdit.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SignEdit.java index 95f0a337..5fcbcbf5 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SignEdit.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SignEdit.java @@ -101,7 +101,7 @@ public class SignEdit implements Listener { } { - TinyProtocol.instance.addTypedFilter(ServerboundSignUpdatePacket.class, (player, o) -> { + TinyProtocol.instance.addFilter(ServerboundSignUpdatePacket.class, (player, o) -> { Bukkit.getScheduler().runTask(BauSystem.getInstance(), () -> { ServerLevel serverLevel = ((CraftWorld) player.getWorld()).getHandle(); Block signLoc = CraftBlock.at(serverLevel, o.getPos()); diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java index c9e6a3a6..92a5c59e 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java @@ -25,7 +25,6 @@ import de.steamwar.bausystem.config.BauServer; import de.steamwar.bausystem.utils.BauMemberUpdateEvent; import de.steamwar.linkage.Linked; import de.steamwar.sql.BauweltMember; -import de.steamwar.techhider.TechHider; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -40,6 +39,7 @@ import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.player.*; import org.bukkit.util.Vector; +import de.steamwar.techhider.legacy.TechHider; import java.util.HashSet; import java.util.Set; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java index c2cb9091..55bcbeeb 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java @@ -28,7 +28,6 @@ import de.steamwar.command.SWCommand; import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.linkage.Linked; import de.steamwar.linkage.LinkedInstance; -import de.steamwar.techhider.TechHider; import net.md_5.bungee.api.ChatMessageType; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import net.minecraft.server.level.ServerPlayer; @@ -41,6 +40,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerQuitEvent; +import de.steamwar.techhider.legacy.TechHider; import java.util.*; import java.util.function.BiFunction; @@ -115,8 +115,8 @@ public class XrayCommand extends SWCommand implements Listener, ScoreboardElemen return packet; }; - TinyProtocol.instance.addTypedFilter(ServerboundMovePlayerPacket.Pos.class, positionSetter); - TinyProtocol.instance.addTypedFilter(ServerboundMovePlayerPacket.PosRot.class, positionSetter); + TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, positionSetter); + TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, positionSetter); } @EventHandler diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/NMSWrapper.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/NMSWrapper.java deleted file mode 100644 index 7141ff96..00000000 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/NMSWrapper.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.bausystem.utils; - -import de.steamwar.Reflection; -import io.papermc.paper.datacomponent.DataComponentTypes; -import io.papermc.paper.datacomponent.item.ItemContainerContents; -import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; -import net.minecraft.network.protocol.game.ClientboundExplodePacket; -import net.minecraft.server.level.ServerPlayerGameMode; -import net.minecraft.world.entity.player.Abilities; -import net.minecraft.world.level.GameType; -import org.bukkit.GameMode; -import org.bukkit.Material; -import org.bukkit.craftbukkit.entity.CraftPlayer; -import org.bukkit.craftbukkit.inventory.CraftItemStack; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import java.util.List; -import java.util.Optional; - -public class NMSWrapper { - public static final NMSWrapper impl = new NMSWrapper(); - - private static final Reflection.Field playerGameMode = Reflection.getField(ServerPlayerGameMode.class, GameType.class, 0); - - public void setInternalGameMode(Player player, GameMode gameMode) { - playerGameMode.set(((CraftPlayer) player).getHandle().gameMode, GameType.byId(gameMode.getValue())); - } - - public void setSlotToItemStack(Player player, Object o) { - ClientboundContainerSetSlotPacket packetPlayInSetCreativeSlot = (ClientboundContainerSetSlotPacket) o; - int index = packetPlayInSetCreativeSlot.getSlot(); - if (index >= 36 && index <= 44) { - index -= 36; - } else if (index > 44) { - index -= 5; - } else if (index <= 8) { - index = index - 8 + 36; - } - player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.getItem())); - if (index < 9) player.getInventory().setHeldItemSlot(index); - player.updateInventory(); - } - - public void setPlayerBuildAbilities(Player player) { - Abilities abilities = (((CraftPlayer) player).getHandle()).getAbilities(); - abilities.mayBuild = true; - abilities.mayfly = true; - } - - public Material pathMaterial() { - return Material.DIRT_PATH; - } - - private static final int threshold = 2048; - - public boolean checkItemStack(ItemStack item) { - ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER); - if (data == null) { - return false; - } - - return drillDown(data.contents(), 0, 0) > threshold; - } - - private int drillDown(List items, int layer, int start) { - if (layer > 2) return start + threshold; - int invalid = start; - for (int i = start; i < items.size(); i++) { - ItemStack item = items.get(i); - if (item.isEmpty()) continue; - - invalid += item.getAmount(); - - ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER); - if (data == null) { - continue; - } - - List subItems = data.contents(); - if (subItems.size() > 1) { - invalid = drillDown(subItems, layer + 1, invalid); - } - } - return invalid; - } - - public Object resetExplosionKnockback(Object packet) { - ClientboundExplodePacket explosion = (ClientboundExplodePacket) packet; - - return new ClientboundExplodePacket( - explosion.center(), - Optional.empty(), - explosion.explosionParticle(), - explosion.explosionSound() - ); - } -} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickEndEvent.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickEndEvent.java deleted file mode 100644 index ad145dbe..00000000 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickEndEvent.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.bausystem.utils; - -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; - -public class TickEndEvent extends Event { - - private static final HandlerList handlers = new HandlerList(); - - public HandlerList getHandlers() { - return handlers; - } - - public static HandlerList getHandlerList() { - return handlers; - } -} diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickManager.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickManager.java index 086999e3..59d12df2 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickManager.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickManager.java @@ -42,7 +42,7 @@ public class TickManager implements Listener { TinyProtocol.instance.addFilter(ClientboundTickingStatePacket.class, this::blockPacket); } - private Object blockPacket(Player player, Object packet) { + private Object blockPacket(Player player, ClientboundTickingStatePacket packet) { if (blockTpsPacket) { return new ClientboundTickingStatePacket(20, manager.isFrozen()); } else { diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickStartEvent.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickStartEvent.java deleted file mode 100644 index fbb02f22..00000000 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickStartEvent.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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.bausystem.utils; - -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; - -public class TickStartEvent extends Event { - - private static final HandlerList handlers = new HandlerList(); - - public HandlerList getHandlers() { - return handlers; - } - - public static HandlerList getHandlerList() { - return handlers; - } -} diff --git a/BauSystem/build.gradle.kts b/BauSystem/build.gradle.kts index 574ee012..de9fe1b8 100644 --- a/BauSystem/build.gradle.kts +++ b/BauSystem/build.gradle.kts @@ -31,15 +31,6 @@ dependencies { implementation(project(":BauSystem:BauSystem_Main")) } -tasks.register("DevBau20") { - group = "run" - description = "Run a 1.20 Dev Bau" - dependsOn(":SpigotCore:shadowJar") - dependsOn(":BauSystem:shadowJar") - dependsOn(":SchematicSystem:shadowJar") - template = "Bau20" -} - tasks.register("DevBau21") { group = "run" description = "Run a 1.21 Dev Bau" diff --git a/CommonCore/SQL/src/de/steamwar/sql/SchematicNode.kt b/CommonCore/SQL/src/de/steamwar/sql/SchematicNode.kt index 0dde368b..860aeebd 100644 --- a/CommonCore/SQL/src/de/steamwar/sql/SchematicNode.kt +++ b/CommonCore/SQL/src/de/steamwar/sql/SchematicNode.kt @@ -434,7 +434,7 @@ class SchematicNode(id: EntityID) : IntEntity(id) { private var nodeItem by SchematicNodeTable.item var item: String get() = nodeItem.ifEmpty { - if (isDir()) "CHEST" else "CAULDRON_ITEM" + if (isDir()) "CHEST" else "CAULDRON" } set(value) = useDb { nodeItem = value diff --git a/CommonCore/SQL/src/de/steamwar/sql/SteamwarUser.kt b/CommonCore/SQL/src/de/steamwar/sql/SteamwarUser.kt index 7466a96d..0d19c712 100644 --- a/CommonCore/SQL/src/de/steamwar/sql/SteamwarUser.kt +++ b/CommonCore/SQL/src/de/steamwar/sql/SteamwarUser.kt @@ -25,6 +25,7 @@ import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.v1.core.inList +import org.jetbrains.exposed.v1.core.neq import org.jetbrains.exposed.v1.dao.IntEntity import org.jetbrains.exposed.v1.dao.IntEntityClass import org.jetbrains.exposed.v1.jdbc.insert @@ -136,6 +137,12 @@ class SteamwarUser(id: EntityID) : IntEntity(id) { .select(SteamwarUserTable.fields).where { UserPermTable.perm eq perm }.map { wrapRow(it) } } + @JvmStatic + fun getUsersWithDiscordId() = + useDb { + find { SteamwarUserTable.discordId neq null }.toList() + } + @JvmStatic fun getServerTeam() = useDb { diff --git a/FightSystem/FightSystem_Core/build.gradle.kts b/FightSystem/FightSystem_Core/build.gradle.kts index b346e97c..9b2f746b 100644 --- a/FightSystem/FightSystem_Core/build.gradle.kts +++ b/FightSystem/FightSystem_Core/build.gradle.kts @@ -37,4 +37,5 @@ dependencies { compileOnly(libs.authlib) compileOnly(libs.nms) compileOnly(libs.fawe) + compileOnly(libs.datafixer) } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java index ab85a9b2..d3339975 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java @@ -41,6 +41,9 @@ import de.steamwar.sql.SchematicNode; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.GameRule; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerPickupArrowEvent; import org.bukkit.plugin.java.JavaPlugin; public class FightSystem extends JavaPlugin { @@ -95,13 +98,20 @@ public class FightSystem extends JavaPlugin { getMessage().broadcast("PISTON_PUSHED_OUTSIDE"); shutdown(); }); - new StateDependentListener(ArenaMode.All, FightState.All, BountifulWrapper.impl.newDenyArrowPickupListener()); + new StateDependentListener(ArenaMode.All, FightState.All, new Listener() { + @EventHandler + public void onArrowPickup(PlayerPickupArrowEvent e) { + if (Fight.fighting(e.getPlayer())) e.setCancelled(true); + } + }); new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f)); new OneShotStateDependent(ArenaMode.Test, FightState.All, WorldEditRendererCUIEditor::new); Config.world.setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode)); - techHider = new TechHiderWrapper(); hullHider = new HullHider(); + techHider = new TechHiderWrapper(hullHider); + FightSystem.getHullHider().getHullMap().values().forEach(hull -> hull.fill(true)); + FileSource.startReplay(); @@ -120,7 +130,7 @@ public class FightSystem extends JavaPlugin { Fight.getUnrotated().setSchem(SchematicNode.getSchematicNode(Config.PrepareSchemID)); } - CraftbukkitWrapper.impl.setupGamerule(); + Config.world.setGameRule(GameRule.LOCATOR_BAR, false); } @Override diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TPSWarpCommand.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TPSWarpCommand.java index e6755bb9..6ec55acd 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TPSWarpCommand.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/commands/TPSWarpCommand.java @@ -21,8 +21,8 @@ import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.StateDependentCommand; -import de.steamwar.fightsystem.utils.TpsWarper; import de.steamwar.linkage.Linked; +import net.minecraft.server.MinecraftServer; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -45,8 +45,7 @@ public class TPSWarpCommand implements CommandExecutor { return false; } - TpsWarper warper = TpsWarper.impl; - warper.warp(tps); + MinecraftServer.getServer().tickRateManager().setTickRate(tps); FightSystem.getMessage().broadcastActionbar("TPSWARP_SET", tps); return false; diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java index 790a1cec..c8174349 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightSchematic.java @@ -158,6 +158,8 @@ public class FightSchematic extends StateDependent { FreezeWorld freezer = new FreezeWorld(); team.teleportToSpawn(); + // TODO: Implement hull generation based on clipboard content! + FightSystem.getHullHider().fill(team, false); Vector dims = WorldeditWrapper.impl.getDimensions(clipboard); WorldeditWrapper.impl.pasteClipboard( clipboard, diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java index 78dff55b..a8cddb3a 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java @@ -44,6 +44,8 @@ import de.steamwar.sql.SteamwarUser; import lombok.Getter; import net.md_5.bungee.api.ChatMessageType; import org.bukkit.*; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.scoreboard.NameTagVisibility; @@ -151,8 +153,8 @@ public class FightTeam { new TeamArea(this); team = FightScoreboard.getBukkitTeam(name); - WorldOfColorWrapper.impl.setTeamColor(team, color); - BountifulWrapper.impl.setNametagVisibility(team); + team.setColor(color); + team.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.FOR_OWN_TEAM); team.setNameTagVisibility(NameTagVisibility.HIDE_FOR_OTHER_TEAMS); if (!Config.GameModeConfig.WinConditions.contains(Winconditions.AMONG_US)) { team.setAllowFriendlyFire(false); @@ -284,7 +286,8 @@ public class FightTeam { entity.teleport(spawn); fightPlayer.ifPlayer(player -> { - BountifulWrapper.impl.setAttackSpeed(player); + AttributeInstance attribute = player.getAttribute(Attribute.ATTACK_SPEED); + attribute.setBaseValue(16); player.setFoodLevel(20); player.getInventory().clear(); FightSystem.getHullHider().updatePlayer(player); diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightWorld.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightWorld.java index e978f038..83c15170 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightWorld.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightWorld.java @@ -26,7 +26,6 @@ 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 de.steamwar.linkage.Linked; import lombok.Getter; import org.bukkit.Bukkit; @@ -64,11 +63,18 @@ public class FightWorld extends StateDependent { public static void forceLoad() { Config.ArenaRegion.forEachChunk((cX, cZ) -> { Config.world.loadChunk(cX, cZ); - FlatteningWrapper.impl.forceLoadChunk(Config.world, cX, cZ); + Config.world.setChunkForceLoaded(cX, cZ, true); }); } public static void resetWorld() { + 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); + }); + Bukkit.unloadWorld(backup, false); + List entities = new ArrayList<>(); Recording.iterateOverEntities(Objects::nonNull, entity -> { if (entity.getType() != EntityType.PLAYER && (!Config.GameModeConfig.Arena.Leaveable || Config.ArenaRegion.inRegion(entity.getLocation()))) { @@ -78,14 +84,12 @@ public class FightWorld extends StateDependent { entities.forEach(Entity::remove); entities.clear(); - World backup = new WorldCreator(Config.world.getName() + "/backup").createWorld(); - assert backup != null; + FightSystem.getHullHider().getHullMap().values().forEach(hull -> hull.fill(true)); + Config.ArenaRegion.forEachChunk((x, z) -> { - CraftbukkitWrapper.impl.resetChunk(Config.world, backup, x, z); for (Player p : Bukkit.getOnlinePlayers()) { de.steamwar.core.CraftbukkitWrapper.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 index e120d234..71c579bd 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FreezeWorld.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FreezeWorld.java @@ -20,7 +20,6 @@ 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; @@ -30,19 +29,16 @@ import org.bukkit.event.block.*; import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.event.inventory.InventoryMoveItemEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerSwapHandItemsEvent; 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 @@ -94,4 +90,9 @@ public class FreezeWorld implements Listener { public void handlePlayerInteract(PlayerInteractEvent event) { event.setCancelled(true); } + + @EventHandler + public void onSwapItems(PlayerSwapHandItemsEvent event) { + if (Fight.fighting(event.getPlayer())) event.setCancelled(true); + } } 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 672ab18c..a6844252 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/Kit.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/Kit.java @@ -24,12 +24,12 @@ 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.fightsystem.utils.ReflectionWrapper; import de.steamwar.inventory.SWInventory; import de.steamwar.inventory.SWItem; import de.steamwar.sql.PersonalKit; import de.steamwar.sql.SteamwarUser; +import io.papermc.paper.datacomponent.DataComponentType; +import io.papermc.paper.datacomponent.DataComponentTypes; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -39,6 +39,7 @@ 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.BlockDataMeta; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.potion.PotionEffect; @@ -193,13 +194,13 @@ public class Kit { if (Config.GameModeConfig.Kits.ForbiddenItems.contains(stack.getType())) return true; //Check for attribute modifiers - if (FlatteningWrapper.impl.hasAttributeModifier(stack)) { + if (stack.hasItemMeta() && stack.getItemMeta() != null && stack.getItemMeta().hasAttributeModifiers()) { return true; } if (stack.hasItemMeta()) { ItemMeta meta = stack.getItemMeta(); - if (FlatteningWrapper.impl.containsBlockMeta(meta)) return true; //Blocks always upwards slabs etc. + if (meta instanceof BlockDataMeta && ((BlockDataMeta) meta).hasBlockData()) return true; //Blocks always upwards slabs etc. if (hasItems(stack)) return true; //Blocks prefilled inventories } @@ -208,8 +209,42 @@ public class Kit { return !normal.isEnchantmentInKit(stack) && !stack.getEnchantments().isEmpty(); } + private static final Set FORBIDDEN_TYPES = new HashSet<>(); + + static { + FORBIDDEN_TYPES.add(DataComponentTypes.CUSTOM_NAME); + FORBIDDEN_TYPES.add(DataComponentTypes.PROFILE); + FORBIDDEN_TYPES.add(DataComponentTypes.UNBREAKABLE); + FORBIDDEN_TYPES.add(DataComponentTypes.BLOCK_DATA); + FORBIDDEN_TYPES.add(DataComponentTypes.BLOCKS_ATTACKS); + FORBIDDEN_TYPES.add(DataComponentTypes.BUNDLE_CONTENTS); + FORBIDDEN_TYPES.add(DataComponentTypes.CUSTOM_MODEL_DATA); + + FORBIDDEN_TYPES.add(DataComponentTypes.ATTRIBUTE_MODIFIERS); + FORBIDDEN_TYPES.add(DataComponentTypes.TOOL); + FORBIDDEN_TYPES.add(DataComponentTypes.WEAPON); + FORBIDDEN_TYPES.add(DataComponentTypes.FOOD); + FORBIDDEN_TYPES.add(DataComponentTypes.CONSUMABLE); + FORBIDDEN_TYPES.add(DataComponentTypes.POTION_CONTENTS); + FORBIDDEN_TYPES.add(DataComponentTypes.STORED_ENCHANTMENTS); + FORBIDDEN_TYPES.add(DataComponentTypes.CAN_BREAK); + FORBIDDEN_TYPES.add(DataComponentTypes.CAN_PLACE_ON); + FORBIDDEN_TYPES.add(DataComponentTypes.MAX_DAMAGE); + FORBIDDEN_TYPES.add(DataComponentTypes.USE_REMAINDER); + FORBIDDEN_TYPES.add(DataComponentTypes.USE_COOLDOWN); + FORBIDDEN_TYPES.add(DataComponentTypes.SUSPICIOUS_STEW_EFFECTS); + FORBIDDEN_TYPES.add(DataComponentTypes.CHARGED_PROJECTILES); + FORBIDDEN_TYPES.add(DataComponentTypes.INTANGIBLE_PROJECTILE); + FORBIDDEN_TYPES.add(DataComponentTypes.FIREWORKS); + FORBIDDEN_TYPES.add(DataComponentTypes.FIREWORK_EXPLOSION); + FORBIDDEN_TYPES.add(DataComponentTypes.EQUIPPABLE); + FORBIDDEN_TYPES.add(DataComponentTypes.REPAIR_COST); + FORBIDDEN_TYPES.add(DataComponentTypes.ENCHANTABLE); + } + public static boolean hasItems(ItemStack stack) { - return ReflectionWrapper.impl.hasItems(stack); + FORBIDDEN_TYPES.forEach(stack::resetData); + return false; } private boolean isEnchantmentInKit(ItemStack stack) { diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArrowStopper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArrowStopper.java index 03e00bbf..e6b0c64e 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArrowStopper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ArrowStopper.java @@ -22,12 +22,12 @@ package de.steamwar.fightsystem.listener; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.StateDependentTask; -import de.steamwar.fightsystem.utils.WorldOfColorWrapper; import de.steamwar.linkage.Linked; import net.minecraft.world.entity.projectile.AbstractArrow; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; +import org.bukkit.entity.Arrow; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; import org.bukkit.projectiles.ProjectileSource; @@ -93,8 +93,12 @@ public class ArrowStopper { 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) || + if (otherSide || !Config.ArenaRegion.inRegion(location)) return true; + boolean result = false; + if (entity instanceof Arrow arrow) { + result = arrow.isInBlock(); + } + return result || entity.getVelocity().equals(NULL_VECTOR); } } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/BlockPlaceCollision.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/BlockPlaceCollision.java index efce662e..97b49283 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/BlockPlaceCollision.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/BlockPlaceCollision.java @@ -22,11 +22,11 @@ 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 de.steamwar.linkage.Linked; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.entity.Player; +import org.bukkit.entity.Pose; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; @@ -46,7 +46,7 @@ public class BlockPlaceCollision implements Listener { // 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 max = player.getLocation().add(0.3, player.getPose() == Pose.SWIMMING ? 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); diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Border.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Border.java index 39fb1870..55864abd 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Border.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Border.java @@ -24,7 +24,6 @@ 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; @@ -141,7 +140,7 @@ public class Border { private void sendChange(Player player, Block block, Material type) { if (block.getType() == Material.AIR) { - FlatteningWrapper.impl.sendBlockChange(player, block, type); + player.sendBlockChange(block.getLocation(), type.createBlockData()); } } } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ClickAnalyzer.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ClickAnalyzer.java index 3def965c..3e84d916 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ClickAnalyzer.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/ClickAnalyzer.java @@ -24,6 +24,7 @@ import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.utils.CraftbukkitWrapper; import de.steamwar.linkage.Linked; import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket; +import net.minecraft.network.protocol.game.ServerboundUseItemPacket; import org.bukkit.entity.Player; import java.io.*; @@ -42,7 +43,7 @@ public class ClickAnalyzer { } public ClickAnalyzer() { - TinyProtocol.instance.addFilter(Recording.blockPlacePacket, this::onBlockPlace); + TinyProtocol.instance.addFilter(ServerboundUseItemPacket.class, this::onBlockPlace); TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, this::onBlockPlace); } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/DenyInventoryMovement.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/DenyInventoryMovement.java index e9c1d797..0452691c 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/DenyInventoryMovement.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/DenyInventoryMovement.java @@ -20,24 +20,22 @@ 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 de.steamwar.fightsystem.utils.BountifulWrapper; import de.steamwar.linkage.Linked; 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; +import org.bukkit.event.player.PlayerSwapHandItemsEvent; @Linked 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 @@ -54,4 +52,9 @@ public class DenyInventoryMovement implements Listener { public void onItemPickup(PlayerPickupItemEvent event) { event.setCancelled(true); } + + @EventHandler + public void onSwapItems(PlayerSwapHandItemsEvent event) { + if (Fight.fighting(event.getPlayer())) event.setCancelled(true); + } } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Permanent.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Permanent.java index 27ca0dc5..75340bd5 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Permanent.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Permanent.java @@ -27,13 +27,12 @@ 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 de.steamwar.linkage.Linked; import net.md_5.bungee.api.ChatMessageType; import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.block.data.type.Dispenser; import org.bukkit.entity.Player; import org.bukkit.entity.TNTPrimed; import org.bukkit.event.EventHandler; @@ -62,7 +61,7 @@ public class Permanent implements Listener { private static final Team spectatorTeam = FightScoreboard.getBukkitTeam("Spectator"); static { - BountifulWrapper.impl.setNametagVisibility(spectatorTeam); + spectatorTeam.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.FOR_OWN_TEAM); spectatorTeam.setNameTagVisibility(NameTagVisibility.NEVER); } @@ -234,7 +233,7 @@ public class Permanent implements Listener { return; } - if (e.getItem().getType() == Material.TNT || FlatteningWrapper.impl.isFacingWater(block)) { + if (e.getItem().getType() == Material.TNT || block.getRelative(((Dispenser) block.getBlockData()).getFacing()).isLiquid()) { e.setCancelled(true); } } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PlayerJoinListener.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PlayerJoinListener.java deleted file mode 100644 index 71ecf2a7..00000000 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PlayerJoinListener.java +++ /dev/null @@ -1,74 +0,0 @@ -package de.steamwar.fightsystem.listener; - -import de.steamwar.fightsystem.states.FightState; -import de.steamwar.fightsystem.states.StateDependentListener; -import de.steamwar.linkage.Linked; -import net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket; -import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.LevelChunk; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.entity.CraftPlayer; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; - -@Linked -public class PlayerJoinListener implements Listener { - - public PlayerJoinListener() { - new StateDependentListener(true, FightState.All, this); - } - - @EventHandler() - public void onPlayerJoin(PlayerJoinEvent event) { - Player player = event.getPlayer(); - World world = player.getWorld(); - - Location loc = player.getLocation(); - int viewDistance = Bukkit.getViewDistance(); - - int chunkX = loc.getChunk().getX(); - int chunkZ = loc.getChunk().getZ(); - - // Iterate through the chunks around the player and force a resend - for (int x = -viewDistance; x <= viewDistance; x++) { - for (int z = -viewDistance; z <= viewDistance; z++) { - Chunk chunk = world.getChunkAt(chunkX + x, chunkZ + z); - this.forceRefreshChunkForPlayer(player, chunk); - } - } - } - - public void forceRefreshChunkForPlayer(Player player, Chunk bukkitChunk) { - ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); - CraftWorld craftWorld = (CraftWorld) bukkitChunk.getWorld(); - - int chunkX = bukkitChunk.getX(); - int chunkZ = bukkitChunk.getZ(); - - LevelChunk nmsChunk = craftWorld.getHandle().getChunkSource().getChunk(chunkX, chunkZ, false); - if (nmsChunk == null) { - // Chunk isn't loaded in memory on the server side; - return; - } - - ClientboundForgetLevelChunkPacket unloadPacket = new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)); - serverPlayer.connection.send(unloadPacket); - - ClientboundLevelChunkWithLightPacket loadPacket = new ClientboundLevelChunkWithLightPacket( - nmsChunk, - craftWorld.getHandle().getLightEngine(), - null, - null, - true - ); - serverPlayer.connection.send(loadPacket); - } -} diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PrepareSchem.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PrepareSchem.java index d8ac07a6..f83c4c21 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PrepareSchem.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/PrepareSchem.java @@ -28,12 +28,12 @@ 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.linkage.Linked; import de.steamwar.sql.SchematicNode; import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -52,7 +52,7 @@ public class PrepareSchem implements Listener { 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))) { + if (Config.world.getBlockAt(x, y, z).getType() == Material.MOVING_PISTON) { stationaryMovingPistons.add(new Vector(x, y, z)); } }); @@ -76,7 +76,7 @@ public class PrepareSchem implements Listener { try { region.forEach((x, y, z) -> { - if (FlatteningWrapper.impl.checkPistonMoving(Config.world.getBlockAt(x, y, z)) && !stationaryMovingPistons.contains(new Vector(x, y, z))) { + if (Config.world.getBlockAt(x, y, z).getType() == Material.MOVING_PISTON && !stationaryMovingPistons.contains(new Vector(x, y, z))) { FightSystem.getMessage().broadcast("PREPARE_ACTIVE_PISTON"); Bukkit.shutdown(); throw new IllegalStateException(); diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java index 0a506cbf..0187d089 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java @@ -22,6 +22,7 @@ package de.steamwar.fightsystem.listener; import com.comphenix.tinyprotocol.TinyProtocol; import de.steamwar.Reflection; import de.steamwar.fightsystem.ArenaMode; +import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.events.TeamDeathEvent; import de.steamwar.fightsystem.events.TeamLeaveEvent; @@ -34,17 +35,17 @@ 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 de.steamwar.linkage.Linked; +import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.network.protocol.game.ServerboundUseItemPacket; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.item.PrimedTnt; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; @@ -63,6 +64,7 @@ import java.util.Random; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.stream.StreamSupport; @Linked public class Recording implements Listener { @@ -83,12 +85,22 @@ public class Recording implements Listener { public static final Class primedTnt = PrimedTnt.class; public static void iterateOverEntities(Predicate filter, Consumer consumer) { - CraftbukkitWrapper.impl.entityIterator().filter(filter).map(net.minecraft.world.entity.Entity::getBukkitEntity).forEach(consumer); + StreamSupport.stream(((CraftWorld) Config.world).getHandle().getEntities().getAll().spliterator(), false).filter(filter).map(net.minecraft.world.entity.Entity::getBukkitEntity).forEach(consumer); } public Recording() { new StateDependentListener(ArenaMode.AntiReplay, FightState.All, this); - new StateDependentListener(ArenaMode.AntiReplay, FightState.All, BountifulWrapper.impl.newHandSwapRecorder()); + new StateDependentListener(ArenaMode.AntiReplay, FightState.All, new Listener() { + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onItemSwap(PlayerSwapHandItemsEvent e) { + if (isNotSent(e.getPlayer())) + return; + + Player player = e.getPlayer(); + GlobalRecorder.getInstance().item(player, disarmNull(e.getMainHandItem()), "MAINHAND"); + GlobalRecorder.getInstance().item(player, disarmNull(e.getOffHandItem()), "OFFHAND"); + } + }); new StateDependent(ArenaMode.AntiReplay, FightState.Ingame) { @Override public void enable() { @@ -102,18 +114,18 @@ public class Recording implements Listener { } }.register(); new StateDependent(ArenaMode.AntiReplay, FightState.Ingame) { - private final BiFunction place = Recording.this::blockPlace; + 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(ServerboundUseItemPacket.class, place); TinyProtocol.instance.addFilter(blockDigPacket, dig); } @Override public void disable() { - TinyProtocol.instance.removeFilter(blockPlacePacket, place); + TinyProtocol.instance.removeFilter(ServerboundUseItemPacket.class, place); TinyProtocol.instance.removeFilter(blockDigPacket, dig); } }.register(); @@ -131,7 +143,7 @@ public class Recording implements Listener { GlobalRecorder.getInstance().entitySpeed(entity); } - private static final Class blockDigPacket = ServerboundPlayerActionPacket.class; + private static final Class> blockDigPacket = ServerboundPlayerActionPacket.class; private static final Class playerDigType = blockDigPacket.getDeclaredClasses()[0]; private static final Reflection.Field blockDigType = Reflection.getField(blockDigPacket, playerDigType, 0); private static final Object releaseUseItem = playerDigType.getEnumConstants()[5]; @@ -143,11 +155,9 @@ public class Recording implements Listener { return packet; } - public static final Class blockPlacePacket = ServerboundUseItemPacket.class; - - private Object blockPlace(Player p, Object packet) { - boolean mainHand = BountifulWrapper.impl.mainHand(packet); - if (!isNotSent(p) && BountifulWrapper.impl.bowInHand(mainHand, p)) { + private Object blockPlace(Player p, ServerboundUseItemPacket packet) { + boolean mainHand = packet.getHand() == InteractionHand.MAIN_HAND; + if (!isNotSent(p) && (mainHand ? p.getInventory().getItemInMainHand() : p.getInventory().getItemInOffHand()).getType() == Material.BOW) { GlobalRecorder.getInstance().bowSpan(p, true, !mainHand); } return packet; @@ -177,7 +187,7 @@ public class Recording implements Listener { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onBlockPhysics(BlockPhysicsEvent e) { - if (FlatteningWrapper.impl.doRecord(e)) { + if (e.getBlock() == e.getSourceBlock() || e.getChangedType() == Material.AIR) { GlobalRecorder.getInstance().blockChange(e.getBlock()); } } @@ -285,7 +295,8 @@ public class Recording implements Listener { if (!fp.isLiving()) continue; fp.ifPlayer(player -> { - BountifulWrapper.impl.recordHandItems(player); + GlobalRecorder.getInstance().item(player, disarmNull(player.getInventory().getItemInMainHand()), "MAINHAND"); + GlobalRecorder.getInstance().item(player, disarmNull(player.getInventory().getItemInOffHand()), "OFFHAND"); 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"); diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/WaterRemover.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/WaterRemover.java index 6ac8183a..c85da5ce 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/WaterRemover.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/WaterRemover.java @@ -25,11 +25,13 @@ 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 de.steamwar.linkage.Linked; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Waterlogged; import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -101,7 +103,25 @@ public class WaterRemover implements Listener { if (!Config.BlueExtendRegion.inRegion(b) && !Config.RedExtendRegion.inRegion(b)) return; //checks for water and removes it, if present - if (!FlatteningWrapper.impl.removeWater(b)) return; + boolean result = true; + Material type = b.getType(); + if (type == Material.WATER || type == Material.LAVA) { + b.setType(Material.AIR); + } else { + BlockData data = b.getBlockData(); + if (!(data instanceof Waterlogged)) { + result = false; + } else { + Waterlogged waterlogged = (Waterlogged) data; + if (waterlogged.isWaterlogged()) { + b.setType(Material.AIR); + } else { + result = false; + } + } + } + + if (!result) return; if (b.getY() < MIN_Y) return; diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java index 3c010418..b62ef662 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java @@ -42,11 +42,14 @@ 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 net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Pose; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Sound; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.*; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; @@ -72,7 +75,7 @@ public class PacketProcessor implements Listener { private static final org.bukkit.scoreboard.Team team = FightScoreboard.getBukkitTeam("Replay"); static { - BountifulWrapper.impl.setNametagVisibility(team); + team.setOption(org.bukkit.scoreboard.Team.Option.NAME_TAG_VISIBILITY, org.bukkit.scoreboard.Team.OptionStatus.FOR_OWN_TEAM); team.setNameTagVisibility(NameTagVisibility.NEVER); } @@ -460,8 +463,15 @@ public class PacketProcessor implements Listener { 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)); + int blockState1 = TechHiderWrapper.ENABLED && hiddenBlockIds.contains(blockState) ? obfuscateWith : blockState; + BlockState blockData = Block.stateById(blockState1); + ServerLevel level = ((CraftWorld) Config.world).getHandle(); + BlockPos pos = new BlockPos(x, y, z); + + level.removeBlockEntity(pos); + level.setBlock(pos, blockData, blockState1); + level.getChunkSource().blockChanged(pos); + FightSystem.getHullHider().blockUpdate(Config.world.getBlockAt(x, y, z), CraftMagicNumbers.getMaterial(Block.stateById(blockState)).getItemType()); }); } @@ -478,7 +488,7 @@ public class PacketProcessor implements Listener { 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())); + execSync(() -> Config.world.spawnParticle(Particle.valueOf(particleName), finalX + Config.ArenaRegion.getMinX(), y + Config.BluePasteRegion.getMinY(), finalZ + Config.ArenaRegion.getMinZ(), 1)); } private void sound() throws IOException { @@ -502,7 +512,10 @@ public class PacketProcessor implements Listener { Sound sound = Sound.valueOf(soundName); - execSync(() -> WorldOfColorWrapper.impl.playSound(new Location(Config.world, x, y, z), sound, soundCategory, volume, pitch)); + execSync(() -> { + Location location = new Location(Config.world, x, y, z); + location.getWorld().playSound(location, sound, SoundCategory.valueOf(soundCategory), volume, pitch); + }); } private void soundAtPlayer() throws IOException { @@ -603,7 +616,7 @@ public class PacketProcessor implements Listener { Bukkit.getOnlinePlayers().forEach(p -> { p.resetTitle(); - WorldOfColorWrapper.impl.sendTitle(p, title, subtitle, 5, 40, 5); + p.sendTitle(title, subtitle, 5, 40, 5); }); } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/Recorder.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/Recorder.java index dec7160c..69f2bbcb 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/Recorder.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/Recorder.java @@ -25,7 +25,6 @@ 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; @@ -35,6 +34,7 @@ import de.steamwar.sql.SteamwarUser; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.block.Block; +import org.bukkit.craftbukkit.block.CraftBlockState; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -219,7 +219,7 @@ public interface Recorder { } default void blockChange(Block block) { - int blockState = BlockIdWrapper.impl.blockToId(block); + int blockState = net.minecraft.world.level.block.Block.getId(((CraftBlockState) block.getState()).getHandle()); int shortX = block.getX() - Config.ArenaRegion.getMinX(); int shortY = block.getY() - Config.BluePasteRegion.getMinY(); diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BlockIdWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BlockIdWrapper.java deleted file mode 100644 index e6945ba5..00000000 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BlockIdWrapper.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 BlockIdWrapper { - public static final BlockIdWrapper impl = new BlockIdWrapper(); - - public Material idToMaterial(int blockState) { - return CraftMagicNumbers.getMaterial(net.minecraft.world.level.block.Block.stateById(blockState)).getItemType(); - } - - public int blockToId(Block block) { - return net.minecraft.world.level.block.Block.getId(((CraftBlockState) block.getState()).getHandle()); - } - - 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); - } - - public void trackEntity(Player player, Entity entity) { - if (entity instanceof Player) { - TinyProtocol.instance.sendPacket(player, ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE)); - } - - player.showEntity(FightSystem.getPlugin(), entity); - } - - public void untrackEntity(Player player, Entity entity) { - player.hideEntity(FightSystem.getPlugin(), entity); - - if (entity instanceof Player) { - TinyProtocol.instance.sendPacket(player, ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE)); - } - } -} diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BountifulWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BountifulWrapper.java deleted file mode 100644 index e1cefc9c..00000000 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/BountifulWrapper.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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 de.steamwar.fightsystem.fight.Fight; -import de.steamwar.fightsystem.fight.FightTeam; -import de.steamwar.fightsystem.listener.Recording; -import de.steamwar.fightsystem.record.GlobalRecorder; -import net.minecraft.world.InteractionHand; -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 BountifulWrapper { - public static final BountifulWrapper impl = new BountifulWrapper(); - - private static final Class enumHand = InteractionHand.class; - private static final Object mainHand = enumHand.getEnumConstants()[0]; - private static final Reflection.Field blockPlaceHand = Reflection.getField(Recording.blockPlacePacket, enumHand, 0); - - public boolean mainHand(Object packet) { - return blockPlaceHand.get(packet) == mainHand; - } - - public boolean bowInHand(boolean mainHand, Player p) { - return (mainHand ? p.getInventory().getItemInMainHand() : p.getInventory().getItemInOffHand()).getType() == Material.BOW; - } - - public void setAttackSpeed(Player player) { - AttributeInstance attribute = player.getAttribute(Attribute.ATTACK_SPEED); - attribute.setBaseValue(16); - } - - public void setNametagVisibility(Team team) { - team.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.FOR_OWN_TEAM); - } - - public Listener newDenyArrowPickupListener() { - return new Listener() { - @EventHandler - public void onArrowPickup(PlayerPickupArrowEvent e) { - if (Fight.fighting(e.getPlayer())) e.setCancelled(true); - } - }; - } - - public Listener newDenyHandSwapListener() { - return new Listener() { - @EventHandler - public void onSwapItems(PlayerSwapHandItemsEvent event) { - if (Fight.fighting(event.getPlayer())) event.setCancelled(true); - } - }; - } - - 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"); - } - - 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"); - } - }; - } - - 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<>(); - - 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_Core/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper.java index 6a4518c8..031ac904 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/CraftbukkitWrapper.java @@ -19,11 +19,9 @@ 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.craftbukkit.entity.CraftEntity; @@ -31,26 +29,12 @@ import org.bukkit.entity.Entity; import java.util.HashSet; import java.util.Set; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; public class CraftbukkitWrapper { public static final CraftbukkitWrapper impl = new CraftbukkitWrapper(); - protected net.minecraft.world.entity.Entity getEntity(Entity e) { - return ((CraftEntity) e).getHandle(); - } - public float headRotation(Entity e) { - return getEntity(e).getYHeadRot(); - } - - public Stream entityIterator() { - return StreamSupport.stream(((CraftWorld) Config.world).getHandle().getEntities().getAll().spliterator(), false); - } - - public void setupGamerule() { - Config.world.setGameRule(GameRule.LOCATOR_BAR, false); + return ((CraftEntity) e).getHandle().getYHeadRot(); } private LevelChunk getChunk(World world, int x, int z) { diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FightUI.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FightUI.java index ce6fa5d4..68103b76 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FightUI.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FightUI.java @@ -34,12 +34,12 @@ import de.steamwar.fightsystem.winconditions.Wincondition; import de.steamwar.linkage.Linked; import lombok.Getter; import org.bukkit.Bukkit; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; import org.bukkit.entity.Player; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; +import java.util.*; import java.util.logging.Level; import java.util.stream.Collectors; @@ -142,6 +142,7 @@ public class FightUI { BossBarType.RED_LEFT.text = leftRedText; } + private final Map barMap = new HashMap<>(); private void sendToPlayers() { Bukkit.getOnlinePlayers().forEach(player -> { BossBarType bar = BossBarType.byAngle(CraftbukkitWrapper.impl.headRotation(player)); @@ -154,7 +155,55 @@ public class FightUI { } } - BountifulWrapper.impl.sendBar(player, bar.team, bar.progress, FightSystem.getMessage().parse(bar.text.getMsg(), player, params)); + String text = FightSystem.getMessage().parse(bar.text.getMsg(), player, params); + barMap.keySet().removeIf(p -> !p.isOnline()); + + if (!barMap.containsKey(player)) { + BossBar bar1 = Bukkit.createBossBar(player.getName(), BarColor.WHITE, BarStyle.SOLID); + barMap.put(player, bar1); + bar1.addPlayer(player); + } + + BossBar bar1 = barMap.get(player); + BarColor color; + switch (bar.team.getColor()) { + case DARK_BLUE: + case DARK_AQUA: + case BLUE: + case AQUA: + color = BarColor.BLUE; + break; + case GREEN: + case DARK_GREEN: + color = BarColor.GREEN; + break; + case DARK_RED: + case RED: + color = BarColor.RED; + break; + case DARK_PURPLE: + color = BarColor.PURPLE; + break; + case GOLD: + case YELLOW: + color = BarColor.YELLOW; + break; + case LIGHT_PURPLE: + color = BarColor.PINK; + break; + case BLACK: + case WHITE: + case GRAY: + case DARK_GRAY: + default: + color = BarColor.WHITE; + break; + } + if (bar1.getColor() != color) bar1.setColor(color); + + if (bar1.getProgress() != bar.progress) bar1.setProgress(bar.progress); + + if (!bar1.getTitle().equals(text)) bar1.setTitle(text); }); } @@ -208,9 +257,17 @@ public class FightUI { 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)); + Bukkit.getOnlinePlayers().forEach(p -> { + String title = FightSystem.getMessage().parse("UI_WIN", p, winner.getColor(), winner.getName()); + String subtitle1 = FightSystem.getMessage().parse(subtitle, p, params); + p.sendTitle(title, subtitle1, 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)); + Bukkit.getOnlinePlayers().forEach(p -> { + String title = FightSystem.getMessage().parse("UI_DRAW", p); + String subtitle1 = FightSystem.getMessage().parse(subtitle, p, params); + p.sendTitle(title, subtitle1, 5, 40, 5); + }); } } @@ -232,7 +289,10 @@ public class FightUI { } Message message = queue.poll(); - Bukkit.getOnlinePlayers().forEach(p -> WorldOfColorWrapper.impl.sendTitle(p, " ", FightSystem.getMessage().parse(message.getMsg(), p, message.getParams()), 5, 40, 5)); + Bukkit.getOnlinePlayers().forEach(p -> { + String subtitle = FightSystem.getMessage().parse(message.getMsg(), p, message.getParams()); + p.sendTitle(" ", subtitle, 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 deleted file mode 100644 index 099ed3ef..00000000 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/FlatteningWrapper.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 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; - -public class FlatteningWrapper { - public static final FlatteningWrapper impl = new FlatteningWrapper(); - - 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(); - } - - 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; - } - - public boolean containsBlockMeta(ItemMeta meta) { - return meta instanceof BlockDataMeta && ((BlockDataMeta) meta).hasBlockData(); - } - - public boolean hasAttributeModifier(ItemStack stack) { - return stack.hasItemMeta() && stack.getItemMeta() != null && stack.getItemMeta().hasAttributeModifiers(); - } - - public boolean doRecord(BlockPhysicsEvent e) { - return e.getBlock() == e.getSourceBlock() || e.getChangedType() == Material.AIR; - } - - public void forceLoadChunk(World world, int cX, int cZ) { - world.setChunkForceLoaded(cX, cZ, true); - } - - public boolean checkPistonMoving(Block block) { - return block.getType() == Material.MOVING_PISTON; - } - - public boolean isFacingWater(Block dispenser) { - return dispenser.getRelative(((Dispenser) dispenser.getBlockData()).getFacing()).isLiquid(); - } - - public boolean isCrouching(Player player) { - return player.getPose() == Pose.SWIMMING; - } - - public void sendBlockChange(Player player, Block block, Material type) { - player.sendBlockChange(block.getLocation(), type.createBlockData()); - } -} diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java index 506e105d..3ac2528d 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/Hull.java @@ -20,15 +20,25 @@ package de.steamwar.fightsystem.utils; import com.comphenix.tinyprotocol.TinyProtocol; +import com.mojang.authlib.GameProfile; +import de.steamwar.core.ProtocolWrapper; import de.steamwar.entity.REntity; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.fight.FightTeam; +import it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap; import lombok.AllArgsConstructor; import lombok.Getter; +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; +import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -91,7 +101,7 @@ public class Hull { public void addPlayer(Player player) { if (players.add(player)) { for (Entity entity : entities) { - BlockIdWrapper.impl.untrackEntity(player, entity); + untrackEntity(player, entity); } } } @@ -99,7 +109,7 @@ public class Hull { public void removePlayer(Player player, boolean activeRemoval) { if (players.remove(player) && activeRemoval) { for (Entity entity : entities) { - BlockIdWrapper.impl.trackEntity(player, entity); + trackEntity(player, entity); } // techhider triggers block change sending } @@ -110,18 +120,34 @@ public class Hull { if (region.inRegion(location) && !visibility.get(new IntVector(location).toId(region))) { if (entities.add(entity)) { for (Player player : players) { - BlockIdWrapper.impl.untrackEntity(player, entity); + untrackEntity(player, entity); } } } else { if (entities.remove(entity)) { for (Player player : players) { - BlockIdWrapper.impl.trackEntity(player, entity); + trackEntity(player, entity); } } } } + public void trackEntity(Player player, Entity entity) { + if (entity instanceof Player) { + TinyProtocol.instance.sendPacket(player, ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE)); + } + + player.showEntity(FightSystem.getPlugin(), entity); + } + + public void untrackEntity(Player player, Entity entity) { + player.hideEntity(FightSystem.getPlugin(), entity); + + if (entity instanceof Player) { + TinyProtocol.instance.sendPacket(player, ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE)); + } + } + public void removeEntity(Entity entity) { entities.remove(entity); } @@ -139,6 +165,15 @@ public class Hull { rentities.remove(entity); } + public void fill(boolean visible) { + visibility.set(0, visibility.size(), visible); + occluding.set(0, occluding.size(), !visible); + uncoveredSurface.clear(); + for (BitSet directionalVisibility : visibilityDirections.values()) { + directionalVisibility.set(0, directionalVisibility.size(), visible); + } + } + public void initialize() { visibility.clear(); occluding.clear(); @@ -189,7 +224,34 @@ public class Hull { uncoveredSurface.clear(); for (Map.Entry> entry : sectionWise.entrySet()) { - Object packet = HullHiderWrapper.impl.generateBlockChangePacket(entry.getValue()); + Object result; + List changes = entry.getValue(); + Object[] blockdata = new Object[changes.size()]; + for (int i = 0; i < blockdata.length; i++) { + IntVector change = changes.get(i); + blockdata[i] = ((CraftBlockData) Config.world.getBlockData(change.getX(), change.getY(), change.getZ())).getState(); + } + + if (changes.size() > 1) { + IntVector section = changes.get(0); + section = new 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++) { + IntVector change = changes.get(i); + + pos[i] = (short) (((change.getX() - xOffset) << 8) + ((change.getZ() - zOffset) << 4) + (change.getY() - yOffset)); + } + + result = new ClientboundSectionBlocksUpdatePacket(SectionPos.of(section.getX(), section.getY(), section.getZ()), new Short2ObjectArrayMap<>(pos, blockdata, blockdata.length)); + } else { + IntVector pos = changes.get(0); + result = new ClientboundBlockUpdatePacket(new BlockPos(pos.getX(), pos.getY(), pos.getZ()), (BlockState) blockdata[0]); + } + Object packet = result; players.forEach(player -> TinyProtocol.instance.sendPacket(player, packet)); } } 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 8576a50a..58bfce48 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java @@ -19,8 +19,6 @@ package de.steamwar.fightsystem.utils; -import com.comphenix.tinyprotocol.TinyProtocol; -import de.steamwar.Reflection; import de.steamwar.entity.REntity; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.fight.Fight; @@ -28,18 +26,9 @@ 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 lombok.Getter; -import net.minecraft.core.Vec3i; -import net.minecraft.network.protocol.game.ClientboundExplodePacket; -import net.minecraft.network.protocol.game.ClientboundLevelEventPacket; -import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket; -import net.minecraft.network.protocol.game.ClientboundSoundPacket; -import org.bukkit.Bukkit; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Player; @@ -55,17 +44,12 @@ import org.bukkit.event.player.PlayerQuitEvent; import java.util.HashMap; import java.util.Map; import java.util.Objects; -import java.util.function.BiFunction; -import java.util.function.Function; public class HullHider implements Listener { @Getter private final Map hullMap = new HashMap<>(); private final Hull[] hulls; - private final Map, BiFunction> packetHiders = new HashMap<>(); - - private static final Class packetPlayOutExplosion = ClientboundExplodePacket.class; public HullHider() { if (!TechHiderWrapper.ENABLED) { @@ -76,34 +60,20 @@ public class HullHider implements Listener { 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(ClientboundLevelParticlesPacket.class, double.class, 1.0); - posHiderGenerator(ClientboundSoundPacket.class, 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(); } + public void fill(FightTeam team, boolean visible) { + if (!TechHiderWrapper.ENABLED) return; + hullMap.get(team).fill(visible); + } @EventHandler(priority = EventPriority.HIGH) public void onJoin(PlayerJoinEvent e) { @@ -138,7 +108,7 @@ public class HullHider implements Listener { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onBlockPhysic(BlockPhysicsEvent e) { - if (FlatteningWrapper.impl.doRecord(e)) { + if (e.getBlock() == e.getSourceBlock() || e.getChangedType() == Material.AIR) { blockUpdate(e.getBlock(), e.getChangedType()); } } @@ -161,18 +131,6 @@ public class HullHider implements Listener { 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) { @@ -210,39 +168,4 @@ public class HullHider implements Listener { hull.removeREntity(e); } } - - - private static final Class packetPlayOutWorldEvent = ClientboundLevelEventPacket.class; - private static final Reflection.Field worldEventPosition = Reflection.getField(packetPlayOutWorldEvent, TechHider.blockPosition, 0); - public static final Reflection.Field blockPositionY = Reflection.getField(Vec3i.class, 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 Object explosionHider(Player player, Object packet) { - return ReflectionWrapper.impl.explosionHider(player, packet, this::packetHider); - } - - private void posHiderGenerator(Class type, Class posType, double factor) { - Function location = posPacketToLocation(type, posType, factor); - packetHiders.put(type, (player, packet) -> packetHider(player, packet, location.apply(packet))); - } - - 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); - - 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 deleted file mode 100644 index 217b4e0b..00000000 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHiderWrapper.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.fightsystem.Config; -import it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap; -import net.minecraft.core.BlockPos; -import net.minecraft.core.SectionPos; -import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; -import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; -import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.craftbukkit.block.data.CraftBlockData; - -import java.util.List; - -public class HullHiderWrapper { - public static final HullHiderWrapper impl = new HullHiderWrapper(); - - public Object generateBlockChangePacket(List changes) { - Object[] blockdata = new Object[changes.size()]; - for (int i = 0; i < blockdata.length; i++) { - Hull.IntVector change = changes.get(i); - blockdata[i] = ((CraftBlockData) Config.world.getBlockData(change.getX(), change.getY(), change.getZ())).getState(); - } - - return generateBlockChangePacket(changes, blockdata); - } - - 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 ClientboundBlockUpdatePacket(new BlockPos(pos.getX(), pos.getY(), pos.getZ()), (BlockState) blockdata[0]); - } - } - - protected Object constructMultiBlockChange(Hull.IntVector section, short[] pos, Object[] blockdata) { - return new ClientboundSectionBlocksUpdatePacket(SectionPos.of(section.getX(), section.getY(), section.getZ()), new Short2ObjectArrayMap<>(pos, blockdata, blockdata.length)); - } -} diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/ReflectionWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/ReflectionWrapper.java deleted file mode 100644 index 9044f7d0..00000000 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/ReflectionWrapper.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.DataComponentType; -import io.papermc.paper.datacomponent.DataComponentTypes; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -import java.util.HashSet; -import java.util.Set; - -public class ReflectionWrapper { - public static final ReflectionWrapper impl = new ReflectionWrapper(); - - private static final Set FORBIDDEN_TYPES = new HashSet<>(); - - static { - FORBIDDEN_TYPES.add(DataComponentTypes.CUSTOM_NAME); - FORBIDDEN_TYPES.add(DataComponentTypes.PROFILE); - FORBIDDEN_TYPES.add(DataComponentTypes.UNBREAKABLE); - FORBIDDEN_TYPES.add(DataComponentTypes.BLOCK_DATA); - FORBIDDEN_TYPES.add(DataComponentTypes.BLOCKS_ATTACKS); - FORBIDDEN_TYPES.add(DataComponentTypes.BUNDLE_CONTENTS); - FORBIDDEN_TYPES.add(DataComponentTypes.CUSTOM_MODEL_DATA); - - FORBIDDEN_TYPES.add(DataComponentTypes.ATTRIBUTE_MODIFIERS); - FORBIDDEN_TYPES.add(DataComponentTypes.TOOL); - FORBIDDEN_TYPES.add(DataComponentTypes.WEAPON); - FORBIDDEN_TYPES.add(DataComponentTypes.FOOD); - FORBIDDEN_TYPES.add(DataComponentTypes.CONSUMABLE); - FORBIDDEN_TYPES.add(DataComponentTypes.POTION_CONTENTS); - FORBIDDEN_TYPES.add(DataComponentTypes.STORED_ENCHANTMENTS); - FORBIDDEN_TYPES.add(DataComponentTypes.CAN_BREAK); - FORBIDDEN_TYPES.add(DataComponentTypes.CAN_PLACE_ON); - FORBIDDEN_TYPES.add(DataComponentTypes.MAX_DAMAGE); - FORBIDDEN_TYPES.add(DataComponentTypes.USE_REMAINDER); - FORBIDDEN_TYPES.add(DataComponentTypes.USE_COOLDOWN); - FORBIDDEN_TYPES.add(DataComponentTypes.SUSPICIOUS_STEW_EFFECTS); - FORBIDDEN_TYPES.add(DataComponentTypes.CHARGED_PROJECTILES); - FORBIDDEN_TYPES.add(DataComponentTypes.INTANGIBLE_PROJECTILE); - FORBIDDEN_TYPES.add(DataComponentTypes.FIREWORKS); - FORBIDDEN_TYPES.add(DataComponentTypes.FIREWORK_EXPLOSION); - FORBIDDEN_TYPES.add(DataComponentTypes.EQUIPPABLE); - FORBIDDEN_TYPES.add(DataComponentTypes.REPAIR_COST); - FORBIDDEN_TYPES.add(DataComponentTypes.ENCHANTABLE); - } - - public Object explosionHider(Player player, Object packet, PacketHiderFunction packetHiderFunction) { - return packet; - } - - public boolean hasItems(ItemStack stack) { - FORBIDDEN_TYPES.forEach(stack::resetData); - return false; - } - - public interface PacketHiderFunction { - Object hide(Player player, Object packet, Location location); - } -} diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java index b3fae41f..091d5717 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -19,9 +19,9 @@ package de.steamwar.fightsystem.utils; +import de.steamwar.Reflection; 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; @@ -30,56 +30,103 @@ 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.sql.SteamwarUser; +import de.steamwar.techhider.AccessPrivilegeProvider; import de.steamwar.techhider.TechHider; import lombok.Getter; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityType; import org.bukkit.GameMode; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; -import javax.crypto.*; -import javax.crypto.spec.SecretKeySpec; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; -public class TechHiderWrapper extends StateDependent implements TechHider.LocationEvaluator, Listener { +public class TechHiderWrapper extends StateDependent implements Listener { public static final boolean ENABLED = !Config.OnlyPublicSchematics && !Config.test() && Config.GameModeConfig.Techhider.Active; @Getter private final ConcurrentHashMap hiddenRegion = new ConcurrentHashMap<>(); - private final ConcurrentHashMap patterns = new ConcurrentHashMap<>(); - private final TechHider techHider; - private final SecretKey key; - public TechHiderWrapper() { - super(ENABLED, FightState.Schem); - techHider = new TechHider(this, Config.GameModeConfig.Techhider.ObfuscateWith, Config.GameModeConfig.Techhider.HiddenBlocks, Config.GameModeConfig.Techhider.HiddenBlockEntities); + public TechHiderWrapper(HullHider hullHider) { + super(ENABLED, FightState.All); + Set blocksToObfuscate = Config.GameModeConfig.Techhider.HiddenBlocks.stream() + .map(CraftMagicNumbers::getBlock) + .collect(Collectors.toUnmodifiableSet()); + Object blockEntityType; try { - key = new SecretKeySpec(Files.readAllBytes(new File(System.getProperty("user.home"), "hullhider.key").toPath()), "AES"); - } catch (IOException e) { - throw new SecurityException(e); + blockEntityType = BuiltInRegistries.class.getDeclaredField("BLOCK_ENTITY_TYPE").get(null); + } catch (Exception e) { + throw new IllegalStateException(e); } + Reflection.Method method = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "get", Optional.class, ResourceLocation.class); + Set> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream() + .map((id) -> { + ResourceLocation loc = ResourceLocation.parse(id); + return ((Optional>>) method.invoke(blockEntityType, loc)).get().value(); + }) + .collect(Collectors.toUnmodifiableSet()); - new StateDependentListener(ENABLED, FightState.Schem, this); + new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith), new AccessPrivilegeProvider() { + @Override + public boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ) { + return Fight.teams().stream().map(FightTeam::getExtendRegion).allMatch(region -> region.chunkOutside(chunkX, chunkZ)); + } + + @Override + public boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ) { + return !hullHider.isBlockHidden(p, blockX, blockY, blockZ); + } + + @Override + public boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block) { + return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block); + } + + // TODO will require entity tracking on the netty thread to prevent future race conditions + @Override + public boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId) { + net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld) p.getWorld()).getHandle().moonrise$getEntityLookup().get(entityId); + + if (nmsEntity != null) { + return !hullHider.isBlockHidden(p, nmsEntity.getBlockX(), nmsEntity.getBlockY(), nmsEntity.getBlockZ()); + } else { + return true; + } + } + + @Override + public boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type) { + return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type); + } + + @Override + public boolean isPlayerPrivilegedToPerformAction(Player p) { + return p.getGameMode() != GameMode.SPECTATOR; + } + }); + + new StateDependentListener(ENABLED, FightState.All, this); register(); } @Override public void enable() { - techHider.enable(); } @Override public void disable() { - techHider.disable(); hiddenRegion.clear(); } @@ -118,72 +165,6 @@ public class TechHiderWrapper extends StateDependent implements TechHider.Locati }); } - @Override - public boolean suppressInteractions(Player player) { - return player.getGameMode() == GameMode.SPECTATOR; - } - - @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)) { - int id = ((y & 3) << 4) + ((z & 3) << 2) + (x & 3); - return (patterns.computeIfAbsent(player, this::getPattern) & (1L << id)) == 0 ? TechHider.State.HIDE_AIR : TechHider.State.HIDE; - } else { - return TechHider.State.CHECK; - } - } else { - return TechHider.State.SKIP; - } - } - - public long getPattern(Player player) { - long pattern = SteamwarUser.get(player.getUniqueId()).getId(); - - byte[] bytes = new byte[]{ - (byte) (pattern & 0xFF), - (byte) ((pattern >>> 8) & 0xFF), - (byte) ((pattern >>> 16) & 0xFF), - (byte) ((pattern >>> 24) & 0xFF) - }; - - try { - Cipher cipher = Cipher.getInstance("AES_256/CFB/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, key); - bytes = cipher.doFinal(bytes); - } catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException | - InvalidKeyException e) { - throw new SecurityException(e); - } - - pattern = bytes[0] & 0xFFL | - ((bytes[1] & 0xFFL) << 8) | - ((bytes[2] & 0xFFL) << 16) | - ((bytes[3] & 0xFFL) << 24); - - /* XXXO OOOX - * XXXO OOOX - * XXOX OOXO - * OOXX XXOO */ - pattern |= 0b1110_1110_1101_0011___0001_0001_0010_1100___0000_0000_0000_0000___0000_0000_0000_0000L; - return pattern; - } - - @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; diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TpsWarper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TpsWarper.java deleted file mode 100644 index 7f45d87c..00000000 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TpsWarper.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.steamwar.fightsystem.utils; - -import net.minecraft.server.MinecraftServer; - -public class TpsWarper { - public static final TpsWarper impl = new TpsWarper(); - - public void warp(float tps) { - MinecraftServer.getServer().tickRateManager().setTickRate(tps); - } -} diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/WorldOfColorWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/WorldOfColorWrapper.java deleted file mode 100644 index 7075bce1..00000000 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/WorldOfColorWrapper.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 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 WorldOfColorWrapper { - public static final WorldOfColorWrapper impl = new WorldOfColorWrapper(); - - public void setTeamColor(Team team, ChatColor color) { - team.setColor(color); - } - - public boolean isInBlock(Projectile e) { - if (e instanceof Arrow arrow) return arrow.isInBlock(); - return false; - } - - public void playSound(Location location, Sound sound, String soundCategory, float volume, float pitch) { - location.getWorld().playSound(location, sound, SoundCategory.valueOf(soundCategory), volume, pitch); - } - - 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_Core/src/de/steamwar/fightsystem/winconditions/WinconditionWaterTechKO.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionWaterTechKO.java index 89b72b5e..b7897d77 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionWaterTechKO.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/winconditions/WinconditionWaterTechKO.java @@ -19,13 +19,22 @@ package de.steamwar.fightsystem.winconditions; -import de.steamwar.fightsystem.utils.FlatteningWrapper; import de.steamwar.linkage.Linked; +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Waterlogged; @Linked public class WinconditionWaterTechKO extends WinconditionBlocks { public WinconditionWaterTechKO() { - super(Winconditions.WATER_TECH_KO, "WaterTechKO", "BAR_WATER", FlatteningWrapper.impl::isWater); + super(Winconditions.WATER_TECH_KO, "WaterTechKO", "BAR_WATER", block -> { + if (block.getType() == Material.WATER) return true; + + BlockData data = block.getBlockData(); + if (!(data instanceof Waterlogged)) return false; + + return ((Waterlogged) data).isWaterlogged(); + }); } } diff --git a/FightSystem/build.gradle.kts b/FightSystem/build.gradle.kts index 6f0c40a7..c6b14b8b 100644 --- a/FightSystem/build.gradle.kts +++ b/FightSystem/build.gradle.kts @@ -57,9 +57,10 @@ tasks.register("WarGear21") { description = "Run a WarGear 1.21 Fight Server" dependsOn(":SpigotCore:shadowJar") dependsOn(":FightSystem:shadowJar") + dependsOn(":KotlinCore:shadowJar") template = "WarGear21" worldName = "arenas/Pentraki" - config = "WarGear20.yml" + config = "WarGear21.yml" jar = "/jars/paper-1.21.6.jar" } diff --git a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java index 59def653..f7df16c1 100644 --- a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java +++ b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java @@ -1,26 +1,8 @@ -/* - * 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 com.comphenix.tinyprotocol; import com.google.common.collect.MapMaker; import de.steamwar.Reflection; +import de.steamwar.core.CRIUWakeupEvent; import de.steamwar.core.Core; import io.netty.channel.*; import net.minecraft.network.Connection; @@ -76,6 +58,7 @@ public class TinyProtocol { public static final TinyProtocol instance = new TinyProtocol(Core.getInstance()); private final Map, List>> packetFilters = new HashMap<>(); + private final Set> globalClientboundFilters = new HashSet<>(); public static void init() { // enforce init @@ -182,6 +165,10 @@ public class TinyProtocol { } } + @EventHandler + public void onCRIUWakeup(CRIUWakeupEvent event) { + registerChannelHandler(); + } }; plugin.getServer().getPluginManager().registerEvents(listener, plugin); @@ -240,19 +227,18 @@ public class TinyProtocol { } } - public void addTypedFilter(Class packetType, BiFunction filter) { + public > void addFilter(Class packetType, BiFunction filter) { packetFilters.computeIfAbsent(packetType, c -> new CopyOnWriteArrayList<>()).add((BiFunction) filter); } - @Deprecated - public void addFilter(Class packetType, BiFunction filter) { - packetFilters.computeIfAbsent(packetType, c -> new CopyOnWriteArrayList<>()).add(filter); - } - public void removeFilter(Class packetType, BiFunction filter) { packetFilters.getOrDefault(packetType, Collections.emptyList()).remove(filter); } + public void addGlobalClientboundFilter(BiFunction, Object> filter) { + globalClientboundFilters.add((BiFunction) filter); + } + /** * Send a packet to a particular player. * @@ -356,6 +342,8 @@ public class TinyProtocol { // Lookup channel again if (channel == null) { + System.out.println(((CraftPlayer) player).getHandle()); + System.out.println(((CraftPlayer) player).getHandle().connection); Channel playerChannel = ((CraftPlayer) player).getHandle().connection.connection.channel; channelLookup.put(player.getName(), channel = playerChannel); } @@ -464,6 +452,13 @@ public class TinyProtocol { public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { try { msg = filterPacket(player, msg); + + if (msg instanceof Packet) { + for (BiFunction filter : globalClientboundFilters) { + msg = filter.apply(player, msg); + if (msg == null) break; + } + } } catch (Exception e) { plugin.getLogger().log(Level.SEVERE, "Error in onPacketOutAsync().", e); } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/CheckpointUtilsJ9.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/CheckpointUtilsJ9.java index 176715ab..4ccbaaa7 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/CheckpointUtilsJ9.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/CheckpointUtilsJ9.java @@ -79,11 +79,14 @@ class CheckpointUtilsJ9 { return; } + e.printStackTrace(); + Bukkit.shutdown(); if (!message.contains("Can't dump ghost file") && !message.contains("Can't create link remap")) // File/Jar has been updated throw new SecurityException(e); } finally { + if (true) return; // Delete checkpoint try (Stream stream = Files.walk(path)) { stream.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldIdentifier.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldIdentifier.java index d9edd2c7..2ca9d085 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldIdentifier.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldIdentifier.java @@ -43,9 +43,8 @@ public class WorldIdentifier { } public WorldIdentifier() { - TinyProtocol.instance.addFilter(ClientboundLoginPacket.class, (player, o) -> { - if (resourceKey == null) return o; - ClientboundLoginPacket packet = (ClientboundLoginPacket) o; + TinyProtocol.instance.addFilter(ClientboundLoginPacket.class, (player, packet) -> { + if (resourceKey == null) return packet; return new ClientboundLoginPacket(packet.playerId(), packet.hardcore(), diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java index 858a2af0..b5276c00 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java @@ -26,6 +26,7 @@ import de.steamwar.linkage.Linked; import de.steamwar.sql.SWException; import de.steamwar.techhider.ProtocolUtils; import de.steamwar.techhider.TechHider; +import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket; import net.minecraft.world.phys.BlockHitResult; @@ -45,7 +46,7 @@ public class AntiNocom implements Listener { private final Map flags = new ConcurrentHashMap<>(); public AntiNocom() { - TinyProtocol.instance.addFilter(blockDig, this::onDig); + TinyProtocol.instance.addFilter(ServerboundPlayerActionPacket.class, this::onDig); registerUseItem(); } @@ -55,26 +56,15 @@ public class AntiNocom implements Listener { } private void registerUseItem() { - Class useItem = ServerboundUseItemOnPacket.class; - - Class movingObjectPositionBlock = BlockHitResult.class; - Reflection.Field useItemPosition = Reflection.getField(useItem, movingObjectPositionBlock, 0); - Reflection.Field movingBlockPosition = Reflection.getField(movingObjectPositionBlock, TechHider.blockPosition, 0); - - Function getPosition = (packet) -> movingBlockPosition.get(useItemPosition.get(packet)); - - TinyProtocol.instance.addFilter(useItem, (player, packet) -> { - Object pos = getPosition.apply(packet); - return isValid(player, "UseItem", TechHider.blockPositionX.get(pos), TechHider.blockPositionZ.get(pos)) ? packet : null; + TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, (player, packet) -> { + BlockPos pos = packet.getHitResult().getBlockPos(); + return isValid(player, "UseItem", pos.getX(), pos.getZ()) ? packet : null; }); } - private static final Class blockDig = ServerboundPlayerActionPacket.class; - private static final Reflection.Field digPosition = Reflection.getField(blockDig, TechHider.blockPosition, 0); - - private Object onDig(Player player, Object packet) { - Object pos = digPosition.get(packet); - return isValid(player, "Dig", TechHider.blockPositionX.get(pos), TechHider.blockPositionZ.get(pos)) ? packet : null; + private ServerboundPlayerActionPacket onDig(Player player, ServerboundPlayerActionPacket packet) { + BlockPos pos = packet.getPos(); + return isValid(player, "Dig", pos.getX(), pos.getZ()) ? packet : null; } private boolean isValid(Player player, String type, int x, int z) { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java index 1a7532d8..1372ed17 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java @@ -78,7 +78,7 @@ public class REntityServer implements Listener { this.callback = callback; if (uninitialized) { - TinyProtocol.instance.addTypedFilter(ServerboundInteractPacket.class, filter); + TinyProtocol.instance.addFilter(ServerboundInteractPacket.class, filter); } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java new file mode 100644 index 00000000..16df8ab0 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/AccessPrivilegeProvider.java @@ -0,0 +1,38 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more 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 net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityType; +import org.bukkit.entity.Player; + +public interface AccessPrivilegeProvider { + boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ); + + boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ); + + boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block); + + boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId); + + boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType type); + + boolean isPlayerPrivilegedToPerformAction(Player p); +} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java index ec4a5f95..b13295bf 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java @@ -19,8 +19,7 @@ package de.steamwar.techhider; -import de.steamwar.Reflection; -import net.minecraft.core.IdMapper; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; @@ -39,18 +38,20 @@ public class BlockIds { } private static final FluidState water = Fluids.WATER.getSource(false); - private static final Iterable registryBlockId = (Iterable) Reflection.getField(TechHider.block, IdMapper.class, 0).get(null); public Set materialToAllIds(Material material) { Set ids = new HashSet<>(); + for (BlockState data : getBlock(material).getStateDefinition().getPossibleStates()) { ids.add(getCombinedId(data)); } if (material == Material.WATER) { - for (BlockState data : registryBlockId) { - if (data.getFluidState() == water) { - ids.add(getCombinedId(data)); + for (Block block : BuiltInRegistries.BLOCK) { + for (BlockState data : block.getStateDefinition().getPossibleStates()) { + if (data.getFluidState() == water) { + ids.add(getCombinedId(data)); + } } } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index ea4c809d..0a280bee 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -22,295 +22,278 @@ package de.steamwar.techhider; import de.steamwar.Reflection; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import lombok.Getter; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.SectionPos; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; -import net.minecraft.resources.ResourceLocation; import net.minecraft.util.SimpleBitStorage; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; import org.bukkit.entity.Player; -import java.util.Collections; 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 ChunkHider { - public static final ChunkHider impl = new ChunkHider(); + private static final UnaryOperator chunkPacketShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); + private static final UnaryOperator chunkDataShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); - public Class mapChunkPacket() { - return ClientboundLevelChunkWithLightPacket.class; + private static final Reflection.Field levelChunkPacketDataField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0); + + private static final Reflection.Field chunkBlockDataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0); + private static final Reflection.Field chunkBlockEntitiesDataField = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0); + + private final int SECTION_SPAN_SIZE = 16; + private final byte BIT_PER_BLOCK_INDIRECTION_LIMIT = 8; + private final byte BIT_PER_BIOME_INDIRECTION_LIMIT = 3; + + private final int BLOCKS_PER_SECTION = 4096; + private final int BIOMES_PER_SECTION = 64; + + private final int blockIdUsedForHiding; + private final AccessPrivilegeProvider accessPrivilegeProvider; + + public ChunkHider(Block blockUsedForObfuscation, AccessPrivilegeProvider accessPrivilegeProvider) { + blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState()); + this.accessPrivilegeProvider = accessPrivilegeProvider; } - private static final UnaryOperator chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); - private static final UnaryOperator chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); + private int getLongsRequiredToEncodeEntries(int bitsPerEntry, int entryCount) { + int entriesPerLong = Long.SIZE / bitsPerEntry; + int dataLengthAsLongCount = (entryCount + entriesPerLong - 1) / entriesPerLong; - 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); - - 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; - }; + return dataLengthAsLongCount; } - private static final Registry> registry = Reflection.getField(BuiltInRegistries.class, "BLOCK_ENTITY_TYPE", Registry.class).get(null); - private static final Reflection.Method getKey = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "getKey", ResourceLocation.class, Object.class); - 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 long[] readSectionDataFromBuffer(ByteBuf dataSource, short bitsPerEntry, int entryCount) { + int dataLengthAsLongCount = getLongsRequiredToEncodeEntries(bitsPerEntry, entryCount); + long[] dataArray = new long[dataLengthAsLongCount]; - protected boolean tileEntityVisible(Set hiddenBlockEntities, Object tile) { - BlockEntityType type = entityType.get(tile); - String path = ((ResourceLocation) getKey.invoke(registry, type)).getPath(); - return !hiddenBlockEntities.contains(path); - } - - 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 if (section.getBitsPerBlock() < 9) { - // Indirect (paletted) storage – only present when bitsPerBlock < 9 in 1.21+ - section.processPalette(); + for (int i = 0; i < dataLengthAsLongCount; i++) { + dataArray[i] = dataSource.readLong(); } - if (section.isSkipSection() || (!section.blockPrecise() && section.isPaletted())) { - section.skipNewDataArray(4096); - return; + return dataArray; + } + + public ClientboundLevelChunkWithLightPacket processLevelChunkWithLightPacket(Player player, ClientboundLevelChunkWithLightPacket packet) { + int chunkX = packet.getX(); + int chunkZ = packet.getZ(); + ClientboundLevelChunkPacketData chunkData = packet.getChunkData(); + + ByteBuf in = Unpooled.wrappedBuffer(chunkData.getReadBuffer()); + ByteBuf out = Unpooled.buffer(in.readableBytes() + 64); + + int worldMinHeight = player.getWorld().getMinHeight(); + int worldMaxHeight = player.getWorld().getMaxHeight(); + + for (int yOffset = worldMinHeight; yOffset < worldMaxHeight; yOffset += SECTION_SPAN_SIZE) { + short blockCount = in.readShort(); + + byte bitsPerBlock = in.readByte(); + + if (bitsPerBlock == 0) { + int sectionBlockId = ProtocolUtils.readVarInt(in); + + out.writeShort(blockCount); + out.writeByte(bitsPerBlock); + ProtocolUtils.writeVarInt(out, sectionBlockId); + } else if (bitsPerBlock <= BIT_PER_BLOCK_INDIRECTION_LIMIT) { + int palletLength = ProtocolUtils.readVarInt(in); + int[] pallet = ProtocolUtils.readVarIntArray(in, palletLength); + + long[] rawData = readSectionDataFromBuffer(in, bitsPerBlock, BLOCKS_PER_SECTION); + SimpleBitStorage data = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, rawData); + + int[] resolvedData = new int[BLOCKS_PER_SECTION]; + for (int i = 0; i < BLOCKS_PER_SECTION; i++) { + int palletReference = data.get(i); + resolvedData[i] = pallet[palletReference]; + } + + int[] obfuscatedBlockData = obfuscateBlockDataArray(player, chunkX, chunkZ, yOffset, resolvedData); + + long[] reEncodedData = encodeDirectBlockDataArray(obfuscatedBlockData); + + + out.writeShort(blockCount); + out.writeByte(15); + for (long rawDataSegment : reEncodedData) { + out.writeLong(rawDataSegment); + } + /* + Int2IntMap blockIdToPalletIndex = new Int2IntOpenHashMap(); + IntArrayList newPallet = new IntArrayList(); + + for(int blockId : obfuscatedData) { + if(!blockIdToPalletIndex.containsKey(blockId)) { + newPallet.add(blockId); + blockIdToPalletIndex.put(blockId, newPallet.size()); + } + } + + byte newBitsPerBlock = (byte) getUnsignedBitLength(newPallet.size()); + int[] newPalletRaw = newPallet.toArray(new int[newPallet.size()]); + + SimpleBitStorage reEncodedData = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, new long[rawData.length]); + for(int i = 0; i < obfuscatedData.length; i++) { + int blockId = obfuscatedData[i]; + int palletReference = blockIdToPalletIndex.get(blockId); + + reEncodedData.set(i, palletReference); + } + + out.writeShort(blockCount); + out.writeByte(newBitsPerBlock); + ProtocolUtils.writeVarInt(out, palletLength); + ProtocolUtils.writeVarIntArray(out, newPalletRaw); + for(long rawDataSegment : reEncodedData.getRaw()) { + out.writeLong(rawDataSegment); + }*/ + + } else { + long[] rawData = readSectionDataFromBuffer(in, bitsPerBlock, BLOCKS_PER_SECTION); + + int[] blockData = resolveDirectBlockDataArray(rawData, bitsPerBlock); + + int[] obfuscatedBlockData = obfuscateBlockDataArray(player, chunkX, chunkZ, yOffset, blockData); + + long[] reEncodedData = encodeDirectBlockDataArray(obfuscatedBlockData); + + + out.writeShort(blockCount); + out.writeByte(15); + for (long rawDataSegment : reEncodedData) { + out.writeLong(rawDataSegment); + } + } + + copyOverSectionBiomeData(in, out); } - SimpleBitStorage values = new SimpleBitStorage(section.getBitsPerBlock(), 4096, section.readNewDataArray(4096)); + if (in.readableBytes() != 0) { + return null; + } - 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; + byte[] data = new byte[out.readableBytes()]; + out.readBytes(data); - TechHider.State test = section.test(x, y, z); + List blockEntities = chunkBlockEntitiesDataField.get(chunkData); + List filteredBlockEntities = filterBlockEntities(player, blockEntities); - 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()); + return buildNewChunkPacket(packet, data, filteredBlockEntities); + + } + + private int[] obfuscateBlockDataArray(Player player, int chunkX, int chunkZ, int yOffset, int[] blockDataArray) { + int[] obfuscatedData = new int[BLOCKS_PER_SECTION]; + for (int sectionY = 0; sectionY < SECTION_SPAN_SIZE; sectionY++) { + for (int sectionZ = 0; sectionZ < SECTION_SPAN_SIZE; sectionZ++) { + for (int sectionX = 0; sectionX < SECTION_SPAN_SIZE; sectionX++) { + int blockDataIndex = (((sectionY * SECTION_SPAN_SIZE) + sectionZ) * SECTION_SPAN_SIZE) + sectionX; + + int worldX = sectionX + (SECTION_SPAN_SIZE * chunkX); + int worldY = sectionY + yOffset; + int worldZ = sectionZ + (SECTION_SPAN_SIZE * chunkZ); + + int blockId = blockDataArray[blockDataIndex]; + BlockState blockState = Block.BLOCK_STATE_REGISTRY.byId(blockId); + Block block = blockState.getBlock(); + + + if (accessPrivilegeProvider.isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block)) { + obfuscatedData[blockDataIndex] = blockId; + } else { + obfuscatedData[blockDataIndex] = blockIdUsedForHiding; } } } } - section.writeDataArray(values.getRaw()); + return obfuscatedData; } - private void biomes(SectionHider section) { - section.copyBitsPerBlock(); - if (section.getBitsPerBlock() == 0) { - section.copyVarInt(); - } else if (section.getBitsPerBlock() < 6) { - section.skipPalette(); - section.skipNewDataArray(64); + private int[] resolveDirectBlockDataArray(long[] rawDataArray, byte bitsPerBlock) { + SimpleBitStorage data = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, rawDataArray); + + int[] resolvedData = new int[BLOCKS_PER_SECTION]; + for (int i = 0; i < BLOCKS_PER_SECTION; i++) { + resolvedData[i] = data.get(i); + } + + return resolvedData; + } + + private long[] encodeDirectBlockDataArray(int[] blockDataArray) { + int longsRequiredToEncodeData = getLongsRequiredToEncodeEntries(15, blockDataArray.length); + + SimpleBitStorage reEncodedData = new SimpleBitStorage(15, BLOCKS_PER_SECTION, new long[longsRequiredToEncodeData]); + for (int i = 0; i < blockDataArray.length; i++) { + int blockId = blockDataArray[i]; + + reEncodedData.set(i, blockId); + } + + return reEncodedData.getRaw(); + } + + private ClientboundLevelChunkWithLightPacket buildNewChunkPacket(ClientboundLevelChunkWithLightPacket originalPacket, byte[] newBlockDataBuffer, List newBlockEntities) { + ClientboundLevelChunkWithLightPacket clonedPacket = (ClientboundLevelChunkWithLightPacket) chunkPacketShallowCloner.apply(originalPacket); + ClientboundLevelChunkPacketData clonedPacketChunkData = (ClientboundLevelChunkPacketData) chunkDataShallowCloner.apply(originalPacket.getChunkData()); + + chunkBlockDataField.set(clonedPacketChunkData, newBlockDataBuffer); + chunkBlockEntitiesDataField.set(clonedPacketChunkData, newBlockEntities); + + levelChunkPacketDataField.set(clonedPacket, clonedPacketChunkData); + + return clonedPacket; + } + + + private static final Class blockEntitiyInfoClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo"); + + private static final Reflection.Field blockEntityInfoTypeField = Reflection.getField(blockEntitiyInfoClass, BlockEntityType.class, 0); + private static final Reflection.Field packedXZField = Reflection.getField(blockEntitiyInfoClass, int.class, 0); + private static final Reflection.Field yField = Reflection.getField(blockEntitiyInfoClass, int.class, 1); + + private List filterBlockEntities(Player player, List blockEntities) { + return blockEntities.stream() + .filter((blockEntityInfo) -> { + BlockEntityType type = blockEntityInfoTypeField.get(blockEntityInfo); + + int packedXZ = packedXZField.get(blockEntityInfo); + + int y = yField.get(blockEntityInfo); + int x = SectionPos.sectionRelativeX((short) packedXZ); + int z = SectionPos.sectionRelativeZ((short) packedXZ); + + return accessPrivilegeProvider.isPlayerPrivilegedToAccessPosition(player, x, y, z) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlockEntity(player, x, y, z, type); + }).toList(); + } + + private void copyOverSectionBiomeData(ByteBuf oldData, ByteBuf newData) { + short bitsPerBiome = oldData.readByte(); + newData.writeByte(bitsPerBiome); + if (bitsPerBiome == 0) { + int sectionBiomeId = ProtocolUtils.readVarInt(oldData); + ProtocolUtils.writeVarInt(newData, sectionBiomeId); + } else if (bitsPerBiome <= BIT_PER_BIOME_INDIRECTION_LIMIT) { + int palletLength = ProtocolUtils.readVarInt(oldData); + ProtocolUtils.writeVarInt(newData, palletLength); + + for (int i = 0; i < palletLength; i++) { + int palletEntry = ProtocolUtils.readVarInt(oldData); + ProtocolUtils.writeVarInt(newData, palletEntry); + } + + long[] rawData = readSectionDataFromBuffer(oldData, bitsPerBiome, BIOMES_PER_SECTION); + for (long rawDataSegment : rawData) { + newData.writeLong(rawDataSegment); + } } else { - // Direct (global) biome IDs – no palette present - section.skipNewDataArray(64); - } - } - - @Getter - class SectionHider { - private final Player player; - private final TechHider techHider; - private final ByteBuf in; - private final ByteBuf out; - - private final int chunkX; - private final int chunkY; - private final int chunkZ; - private final int offsetX; - private final int offsetY; - private final int offsetZ; - - private final boolean skipSection; - - private boolean paletted; - private int bitsPerBlock; - private int blockCount; - private int air; - private int target; - private Set obfuscate; - - public SectionHider(Player player, TechHider techHider, ByteBuf in, ByteBuf out, int chunkX, int chunkY, int chunkZ) { - this.player = player; - this.techHider = techHider; - this.in = in; - this.out = out; - this.chunkX = chunkX; - this.chunkY = chunkY; - this.chunkZ = chunkZ; - this.offsetX = 16 * chunkX; - this.offsetY = 16 * chunkY; - this.offsetZ = 16 * chunkZ; - this.skipSection = techHider.getLocationEvaluator().skipChunkSection(player, chunkX, chunkY, chunkZ); - - this.paletted = false; - this.bitsPerBlock = 0; - this.air = TechHider.AIR_ID; - this.target = techHider.getObfuscationTargetId(); - this.obfuscate = techHider.getObfuscateIds(); - } - - public boolean blockPrecise() { - return techHider.getLocationEvaluator().blockPrecise(player, chunkX, chunkY, chunkZ); - } - - public TechHider.State test(int x, int y, int z) { - return techHider.getLocationEvaluator().check(player, offsetX + x, offsetY + y, offsetZ + z); - } - - public void copyBlockCount() { - this.blockCount = in.readShort(); - out.writeShort(blockCount); - } - - public void copyBitsPerBlock() { - bitsPerBlock = in.readByte(); - out.writeByte(bitsPerBlock); - } - - public int copyVarInt() { - int value = ProtocolUtils.readVarInt(in); - ProtocolUtils.writeVarInt(out, value); - return value; - } - - public void skipPalette() { - int paletteLength = copyVarInt(); - for (int i = 0; i < paletteLength; i++) { - copyVarInt(); - } - } - - public void processPalette() { - if (skipSection) { - skipPalette(); - return; - } - - int paletteLength = copyVarInt(); - if (paletteLength == 0) return; - - paletted = true; - air = 0; - target = 0; - - for (int i = 0; i < paletteLength; i++) { - int entry = ProtocolUtils.readVarInt(in); - if (obfuscate.contains(entry)) { - entry = techHider.getObfuscationTargetId(); - } - - if (entry == TechHider.AIR_ID) { - air = i; - } else if (entry == techHider.getObfuscationTargetId()) { - target = i; - } - - ProtocolUtils.writeVarInt(out, entry); - } - obfuscate = Collections.emptySet(); - } - - public void skipDataArray() { - int dataArrayLength = copyVarInt(); - 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++) { - array[i] = in.readLong(); - } - - 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); + long[] rawData = readSectionDataFromBuffer(oldData, bitsPerBiome, BIOMES_PER_SECTION); + for (long rawDataSegment : rawData) { + newData.writeLong(rawDataSegment); } } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java index 736027cc..e71b522a 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java @@ -140,6 +140,16 @@ public class ProtocolUtils { return result; } + public static int[] readVarIntArray(ByteBuf buffer, int length) { + int[] array = new int[length]; + + for (int i = 0; i < length; i++) { + array[i] = readVarInt(buffer); + } + + return array; + } + public static void writeVarInt(ByteBuf buf, int value) { do { int temp = value & 0b01111111; @@ -152,6 +162,12 @@ public class ProtocolUtils { } while (value != 0); } + public static void writeVarIntArray(ByteBuf buf, int[] values) { + for (int varInt : values) { + writeVarInt(buf, varInt); + } + } + @Deprecated public static int readVarIntLength(byte[] array, int startPos) { int numRead = 0; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 2c291091..ed18f556 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -1,7 +1,7 @@ /* * This file is a part of the SteamWar software. * - * Copyright (C) 2025 SteamWar.de-Serverteam + * Copyright (C) 2026 SteamWar.de-Serverteam * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -21,161 +21,534 @@ package de.steamwar.techhider; import com.comphenix.tinyprotocol.TinyProtocol; import de.steamwar.Reflection; -import lombok.Getter; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.shorts.ShortArraySet; +import it.unimi.dsi.fastutil.shorts.ShortSets; import net.minecraft.core.BlockPos; -import net.minecraft.core.Vec3i; +import net.minecraft.core.SectionPos; +import net.minecraft.network.PacketListener; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.common.*; +import net.minecraft.network.protocol.configuration.ClientboundFinishConfigurationPacket; +import net.minecraft.network.protocol.configuration.ClientboundRegistryDataPacket; +import net.minecraft.network.protocol.configuration.ClientboundSelectKnownPacks; +import net.minecraft.network.protocol.configuration.ClientboundUpdateEnabledFeaturesPacket; +import net.minecraft.network.protocol.cookie.ClientboundCookieRequestPacket; import net.minecraft.network.protocol.game.*; +import net.minecraft.network.protocol.login.*; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; -import org.bukkit.Material; -import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import net.minecraft.world.phys.Vec3; import org.bukkit.entity.Player; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.function.BiFunction; import java.util.function.Function; -import java.util.function.UnaryOperator; -import java.util.stream.Collectors; +import java.util.function.ToIntFunction; +/** + * The TechHider follows the default-deny security principle, + * any packet must be explicitly whitelisted or processed in a + * way that is also compliant with the principle of default-deny. + */ public class TechHider { + private final Set> bypassingPackets; + private final Map>, BiFunction, Packet>> packetProcessors; - public static final Class blockPosition = BlockPos.class; - private static final Class baseBlockPosition = Vec3i.class; - public static final Reflection.Field blockPositionX = Reflection.getField(baseBlockPosition, int.class, 0); - public static final Reflection.Field blockPositionY = Reflection.getField(baseBlockPosition, int.class, 1); - public static final Reflection.Field blockPositionZ = Reflection.getField(baseBlockPosition, int.class, 2); + private final BlockState blockStateUsedForObfuscation; + private final ChunkHider chunkHider; - public static final Class iBlockData = BlockState.class; - public static final Class block = Block.class; + private final AccessPrivilegeProvider privilegeProvider; - public boolean iBlockDataHidden(BlockState iBlockData) { - return obfuscateIds.contains(BlockIds.impl.getCombinedId(iBlockData)); - } + // TODO handle packet bundle + public TechHider(Block blockUsedForObfuscation, AccessPrivilegeProvider privilegeProvider) { + this.blockStateUsedForObfuscation = blockUsedForObfuscation.defaultBlockState(); - public static final Object AIR = CraftMagicNumbers.getBlock(Material.AIR).defaultBlockState(); - public static final int AIR_ID = BlockIds.impl.materialToId(Material.AIR); + this.chunkHider = new ChunkHider(blockUsedForObfuscation, privilegeProvider); + this.privilegeProvider = privilegeProvider; - private final Map, BiFunction> techhiders = new HashMap<>(); - @Getter - private final LocationEvaluator locationEvaluator; - @Getter - private final Object obfuscationTarget; - @Getter - private final int obfuscationTargetId; - @Getter - private final Set obfuscateIds; - @Getter - private final Set hiddenBlockEntities; + this.bypassingPackets = new HashSet<>(List.of( + // --- 5.1.x Login Protocol --- + ClientboundLoginDisconnectPacket.class, // 5.1.1 Disconnect + ClientboundHelloPacket.class, // 5.1.2 Encryption Request + ClientboundLoginFinishedPacket.class, // 5.1.3 Login Success + ClientboundLoginCompressionPacket.class, // 5.1.4 Set Compression + ClientboundCustomQueryPacket.class, // 5.1.5 Login Plugin Request + ClientboundCookieRequestPacket.class, // 5.1.6 Cookie Request - public TechHider(LocationEvaluator locationEvaluator, Material obfuscationTarget, Set obfuscate, Set hiddenBlockEntities) { - this.locationEvaluator = locationEvaluator; - this.obfuscateIds = obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet()); - this.hiddenBlockEntities = hiddenBlockEntities; - this.obfuscationTarget = CraftMagicNumbers.getBlock(obfuscationTarget).defaultBlockState(); - this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget); + // --- 6.1.x Configuration Protocol --- + ClientboundSelectKnownPacks.class, ClientboundCustomPayloadPacket.class, // 6.1.2 Clientbound Plugin Message + ClientboundFinishConfigurationPacket.class, // 6.1.4 Finish Configuration + ClientboundKeepAlivePacket.class, // 6.1.5 Clientbound Keep Alive + ClientboundPingPacket.class, // 6.1.6 Ping + ClientboundRegistryDataPacket.class, // 6.1.8 Registry Data + ClientboundResourcePackPopPacket.class, // 6.1.9 Remove Resource Pack + ClientboundResourcePackPushPacket.class, // 6.1.10 Add Resource Pack + ClientboundStoreCookiePacket.class, // 6.1.11 Store Cookie + ClientboundTransferPacket.class, // 6.1.12 Transfer + ClientboundUpdateEnabledFeaturesPacket.class, // 6.1.13 Feature Flags + ClientboundCustomReportDetailsPacket.class, // 6.1.16 Custom Report Details + ClientboundServerLinksPacket.class, // 6.1.17 Server Links + ClientboundSystemChatPacket.class, // 6.1.18/19 Dialogs are often handled via System Chat or Custom + // Payloads + ClientboundServerDataPacket.class, // 6.1.20 Code of Conduct is usually in Server Data - techhiders.put(blockActionPacket, this::blockActionHider); - techhiders.put(blockChangePacket, this::blockChangeHider); - techhiders.put(tileEntityDataPacket, this::tileEntityDataHider); - techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this)); - techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(this)); - techhiders.put(ServerboundUseItemOnPacket.class, (p, packet) -> locationEvaluator.suppressInteractions(p) ? null : packet); - techhiders.put(ServerboundInteractPacket.class, (p, packet) -> locationEvaluator.suppressInteractions(p) ? null : packet); + // --- 7.1.x Play Protocol --- - } + // --- Safe packets; Not involved with a critical subdomain --- + ClientboundBundleDelimiterPacket.class, // 7.1.1 Bundle Delimiter + ClientboundBossEventPacket.class, // 7.1.10 Boss Bar + ClientboundChangeDifficultyPacket.class, // 7.1.11 Change Difficulty + ClientboundClearTitlesPacket.class, // 7.1.15 Clear Titles + ClientboundCommandSuggestionsPacket.class, // 7.1.16 Command Suggestions Response + ClientboundCommandsPacket.class, // 7.1.17 Commands + ClientboundCookieRequestPacket.class, // 7.1.22 Cookie Request (play) + ClientboundCooldownPacket.class, // 7.1.23 Set Cooldown + ClientboundCustomChatCompletionsPacket.class, // 7.1.24 Chat Suggestions + ClientboundDeleteChatPacket.class, // 7.1.32 Delete Message + ClientboundDisconnectPacket.class, // 7.1.33 Disconnect (play) + ClientboundDisguisedChatPacket.class, // 7.1.34 Disguised Chat Message + ClientboundGameEventPacket.class, // 7.1.39 Game Event (like ElderGuardian effect, rain, thunder, etc.) + ClientboundHorseScreenOpenPacket.class, // 7.1.41 Open Horse Screen: entity id based container. + ClientboundInitializeBorderPacket.class, // 7.1.43 Initialize World Border + ClientboundKeepAlivePacket.class, // 7.1.44 Clientbound Keep Alive (play) + ClientboundLoginPacket.class, // 7.1.49 Login (play) + ClientboundMerchantOffersPacket.class, // 7.1.51 Merchant Offers + ClientboundOpenBookPacket.class, // 7.1.57 Open Book + ClientboundOpenScreenPacket.class, // 7.1.58 Open Screen + ClientboundPingPacket.class, // 7.1.60 Ping (play) + ClientboundPlaceGhostRecipePacket.class, // 7.1.62 Place Ghost Recipe + ClientboundPlayerAbilitiesPacket.class, // 7.1.63 Player Abilities (clientbound) + ClientboundPlayerChatPacket.class, // 7.1.64 Player Chat Message + ClientboundPlayerCombatEndPacket.class, // 7.1.65 End Combat + ClientboundPlayerCombatEnterPacket.class, // 7.1.66 Enter Combat + ClientboundPlayerLookAtPacket.class, // 7.1.70 Look At (Player owning channel) + ClientboundPlayerPositionPacket.class, // 7.1.71 Synchronize Player Position (Player owning the channel) + ClientboundPlayerRotationPacket.class, // 7.1.72 Player Rotation (Player owning the channel) + ClientboundRecipeBookAddPacket.class, // 7.1.73 Recipe Book Add + ClientboundRecipeBookRemovePacket.class, // 7.1.74 Recipe Book Remove + ClientboundRecipeBookSettingsPacket.class, // 7.1.75 Recipe Book Settings + ClientboundResetScorePacket.class, // 7.1.78 Reset Score + ClientboundResourcePackPopPacket.class, // 7.1.79 Remove Resource Pack (play) + ClientboundResourcePackPushPacket.class, // 7.1.80 Add Resource Pack (play) + ClientboundRespawnPacket.class, // 7.1.81 Respawn + ClientboundSelectAdvancementsTabPacket.class, // 7.1.84 Select Advancements Tab + ClientboundServerDataPacket.class, // 7.1.85 Server Data + ClientboundSetActionBarTextPacket.class, // 7.1.86 Set Action Bar Text + ClientboundSetBorderCenterPacket.class, // 7.1.87 Set Border Center + ClientboundSetBorderLerpSizePacket.class, // 7.1.88 Set Border Lerp Size + ClientboundSetBorderSizePacket.class, // 7.1.89 Set Border Size + ClientboundSetBorderWarningDelayPacket.class, // 7.1.90 Set Border Warning Delay + ClientboundSetBorderWarningDistancePacket.class, // 7.1.91 Set Border Warning Distance + ClientboundSetCameraPacket.class, // 7.1.92 Set Camera + ClientboundSetChunkCacheRadiusPacket.class, // 7.1.94 Set Render Distance + ClientboundSetCursorItemPacket.class, // 7.1.95 Set Cursor Item + ClientboundSetDefaultSpawnPositionPacket.class, // 7.1.96 Set Default Spawn Position + ClientboundSetDisplayObjectivePacket.class, // 7.1.97 Display Objective + ClientboundSetExperiencePacket.class, // 7.1.102 Set Experience + ClientboundSetHealthPacket.class, // 7.1.103 Set Health, Saturation and food (Player owning the channel) + ClientboundSetHeldSlotPacket.class, // 7.1.104 Set Held Item (Player owning the channel) + ClientboundSetObjectivePacket.class, // 7.1.105 Update Objectives + ClientboundSetPlayerInventoryPacket.class, // 7.1.107 Set Player Inventory Slot (Player owning the channel) + ClientboundSetPlayerTeamPacket.class, // 7.1.108 Update Teams + ClientboundSetScorePacket.class, // 7.1.109 Update Score + ClientboundSetSimulationDistancePacket.class, // 7.1.110 Set Simulation Distance + ClientboundSetSubtitleTextPacket.class, // 7.1.111 Set Subtitle Text + ClientboundSetTimePacket.class, // 7.1.112 Update Time + ClientboundSetTitleTextPacket.class, // 7.1.113 Set Title Text + ClientboundSetTitlesAnimationPacket.class, // 7.1.114 Set Title Animation Times + ClientboundStartConfigurationPacket.class, // 7.1.117 Start Configuration + ClientboundStoreCookiePacket.class, // 7.1.119 Store Cookie (play) + ClientboundSystemChatPacket.class, // 7.1.120 System Chat Message + ClientboundTabListPacket.class, // 7.1.121 Set Tab List Header And Footer + ClientboundTickingStatePacket.class, // 7.1.126 Set Ticking State + ClientboundTickingStepPacket.class, // 7.1.127 Step Tick + ClientboundTransferPacket.class, // 7.1.128 Transfer (play) + ClientboundUpdateAdvancementsPacket.class, // 7.1.129 Update Advancements + ClientboundUpdateRecipesPacket.class, // 7.1.132 Update Recipes + ClientboundCustomReportDetailsPacket.class, // 7.1.135 Custom Report Details + ClientboundServerLinksPacket.class, // 7.1.136 Server Links + ClientboundClearDialogPacket.class, // 7.1.138 Clear Dialog (play) + ClientboundShowDialogPacket.class, // 7.1.139 Show Dialog (play) - public void enable() { - techhiders.forEach(TinyProtocol.instance::addFilter); - } + // --- Mostly safe packets; Are involved with critical subdomain, but in not critical way --- + ClientboundBlockChangedAckPacket.class, // 7.1.5 Acknowledge Block Change + ClientboundChunkBatchFinishedPacket.class, // 7.1.12 Chunk Batch Finished (Delimiter) + ClientboundChunkBatchStartPacket.class, // 7.1.13 Chunk Batch Start (Delimiter) + ClientboundChunksBiomesPacket.class, // 7.1.14 Chunk Biomes + ClientboundContainerClosePacket.class, // 7.1.18 Close Container + ClientboundSetChunkCacheCenterPacket.class, // 7.1.93 Set Center Chunk + ClientboundForgetLevelChunkPacket.class, // 7.1.38 Unload Chunk + ClientboundUpdateTagsPacket.class, // 7.1.133 Update Tags (play) + ClientboundPlayerInfoRemovePacket.class, // 7.1.68 Player Info Remove + ClientboundPlayerInfoUpdatePacket.class, // 7.1.69 Player Info Update + ClientboundMoveVehiclePacket.class, // 7.1.56 Move Vehicle (vehicle the player is in) + ClientboundStopSoundPacket.class, // 7.1.118 Stop Sound: sound state side channel - public void disable() { - techhiders.forEach(TinyProtocol.instance::removeFilter); - } + ClientboundLightUpdatePacket.class, // 7.1.48 Update Light - public static final Class multiBlockChangePacket = ClientboundSectionBlocksUpdatePacket.class; - public static final UnaryOperator multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(TechHider.multiBlockChangePacket); + ClientboundContainerSetContentPacket.class, // 7.1.19 Set Container Content + ClientboundContainerSetDataPacket.class, // 7.1.20 Set Container Property + ClientboundContainerSetSlotPacket.class // 7.1.21 Set Container Slot + )); - private static final Class blockChangePacket = ClientboundBlockUpdatePacket.class; - private static final Function blockChangeCloner = ProtocolUtils.shallowCloneGenerator(blockChangePacket); - private static final Reflection.Field blockChangePosition = Reflection.getField(blockChangePacket, blockPosition, 0); - private static final Reflection.Field blockChangeBlockData = Reflection.getField(blockChangePacket, iBlockData, 0); + BiFunction, Packet> tossPacket = (p, packet) -> null; + Map>, BiFunction, Packet>> processors = new HashMap<>(); - private Object blockChangeHider(Player p, Object packet) { - switch (locationEvaluator.checkBlockPos(p, blockChangePosition.get(packet))) { - case SKIP: - return packet; - case CHECK: - if (!iBlockDataHidden((BlockState) blockChangeBlockData.get(packet))) { - return packet; + // --- Entity packets - + // 7.1.2 Spawn Entity: entity type and position can reveal hidden contraptions. + processors.put(ClientboundAddEntityPacket.class, (p, packet) -> this.processAddEntityPacket(p, (ClientboundAddEntityPacket) packet)); + // 7.1.3 Entity Animation: entity id based signal, keep blocked until entity visibility is modeled. + processors.put(ClientboundAnimatePacket.class, this.buildEntityPacketProcessor(ClientboundAnimatePacket::getId)); + // 7.1.35 Entity Event: entity id based signal. + processors.put(ClientboundEntityEventPacket.class, this.buildEntityPacketProcessor(ClientboundEntityEventPacket::getEventId)); + // 7.1.36 Teleport Entity: entity id and absolute position. + processors.put(ClientboundTeleportEntityPacket.class, this.buildEntityPacketProcessor(ClientboundTeleportEntityPacket::id)); + // 7.1.42 Hurt Animation: entity id based signal. + processors.put(ClientboundHurtAnimationPacket.class, this.buildEntityPacketProcessor(ClientboundHurtAnimationPacket::id)); + // 7.1.100 Set Entity Velocity: entity id and movement. + processors.put(ClientboundSetEntityMotionPacket.class, this.buildEntityPacketProcessor(ClientboundSetEntityMotionPacket::getId)); + // 7.1.101 Set Equipment: entity equipment can reveal wargear. + processors.put(ClientboundSetEquipmentPacket.class, this.buildEntityPacketProcessor(ClientboundSetEquipmentPacket::getEntity)); + // 7.1.106 Set Passengers: entity relationship signal. + processors.put(ClientboundSetPassengersPacket.class, this.buildEntityPacketProcessor(ClientboundSetPassengersPacket::getVehicle)); + // 7.1.130 Update Attributes: entity id and attribute state. + processors.put(ClientboundUpdateAttributesPacket.class, this.buildEntityPacketProcessor(ClientboundUpdateAttributesPacket::getEntityId)); + // 7.1.131 Entity Effect: entity id and effect state. + processors.put(ClientboundUpdateMobEffectPacket.class, this.buildEntityPacketProcessor(ClientboundUpdateMobEffectPacket::getEntityId)); + // 7.1.77 Remove Entity Effect: entity id and effect state. + processors.put(ClientboundRemoveMobEffectPacket.class, this.buildEntityPacketProcessor(ClientboundRemoveMobEffectPacket::entityId)); + // 7.1.98 Set Entity Metadata: entity state can reveal blocks/items/displays. + processors.put(ClientboundSetEntityDataPacket.class, this.buildEntityPacketProcessor(ClientboundSetEntityDataPacket::id)); + // 7.1.26 Damage Event: entity ids and damage source location can leak hide activity. + processors.put(ClientboundDamageEventPacket.class, this.buildEntityPacketProcessor(ClientboundDamageEventPacket::entityId)); + // 7.1.54 Move Minecart Along Track: entity path and position signal. + processors.put(ClientboundMoveMinecartPacket.class, this.buildEntityPacketProcessor(ClientboundMoveMinecartPacket::entityId)); + // 7.1.124 Synchronize Vehicle Position: entity/vehicle position. + processors.put(ClientboundEntityPositionSyncPacket.class, this.buildEntityPacketProcessor(ClientboundEntityPositionSyncPacket::id)); + // 7.1.134 Projectile Power: projectile/entity signal. + processors.put(ClientboundProjectilePowerPacket.class, this.buildEntityPacketProcessor(ClientboundProjectilePowerPacket::getId)); + // 7.1.123 Pickup Item: item/entity ids and pickup activity. + processors.put(ClientboundTakeItemEntityPacket.class, this.buildEntityPacketProcessor(ClientboundTakeItemEntityPacket::getItemId)); + // 7.1.67 Combat Death: entity/player ids and death message context. + processors.put(ClientboundPlayerCombatKillPacket.class, this.buildEntityPacketProcessor(ClientboundPlayerCombatKillPacket::playerId)); + // 7.1.52/53/55 Update Entity Position/Rotation: entity id and movement signal. + processors.put(ClientboundMoveEntityPacket.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket) packet)); + processors.put(ClientboundMoveEntityPacket.Pos.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket.Pos) packet)); + processors.put(ClientboundMoveEntityPacket.Rot.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket.Rot) packet)); + processors.put(ClientboundMoveEntityPacket.PosRot.class, (p, packet) -> this.processMoveEntityPacket(p, (ClientboundMoveEntityPacket.PosRot) packet)); + + // 7.1.82 Set Head Rotation: entity id and rotation. + processors.put(ClientboundRotateHeadPacket.class, (p, packet) -> this.processRotateHeadPacket(p, (ClientboundRotateHeadPacket) packet)); + // 7.1.76 Remove Entities: entity id visibility side channel. + processors.put(ClientboundRemoveEntitiesPacket.class, (p, packet) -> this.processRemoveEntitiesPacket(p, (ClientboundRemoveEntitiesPacket) packet)); + // 7.1.99 Link Entities: entity relationship signal. + processors.put(ClientboundSetEntityLinkPacket.class, (p, packet) -> this.processSetEntityLinkPacket(p, (ClientboundSetEntityLinkPacket) packet)); + + // --- Block entity packets --- + // 7.1.7 Block Entity Data: currently filtered by block position and allowed tile-entity actions. + processors.put(ClientboundBlockEntityDataPacket.class, (p, packet) -> processBlockEntityDataPacket(p, (ClientboundBlockEntityDataPacket) packet)); + // 7.1.122 Tag Query Response: block-entity query result. + processors.put(ClientboundTagQueryPacket.class, tossPacket); + + // --- Block packets --- + // 7.1.6 Set Block Destroy Stage: block position and mining progress can reveal activity. + processors.put(ClientboundBlockDestructionPacket.class, (p, packet) -> proccessBlockDestructionPacket(p, (ClientboundBlockDestructionPacket) packet)); + // 7.1.8 Block Action: currently filtered by block position. + processors.put(ClientboundBlockEventPacket.class, (p, packet) -> processBlockEventPacket(p, (ClientboundBlockEventPacket) packet)); + // 7.1.9 Block Update: currently filtered/obfuscated by block position and block id. + processors.put(ClientboundBlockUpdatePacket.class, (p, packet) -> processBlockUpdatePacket(p, (ClientboundBlockUpdatePacket) packet)); + + // --- Chunk packets --- + // 7.1.45 Chunk Data and Update Light: currently filtered/obfuscated by chunk data processor. + processors.put(ClientboundLevelChunkWithLightPacket.class, (p, packet) -> processChunkWithLight(p, (ClientboundLevelChunkWithLightPacket) packet)); + + // --- Section packets --- + // 7.1.83 Update Section Blocks: currently filtered/obfuscated by section block processor. + processors.put(ClientboundSectionBlocksUpdatePacket.class, (p, packet) -> processSectionUpdate(p, (ClientboundSectionBlocksUpdatePacket) packet)); + + // --- Particle packets --- + // 7.1.47 Particle: particle type and position can reveal hidden machinery. + processors.put(ClientboundLevelParticlesPacket.class, (p, packet) -> processLevelParticlesPacket(p, (ClientboundLevelParticlesPacket) packet)); + + // --- Sound packets --- + // 7.1.115 Entity Sound Effect: entity id and sound. + processors.put(ClientboundSoundEntityPacket.class, this.buildEntityPacketProcessor(ClientboundSoundEntityPacket::getId)); + // 7.1.116 Sound Effect: sound type and position. + processors.put(ClientboundSoundPacket.class, this.buildPositionBasedPacketProcessor(packet -> { + return new BlockPos.MutableBlockPos(packet.getX(), packet.getY(), packet.getZ()).immutable(); + })); + + // --- Others --- + // 7.1.137 Waypoint: world/entity tracking signal. + processors.put(ClientboundTrackedWaypointPacket.class, tossPacket); + // 7.1.50 Map Data: map pixels/icons can reveal player/world state. + processors.put(ClientboundMapItemDataPacket.class, tossPacket); + // 7.1.46 World Event: block position/event id can leak activity. + processors.put(ClientboundLevelEventPacket.class, buildPositionBasedPacketProcessor(ClientboundLevelEventPacket::getPos)); + // 7.1.59 Open Sign Editor: block position. + processors.put(ClientboundOpenSignEditorPacket.class, buildPositionBasedPacketProcessor(ClientboundOpenSignEditorPacket::getPos)); + // 7.1.37 Explosion: position, affected blocks, and knockback can reveal TNT/tech. + processors.put(ClientboundExplodePacket.class, (p, rawPacket) -> { + ClientboundExplodePacket packet = (ClientboundExplodePacket) rawPacket; + Vec3 pos = packet.center(); + int blockX = (int) pos.x; + int blockY = (int) pos.y; + int blockZ = (int) pos.z; + + + return proccessPositionBasedPacket(p, blockX, blockY, blockZ, packet); + }); + + this.packetProcessors = processors; + + TinyProtocol.instance.addGlobalClientboundFilter((player, rawPacket) -> { + try { + if (rawPacket instanceof ClientboundBundlePacket bundlePacket) { + List> processedPackets = new ArrayList<>(); + + for (Packet packet : bundlePacket.subPackets()) { + Packet processedPacket = (Packet) processPacket(player, packet); + + if (processedPacket != null) { + processedPackets.add(processedPacket); + } + } + + return new ClientboundBundlePacket(processedPackets); + } else { + return processPacket(player, rawPacket); } - case HIDE: - packet = blockChangeCloner.apply(packet); - blockChangeBlockData.set(packet, obfuscationTarget); - return packet; - case HIDE_AIR: - default: - packet = blockChangeCloner.apply(packet); - blockChangeBlockData.set(packet, AIR); - return packet; + } catch (Throwable t) { + t.printStackTrace(); + return null; + } + }); + + TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, (p, packet) -> privilegeProvider.isPlayerPrivilegedToPerformAction(p) ? packet : null); + TinyProtocol.instance.addFilter(ServerboundInteractPacket.class, (p, packet) -> privilegeProvider.isPlayerPrivilegedToPerformAction(p) ? packet : null); + } + + private Packet processPacket(Player player, Packet packet) { + if (bypassingPackets.contains(packet.getClass())) { + return packet; + } else if (packetProcessors.containsKey(packet.getClass())) { + return packetProcessors.get(packet.getClass()).apply(player, packet); + } else { + return null; } } - private static final Class blockActionPacket = ClientboundBlockEventPacket.class; - private static final Reflection.Field blockActionPosition = Reflection.getField(blockActionPacket, blockPosition, 0); + private Packet processAddEntityPacket(Player player, ClientboundAddEntityPacket packet) { + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, packet.getId()) && privilegeProvider.isPlayerPrivilegedToAccessPosition(player, (int) packet.getX(), (int) packet.getY(), (int) packet.getZ())) { + return packet; + } else { + return null; + } + } - private Object blockActionHider(Player p, Object packet) { - if (locationEvaluator.checkBlockPos(p, blockActionPosition.get(packet)) == State.SKIP) { + private Packet processEntityPacket(Player player, int entityId, Packet packet) { + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) { + return packet; + } else { + return null; + } + } + + private > BiFunction, Packet> buildEntityPacketProcessor(ToIntFunction entityIdExtractor) { + return (p, rawPacket) -> { + TTargetPacket packet = (TTargetPacket) rawPacket; + return processEntityPacket(p, entityIdExtractor.applyAsInt(packet), packet); + }; + } + + private final Reflection.Field moveEntityPacketEntityIdField = Reflection.getField(ClientboundMoveEntityPacket.class, int.class, 0); + + private Packet processMoveEntityPacket(Player player, ClientboundMoveEntityPacket packet) { + int entityId = moveEntityPacketEntityIdField.get(packet); + + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) { + return packet; + } else { + return null; + } + } + + private final Reflection.Field rotateHeadPacketEntityIdField = Reflection.getField(ClientboundRotateHeadPacket.class, int.class, 0); + + private Packet processRotateHeadPacket(Player player, ClientboundRotateHeadPacket packet) { + int entityId = rotateHeadPacketEntityIdField.get(packet); + + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) { + return packet; + } else { + return null; + } + } + + private Packet processRemoveEntitiesPacket(Player player, ClientboundRemoveEntitiesPacket packet) { + IntList entityIdsToRemove = packet.getEntityIds(); + + IntList filteredEntitiesToRemove = entityIdsToRemove.intStream().filter((id) -> privilegeProvider.isPlayerPrivilegedToAccessEntity(player, id)).collect(IntArrayList::new, IntList::add, IntList::addAll); + + return new ClientboundRemoveEntitiesPacket(filteredEntitiesToRemove); + } + + public Packet processSetEntityLinkPacket(Player player, ClientboundSetEntityLinkPacket packet) { + int fromEntityId = packet.getSourceId(); + int toEntityId = packet.getDestId(); + + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, fromEntityId) && privilegeProvider.isPlayerPrivilegedToAccessEntity(player, toEntityId)) { + return packet; + } else { + return null; + } + } + + private Packet processBlockEventPacket(Player player, ClientboundBlockEventPacket packet) { + BlockPos blockPos = packet.getPos(); + Block block = packet.getBlock(); + int blockX = blockPos.getX(); + int blockY = blockPos.getY(); + int blockZ = blockPos.getZ(); + + if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && privilegeProvider.isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) { + return packet; + } else { + return null; + } + } + + private Packet processBlockUpdatePacket(Player player, ClientboundBlockUpdatePacket packet) { + BlockPos blockPos = packet.getPos(); + Block block = packet.getBlockState().getBlock(); + int blockX = blockPos.getX(); + int blockY = blockPos.getY(); + int blockZ = blockPos.getZ(); + + if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && privilegeProvider.isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) { + return packet; + } else if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + return new ClientboundBlockUpdatePacket(blockPos, blockStateUsedForObfuscation); + } else { + return null; + } + } + + private Packet processBlockEntityDataPacket(Player player, ClientboundBlockEntityDataPacket packet) { + BlockPos blockPos = packet.getPos(); + BlockEntityType blockEntityType = packet.getType(); + int blockX = blockPos.getX(); + int blockY = blockPos.getY(); + int blockZ = blockPos.getZ(); + + if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && privilegeProvider.isPlayerPrivilegedToAccessBlockEntity(player, blockX, blockY, blockZ, blockEntityType)) { + return packet; + } else { + return null; + } + } + + private Packet proccessBlockDestructionPacket(Player player, ClientboundBlockDestructionPacket packet) { + BlockPos blockPos = packet.getPos(); + int entityBreakingBlockId = packet.getId(); + + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityBreakingBlockId) && privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) { + return packet; + } else { + return null; + } + } + + private final Reflection.Field sectionPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, SectionPos.class, 0); + private final Reflection.Field oldPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, short[].class, 0); + private final Reflection.Field oldStatesField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, BlockState[].class, 0); + + private ClientboundSectionBlocksUpdatePacket processSectionUpdate(Player p, ClientboundSectionBlocksUpdatePacket packet) { + SectionPos sectionPos = sectionPosField.get(packet); + short[] oldPos = oldPosField.get(packet); + BlockState[] oldStates = oldStatesField.get(packet); + + boolean modified = false; + List filteredPos = new ArrayList<>(oldPos.length); + List filteredStates = new ArrayList<>(oldStates.length); + + for (int i = 0; i < oldPos.length; i++) { + short posShort = oldPos[i]; + BlockState state = oldStates[i]; + Block block = state.getBlock(); + + int worldX = sectionPos.relativeToBlockX(posShort); + int worldY = sectionPos.relativeToBlockY(posShort); + int worldZ = sectionPos.relativeToBlockZ(posShort); + + if (privilegeProvider.isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ) && privilegeProvider.isPlayerPrivilegedToAccessBlock(p, worldX, worldY, worldZ, block)) { + // TODO statefull !!! + filteredPos.add(posShort); + filteredStates.add(state); + } else if (privilegeProvider.isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ)) { + modified = true; + filteredPos.add(posShort); + filteredStates.add(blockStateUsedForObfuscation); + } + } + + if (filteredStates.isEmpty()) { + return null; + } + if (!modified) { return packet; } - return null; + + short[] newPos = new short[filteredPos.size()]; + for (int i = 0; i < newPos.length; i++) { + newPos[i] = filteredPos.get(i); + } + + BlockState[] newStates = filteredStates.toArray(new BlockState[0]); + + return new ClientboundSectionBlocksUpdatePacket(sectionPos, ShortSets.unmodifiable(new ShortArraySet(newPos)), newStates); } - public static final Class tileEntityDataPacket = ClientboundBlockEntityDataPacket.class; - private static final Reflection.Field tileEntityDataPosition = Reflection.getField(tileEntityDataPacket, blockPosition, 0); + private Packet processLevelParticlesPacket(Player player, ClientboundLevelParticlesPacket packet) { + int blockX = (int) packet.getX(); + int blockY = (int) packet.getY(); + int blockZ = (int) packet.getZ(); - private Object tileEntityDataHider(Player p, Object packet) { - switch (locationEvaluator.checkBlockPos(p, tileEntityDataPosition.get(packet))) { - case SKIP: - return packet; - case CHECK: - if (ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)) { - return packet; - } - default: - return null; + if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + return packet; + } else { + return null; } } - public enum State { - SKIP, - CHECK, - HIDE, - HIDE_AIR + private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) { + if (privilegeProvider.isEveryonePrivilegedToAccessAllDataWithinChunk(packet.getX(), packet.getZ())) { + return packet; + } else { + return chunkHider.processLevelChunkWithLightPacket(p, packet); + } } - public interface LocationEvaluator { - default boolean suppressInteractions(Player player) { - return false; + private Packet proccessPositionBasedPacket(Player player, int blockX, int blockY, int blockZ, Packet packet) { + if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) { + return packet; + } else { + return null; } + } - boolean skipChunk(Player player, int x, int z); + private > BiFunction, Packet> buildPositionBasedPacketProcessor(Function positionExtractor) { + return (p, rawPacket) -> { + TTargetPacket packet = (TTargetPacket) rawPacket; + BlockPos pos = positionExtractor.apply(packet); + int blockX = pos.getX(); + int blockY = pos.getY(); + int blockZ = pos.getZ(); - default boolean skipChunkSection(Player player, int x, int y, int z) { - return skipChunk(player, x, z); - } - - default State check(Player player, int x, int y, int z) { - return skipChunkSection(player, ProtocolUtils.posToChunk(x), ProtocolUtils.posToChunk(y), ProtocolUtils.posToChunk(z)) ? State.SKIP : State.CHECK; - } - - default State checkBlockPos(Player player, Object pos) { - return check(player, blockPositionX.get(pos), blockPositionY.get(pos), blockPositionZ.get(pos)); - } - - default boolean blockPrecise(Player player, int x, int y, int z) { - return false; - } + return proccessPositionBasedPacket(p, blockX, blockY, blockZ, packet); + }; } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java new file mode 100644 index 00000000..c3df8e6f --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java @@ -0,0 +1,319 @@ +/* + * 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.legacy; + +import de.steamwar.Reflection; +import de.steamwar.techhider.ProtocolUtils; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import lombok.Getter; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.SimpleBitStorage; +import net.minecraft.world.level.block.entity.BlockEntityType; +import org.bukkit.entity.Player; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +@Deprecated +public class ChunkHider { + public static final ChunkHider impl = new ChunkHider(); + + 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); + + 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; + }; + } + + private static final Registry> registry = Reflection.getField(BuiltInRegistries.class, "BLOCK_ENTITY_TYPE", Registry.class).get(null); + private static final Reflection.Method getKey = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "getKey", ResourceLocation.class, Object.class); + 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); + + protected boolean tileEntityVisible(Set hiddenBlockEntities, Object tile) { + BlockEntityType type = entityType.get(tile); + String path = ((ResourceLocation) getKey.invoke(registry, type)).getPath(); + return !hiddenBlockEntities.contains(path); + } + + 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 if (section.getBitsPerBlock() < 9) { + // Indirect (paletted) storage – only present when bitsPerBlock < 9 in 1.21+ + 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); + } else { + // Direct (global) biome IDs – no palette present + section.skipNewDataArray(64); + } + } + + @Getter + class SectionHider { + private final Player player; + private final TechHider techHider; + private final ByteBuf in; + private final ByteBuf out; + + private final int chunkX; + private final int chunkY; + private final int chunkZ; + private final int offsetX; + private final int offsetY; + private final int offsetZ; + + private final boolean skipSection; + + private boolean paletted; + private int bitsPerBlock; + private int blockCount; + private int air; + private int target; + private Set obfuscate; + + public SectionHider(Player player, TechHider techHider, ByteBuf in, ByteBuf out, int chunkX, int chunkY, int chunkZ) { + this.player = player; + this.techHider = techHider; + this.in = in; + this.out = out; + this.chunkX = chunkX; + this.chunkY = chunkY; + this.chunkZ = chunkZ; + this.offsetX = 16 * chunkX; + this.offsetY = 16 * chunkY; + this.offsetZ = 16 * chunkZ; + this.skipSection = techHider.getLocationEvaluator().skipChunkSection(player, chunkX, chunkY, chunkZ); + + this.paletted = false; + this.bitsPerBlock = 0; + this.air = TechHider.AIR_ID; + this.target = techHider.getObfuscationTargetId(); + this.obfuscate = techHider.getObfuscateIds(); + } + + public boolean blockPrecise() { + return techHider.getLocationEvaluator().blockPrecise(player, chunkX, chunkY, chunkZ); + } + + public TechHider.State test(int x, int y, int z) { + return techHider.getLocationEvaluator().check(player, offsetX + x, offsetY + y, offsetZ + z); + } + + public void copyBlockCount() { + this.blockCount = in.readShort(); + out.writeShort(blockCount); + } + + public void copyBitsPerBlock() { + bitsPerBlock = in.readByte(); + out.writeByte(bitsPerBlock); + } + + public int copyVarInt() { + int value = ProtocolUtils.readVarInt(in); + ProtocolUtils.writeVarInt(out, value); + return value; + } + + public void skipPalette() { + int paletteLength = copyVarInt(); + for (int i = 0; i < paletteLength; i++) { + copyVarInt(); + } + } + + public void processPalette() { + if (skipSection) { + skipPalette(); + return; + } + + int paletteLength = copyVarInt(); + if (paletteLength == 0) return; + + paletted = true; + air = 0; + target = 0; + + for (int i = 0; i < paletteLength; i++) { + int entry = ProtocolUtils.readVarInt(in); + if (obfuscate.contains(entry)) { + entry = techHider.getObfuscationTargetId(); + } + + if (entry == TechHider.AIR_ID) { + air = i; + } else if (entry == techHider.getObfuscationTargetId()) { + target = i; + } + + ProtocolUtils.writeVarInt(out, entry); + } + obfuscate = Collections.emptySet(); + } + + public void skipDataArray() { + int dataArrayLength = copyVarInt(); + 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++) { + array[i] = in.readLong(); + } + + 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/ProtocolWrapper.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java similarity index 95% rename from SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java rename to SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java index 72469241..00e0dc31 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package de.steamwar.techhider; +package de.steamwar.techhider.legacy; import de.steamwar.Reflection; import net.minecraft.core.SectionPos; @@ -29,6 +29,7 @@ import org.bukkit.entity.Player; import java.util.ArrayList; import java.util.function.BiFunction; +@Deprecated public class ProtocolWrapper { public static final ProtocolWrapper impl = new ProtocolWrapper(); @@ -88,8 +89,4 @@ public class ProtocolWrapper { public boolean unfilteredTileEntityDataAction(Object packet) { return tileEntityType.get(packet) != signType; } - - public BiFunction blockBreakHiderGenerator(Class blockBreakPacket, TechHider techHider) { - return null; - } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java new file mode 100644 index 00000000..25b3f215 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java @@ -0,0 +1,184 @@ +/* + * 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.legacy; + +import com.comphenix.tinyprotocol.TinyProtocol; +import de.steamwar.Reflection; +import de.steamwar.techhider.BlockIds; +import de.steamwar.techhider.ProtocolUtils; +import lombok.Getter; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; +import net.minecraft.network.protocol.game.*; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.bukkit.Material; +import org.bukkit.craftbukkit.util.CraftMagicNumbers; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +@Deprecated +public class TechHider { + + public static final Class blockPosition = BlockPos.class; + private static final Class baseBlockPosition = Vec3i.class; + public static final Reflection.Field blockPositionX = Reflection.getField(baseBlockPosition, int.class, 0); + public static final Reflection.Field blockPositionY = Reflection.getField(baseBlockPosition, int.class, 1); + public static final Reflection.Field blockPositionZ = Reflection.getField(baseBlockPosition, int.class, 2); + + public static final Class iBlockData = BlockState.class; + public static final Class block = Block.class; + + public boolean iBlockDataHidden(BlockState iBlockData) { + return obfuscateIds.contains(BlockIds.impl.getCombinedId(iBlockData)); + } + + public static final Object AIR = CraftMagicNumbers.getBlock(Material.AIR).defaultBlockState(); + public static final int AIR_ID = BlockIds.impl.materialToId(Material.AIR); + + private final Map, BiFunction> techhiders = new HashMap<>(); + @Getter + private final LocationEvaluator locationEvaluator; + @Getter + private final Object obfuscationTarget; + @Getter + private final int obfuscationTargetId; + @Getter + private final Set obfuscateIds; + @Getter + private final Set hiddenBlockEntities; + + public TechHider(LocationEvaluator locationEvaluator, Material obfuscationTarget, Set obfuscate, Set hiddenBlockEntities) { + this.locationEvaluator = locationEvaluator; + this.obfuscateIds = obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet()); + this.hiddenBlockEntities = hiddenBlockEntities; + this.obfuscationTarget = CraftMagicNumbers.getBlock(obfuscationTarget).defaultBlockState(); + this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget); + + techhiders.put(blockActionPacket, this::blockActionHider); + techhiders.put(blockChangePacket, this::blockChangeHider); + techhiders.put(tileEntityDataPacket, this::tileEntityDataHider); + techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this)); + techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(this)); + techhiders.put(ServerboundUseItemOnPacket.class, (p, packet) -> locationEvaluator.suppressInteractions(p) ? null : packet); + techhiders.put(ServerboundInteractPacket.class, (p, packet) -> locationEvaluator.suppressInteractions(p) ? null : packet); + + } + + public void enable() { + techhiders.forEach((type, playerObjectBiFunction) -> TinyProtocol.instance.addFilter((Class) type, playerObjectBiFunction)); + } + + public void disable() { + techhiders.forEach(TinyProtocol.instance::removeFilter); + } + + public static final Class multiBlockChangePacket = ClientboundSectionBlocksUpdatePacket.class; + public static final UnaryOperator multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(TechHider.multiBlockChangePacket); + + private static final Class blockChangePacket = ClientboundBlockUpdatePacket.class; + private static final Function blockChangeCloner = ProtocolUtils.shallowCloneGenerator(blockChangePacket); + private static final Reflection.Field blockChangePosition = Reflection.getField(blockChangePacket, blockPosition, 0); + private static final Reflection.Field blockChangeBlockData = Reflection.getField(blockChangePacket, iBlockData, 0); + + private Object blockChangeHider(Player p, Object packet) { + switch (locationEvaluator.checkBlockPos(p, blockChangePosition.get(packet))) { + case SKIP: + return packet; + case CHECK: + if (!iBlockDataHidden((BlockState) blockChangeBlockData.get(packet))) { + return packet; + } + case HIDE: + packet = blockChangeCloner.apply(packet); + blockChangeBlockData.set(packet, obfuscationTarget); + return packet; + case HIDE_AIR: + default: + packet = blockChangeCloner.apply(packet); + blockChangeBlockData.set(packet, AIR); + return packet; + } + } + + private static final Class blockActionPacket = ClientboundBlockEventPacket.class; + private static final Reflection.Field blockActionPosition = Reflection.getField(blockActionPacket, blockPosition, 0); + + private Object blockActionHider(Player p, Object packet) { + if (locationEvaluator.checkBlockPos(p, blockActionPosition.get(packet)) == State.SKIP) { + return packet; + } + return null; + } + + public static final Class tileEntityDataPacket = ClientboundBlockEntityDataPacket.class; + private static final Reflection.Field tileEntityDataPosition = Reflection.getField(tileEntityDataPacket, blockPosition, 0); + + private Object tileEntityDataHider(Player p, Object packet) { + switch (locationEvaluator.checkBlockPos(p, tileEntityDataPosition.get(packet))) { + case SKIP: + return packet; + case CHECK: + if (ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)) { + return packet; + } + default: + return null; + } + } + + public enum State { + SKIP, + CHECK, + HIDE, + HIDE_AIR + } + + public interface LocationEvaluator { + default boolean suppressInteractions(Player player) { + return false; + } + + boolean skipChunk(Player player, int x, int z); + + default boolean skipChunkSection(Player player, int x, int y, int z) { + return skipChunk(player, x, z); + } + + default State check(Player player, int x, int y, int z) { + return skipChunkSection(player, ProtocolUtils.posToChunk(x), ProtocolUtils.posToChunk(y), ProtocolUtils.posToChunk(z)) ? State.SKIP : State.CHECK; + } + + default State checkBlockPos(Player player, Object pos) { + return check(player, blockPositionX.get(pos), blockPositionY.get(pos), blockPositionZ.get(pos)); + } + + default boolean blockPrecise(Player player, int x, int y, int z) { + return false; + } + } +} diff --git a/VelocityCore/Dependencies/build.gradle.kts b/VelocityCore/Dependencies/build.gradle.kts index 2b0beeb2..34b43f07 100644 --- a/VelocityCore/Dependencies/build.gradle.kts +++ b/VelocityCore/Dependencies/build.gradle.kts @@ -37,12 +37,7 @@ tasks.build { } kotlin { - jvmToolchain(17) -} - -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + jvmToolchain(21) } dependencies { diff --git a/VelocityCore/Persistent/build.gradle.kts b/VelocityCore/Persistent/build.gradle.kts index 462fccfd..34e0830d 100644 --- a/VelocityCore/Persistent/build.gradle.kts +++ b/VelocityCore/Persistent/build.gradle.kts @@ -21,11 +21,6 @@ plugins { steamwar.java } -java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - dependencies { compileOnly(libs.velocity) annotationProcessor(libs.velocityapi) diff --git a/VelocityCore/src/de/steamwar/messages/BungeeCore.properties b/VelocityCore/src/de/steamwar/messages/BungeeCore.properties index 900c7b64..0dd2a7d6 100644 --- a/VelocityCore/src/de/steamwar/messages/BungeeCore.properties +++ b/VelocityCore/src/de/steamwar/messages/BungeeCore.properties @@ -495,7 +495,7 @@ TEAM_INFO_MEMBER = §7Member ({0})§8: {1} TEAM_INFO_EVENTS = §7Events ({0})§8: §e{1} #Server Team TEAM_INFO_SW_USAGE = §8/§7team info §8[§eSW§8|§eSteamWar§8] -TEAM_INFO_SW_HEADER = §eSteam§8War Server Team +TEAM_INFO_SW_HEADER = §7Server Team §eSteam§8War §8[§eS§8W] TEAM_INFO_SW_RANK = {0} §7({1})§8: {2} #TEAM Prefix @@ -596,8 +596,8 @@ JOIN_STREAMING = §5Streaming Mode§7 is still active§8.§7 Keep in mind that y EVENTMODE_KICK = §cYou are not an event participant. #TablistManager -TABLIST_PHASE_WEBSITE = §8Website: https://§eSteam§8War.de -TABLIST_PHASE_DISCORD = §8Discord: https://§eSteam§8War.de/discord +TABLIST_PHASE_WEBSITE = §7Website§8: §7https://§eSteam§8War§7.de +TABLIST_PHASE_DISCORD = §7Discord§8: §7https://§eSteam§8War§7.de/discord TABLIST_FOOTER = §e{0} {1}§8ms §ePlayers§8: §7{2} TABLIST_BAU = §7§lBuild LIST_COMMAND = §e{0}§8 [{1}]: §7{2} diff --git a/VelocityCore/src/de/steamwar/messages/BungeeCore_de.properties b/VelocityCore/src/de/steamwar/messages/BungeeCore_de.properties index 83970fd7..47b815ea 100644 --- a/VelocityCore/src/de/steamwar/messages/BungeeCore_de.properties +++ b/VelocityCore/src/de/steamwar/messages/BungeeCore_de.properties @@ -465,10 +465,6 @@ TEAM_INFO_TEAM = §7Team §e{0} §8[§{1}{2}§8] TEAM_INFO_LEADER = §7Leader ({0})§8: {1} TEAM_INFO_MEMBER = §7Member ({0})§8: {1} TEAM_INFO_EVENTS = §7Events ({0})§8: §e{1} -#Server Team -TEAM_INFO_SW_USAGE = §8/§7team info §8[§eSW§8|§eSteamWar§8] -TEAM_INFO_SW_HEADER = §eSteam§8War Serverteam -TEAM_INFO_SW_RANK = {0} §7({1})§8: {2} #Team Prefix TEAM_PREFIX_USAGE = §8/§7team prefix §8[§eteam§8|§eSW§8] diff --git a/VelocityCore/src/de/steamwar/velocitycore/Config.java b/VelocityCore/src/de/steamwar/velocitycore/Config.java index 5c2a886f..6c8ec6d8 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/Config.java +++ b/VelocityCore/src/de/steamwar/velocitycore/Config.java @@ -21,6 +21,7 @@ package de.steamwar.velocitycore; import com.velocitypowered.api.proxy.server.RegisteredServer; import lombok.Getter; +import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.TypeDescription; import org.yaml.snakeyaml.Yaml; @@ -46,7 +47,7 @@ public class Config { Constructor constructor = new Constructor(clazz, new LoaderOptions()); constructor.addTypeDescription(typeDescription); - Representer representer = new Representer(); + Representer representer = new Representer(new DumperOptions()); representer.getPropertyUtils().setSkipMissingProperties(true); Yaml yaml = new Yaml(constructor, representer); diff --git a/VelocityCore/src/de/steamwar/velocitycore/commands/TeamCommand.java b/VelocityCore/src/de/steamwar/velocitycore/commands/TeamCommand.java index 502694bd..c73b256c 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/commands/TeamCommand.java +++ b/VelocityCore/src/de/steamwar/velocitycore/commands/TeamCommand.java @@ -362,13 +362,13 @@ public class TeamCommand extends SWCommand { @Register(value = "prefix", description = "TEAM_PREFIX_USAGE") public void prefix(@Validator("canUseTeamPrefix") Chatter sender) { boolean swPrefix = SteamwarPrefix.usesSWPrefix(sender.user()); - sender.system("TEAM_PREFIX_CURRENT", swPrefix ? STEAM_WAR : sender.parse("TEAM_PREFIX_TEAM")); + sender.system("TEAM_PREFIX_CURRENT", swPrefix ? STEAM_WAR : sender.parseToPlain("TEAM_PREFIX_TEAM")); } @Register(value = "prefix", description = "TEAM_PREFIX_USAGE") public void prefix(@Validator("canUseTeamPrefix") Chatter sender, @StaticValue(value = {SW, STEAM_WAR, "Team"}, falseValues = {2}, allowISE = true) boolean useSWTeamTag) { SteamwarPrefix.setSWPrefix(sender.user(), useSWTeamTag); - sender.system("TEAM_PREFIX_SET", useSWTeamTag ? STEAM_WAR : sender.parse("TEAM_PREFIX_TEAM")); + sender.system("TEAM_PREFIX_SET", useSWTeamTag ? STEAM_WAR : sender.parseToPlain("TEAM_PREFIX_TEAM")); } @Validator(value = "canUseTeamPrefix", local = true) diff --git a/VelocityCore/src/de/steamwar/velocitycore/discord/DiscordBot.java b/VelocityCore/src/de/steamwar/velocitycore/discord/DiscordBot.java index cdcb8e51..9cbd4061 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/discord/DiscordBot.java +++ b/VelocityCore/src/de/steamwar/velocitycore/discord/DiscordBot.java @@ -22,6 +22,7 @@ package de.steamwar.velocitycore.discord; import de.steamwar.command.SWCommand; import de.steamwar.messages.Chatter; import de.steamwar.sql.Event; +import de.steamwar.sql.SteamwarUser; import de.steamwar.velocitycore.VelocityCore; import de.steamwar.velocitycore.discord.channels.*; import de.steamwar.velocitycore.discord.listeners.ChannelListener; @@ -52,12 +53,12 @@ import net.dv8tion.jda.api.utils.MemberCachePolicy; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import java.awt.*; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.*; import java.util.List; -import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.stream.Collectors; public class DiscordBot { public static final String ARGUMENT_NAME = "arguments"; @@ -169,7 +170,7 @@ public class DiscordBot { eventChannel = new StaticMessageChannel(config.channel("events"), EventChannel::get); checklistChannel = new ChecklistChannel(config.channel("checklist")); - config.getCouncilThread().forEach((roleId, threadId) -> new CouncilChannel(DiscordBot.getGuild().getRoleById(roleId), DiscordBot.getGuild().getThreadChannelById(threadId))); + CouncilChannel.initCouncilChannels(); announcementChannel = new DiscordChannel(config.channel("announcement"), 0) { @Override diff --git a/VelocityCore/src/de/steamwar/velocitycore/discord/channels/CouncilChannel.java b/VelocityCore/src/de/steamwar/velocitycore/discord/channels/CouncilChannel.java index 5a11bb7c..81530918 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/discord/channels/CouncilChannel.java +++ b/VelocityCore/src/de/steamwar/velocitycore/discord/channels/CouncilChannel.java @@ -20,9 +20,7 @@ package de.steamwar.velocitycore.discord.channels; import de.steamwar.sql.SteamwarUser; -import de.steamwar.velocitycore.VelocityCore; import de.steamwar.velocitycore.discord.DiscordBot; -import it.unimi.dsi.fastutil.Pair; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Role; @@ -30,45 +28,90 @@ import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; public class CouncilChannel extends StaticMessageChannel { private static final Set channels = new HashSet<>(); + private static final Set roleSet = DiscordBot.getInstance() + .getConfig() + .getCouncilThread() + .keySet() + .stream() + .map(roleId -> DiscordBot.getGuild().getRoleById(roleId)) + .collect(Collectors.toSet()); + + private static final Map> membersByRole = new HashMap<>(); + + private static void initRolesAndMembers(Runnable loaded) { + List discordIds = SteamwarUser.getUsersWithDiscordId().stream() + .map(SteamwarUser::getDiscordId) + .collect(Collectors.toList()); + + membersByRole.clear(); + AtomicInteger countdown = new AtomicInteger(0); + for (int i = 0; i < discordIds.size(); i += 100) { + countdown.incrementAndGet(); + + List retrieveIds = discordIds.subList(i, Math.min(discordIds.size(), i + 100)); + + DiscordBot.getGuild().retrieveMembersByIds(retrieveIds) + .onSuccess(members -> { + members.forEach(member -> { + for (Role role : roleSet) { + if (member.getUnsortedRoles().contains(role)) { + membersByRole.computeIfAbsent(role, __ -> new ArrayList<>()).add(member); + } + } + }); + + if (countdown.decrementAndGet() == 0) { + loaded.run(); + } + }); + } + } + + public static void initCouncilChannels() { + initRolesAndMembers(() -> { + DiscordBot.getInstance() + .getConfig() + .getCouncilThread() + .forEach((roleId, threadId) -> { + new CouncilChannel(DiscordBot.getGuild().getRoleById(roleId), DiscordBot.getGuild().getThreadChannelById(threadId)); + }); + }); + } + public static void updateAll() { - channels.forEach(StaticMessageChannel::update); + initRolesAndMembers(() -> { + channels.forEach(StaticMessageChannel::update); + }); } public CouncilChannel(Role role, ThreadChannel threadChannel) { super(threadChannel, () -> { MessageCreateBuilder messageCreateBuilder = new MessageCreateBuilder(); messageCreateBuilder.setContent("# Ratsmitglieder"); - - List members; - try { - members = DiscordBot.getGuild().findMembersWithRoles(role).onError(throwable -> { - // Ignore - }).get(); - } catch (Exception e) { - VelocityCore.getLogger().warning("Could not get members for " + role.getName()); - return messageCreateBuilder; - } - - members.stream() + Set uniqueNames = new HashSet<>(); + membersByRole.get(role) + .stream() .map(member -> { SteamwarUser steamwarUser = SteamwarUser.get(member.getIdLong()); String name = steamwarUser == null ? member.getEffectiveName() : steamwarUser.getUserName(); UUID uuid = steamwarUser == null ? null : steamwarUser.getUUID(); - return Pair.of(name, uuid); + return Map.entry(name, uuid); }) - .sorted(Comparator.comparing(Pair::key)) - .forEach(pair -> { + .sorted(Map.Entry.comparingByKey()) + .forEach(entry -> { + if (!uniqueNames.add(entry.getKey())) return; messageCreateBuilder.addEmbeds(new EmbedBuilder() - .setTitle(pair.key()) - .setImage(pair.value() == null ? null : "https://api.steamwar.de/data/skin/" + pair.value().toString()) + .setTitle(entry.getKey()) + .setImage(entry.getValue() == null ? null : "https://api.steamwar.de/data/skin/" + entry.getValue()) .build()); }); - return messageCreateBuilder; }, event -> { }); diff --git a/VelocityCore/src/de/steamwar/velocitycore/tablist/Tablist.java b/VelocityCore/src/de/steamwar/velocitycore/tablist/Tablist.java index 206626ca..0fb7b202 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/tablist/Tablist.java +++ b/VelocityCore/src/de/steamwar/velocitycore/tablist/Tablist.java @@ -51,7 +51,7 @@ public class Tablist extends ChannelInboundHandlerAdapter { private static final UUID[] swUuids = IntStream.range(0, 80).mapToObj(i -> UUID.randomUUID()).toArray(UUID[]::new); private static final String[] swNames = IntStream.range(0, 80).mapToObj(i -> " »SW« " + String.format("%02d", i)).toArray(String[]::new); - public static final UpdateTeamsPacket createTeamPacket = new UpdateTeamsPacket21("zzzzzsw-tab", UpdateTeamsPacket.Mode.CREATE, Component.empty(), Component.empty(), Component.empty(), UpdateTeamsPacket.NameTagVisibility.NEVER, UpdateTeamsPacket.CollisionRule.ALWAYS, 21, (byte) 0x00, Arrays.stream(Tablist.swNames).toList()); + public static final UpdateTeamsPacket createTeamPacket = new UpdateTeamsPacketImpl("zzzzzsw-tab", UpdateTeamsPacket.Mode.CREATE, Component.empty(), Component.empty(), Component.empty(), UpdateTeamsPacket.NameTagVisibility.NEVER, UpdateTeamsPacket.CollisionRule.ALWAYS, 21, (byte) 0x00, Arrays.stream(Tablist.swNames).toList()); private final Map directTabItems; private final List current = new ArrayList<>(); diff --git a/VelocityCore/src/de/steamwar/velocitycore/tablist/UpdateTeamsPacket21.java b/VelocityCore/src/de/steamwar/velocitycore/tablist/UpdateTeamsPacket21.java deleted file mode 100644 index 131bf413..00000000 --- a/VelocityCore/src/de/steamwar/velocitycore/tablist/UpdateTeamsPacket21.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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.velocitycore.tablist; - -import com.velocitypowered.api.network.ProtocolVersion; -import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.packet.UpdateTeamsPacket; -import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; -import io.netty.buffer.ByteBuf; -import net.kyori.adventure.text.Component; - -import java.util.List; - -public class UpdateTeamsPacket21 extends UpdateTeamsPacket { - - private String name; - private Mode mode; - private Component displayName; - private Component prefix; - private Component suffix; - private NameTagVisibility nameTagVisibility; - private CollisionRule collisionRule; - private int color; - private byte friendlyFlags; - private List players; - - public UpdateTeamsPacket21(String name, Mode mode, Component displayName, Component prefix, Component suffix, NameTagVisibility nameTagVisibility, CollisionRule collisionRule, int color, byte friendlyFlags, List players) { - super(name, mode, displayName, prefix, suffix, nameTagVisibility, collisionRule, color, friendlyFlags, players); - this.name = name; - this.mode = mode; - this.displayName = displayName; - this.prefix = prefix; - this.suffix = suffix; - this.nameTagVisibility = nameTagVisibility; - this.collisionRule = collisionRule; - this.color = color; - this.friendlyFlags = friendlyFlags; - this.players = players; - } - - @Override - public void encode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - ProtocolUtils.writeString(byteBuf, this.name); - byteBuf.writeByte(this.mode.ordinal()); - switch (this.mode) { - case CREATE: - case UPDATE: - (new ComponentHolder(protocolVersion, this.displayName)).write(byteBuf); - if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_13)) { - (new ComponentHolder(protocolVersion, this.prefix)).write(byteBuf); - (new ComponentHolder(protocolVersion, this.suffix)).write(byteBuf); - } - - byteBuf.writeByte(this.friendlyFlags); - if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_21_5)) { - ProtocolUtils.writeVarInt(byteBuf, this.nameTagVisibility.ordinal()); - ProtocolUtils.writeVarInt(byteBuf, this.collisionRule.ordinal()); - } else { - ProtocolUtils.writeString(byteBuf, this.nameTagVisibility.getValue()); - ProtocolUtils.writeString(byteBuf, this.collisionRule.getValue()); - } - if (protocolVersion.greaterThan(ProtocolVersion.MINECRAFT_1_12_2)) { - ProtocolUtils.writeVarInt(byteBuf, this.color); - (new ComponentHolder(protocolVersion, this.prefix)).write(byteBuf); - (new ComponentHolder(protocolVersion, this.suffix)).write(byteBuf); - } else { - byteBuf.writeByte((byte) this.color); - } - - ProtocolUtils.writeVarInt(byteBuf, this.players.size()); - - for (String player : this.players) { - ProtocolUtils.writeString(byteBuf, player); - } - break; - case ADD_PLAYER: - case REMOVE_PLAYER: - ProtocolUtils.writeVarInt(byteBuf, this.players.size()); - - for (String player : this.players) { - ProtocolUtils.writeString(byteBuf, player); - } - case REMOVE: - } - } -} diff --git a/VelocityCore/src/de/steamwar/velocitycore/tablist/UpdateTeamsPacketImpl.java b/VelocityCore/src/de/steamwar/velocitycore/tablist/UpdateTeamsPacketImpl.java new file mode 100644 index 00000000..998658c5 --- /dev/null +++ b/VelocityCore/src/de/steamwar/velocitycore/tablist/UpdateTeamsPacketImpl.java @@ -0,0 +1,76 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.velocitycore.tablist; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.packet.UpdateTeamsPacket; +import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; +import io.netty.buffer.ByteBuf; +import net.kyori.adventure.text.Component; + +import java.util.List; + +public class UpdateTeamsPacketImpl extends UpdateTeamsPacket { + + public UpdateTeamsPacketImpl(String name, Mode mode, Component displayName, Component prefix, Component suffix, NameTagVisibility nameTagVisibility, CollisionRule collisionRule, int color, byte friendlyFlags, List players) { + super(name, mode, displayName, prefix, suffix, nameTagVisibility, collisionRule, color, friendlyFlags, players); + } + + @Override + public void encode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeString(byteBuf, name); + byteBuf.writeByte(mode.ordinal()); + + switch (mode) { + case CREATE, UPDATE: + new ComponentHolder(protocolVersion, displayName).write(byteBuf); + if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_13)) { + new ComponentHolder(protocolVersion, prefix).write(byteBuf); + new ComponentHolder(protocolVersion, suffix).write(byteBuf); + } + + byteBuf.writeByte(friendlyFlags); + if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_21_5)) { + ProtocolUtils.writeVarInt(byteBuf, nameTagVisibility.ordinal()); + ProtocolUtils.writeVarInt(byteBuf, collisionRule.ordinal()); + } else { + ProtocolUtils.writeString(byteBuf, nameTagVisibility.getValue()); + ProtocolUtils.writeString(byteBuf, collisionRule.getValue()); + } + if (protocolVersion.greaterThan(ProtocolVersion.MINECRAFT_1_12_2)) { + ProtocolUtils.writeVarInt(byteBuf, color); + new ComponentHolder(protocolVersion, prefix).write(byteBuf); + new ComponentHolder(protocolVersion, suffix).write(byteBuf); + } else { + byteBuf.writeByte((byte) color); + } + // Fallthrough since players are at the end of the packet! + case ADD_PLAYER, REMOVE_PLAYER: + ProtocolUtils.writeVarInt(byteBuf, players.size()); + for (String player : players) { + ProtocolUtils.writeString(byteBuf, player); + } + break; + case REMOVE: + break; + } + } +} diff --git a/buildSrc/src/steamwar.devserver.gradle b/buildSrc/src/steamwar.devserver.gradle index 19c2570d..618ed2a6 100644 --- a/buildSrc/src/steamwar.devserver.gradle +++ b/buildSrc/src/steamwar.devserver.gradle @@ -49,6 +49,10 @@ class DevServer extends DefaultTask { @Optional Map dParams = new HashMap<>() + @Input + @Optional + String jvmArgs = null + @Input @Optional String checkpointFolder = null @@ -82,6 +86,7 @@ class DevServer extends DefaultTask { if (worldName == null) worldName = properties.get("worldName") host = properties.get("host") + debugPort = new Random().nextInt(5001, 10000) if (host == null) { throw new GradleException("Please supply the 'host' in a 'steamwar.properties' files either in this project dir or any parent project!") @@ -90,6 +95,7 @@ class DevServer extends DefaultTask { doLast { setupTemplate(template) uploadDependencies() + startDebugPort() startDevServer() } finalizedBy(new Finalizer()) @@ -101,6 +107,9 @@ class DevServer extends DefaultTask { @Internal String host + @Internal + int debugPort + @Internal Boolean running = true @@ -209,6 +218,17 @@ class DevServer extends DefaultTask { } } + void startDebugPort() { + def process = new ProcessBuilder("ssh", host, "-L", "5005:localhost:$debugPort").start() + def processOutput = new BufferedReader(new InputStreamReader(process.inputStream)) + new Thread({ + while (running) { + } + processOutput.close() + process.errorStream.close() + }).start() + } + void startDevServer() { def devPy = new StringBuilder().append("dev.py") if (port != null) devPy.append(" --port $port") @@ -221,6 +241,8 @@ class DevServer extends DefaultTask { devPy.append(" -D${dParam.key}=${dParam.value}") } devPy.append(" $template") + devPy.append(" -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:$debugPort") + if (jvmArgs != null) devPy.append(" $jvmArgs") println("Starting $template with command ${devPy.toString()}") def process = new ProcessBuilder("ssh", host, "-T", devPy.toString()).start() diff --git a/settings.gradle.kts b/settings.gradle.kts index c9103d0a..b84535d4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -122,7 +122,7 @@ dependencyResolutionManagement { library("fawe", "de.steamwar:fastasyncworldedit:1.21") library("velocity", "de.steamwar:velocity:RELEASE") - library("velocityapi", "com.velocitypowered:velocity-api:3.3.0-SNAPSHOT") + library("velocityapi", "com.velocitypowered:velocity-api:3.5.0-SNAPSHOT") library("viaapi", "com.viaversion:viaversion-api:4.3.1") library("viavelocity", "com.viaversion:viaversion-velocity:4.3.1") library("jda", "net.dv8tion:JDA:5.5.1")