Add SpigotCore module

This commit is contained in:
2024-08-04 21:37:50 +02:00
parent aeef0f6e49
commit b97890fe7a
110 changed files with 11957 additions and 1 deletions
+64
View File
@@ -0,0 +1,64 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
plugins {
id("java")
id("base")
}
group = "de.steamwar"
version = ""
tasks.compileJava {
options.encoding = "UTF-8"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
sourceSets {
main {
java {
srcDirs("src/")
}
resources {
srcDirs("src/")
exclude("**/*.java", "**/*.kt")
}
}
}
dependencies {
compileOnly("org.projectlombok:lombok:1.18.32")
annotationProcessor("org.projectlombok:lombok:1.18.32")
compileOnly(project(":SpigotCore:SpigotCore_Main"))
compileOnly(project(":SpigotCore:SpigotCore_14"))
compileOnly(project(":CommonCore"))
compileOnly("de.steamwar:fastasyncworldedit:1.18")
compileOnly("de.steamwar:spigot:1.18")
compileOnly("org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT")
compileOnly("com.mojang:datafixerupper:4.0.26")
compileOnly("io.netty:netty-all:4.1.68.Final")
compileOnly("com.mojang:authlib:1.5.25")
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
import com.comphenix.tinyprotocol.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.world.level.World;
import net.minecraft.world.level.chunk.Chunk;
import net.minecraft.world.level.lighting.LightEngine;
import org.bukkit.entity.Player;
public class CraftbukkitWrapper18 implements CraftbukkitWrapper.ICraftbukkitWrapper {
private static final Reflection.MethodInvoker getHandle = Reflection.getMethod("{obc}.CraftChunk", "getHandle");
private static final Reflection.MethodInvoker getLightEngine = Reflection.getTypedMethod(World.class, null, LightEngine.class);
@Override
public void sendChunk(Player p, int chunkX, int chunkZ) {
Chunk chunk = (Chunk) getHandle.invoke(p.getWorld().getChunkAt(chunkX, chunkZ));
TinyProtocol.instance.sendPacket(p, new ClientboundLevelChunkWithLightPacket(chunk, (LightEngine) getLightEngine.invoke(chunk.q), null, null, false, true));
}
}
@@ -0,0 +1,61 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
import com.comphenix.tinyprotocol.Reflection;
import com.mojang.authlib.GameProfile;
import com.mojang.datafixers.util.Pair;
import org.bukkit.GameMode;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
public class ProtocolWrapper18 implements ProtocolWrapper {
private static final Reflection.FieldAccessor<List> equipmentStack = Reflection.getField(equipmentPacket, List.class, 0);
@Override
public void setEquipmentPacketStack(Object packet, Object slot, Object stack) {
equipmentStack.set(packet, Collections.singletonList(new Pair<>(slot, stack)));
}
private static final Class<?> playerInfoPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo");
private static final Class<?> playerInfoActionClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$EnumPlayerInfoAction");
private static final Reflection.FieldAccessor<?> playerInfoAction = Reflection.getField(playerInfoPacket, playerInfoActionClass, 0);
private static final Reflection.FieldAccessor<List> playerInfoData = Reflection.getField(playerInfoPacket, List.class, 0);
private static final EnumMap<PlayerInfoAction, Object> actions = new EnumMap<>(PlayerInfoAction.class);
static {
Object[] nativeActions = playerInfoActionClass.getEnumConstants();
actions.put(PlayerInfoAction.ADD, nativeActions[0]);
actions.put(PlayerInfoAction.GAMEMODE, nativeActions[1]);
actions.put(PlayerInfoAction.REMOVE, nativeActions[4]);
}
private static final Class<?> iChatBaseComponent = Reflection.getClass("{nms.network.chat}.IChatBaseComponent");
private static final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$PlayerInfoData", GameProfile.class, int.class, enumGamemode, iChatBaseComponent);
@Override
@SuppressWarnings("deprecation")
public Object playerInfoPacketConstructor(PlayerInfoAction action, GameProfile profile, GameMode mode) {
Object packet = Reflection.newInstance(playerInfoPacket);
playerInfoAction.set(packet, actions.get(action));
playerInfoData.set(packet, Collections.singletonList(playerInfoDataConstructor.invoke(profile, 0, ProtocolWrapper.getGameModeById.invoke(null, mode.getValue()), null)));
return packet;
}
}
@@ -0,0 +1,44 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.MCEditSchematicReader;
import com.sk89q.worldedit.extent.clipboard.io.SpongeSchematicReader;
import de.steamwar.sql.NoClipboardException;
import java.io.IOException;
import java.io.InputStream;
public class WorldEditWrapper18 extends WorldEditWrapper14 {
@Override
@SuppressWarnings("removal")
public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException {
//Use FAWE reader due to FAWE capability of reading corrupt FAWE schems
NBTInputStream nbtStream = new NBTInputStream(is);
try {
return (schemFormat ? new SpongeSchematicReader(nbtStream) : new MCEditSchematicReader(nbtStream)).read();
} catch (NullPointerException e) {
throw new NoClipboardException();
}
}
}
@@ -0,0 +1,165 @@
/*
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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.techhider;
import com.comphenix.tinyprotocol.Reflection;
import de.steamwar.core.Core;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minecraft.core.IRegistry;
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.util.SimpleBitStorage;
import net.minecraft.world.level.block.entity.TileEntityTypes;
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 ChunkHider18 implements ChunkHider {
@Override
public Class<?> mapChunkPacket() {
return ClientboundLevelChunkWithLightPacket.class;
}
private static final UnaryOperator<Object> chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class);
private static final UnaryOperator<Object> chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class);
private static final Reflection.FieldAccessor<Integer> chunkXField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0);
private static final Reflection.FieldAccessor<Integer> chunkZField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1);
private static final Reflection.FieldAccessor<ClientboundLevelChunkPacketData> chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0);
private static final Reflection.FieldAccessor<byte[]> dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0);
private static final Reflection.FieldAccessor<List> tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0);
@Override
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.LocationEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities) {
return (p, packet) -> {
int chunkX = chunkXField.get(packet);
int chunkZ = chunkZField.get(packet);
if (locationEvaluator.skipChunk(p, chunkX, chunkZ))
return packet;
packet = chunkPacketCloner.apply(packet);
Object dataWrapper = chunkDataCloner.apply(chunkData.get(packet));
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);
int xOffset = 16*chunkX;
int zOffset = 16*chunkZ;
for(int yOffset = p.getWorld().getMinHeight(); yOffset < p.getWorld().getMaxHeight(); yOffset += 16) {
int finalYOffset = yOffset;
int chunkY = yOffset/16;
dataHider((x, y, z) -> locationEvaluator.check(p, x+xOffset, y+finalYOffset, z+zOffset), obfuscationTarget, obfuscate, in, out, locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ), locationEvaluator.blockPrecise(p, chunkX, chunkY, chunkZ));
}
out.writeBytes(in); // MC appends a 0 byte at the end if there is a full chunk, idk why
byte[] data = new byte[out.readableBytes()];
out.readBytes(data);
dataField.set(dataWrapper, data);
chunkData.set(packet, dataWrapper);
return packet;
};
}
public static final Class<?> tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$a");
protected static final Reflection.FieldAccessor<TileEntityTypes> entityType = Reflection.getField(tileEntity, TileEntityTypes.class, 0);
private static final IRegistry<?> tileEntityTypes = Reflection.getField(Core.getVersion() > 18 ? Reflection.getClass("net.minecraft.core.registries.BuiltInRegistries") : IRegistry.class, IRegistry.class, 0, TileEntityTypes.class).get(null);
private static final Reflection.MethodInvoker getKey = Reflection.getTypedMethod(IRegistry.class, null, MinecraftKey.class, Object.class);
private static final Reflection.MethodInvoker getName = Reflection.getTypedMethod(MinecraftKey.class, null, String.class);
protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) {
return !hiddenBlockEntities.contains((String) getName.invoke(getKey.invoke(tileEntityTypes, entityType.get(tile))));
}
private void dataHider(PosEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, ByteBuf in, ByteBuf out, boolean skip, boolean blockPrecise) {
out.writeShort(in.readShort()); // Block count
containerWalker(in, out, 15, obfuscationTarget, skip ? Collections.emptySet() : obfuscate, (paletteTarget, dataArrayLength, bitsPerBlock) -> {
Set<Integer> palettedObfuscate;
if(bitsPerBlock < 15) {
palettedObfuscate = Collections.emptySet();
} else {
paletteTarget = obfuscationTarget;
palettedObfuscate = obfuscate;
}
if(skip || dataArrayLength == 0 || (!blockPrecise && bitsPerBlock < 15)) {
out.writeBytes(in, dataArrayLength*8);
return;
}
long[] array = new long[dataArrayLength];
for(int i = 0; i < dataArrayLength; i++)
array[i] = in.readLong();
SimpleBitStorage values = new SimpleBitStorage(bitsPerBlock, 4096, array);
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;
switch (locationEvaluator.test(x, y, z)) {
case SKIP:
break;
case CHECK:
if(!palettedObfuscate.contains(values.a(pos)))
break;
default:
values.b(pos, paletteTarget);
}
}
}
}
for (long l : values.a())
out.writeLong(l);
});
containerWalker(in, out, 6, 0, Collections.emptySet(), (paletteTarget, dataArrayLength, bitsPerBlock) -> out.writeBytes(in, dataArrayLength * 8)); // Biomes
}
private void containerWalker(ByteBuf in, ByteBuf out, int globalPalette, int obfuscationTarget, Set<Integer> obfuscate, TriConsumer<Integer, Integer, Byte> dataArray) {
byte bitsPerBlock = in.readByte();
out.writeByte(bitsPerBlock);
//blockId -> obfuscate.contains(blockId) ? obfuscationTarget : blockId
if(bitsPerBlock == 0) {
int value = ProtocolUtils.readVarInt(in);
ProtocolUtils.writeVarInt(out, obfuscate.contains(value) ? obfuscationTarget : value);
}else if(bitsPerBlock < globalPalette) {
obfuscationTarget = ChunkHider.processPalette(obfuscationTarget, obfuscate, in, out);
}
int dataArrayLength = ProtocolUtils.readVarInt(in);
ProtocolUtils.writeVarInt(out, dataArrayLength);
dataArray.accept(obfuscationTarget, dataArrayLength, bitsPerBlock);
}
private interface TriConsumer<X, Y, Z> {
void accept(X x, Y y, Z z);
}
}
@@ -0,0 +1,107 @@
/*
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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.techhider;
import com.comphenix.tinyprotocol.Reflection;
import net.minecraft.core.SectionPosition;
import net.minecraft.network.protocol.game.PacketPlayOutBlockBreak;
import net.minecraft.world.level.block.entity.TileEntitySign;
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.ArrayList;
import java.util.Set;
import java.util.function.BiFunction;
public class ProtocolWrapper18 implements ProtocolWrapper {
private static final Reflection.FieldAccessor<SectionPosition> multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPosition.class, 0);
private static final Reflection.FieldAccessor<short[]> multiBlockChangePos = Reflection.getField(TechHider.multiBlockChangePacket, short[].class, 0);
private static final Reflection.FieldAccessor<IBlockData[]> multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, IBlockData[].class, 0);
@Override
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
return (p, packet) -> {
Object chunkCoords = multiBlockChangeChunk.get(packet);
int chunkX = TechHider.blockPositionX.get(chunkCoords);
int chunkY = TechHider.blockPositionY.get(chunkCoords);
int chunkZ = TechHider.blockPositionZ.get(chunkCoords);
if(locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ))
return packet;
packet = TechHider.multiBlockChangeCloner.apply(packet);
final short[] oldPos = multiBlockChangePos.get(packet);
final IBlockData[] oldBlocks = multiBlockChangeBlocks.get(packet);
ArrayList<Short> poss = new ArrayList<>(oldPos.length);
ArrayList<IBlockData> blocks = new ArrayList<>(oldPos.length);
for(int i = 0; i < oldPos.length; i++) {
short pos = oldPos[i];
IBlockData block = oldBlocks[i];
switch(locationEvaluator.check(p, 16*chunkX + (pos >> 8 & 0xF), 16*chunkY + (pos & 0xF), 16*chunkZ + (pos >> 4 & 0xF))) {
case SKIP:
poss.add(pos);
blocks.add(block);
break;
case CHECK:
poss.add(pos);
blocks.add(TechHider.iBlockDataHidden(obfuscate, block) ? (IBlockData) obfuscationTarget : block);
break;
default:
break;
}
}
if(blocks.isEmpty())
return null;
short[] newPos = new short[poss.size()];
for(int i = 0; i < newPos.length; i++)
newPos[i] = poss.get(i);
multiBlockChangePos.set(packet, newPos);
multiBlockChangeBlocks.set(packet, blocks.toArray(new IBlockData[0]));
return packet;
};
}
private static final Reflection.FieldAccessor<TileEntityTypes> tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, TileEntityTypes.class, 0);
private static final TileEntityTypes<?> signType = Reflection.getField(TileEntityTypes.class, TileEntityTypes.class, 0, TileEntitySign.class).get(null);
@Override
public boolean unfilteredTileEntityDataAction(Object packet) {
return tileEntityType.get(packet) != signType;
}
@Override
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
return (p, packet) -> {
PacketPlayOutBlockBreak breakPacket = (PacketPlayOutBlockBreak) packet;
switch (locationEvaluator.checkBlockPos(p, breakPacket.b())) {
case SKIP:
return packet;
case CHECK:
if(!TechHider.iBlockDataHidden(obfuscate, breakPacket.c()))
return packet;
default:
return new PacketPlayOutBlockBreak(breakPacket.b(), (IBlockData) obfuscationTarget, breakPacket.d(), breakPacket.a());
}
};
}
}