From a52c03c3870b9ccd436682e7e5aadbab23785aed Mon Sep 17 00:00:00 2001 From: Lixfel Date: Fri, 24 Jun 2022 17:35:30 +0200 Subject: [PATCH] TechHider to SpigotCore migration --- SpigotCore_14/build.gradle | 2 + .../src/de/steamwar/techhider/BlockIds14.java | 52 ++++++ .../de/steamwar/techhider/ChunkHider14.java | 141 +++++++++++++++ .../steamwar/techhider/ProtocolWrapper14.java | 50 ++++++ .../src/de/steamwar/techhider/BlockIds15.java | 52 ++++++ SpigotCore_18/build.gradle | 3 + .../src/de/steamwar/techhider/BlockIds18.java | 57 ++++++ .../de/steamwar/techhider/ChunkHider18.java | 154 ++++++++++++++++ .../steamwar/techhider/ProtocolWrapper18.java | 77 ++++++++ SpigotCore_19/build.gradle | 2 + .../src/de/steamwar/techhider/BlockIds19.java | 57 ++++++ .../de/steamwar/techhider/ChunkHider19.java | 31 ++++ .../steamwar/techhider/ProtocolWrapper19.java | 67 +++++++ .../src/de/steamwar/techhider/BlockIds8.java | 38 ++++ .../de/steamwar/techhider/ChunkHider8.java | 40 +++++ .../steamwar/techhider/ProtocolWrapper8.java | 76 ++++++++ SpigotCore_9/build.gradle | 1 + .../de/steamwar/techhider/ChunkHider9.java | 108 ++++++++++++ .../src/de/steamwar/techhider/BlockIds.java | 33 ++++ .../src/de/steamwar/techhider/ChunkHider.java | 34 ++++ .../de/steamwar/techhider/ProtocolUtils.java | 164 ++++++++++++++++++ .../steamwar/techhider/ProtocolWrapper.java | 41 +++++ .../src/de/steamwar/techhider/TechHider.java | 128 ++++++++++++++ 23 files changed, 1408 insertions(+) create mode 100644 SpigotCore_14/src/de/steamwar/techhider/BlockIds14.java create mode 100644 SpigotCore_14/src/de/steamwar/techhider/ChunkHider14.java create mode 100644 SpigotCore_14/src/de/steamwar/techhider/ProtocolWrapper14.java create mode 100644 SpigotCore_15/src/de/steamwar/techhider/BlockIds15.java create mode 100644 SpigotCore_18/src/de/steamwar/techhider/BlockIds18.java create mode 100644 SpigotCore_18/src/de/steamwar/techhider/ChunkHider18.java create mode 100644 SpigotCore_18/src/de/steamwar/techhider/ProtocolWrapper18.java create mode 100644 SpigotCore_19/src/de/steamwar/techhider/BlockIds19.java create mode 100644 SpigotCore_19/src/de/steamwar/techhider/ChunkHider19.java create mode 100644 SpigotCore_19/src/de/steamwar/techhider/ProtocolWrapper19.java create mode 100644 SpigotCore_8/src/de/steamwar/techhider/BlockIds8.java create mode 100644 SpigotCore_8/src/de/steamwar/techhider/ChunkHider8.java create mode 100644 SpigotCore_8/src/de/steamwar/techhider/ProtocolWrapper8.java create mode 100644 SpigotCore_9/src/de/steamwar/techhider/ChunkHider9.java create mode 100644 SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java create mode 100644 SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java create mode 100644 SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java create mode 100644 SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java create mode 100644 SpigotCore_Main/src/de/steamwar/techhider/TechHider.java diff --git a/SpigotCore_14/build.gradle b/SpigotCore_14/build.gradle index 688968a..af58ea6 100644 --- a/SpigotCore_14/build.gradle +++ b/SpigotCore_14/build.gradle @@ -44,6 +44,8 @@ sourceSets { dependencies { implementation project(":SpigotCore_Main") + compileOnly project(":SpigotCore_8") + compileOnly project(":SpigotCore_9") compileOnly files("${project.rootDir}/lib/Spigot-1.14.jar") compileOnly files("${project.rootDir}/lib/WorldEdit-1.15.jar") diff --git a/SpigotCore_14/src/de/steamwar/techhider/BlockIds14.java b/SpigotCore_14/src/de/steamwar/techhider/BlockIds14.java new file mode 100644 index 0000000..1a946ef --- /dev/null +++ b/SpigotCore_14/src/de/steamwar/techhider/BlockIds14.java @@ -0,0 +1,52 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import net.minecraft.server.v1_14_R1.*; +import org.bukkit.Material; + +import java.util.HashSet; +import java.util.Set; + +public class BlockIds14 implements BlockIds { + @Override + public int materialToId(Material material) { + return Block.getCombinedId(IRegistry.BLOCK.get(new MinecraftKey(material.name().toLowerCase())).getBlockData()); + } + + @Override + public Set materialToAllIds(Material material) { + Set ids = new HashSet<>(); + for(IBlockData data : IRegistry.BLOCK.get(new MinecraftKey(material.name().toLowerCase())).getStates().a()) { + ids.add(Block.getCombinedId(data)); + } + + if(material == Material.WATER){ + Fluid water = FluidTypes.WATER.a(false); + for(IBlockData data : Block.REGISTRY_ID){ + if(data.p() == water){ + ids.add(Block.getCombinedId(data)); + } + } + } + + return ids; + } +} diff --git a/SpigotCore_14/src/de/steamwar/techhider/ChunkHider14.java b/SpigotCore_14/src/de/steamwar/techhider/ChunkHider14.java new file mode 100644 index 0000000..eae64df --- /dev/null +++ b/SpigotCore_14/src/de/steamwar/techhider/ChunkHider14.java @@ -0,0 +1,141 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.UnpooledByteBufAllocator; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; +import java.util.Set; + +public class ChunkHider14 extends ChunkHider9 { + + @Override + protected byte[] dataHider(int obfuscationTarget, Set obfuscate, byte[] data, Integer primaryBitMask) { + ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.directBuffer(data.length + 100); + int i = 0; + + while(primaryBitMask != 0){ + while((primaryBitMask & 1) == 0){ + primaryBitMask >>= 1; + } + + buffer.writeBytes(data, i, 2); // Block count + i += 2; + byte bitsPerBlock = data[i++]; + buffer.writeByte(bitsPerBlock); + + if(bitsPerBlock < 9){ + int paletteLength = ProtocolUtils.readVarInt(data, i); + int paletteLengthLength = ProtocolUtils.readVarIntLength(data, i); + buffer.writeBytes(data, i, paletteLengthLength); + i += paletteLengthLength; + for(int actPaletteId = 0; actPaletteId < paletteLength; actPaletteId++){ + int blockId = ProtocolUtils.readVarInt(data, i); + int actPaletteLength = ProtocolUtils.readVarIntLength(data, i); + + if(obfuscate.contains(blockId)){ + buffer.writeBytes(ProtocolUtils.writeVarInt(obfuscationTarget)); + }else{ + buffer.writeBytes(data, i, actPaletteLength); + } + i += actPaletteLength; + } + + //We modify only the chunk palette for performance reasons + int dataArrayLength = ProtocolUtils.readVarInt(data, i); + int dataArrayLengthLength = ProtocolUtils.readVarIntLength(data, i); + buffer.writeBytes(data, i, dataArrayLength * 8 + dataArrayLengthLength); + i += dataArrayLengthLength; + i += dataArrayLength * 8; + }else{ + //Full Chunk/no palette, so the chunk has to be crawled through + int dataArrayLength = ProtocolUtils.readVarInt(data, i); + int dataArrayLengthLength = ProtocolUtils.readVarIntLength(data, i); + buffer.writeBytes(data, i, dataArrayLengthLength); + i += dataArrayLengthLength; + + ByteBuffer source = ByteBuffer.wrap(data, i, dataArrayLength * 8); + VariableValueArray values = new VariableValueArray(bitsPerBlock, dataArrayLength, source.asLongBuffer()); + + for(int pos = 0; pos < 4096; pos++){ + if(obfuscate.contains(values.get(pos))){ + values.set(pos, obfuscationTarget); + } + } + + for(long l : values.backing) + buffer.writeLong(l); + + i += dataArrayLength * 8; + } + + primaryBitMask >>= 1; + } + buffer.writeBytes(data, i, data.length - i); // MC appends a 0 byte at the end if there is a full chunk, idk why + + data = new byte[buffer.readableBytes()]; + buffer.readBytes(data); + return data; + } + + private static final class VariableValueArray { + private final long[] backing; + private final int bitsPerValue; + private final long valueMask; + + public VariableValueArray(int bitsPerEntry, int dataArrayLength, LongBuffer buffer) { + this.bitsPerValue = bitsPerEntry; + this.backing = new long[dataArrayLength]; + buffer.get(backing); + this.valueMask = (1L << this.bitsPerValue) - 1; + } + + public int get(int index) { + index *= bitsPerValue; + int i0 = index >> 6; + int i1 = index & 0x3f; + + long value = backing[i0] >>> i1; + + // The value is divided over two long values + if (i1 + bitsPerValue > 64) { + value |= backing[++i0] << 64 - i1; + } + + return (int) (value & valueMask); + } + + public void set(int index, int value) { + index *= bitsPerValue; + int i0 = index >> 6; + int i1 = index & 0x3f; + + backing[i0] = this.backing[i0] & ~(this.valueMask << i1) | (value & valueMask) << i1; + int i2 = i1 + bitsPerValue; + // The value is divided over two long values + if (i2 > 64) { + i0++; + backing[i0] = backing[i0] & -(1L << i2 - 64) | value >> 64 - i1; + } + } + } +} diff --git a/SpigotCore_14/src/de/steamwar/techhider/ProtocolWrapper14.java b/SpigotCore_14/src/de/steamwar/techhider/ProtocolWrapper14.java new file mode 100644 index 0000000..bb52f52 --- /dev/null +++ b/SpigotCore_14/src/de/steamwar/techhider/ProtocolWrapper14.java @@ -0,0 +1,50 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import com.comphenix.tinyprotocol.Reflection; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; + +public class ProtocolWrapper14 extends ProtocolWrapper8 { + + @Override + public BiFunction blockBreakHiderGenerator(Class blockBreakPacket, Object obfuscationTarget, Set obfuscate, TechHider.BypassEvaluator bypass) { + UnaryOperator blockBreakCloner = ProtocolUtils.shallowCloneGenerator(blockBreakPacket); + Reflection.FieldAccessor blockBreakPosition = Reflection.getField(blockBreakPacket, TechHider.blockPosition, 0); + Reflection.FieldAccessor blockBreakBlockData = Reflection.getField(blockBreakPacket, TechHider.iBlockData, 0); + + return (p, packet) -> { + Object pos = blockBreakPosition.get(packet); + if(bypass.bypass(p, ProtocolUtils.posToChunk(TechHider.blockPositionX.get(pos)), ProtocolUtils.posToChunk(TechHider.blockPositionZ.get(pos)))) + return packet; + + if(ProtocolWrapper.impl.iBlockDataHidden(obfuscate, blockBreakBlockData.get(packet))){ + packet = blockBreakCloner.apply(packet); + blockBreakBlockData.set(packet, obfuscationTarget); + } + return packet; + }; + } +} diff --git a/SpigotCore_15/src/de/steamwar/techhider/BlockIds15.java b/SpigotCore_15/src/de/steamwar/techhider/BlockIds15.java new file mode 100644 index 0000000..15e7c4d --- /dev/null +++ b/SpigotCore_15/src/de/steamwar/techhider/BlockIds15.java @@ -0,0 +1,52 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import net.minecraft.server.v1_15_R1.*; +import org.bukkit.Material; + +import java.util.HashSet; +import java.util.Set; + +public class BlockIds15 implements BlockIds { + @Override + public int materialToId(Material material) { + return Block.getCombinedId(IRegistry.BLOCK.get(new MinecraftKey(material.name().toLowerCase())).getBlockData()); + } + + @Override + public Set materialToAllIds(Material material) { + Set ids = new HashSet<>(); + for(IBlockData data : IRegistry.BLOCK.get(new MinecraftKey(material.name().toLowerCase())).getStates().a()) { + ids.add(Block.getCombinedId(data)); + } + + if(material == Material.WATER){ + Fluid water = FluidTypes.WATER.a(false); + for(IBlockData data : Block.REGISTRY_ID){ + if(data.getFluid() == water){ + ids.add(Block.getCombinedId(data)); + } + } + } + + return ids; + } +} diff --git a/SpigotCore_18/build.gradle b/SpigotCore_18/build.gradle index 5181c28..5ff99a8 100644 --- a/SpigotCore_18/build.gradle +++ b/SpigotCore_18/build.gradle @@ -49,5 +49,8 @@ dependencies { compileOnly files("${project.rootDir}/lib/WorldEdit-1.15.jar") compileOnly 'org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT' + compileOnly 'com.mojang:datafixerupper:4.0.26' + compileOnly 'io.netty:netty-all:4.1.68.Final' + compileOnly 'com.mojang:authlib:1.5.25' compileOnly files("${project.rootDir}/lib/Spigot-1.18.jar") } diff --git a/SpigotCore_18/src/de/steamwar/techhider/BlockIds18.java b/SpigotCore_18/src/de/steamwar/techhider/BlockIds18.java new file mode 100644 index 0000000..13cf029 --- /dev/null +++ b/SpigotCore_18/src/de/steamwar/techhider/BlockIds18.java @@ -0,0 +1,57 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import net.minecraft.core.IRegistry; +import net.minecraft.resources.MinecraftKey; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.IBlockData; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidTypes; +import org.bukkit.Material; + +import java.util.HashSet; +import java.util.Set; + +public class BlockIds18 implements BlockIds { + @Override + public int materialToId(Material material) { + return Block.i(IRegistry.U.a(new MinecraftKey(material.name().toLowerCase())).n()); + } + + @Override + public Set materialToAllIds(Material material) { + Set ids = new HashSet<>(); + for(IBlockData data : IRegistry.U.a(new MinecraftKey(material.name().toLowerCase())).m().a()){ + ids.add(Block.i(data)); + } + + if(material == Material.WATER){ + Fluid water = FluidTypes.c.h(); + for(IBlockData data : Block.o) { + if(data.o() == water) { + ids.add(Block.i(data)); + } + } + } + + return ids; + } +} diff --git a/SpigotCore_18/src/de/steamwar/techhider/ChunkHider18.java b/SpigotCore_18/src/de/steamwar/techhider/ChunkHider18.java new file mode 100644 index 0000000..53dc78f --- /dev/null +++ b/SpigotCore_18/src/de/steamwar/techhider/ChunkHider18.java @@ -0,0 +1,154 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import com.comphenix.tinyprotocol.Reflection; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.UnpooledByteBufAllocator; +import net.minecraft.core.IRegistry; +import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.util.SimpleBitStorage; +import net.minecraft.world.level.block.entity.TileEntityTypes; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.IntFunction; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +public class ChunkHider18 implements ChunkHider { + @Override + public Class mapChunkPacket() { + return ClientboundLevelChunkWithLightPacket.class; + } + + private static final UnaryOperator chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); + private static final UnaryOperator chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); + + private static final Reflection.FieldAccessor chunkX = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0); + private static final Reflection.FieldAccessor chunkZ = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1); + private static final Reflection.FieldAccessor chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0); + + private static final Reflection.FieldAccessor dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0); + private static final Reflection.FieldAccessor tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0); + public static final Class tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$a"); + protected static final Reflection.FieldAccessor entityType = Reflection.getField(tileEntity, TileEntityTypes.class, 0); + + @Override + public BiFunction chunkHiderGenerator(TechHider.BypassEvaluator bypass, int obfuscationTarget, Set obfuscate, Set hiddenBlockEntities) { + return (p, packet) -> { + if(bypass.bypass(p, chunkX.get(packet), chunkZ.get(packet))) + return packet; + + packet = chunkPacketCloner.apply(packet); + Object data = chunkDataCloner.apply(chunkData.get(packet)); + + tileEntities.set(data, ((List)tileEntities.get(data)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList())); + + World world = p.getWorld(); + int sections = (world.getMaxHeight() - world.getMinHeight()) / 16; + dataField.set(data, dataHider(obfuscationTarget, obfuscate, dataField.get(data), sections)); + + chunkData.set(packet, data); + return packet; + }; + } + + protected boolean tileEntityVisible(Set hiddenBlockEntities, Object tile) { + return !hiddenBlockEntities.contains(IRegistry.aa.b(entityType.get(tile)).a()); + } + + private byte[] dataHider(int obfuscationTarget, Set obfuscate, byte[] data, int sections) { + ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.directBuffer(data.length + 100); + int i = 0; + + while(sections-- > 0) { + buffer.writeBytes(data, i, 2); // Block count + i += 2; + + i = containerWalker(data, buffer, i, 15, blockId -> obfuscate.contains(blockId) ? obfuscationTarget : blockId, (curI, dataArrayLength, bitsPerBlock) -> { + if(bitsPerBlock < 15) { + buffer.writeBytes(data, curI, dataArrayLength * 8); + } else { + ByteBuffer source = ByteBuffer.wrap(data, curI, dataArrayLength * 8); + long[] array = new long[dataArrayLength]; + source.asLongBuffer().get(array); + SimpleBitStorage values = new SimpleBitStorage(bitsPerBlock, 4096, array); + + for (int pos = 0; pos < 4096; pos++) { + if (obfuscate.contains(values.a(pos))) { + values.b(pos, obfuscationTarget); + } + } + + for (long l : values.a()) + buffer.writeLong(l); + } + }); + i = containerWalker(data, buffer, i, 6, value -> value, (curI, dataArrayLength, bitsPerBlock) -> buffer.writeBytes(data, curI, dataArrayLength * 8)); + } + buffer.writeBytes(data, i, data.length - i); // MC appends a 0 byte at the end if there is a full chunk, idk why + + byte[] outdata = new byte[buffer.readableBytes()]; + buffer.readBytes(outdata); + return outdata; + } + + private int containerWalker(byte[] data, ByteBuf buffer, int i, int globalPalette, IntFunction palette, TriConsumer dataArray) { + byte bitsPerBlock = data[i++]; + buffer.writeByte(bitsPerBlock); + + if(bitsPerBlock == 0) { + int paletteValue = ProtocolUtils.readVarInt(data, i); + i += ProtocolUtils.readVarIntLength(data, i); + buffer.writeBytes(ProtocolUtils.writeVarInt(palette.apply(paletteValue))); + }else if(bitsPerBlock < globalPalette) { + int paletteLength = ProtocolUtils.readVarInt(data, i); + int paletteLengthLength = ProtocolUtils.readVarIntLength(data, i); + buffer.writeBytes(data, i, paletteLengthLength); + i += paletteLengthLength; + + for(int actPaletteId = 0; actPaletteId < paletteLength; actPaletteId++) { + int paletteValue = ProtocolUtils.readVarInt(data, i); + i += ProtocolUtils.readVarIntLength(data, i); + buffer.writeBytes(ProtocolUtils.writeVarInt(palette.apply(paletteValue))); + } + } + + int dataArrayLength = ProtocolUtils.readVarInt(data, i); + int dataArrayLengthLength = ProtocolUtils.readVarIntLength(data, i); + buffer.writeBytes(data, i, dataArrayLengthLength); + i += dataArrayLengthLength; + + dataArray.accept(i, dataArrayLength, bitsPerBlock); + i += dataArrayLength * 8; + + return i; + } + + private interface TriConsumer { + void accept(X x, Y y, Z z); + } +} diff --git a/SpigotCore_18/src/de/steamwar/techhider/ProtocolWrapper18.java b/SpigotCore_18/src/de/steamwar/techhider/ProtocolWrapper18.java new file mode 100644 index 0000000..7edbd8d --- /dev/null +++ b/SpigotCore_18/src/de/steamwar/techhider/ProtocolWrapper18.java @@ -0,0 +1,77 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.core.IRegistry; +import net.minecraft.core.SectionPosition; +import net.minecraft.network.protocol.game.PacketPlayOutBlockBreak; +import net.minecraft.world.level.block.entity.TileEntityTypes; +import net.minecraft.world.level.block.state.IBlockData; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; + +public class ProtocolWrapper18 implements ProtocolWrapper { + + private static final Reflection.FieldAccessor multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPosition.class, 0); + private static final Reflection.FieldAccessor multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, IBlockData[].class, 0); + private static final BiFunction, Object> iBlockDataArrayCloner = ProtocolUtils.arrayCloneGenerator(TechHider.iBlockData); + @Override + public BiFunction multiBlockChangeGenerator(Object obfuscationTarget, Set obfuscate, TechHider.BypassEvaluator bypass) { + return (p, packet) -> { + Object chunkCoords = multiBlockChangeChunk.get(packet); + if(bypass.bypass(p, TechHider.blockPositionX.get(chunkCoords), TechHider.blockPositionZ.get(chunkCoords))) + return packet; + + packet = TechHider.multiBlockChangeCloner.apply(packet); + multiBlockChangeBlocks.set(packet, iBlockDataArrayCloner.apply(multiBlockChangeBlocks.get(packet), block -> ProtocolWrapper.impl.iBlockDataHidden(obfuscate, block) ? obfuscationTarget : block)); + return packet; + }; + } + + private static final Reflection.FieldAccessor tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, TileEntityTypes.class, 0); + @Override + public boolean unfilteredTileEntityDataAction(Object packet) { + return tileEntityType.get(packet) != TileEntityTypes.h; + } + + @Override + public BiFunction blockBreakHiderGenerator(Class blockBreakPacket, Object obfuscationTarget, Set obfuscate, TechHider.BypassEvaluator bypass) { + return (p, packet) -> { + PacketPlayOutBlockBreak breakPacket = (PacketPlayOutBlockBreak) packet; + if(bypass.bypass(p, ProtocolUtils.posToChunk(TechHider.blockPositionX.get(breakPacket.b())), ProtocolUtils.posToChunk(TechHider.blockPositionZ.get(breakPacket.b())))) + return packet; + + if(!ProtocolWrapper.impl.iBlockDataHidden(obfuscate, breakPacket.c())) + return packet; + + return new PacketPlayOutBlockBreak(breakPacket.b(), (IBlockData) obfuscationTarget, breakPacket.d(), breakPacket.a()); + }; + } + + @Override + public boolean iBlockDataHidden(Set obfuscate, Object iBlockData) { + return obfuscate.contains(Material.getMaterial(IRegistry.U.b(((IBlockData) iBlockData).b()).a().toUpperCase())); + } +} diff --git a/SpigotCore_19/build.gradle b/SpigotCore_19/build.gradle index c7b9f98..e4b4b5c 100644 --- a/SpigotCore_19/build.gradle +++ b/SpigotCore_19/build.gradle @@ -45,10 +45,12 @@ sourceSets { dependencies { implementation project(":SpigotCore_Main") implementation project(":SpigotCore_14") + compileOnly project(":SpigotCore_18") compileOnly files("${project.rootDir}/lib/WorldEdit-1.15.jar") compileOnly 'org.spigotmc:spigot-api:1.19-R0.1-SNAPSHOT' compileOnly 'com.mojang:brigadier:1.0.18' + compileOnly 'com.mojang:datafixerupper:4.0.26' compileOnly files("${project.rootDir}/lib/Spigot-1.19.jar") } diff --git a/SpigotCore_19/src/de/steamwar/techhider/BlockIds19.java b/SpigotCore_19/src/de/steamwar/techhider/BlockIds19.java new file mode 100644 index 0000000..e1e0b54 --- /dev/null +++ b/SpigotCore_19/src/de/steamwar/techhider/BlockIds19.java @@ -0,0 +1,57 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import net.minecraft.core.IRegistry; +import net.minecraft.resources.MinecraftKey; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.IBlockData; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidTypes; +import org.bukkit.Material; + +import java.util.HashSet; +import java.util.Set; + +public class BlockIds19 implements BlockIds { + @Override + public int materialToId(Material material) { + return Block.i(IRegistry.V.a(new MinecraftKey(material.name().toLowerCase())).m()); + } + + @Override + public Set materialToAllIds(Material material) { + Set ids = new HashSet<>(); + for(IBlockData data : IRegistry.V.a(new MinecraftKey(material.name().toLowerCase())).k().a()){ + ids.add(Block.i(data)); + } + + if(material == Material.WATER){ + Fluid water = FluidTypes.c.h(); + for(IBlockData data : Block.o) { + if(data.p() == water) { + ids.add(Block.i(data)); + } + } + } + + return ids; + } +} diff --git a/SpigotCore_19/src/de/steamwar/techhider/ChunkHider19.java b/SpigotCore_19/src/de/steamwar/techhider/ChunkHider19.java new file mode 100644 index 0000000..0ebba1c --- /dev/null +++ b/SpigotCore_19/src/de/steamwar/techhider/ChunkHider19.java @@ -0,0 +1,31 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import net.minecraft.core.IRegistry; + +import java.util.Set; + +public class ChunkHider19 extends ChunkHider18 { + @Override + protected boolean tileEntityVisible(Set hiddenBlockEntities, Object tile) { + return !hiddenBlockEntities.contains(IRegistry.ab.b(entityType.get(tile)).a()); + } +} diff --git a/SpigotCore_19/src/de/steamwar/techhider/ProtocolWrapper19.java b/SpigotCore_19/src/de/steamwar/techhider/ProtocolWrapper19.java new file mode 100644 index 0000000..240f854 --- /dev/null +++ b/SpigotCore_19/src/de/steamwar/techhider/ProtocolWrapper19.java @@ -0,0 +1,67 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.core.IRegistry; +import net.minecraft.core.SectionPosition; +import net.minecraft.world.level.block.entity.TileEntityTypes; +import net.minecraft.world.level.block.state.IBlockData; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; + +public class ProtocolWrapper19 implements ProtocolWrapper { + + private static final Reflection.FieldAccessor multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPosition.class, 0); + private static final Reflection.FieldAccessor multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, IBlockData[].class, 0); + private static final BiFunction, Object> iBlockDataArrayCloner = ProtocolUtils.arrayCloneGenerator(TechHider.iBlockData); + @Override + public BiFunction multiBlockChangeGenerator(Object obfuscationTarget, Set obfuscate, TechHider.BypassEvaluator bypass) { + return (p, packet) -> { + Object chunkCoords = multiBlockChangeChunk.get(packet); + if(bypass.bypass(p, TechHider.blockPositionX.get(chunkCoords), TechHider.blockPositionZ.get(chunkCoords))) + return packet; + + packet = TechHider.multiBlockChangeCloner.apply(packet); + multiBlockChangeBlocks.set(packet, iBlockDataArrayCloner.apply(multiBlockChangeBlocks.get(packet), block -> ProtocolWrapper.impl.iBlockDataHidden(obfuscate, block) ? obfuscationTarget : block)); + return packet; + }; + } + + private static final Reflection.FieldAccessor tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, TileEntityTypes.class, 0); + @Override + public boolean unfilteredTileEntityDataAction(Object packet) { + return tileEntityType.get(packet) != TileEntityTypes.h; + } + + @Override + public BiFunction blockBreakHiderGenerator(Class blockBreakPacket, Object obfuscationTarget, Set obfuscate, TechHider.BypassEvaluator bypass) { + return null; + } + + @Override + public boolean iBlockDataHidden(Set obfuscate, Object iBlockData) { + return obfuscate.contains(Material.getMaterial(IRegistry.V.b(((IBlockData) iBlockData).b()).a().toUpperCase())); + } +} diff --git a/SpigotCore_8/src/de/steamwar/techhider/BlockIds8.java b/SpigotCore_8/src/de/steamwar/techhider/BlockIds8.java new file mode 100644 index 0000000..538e8db --- /dev/null +++ b/SpigotCore_8/src/de/steamwar/techhider/BlockIds8.java @@ -0,0 +1,38 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import org.bukkit.Material; + +import java.util.Collections; +import java.util.Set; + +public class BlockIds8 implements BlockIds { + @Override + @SuppressWarnings("deprecation") + public int materialToId(Material material) { + return material.getId() << 4; + } + + @Override + public Set materialToAllIds(Material material) { + return Collections.singleton(materialToId(material)); + } +} diff --git a/SpigotCore_8/src/de/steamwar/techhider/ChunkHider8.java b/SpigotCore_8/src/de/steamwar/techhider/ChunkHider8.java new file mode 100644 index 0000000..3cc0d36 --- /dev/null +++ b/SpigotCore_8/src/de/steamwar/techhider/ChunkHider8.java @@ -0,0 +1,40 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import com.comphenix.tinyprotocol.Reflection; +import org.bukkit.entity.Player; + +import java.util.Set; +import java.util.function.BiFunction; + +public class ChunkHider8 implements ChunkHider { + + protected static final Class mapChunkPacket = Reflection.getClass("{nms}.PacketPlayOutMapChunk"); + @Override + public Class mapChunkPacket() { + return mapChunkPacket; + } + + @Override + public BiFunction chunkHiderGenerator(TechHider.BypassEvaluator bypass, int obfuscationTarget, Set obfuscate, Set hiddenBlockEntities) { + return null; + } +} diff --git a/SpigotCore_8/src/de/steamwar/techhider/ProtocolWrapper8.java b/SpigotCore_8/src/de/steamwar/techhider/ProtocolWrapper8.java new file mode 100644 index 0000000..abc9e61 --- /dev/null +++ b/SpigotCore_8/src/de/steamwar/techhider/ProtocolWrapper8.java @@ -0,0 +1,76 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import com.comphenix.tinyprotocol.Reflection; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; + +public class ProtocolWrapper8 implements ProtocolWrapper { + private static final Class chunkCoordinateIntPair = Reflection.getClass("{nms}.ChunkCoordIntPair"); + private static final Reflection.FieldAccessor multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, chunkCoordinateIntPair, 0); + private static final Reflection.FieldAccessor chunkCoordinateX = Reflection.getField(chunkCoordinateIntPair, int.class, 0); + private static final Reflection.FieldAccessor chunkCoordinateZ = Reflection.getField(chunkCoordinateIntPair, int.class, 1); + private static final Class multiBlockChangeInfo = Reflection.getClass("{nms}.PacketPlayOutMultiBlockChange$MultiBlockChangeInfo"); + private static final Reflection.ConstructorInvoker multiBlockChangeInfoConstructor = Reflection.getConstructor(multiBlockChangeInfo, TechHider.multiBlockChangePacket, short.class, TechHider.iBlockData); + private static final BiFunction, Object> multiBlockChangeInfoArrayCloner = ProtocolUtils.arrayCloneGenerator(multiBlockChangeInfo); + private static final Reflection.FieldAccessor multiBlockChangeInfoBlock = Reflection.getField(multiBlockChangeInfo, TechHider.iBlockData, 0); + private static final Reflection.FieldAccessor multiBlockChangeInfoPos = Reflection.getField(multiBlockChangeInfo, short.class, 0); + private static final Class multiBlockChangeInfoArray = Reflection.getClass("[L{nms}.PacketPlayOutMultiBlockChange$MultiBlockChangeInfo;"); + private static final Reflection.FieldAccessor multiBlockChangeInfos = Reflection.getField(TechHider.multiBlockChangePacket, multiBlockChangeInfoArray, 0); + @Override + public BiFunction multiBlockChangeGenerator(Object obfuscationTarget, Set obfuscate, TechHider.BypassEvaluator bypass) { + return (p, packet) -> { + Object chunkCoords = multiBlockChangeChunk.get(packet); + if(bypass.bypass(p, chunkCoordinateX.get(chunkCoords), chunkCoordinateZ.get(chunkCoords))) + return packet; + + Object modpacket = TechHider.multiBlockChangeCloner.apply(packet); + multiBlockChangeInfos.set(modpacket, multiBlockChangeInfoArrayCloner.apply(multiBlockChangeInfos.get(modpacket), mbci -> { + if(ProtocolWrapper.impl.iBlockDataHidden(obfuscate, multiBlockChangeInfoBlock.get(mbci))) + return multiBlockChangeInfoConstructor.invoke(modpacket, multiBlockChangeInfoPos.get(mbci), obfuscationTarget); + return mbci; + })); + return modpacket; + }; + } + + private static final Reflection.FieldAccessor tileEntityDataAction = Reflection.getField(TechHider.tileEntityDataPacket, int.class, 0); + @Override + public boolean unfilteredTileEntityDataAction(Object packet) { + return tileEntityDataAction.get(packet) != 9; + } + + @Override + public BiFunction blockBreakHiderGenerator(Class blockBreakPacket, Object obfuscationTarget, Set obfuscate, TechHider.BypassEvaluator bypass) { + return null; + } + + private static final Reflection.MethodInvoker getBlockByBlockData = Reflection.getTypedMethod(TechHider.iBlockData, null, TechHider.block); + private static final Reflection.MethodInvoker getMaterialByBlock = Reflection.getTypedMethod(TechHider.craftMagicNumbers, "getMaterial", Material.class, TechHider.block); + @Override + public boolean iBlockDataHidden(Set obfuscate, Object iBlockData) { + return obfuscate.contains((Material) getMaterialByBlock.invoke(null, getBlockByBlockData.invoke(iBlockData))); + } +} diff --git a/SpigotCore_9/build.gradle b/SpigotCore_9/build.gradle index 064f759..6250558 100644 --- a/SpigotCore_9/build.gradle +++ b/SpigotCore_9/build.gradle @@ -44,6 +44,7 @@ sourceSets { dependencies { implementation project(":SpigotCore_Main") + compileOnly project(":SpigotCore_8") compileOnly files("${project.rootDir}/lib/Spigot-1.9.jar") } diff --git a/SpigotCore_9/src/de/steamwar/techhider/ChunkHider9.java b/SpigotCore_9/src/de/steamwar/techhider/ChunkHider9.java new file mode 100644 index 0000000..0272dd6 --- /dev/null +++ b/SpigotCore_9/src/de/steamwar/techhider/ChunkHider9.java @@ -0,0 +1,108 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import com.comphenix.tinyprotocol.Reflection; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.UnpooledByteBufAllocator; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; +import java.util.logging.Level; +import java.util.stream.Collectors; + +public class ChunkHider9 extends ChunkHider8 { + + private static final UnaryOperator mapChunkCloner = ProtocolUtils.shallowCloneGenerator(mapChunkPacket); + + private static final Reflection.FieldAccessor mapChunkX = Reflection.getField(mapChunkPacket, int.class, 0); + private static final Reflection.FieldAccessor mapChunkZ = Reflection.getField(mapChunkPacket, int.class, 1); + private static final Reflection.FieldAccessor mapChunkBitMask = Reflection.getField(mapChunkPacket, int.class, 2); + private static final Reflection.FieldAccessor mapChunkBlockEntities = Reflection.getField(mapChunkPacket, List.class, 0); + private static final Reflection.FieldAccessor mapChunkData = Reflection.getField(mapChunkPacket, byte[].class, 0); + + private static final Class nbtTagCompound = Reflection.getClass("{nms.nbt}.NBTTagCompound"); + private static final Reflection.MethodInvoker nbtTagGetString = Reflection.getTypedMethod(nbtTagCompound, null, String.class, String.class); + + @Override + public BiFunction chunkHiderGenerator(TechHider.BypassEvaluator bypass, int obfuscationTarget, Set obfuscate, Set hiddenBlockEntities) { + return (p, packet) -> { + if(bypass.bypass(p, mapChunkX.get(packet), mapChunkZ.get(packet))) + return packet; + + packet = mapChunkCloner.apply(packet); + mapChunkBlockEntities.set(packet, ((List)mapChunkBlockEntities.get(packet)).stream().filter( + nbttag -> !hiddenBlockEntities.contains((String) nbtTagGetString.invoke(nbttag, "id")) + ).collect(Collectors.toList())); + + byte[] data = dataHider(obfuscationTarget, obfuscate, mapChunkData.get(packet), mapChunkBitMask.get(packet)); + mapChunkData.set(packet, data); + + return packet; + }; + } + + protected byte[] dataHider(int obfuscationTarget, Set obfuscate, byte[] data, Integer primaryBitMask) { + ByteBuf buffer = UnpooledByteBufAllocator.DEFAULT.directBuffer(data.length + 100); + int i = 0; + + while(i < data.length){ + byte bitsPerBlock = data[i++]; + buffer.writeByte(bitsPerBlock); + + if(bitsPerBlock != 13){ + int paletteLength = ProtocolUtils.readVarInt(data, i); + int paletteLengthLength = ProtocolUtils.readVarIntLength(data, i); + buffer.writeBytes(data, i, paletteLengthLength); + i += paletteLengthLength; + + for(int actPaletteId = 0; actPaletteId < paletteLength; actPaletteId++){ + int entry = ProtocolUtils.readVarInt(data, i); + i += ProtocolUtils.readVarIntLength(data, i); + + if(obfuscate.contains(entry)){ + entry = obfuscationTarget; + } + buffer.writeBytes(ProtocolUtils.writeVarInt(entry)); + } + }else{ + buffer.writeByte(data[++i]); //Empty palette + Bukkit.getLogger().log(Level.SEVERE, "Full chunk occured"); + } + + int dataArrayLength = ProtocolUtils.readVarInt(data, i); + int dataArrayLengthLength = ProtocolUtils.readVarIntLength(data, i); + buffer.writeBytes(data, i, dataArrayLength*8 + dataArrayLengthLength); + i += dataArrayLengthLength; + i += dataArrayLength * 8; + + buffer.writeBytes(data, i, 4096); + i += 4096; //Skylight (Not in Nether/End!!!) 2048 + Blocklight 2048 + } + + data = new byte[buffer.readableBytes()]; + buffer.readBytes(data); + return data; + } +} diff --git a/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java b/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java new file mode 100644 index 0000000..8e129a1 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/techhider/BlockIds.java @@ -0,0 +1,33 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import de.steamwar.core.Core; +import de.steamwar.core.VersionDependent; +import org.bukkit.Material; + +import java.util.Set; + +public interface BlockIds { + BlockIds impl = VersionDependent.getVersionImpl(Core.getInstance()); + + int materialToId(Material material); + Set materialToAllIds(Material material); +} diff --git a/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java new file mode 100644 index 0000000..b87d83d --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -0,0 +1,34 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2022 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import de.steamwar.core.Core; +import de.steamwar.core.VersionDependent; +import org.bukkit.entity.Player; + +import java.util.Set; +import java.util.function.BiFunction; + +public interface ChunkHider { + ChunkHider impl = VersionDependent.getVersionImpl(Core.getInstance()); + + Class mapChunkPacket(); + BiFunction chunkHiderGenerator(TechHider.BypassEvaluator bypass, int obfuscationTarget, Set obfuscate, Set hiddenBlockEntities); +} diff --git a/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java b/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java new file mode 100644 index 0000000..1fc9e31 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java @@ -0,0 +1,164 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2022 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import com.comphenix.tinyprotocol.Reflection; +import com.comphenix.tinyprotocol.TinyProtocol; +import com.google.common.primitives.Bytes; +import org.bukkit.Bukkit; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; + +public class ProtocolUtils { + private ProtocolUtils() {} + + public static void broadcastPacket(Object packet) { + Bukkit.getOnlinePlayers().stream().map(TinyProtocol.instance::getChannel).filter(TinyProtocol.instance::hasInjected).forEach(channel -> TinyProtocol.instance.sendPacket(channel, packet)); + } + + public static BiFunction, Object> arrayCloneGenerator(Class elementClass) { + return (array, worker) -> { + int length = Array.getLength(array); + Object result = Array.newInstance(elementClass, length); + + for(int i = 0; i < length; i++) + Array.set(result, i, worker.apply(Array.get(array, i))); + + return result; + }; + } + + public static UnaryOperator shallowCloneGenerator(Class clazz) { + BiConsumer filler = shallowFill(clazz); + + return source -> { + Object clone = Reflection.newInstance(clazz); + filler.accept(source, clone); + return clone; + }; + } + + private static BiConsumer shallowFill(Class clazz) { + if(clazz == null) + return (source, clone) -> {}; + + BiConsumer superFiller = shallowFill(clazz.getSuperclass()); + + Field[] fds = clazz.getDeclaredFields(); + List fields = new ArrayList<>(); + for(Field field : fds) { + if (Modifier.isStatic(field.getModifiers())) + continue; + + field.setAccessible(true); + fields.add(field); + } + + return (source, clone) -> { + superFiller.accept(source, clone); + try { + for(Field field : fields) { + field.set(clone, field.get(source)); + } + } catch (IllegalAccessException e) { + throw new IllegalStateException("Could not set field", e); + } + + }; + } + + public static int posToChunk(int c){ + int chunk = c / 16; + if(c<0) + chunk--; + return chunk; + } + + public static int readVarInt(byte[] array, int startPos) { + int numRead = 0; + int result = 0; + byte read; + do { + read = array[startPos + numRead]; + int value = (read & 0b01111111); + result |= (value << (7 * numRead)); + + numRead++; + if (numRead > 5) { + break; + } + } while ((read & 0b10000000) != 0); + + return result; + } + + public static int readVarIntLength(byte[] array, int startPos) { + int numRead = 0; + byte read; + do { + read = array[startPos + numRead]; + numRead++; + if (numRead > 5) { + break; + } + } while ((read & 0b10000000) != 0); + + return numRead; + } + + public static byte[] writeVarInt(int value) { + List buffer = new ArrayList<>(5); + do { + byte temp = (byte)(value & 0b01111111); + // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone + value >>>= 7; + if (value != 0) { + temp |= 0b10000000; + } + buffer.add(temp); + } while (value != 0); + return Bytes.toArray(buffer); + } + + public static class ChunkPos{ + final int x; + final int z; + + public ChunkPos(int x, int z){ + this.x = x; + this.z = z; + } + + public final int x(){ + return x; + } + + public final int z(){ + return z; + } + } +} diff --git a/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java b/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java new file mode 100644 index 0000000..cc95e03 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/techhider/ProtocolWrapper.java @@ -0,0 +1,41 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import de.steamwar.core.Core; +import de.steamwar.core.VersionDependent; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.Set; +import java.util.function.BiFunction; + +public interface ProtocolWrapper { + ProtocolWrapper impl = VersionDependent.getVersionImpl(Core.getInstance()); + + + boolean unfilteredTileEntityDataAction(Object packet); + + BiFunction blockBreakHiderGenerator(Class blockBreakPacket, Object obfuscationTarget, Set obfuscate, TechHider.BypassEvaluator bypass); + + BiFunction multiBlockChangeGenerator(Object obfuscationTarget, Set obfuscate, TechHider.BypassEvaluator bypass); + + boolean iBlockDataHidden(Set obfuscate, Object iBlockData); +} diff --git a/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java new file mode 100644 index 0000000..7f089e0 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -0,0 +1,128 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2021 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ + +package de.steamwar.techhider; + +import com.comphenix.tinyprotocol.Reflection; +import com.comphenix.tinyprotocol.TinyProtocol; +import de.steamwar.core.Core; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +public class TechHider { + + public static final Class blockPosition = Reflection.getClass("{nms.core}.BlockPosition"); + private static final Class baseBlockPosition = Reflection.getClass("{nms.core}.BaseBlockPosition"); + public static final Reflection.FieldAccessor blockPositionX = Reflection.getField(baseBlockPosition, int.class, 0); + public static final Reflection.FieldAccessor blockPositionZ = Reflection.getField(baseBlockPosition, int.class, 2); + + public static final Class iBlockData = Reflection.getClass("{nms.world.level.block.state}.IBlockData"); + public static final Class block = Reflection.getClass("{nms.world.level.block}.Block"); + private static final Reflection.MethodInvoker getBlockDataByBlock = Reflection.getTypedMethod(block, null, iBlockData); + + public static final Class craftMagicNumbers = Reflection.getClass("{obc}.util.CraftMagicNumbers"); + private static final Reflection.MethodInvoker getBlockByMaterial = Reflection.getTypedMethod(craftMagicNumbers, "getBlock", block, Material.class); + + private final Map, BiFunction> techhiders = new HashMap<>(); + private final BypassEvaluator bypass; + private final Object obfuscationTarget; + private final Set obfuscate; + + public TechHider(BypassEvaluator bypass, Material obfuscationTarget, Set obfuscate, Set hiddenBlockEntities) { + this.bypass = bypass; + this.obfuscate = obfuscate; + this.obfuscationTarget = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, obfuscationTarget)); + + techhiders.put(blockActionPacket, this::blockActionHider); + techhiders.put(blockChangePacket, this::blockChangeHider); + techhiders.put(tileEntityDataPacket, this::tileEntityDataHider); + techhiders.put(multiBlockChangePacket,ProtocolWrapper.impl.multiBlockChangeGenerator(obfuscationTarget, obfuscate, bypass)); + techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(bypass, BlockIds.impl.materialToId(obfuscationTarget), obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet()), hiddenBlockEntities)); + + if(Core.getVersion() > 12 && Core.getVersion() < 19) { + Class blockBreakClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutBlockBreak"); + techhiders.put(blockBreakClass, ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, obfuscationTarget, obfuscate, bypass)); + } + + if(Core.getVersion() > 8){ + techhiders.put(Reflection.getClass("{nms.network.protocol.game}.PacketPlayInUseItem"), (p, packet) -> p.getGameMode() == GameMode.SPECTATOR ? null : packet); + } + techhiders.put(Reflection.getClass("{nms.network.protocol.game}.PacketPlayInUseEntity"), (p, packet) -> p.getGameMode() == GameMode.SPECTATOR ? null : packet); + + } + + public void enable() { + techhiders.forEach(TinyProtocol.instance::addFilter); + } + + public void disable() { + techhiders.forEach(TinyProtocol.instance::removeFilter); + } + + public static final Class multiBlockChangePacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutMultiBlockChange"); + public static final UnaryOperator multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(TechHider.multiBlockChangePacket); + + private static final Class blockChangePacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutBlockChange"); + private static final Function blockChangeCloner = ProtocolUtils.shallowCloneGenerator(blockChangePacket); + private static final Reflection.FieldAccessor blockChangePosition = Reflection.getField(blockChangePacket, blockPosition, 0); + private static final Reflection.FieldAccessor blockChangeBlockData = Reflection.getField(blockChangePacket, iBlockData, 0); + private Object blockChangeHider(Player p, Object packet) { + Object pos = blockChangePosition.get(packet); + if(bypass.bypass(p, ProtocolUtils.posToChunk(blockPositionX.get(pos)), ProtocolUtils.posToChunk(blockPositionZ.get(pos)))) + return packet; + + if(ProtocolWrapper.impl.iBlockDataHidden(obfuscate, blockChangeBlockData.get(packet))) { + packet = blockChangeCloner.apply(packet); + blockChangeBlockData.set(packet, obfuscationTarget); + } + return packet; + } + + private static final Class blockActionPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutBlockAction"); + private static final Reflection.FieldAccessor blockActionPosition = Reflection.getField(blockActionPacket, blockPosition, 0); + private Object blockActionHider(Player p, Object packet) { + Object pos = blockActionPosition.get(packet); + if(bypass.bypass(p, ProtocolUtils.posToChunk(blockPositionX.get(pos)), ProtocolUtils.posToChunk(blockPositionZ.get(pos)))) + return packet; + return null; + } + + public static final Class tileEntityDataPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutTileEntityData"); + private static final Reflection.FieldAccessor tileEntityDataPosition = Reflection.getField(tileEntityDataPacket, blockPosition, 0); + private Object tileEntityDataHider(Player p, Object packet) { + Object pos = tileEntityDataPosition.get(packet); + if(bypass.bypass(p, ProtocolUtils.posToChunk(blockPositionX.get(pos)), ProtocolUtils.posToChunk(blockPositionZ.get(pos)))) + return packet; + + if(ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)) + return packet; + return null; + } + + public interface BypassEvaluator { + boolean bypass(Player p, int chunkX, int chunkZ); + } +}