Cleanup SpigotCore

This commit is contained in:
2026-05-15 14:02:27 +02:00
parent 13185f0e05
commit 060364abb5
94 changed files with 1168 additions and 6935 deletions
+21 -2
View File
@@ -25,6 +25,21 @@ tasks.compileJava {
options.isWarnings = false
}
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.addAll(listOf(
"--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED"
))
}
tasks.withType<Test>().configureEach {
jvmArgs("--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED")
}
dependencies {
compileOnly(libs.classindex)
annotationProcessor(libs.classindex)
@@ -32,11 +47,15 @@ dependencies {
compileOnly(project(":CommandFramework", "default"))
compileOnly(project(":SpigotCore:CRIUDummy", "default"))
compileOnly(libs.worldedit12)
compileOnly(libs.fawe21)
compileOnly(libs.spigotapi)
compileOnly(libs.paperapi21)
compileOnly(libs.nms21)
compileOnly(libs.authlib2)
compileOnly(libs.datafixer)
compileOnly(libs.netty)
compileOnly(libs.authlib)
compileOnly(libs.brigadier)
compileOnly(libs.fastutil)
implementation(libs.anvilgui)
@@ -19,28 +19,100 @@
package de.steamwar.core;
import de.steamwar.Reflection;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.BaseComponent;
import net.minecraft.world.entity.PositionMoveRotation;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import java.util.UUID;
public class BountifulWrapper {
private BountifulWrapper() {}
public static final BountifulWrapper impl = new BountifulWrapper();
public static final IBountifulWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
public void playPling(Player player) {
player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1);
}
public interface IBountifulWrapper {
void playPling(Player player);
public void sendMessage(Player player, ChatMessageType type, BaseComponent... msg) {
if(type == ChatMessageType.CHAT)
type = ChatMessageType.SYSTEM;
void sendMessage(Player player, ChatMessageType type, BaseComponent... msg);
player.spigot().sendMessage(type, msg);
}
Object getDataWatcherObject(int index, Class<?> type);
Object getDataWatcherItem(Object dataWatcherObject, Object value);
private static final Class<?> dataWatcherObject = Reflection.getClass("net.minecraft.network.syncher.EntityDataAccessor");
private static final Class<?> dataWatcherRegistry = Reflection.getClass("net.minecraft.network.syncher.EntityDataSerializers");
private static final Class<?> dataWatcherSerializer = Reflection.getClass("net.minecraft.network.syncher.EntityDataSerializer");
private static final Reflection.Constructor dataWatcherObjectConstructor = Reflection.getConstructor(dataWatcherObject, int.class, dataWatcherSerializer);
public Object getDataWatcherObject(int index, Class<?> type) {
return dataWatcherObjectConstructor.invoke(index, Reflection.getField(dataWatcherRegistry, dataWatcherSerializer, 0, type).get(null));
}
PositionSetter getPositionSetter(Class<?> packetClass, int fieldOffset);
PositionSetter getRelMoveSetter(Class<?> packetClass);
UUIDSetter getUUIDSetter(Class<?> packetClass);
private static final Class<?> item = Reflection.getClass("net.minecraft.network.syncher.SynchedEntityData$DataItem");
private static final Reflection.Constructor itemConstructor = Reflection.getConstructor(item, dataWatcherObject, Object.class);
public Object getDataWatcherItem(Object dwo, Object value) {
return itemConstructor.invoke(dwo, value);
}
public BountifulWrapper.PositionSetter getPositionSetter(Class<?> packetClass, int fieldOffset) {
try {
Reflection.Field<PositionMoveRotation> field = Reflection.getField(packetClass, PositionMoveRotation.class, 0);
return (packet, x, y, z, pitch, yaw) -> {
PositionMoveRotation pos = field.get(packet);
field.set(packet, new PositionMoveRotation(new Vec3(x, y, z), pos.deltaMovement(), yaw, pitch));
};
} catch (IllegalArgumentException e) {
Reflection.Field<Double> posX = Reflection.getField(packetClass, double.class, fieldOffset);
Reflection.Field<Double> posY = Reflection.getField(packetClass, double.class, fieldOffset+1);
Reflection.Field<Double> posZ = Reflection.getField(packetClass, double.class, fieldOffset+2);
boolean isByteClass = packetClass.getSimpleName().contains("PacketPlayOutEntityTeleport") || packetClass.getSimpleName().contains("PacketPlayOutNamedEntitySpawn");
Class<?> pitchYawType = isByteClass ? byte.class : int.class;
Reflection.Field<?> lookYaw = Reflection.getField(packetClass, pitchYawType, isByteClass ? 0 : 1);
Reflection.Field<?> lookPitch = Reflection.getField(packetClass, pitchYawType, isByteClass ? 1 : 2);
return (packet, x, y, z, pitch, yaw) -> {
posX.set(packet, x);
posY.set(packet, y);
posZ.set(packet, z);
if (isByteClass) {
lookYaw.set(packet, (byte) (yaw*256/360));
lookPitch.set(packet, (byte) (pitch*256/360));
} else {
lookYaw.set(packet, (int) (yaw*256/360));
lookPitch.set(packet, (int) (pitch*256/360));
}
};
}
}
public BountifulWrapper.PositionSetter getRelMoveSetter(Class<?> packetClass) {
Class<?> type = Core.getVersion() > 12 ? short.class : int.class;
int fieldOffset = Core.getVersion() > 12 ? 0 : 1;
Reflection.Field<?> moveX = Reflection.getField(packetClass, type, 0 + fieldOffset);
Reflection.Field<?> moveY = Reflection.getField(packetClass, type, 1 + fieldOffset);
Reflection.Field<?> moveZ = Reflection.getField(packetClass, type, 2 + fieldOffset);
Reflection.Field<Byte> moveYaw = Reflection.getField(packetClass, byte.class, 0);
Reflection.Field<Byte> movePitch = Reflection.getField(packetClass, byte.class, 1);
return (packet, x, y, z, pitch, yaw) -> {
moveX.set(packet, (short)(x*4096));
moveY.set(packet, (short)(y*4096));
moveZ.set(packet, (short)(z*4096));
moveYaw.set(packet, (byte)(yaw*256/360));
movePitch.set(packet, (byte)(pitch*256/360));
};
}
public BountifulWrapper.UUIDSetter getUUIDSetter(Class<?> packetClass) {
Reflection.Field<UUID> uuidField = Reflection.getField(packetClass, UUID.class, 0);
return uuidField::set;
}
public interface PositionSetter {
@@ -19,11 +19,26 @@
package de.steamwar.core;
public interface ChatWrapper {
ChatWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.contents.PlainTextContents;
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
import net.minecraft.network.syncher.SynchedEntityData;
import java.util.ArrayList;
Object stringToChatComponent(String text);
public class ChatWrapper {
public static final ChatWrapper impl = new ChatWrapper();
Object getDataWatcherPacket(int entityId, Object... dataWatcherKeyValues);
public Object stringToChatComponent(String text) {
return MutableComponent.create(PlainTextContents.create(text));
}
public Object getDataWatcherPacket(int entityId, Object... dataWatcherKeyValues) {
ArrayList<SynchedEntityData.DataValue<?>> nativeWatchers = new ArrayList<>(1);
for(int i = 0; i < dataWatcherKeyValues.length; i+=2) {
nativeWatchers.add(((SynchedEntityData.DataItem<?>) BountifulWrapper.impl.getDataWatcherItem(dataWatcherKeyValues[i], dataWatcherKeyValues[i+1])).value());
}
return new ClientboundSetEntityDataPacket(entityId, nativeWatchers);
}
}
@@ -19,22 +19,20 @@
package de.steamwar.core;
import de.steamwar.Reflection;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.craftbukkit.CraftServer;
import java.util.Map;
@UtilityClass
public class CommandRemover {
private static final Reflection.Field<SimpleCommandMap> commandMap = Reflection.getField("org.bukkit.craftbukkit.CraftServer", "commandMap", SimpleCommandMap.class);
private static final Reflection.Field<Map> knownCommands = Reflection.getField(SimpleCommandMap.class, "knownCommands", Map.class);
private static final Map<String, Command> knownCommands = ((CraftServer) Bukkit.getServer()).getCommandMap().getKnownCommands();
public static void removeAll(String... cmds) {
Map<String, Command> knownCmds = knownCommands.get(commandMap.get(Bukkit.getServer()));
for (String cmd : cmds) {
knownCmds.remove(cmd.toLowerCase());
knownCommands.remove(cmd.toLowerCase());
}
}
}
@@ -21,16 +21,11 @@ package de.steamwar.core;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.command.*;
import de.steamwar.core.authlib.SteamwarGameProfileRepository;
import de.steamwar.core.events.AntiNocom;
import de.steamwar.core.events.ChattingEvent;
import de.steamwar.core.events.PlayerJoinedEvent;
import de.steamwar.core.events.WorldLoadEvent;
import de.steamwar.command.SWCommandUtils;
import de.steamwar.command.SWTypeMapperCreator;
import de.steamwar.command.TabCompletionCache;
import de.steamwar.command.TypeMapper;
import de.steamwar.core.authlib.SteamwarGameProfileRepository;
import de.steamwar.linkage.AbstractLinker;
import de.steamwar.linkage.SpigotLinker;
import de.steamwar.message.Message;
@@ -54,6 +49,7 @@ public class Core extends JavaPlugin {
public static final Message MESSAGE = new Message("SpigotCore", Core.class.getClassLoader());
@Deprecated
public static int getVersion(){
return Reflection.MAJOR_VERSION;
}
@@ -108,9 +104,8 @@ public class Core extends JavaPlugin {
return;
}
Bukkit.getServer().getPluginManager().registerEvents(RecipeDiscoverWrapper.impl, this);
if(!Statement.productionDatabase()) {
getServer().getPluginManager().registerEvents(LocaleChangeWrapper.impl, this);
getServer().getPluginManager().registerEvents(new LocaleChangeWrapper(), this);
}
getServer().getMessenger().registerIncomingPluginChannel(this, "sw:bridge", new NetworkReceiver());
@@ -19,14 +19,18 @@
package de.steamwar.core;
import com.comphenix.tinyprotocol.TinyProtocol;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import org.bukkit.craftbukkit.CraftChunk;
import org.bukkit.entity.Player;
public class CraftbukkitWrapper {
private CraftbukkitWrapper() {}
public static final CraftbukkitWrapper impl = new CraftbukkitWrapper();
public static final ICraftbukkitWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
public interface ICraftbukkitWrapper {
void sendChunk(Player p, int chunkX, int chunkZ);
public void sendChunk(Player p, int chunkX, int chunkZ) {
LevelChunk chunk = (LevelChunk) ((CraftChunk) p.getWorld().getChunkAt(chunkX, chunkZ)).getHandle(ChunkStatus.FULL);
TinyProtocol.instance.sendPacket(p, new ClientboundLevelChunkWithLightPacket(chunk, chunk.level.getLightEngine(), null, null, true));
}
}
@@ -21,6 +21,7 @@ package de.steamwar.core;
import de.steamwar.Reflection;
import de.steamwar.sql.SWException;
import org.spigotmc.WatchdogThread;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
@@ -39,10 +40,8 @@ public class ErrorHandler extends Handler {
public ErrorHandler(){
Logger.getLogger("").addHandler(this);
Class<?> watchdogThread = Reflection.getClass("org.spigotmc.WatchdogThread");
Reflection.Field<?> getInstance = Reflection.getField(watchdogThread, watchdogThread, 0);
Thread watchdog = (Thread) getInstance.get(null);
watchdogThreadId = watchdog.getId();
Reflection.Field<WatchdogThread> getInstance = Reflection.getField(WatchdogThread.class, WatchdogThread.class, 0);
watchdogThreadId = getInstance.get(null).getId();
}
void unregister() {
@@ -19,38 +19,343 @@
package de.steamwar.core;
import com.destroystokyo.paper.profile.PlayerProfile;
import de.steamwar.Reflection;
import org.bukkit.Material;
import org.bukkit.World;
import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetScorePacket;
import org.bukkit.*;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class FlatteningWrapper {
private FlatteningWrapper() {}
public static final Class<?> scoreboardObjective = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundSetObjectivePacket");
public static final Class<?> scoreboardScore = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundSetScorePacket");
public static final FlatteningWrapper impl = new FlatteningWrapper();
public static final IFlatteningWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
private static final Map<String, Material> renamedLegacy = new HashMap<>();
public interface IFlatteningWrapper {
void setScoreboardTitle(Object packet, String title);
void setScoreAction(Object packet);
static{
renamedLegacy.put("WOOD", Material.OAK_WOOD);
renamedLegacy.put("SAPLING", Material.OAK_SAPLING);
renamedLegacy.put("STATIONARY_WATER", Material.WATER);
renamedLegacy.put("STATIONARY_LAVA", Material.LAVA);
renamedLegacy.put("LOG", Material.OAK_LOG);
renamedLegacy.put("LEAVES", Material.OAK_LEAVES);
renamedLegacy.put("BED_BLOCK", Material.RED_BED);
renamedLegacy.put("BED", Material.RED_BED);
renamedLegacy.put("PISTON_STICKY_BASE", Material.STICKY_PISTON);
renamedLegacy.put("WEB", Material.COBWEB);
renamedLegacy.put("LONG_GRASS", Material.TALL_GRASS);
renamedLegacy.put("PISTON_BASE", Material.PISTON);
renamedLegacy.put("PISTON_EXTENSION", Material.PISTON_HEAD);
renamedLegacy.put("WOOL", Material.WHITE_WOOL);
renamedLegacy.put("PISTON_MOVING_PIECE", Material.MOVING_PISTON);
renamedLegacy.put("YELLOW_FLOWER", Material.DANDELION);
renamedLegacy.put("RED_ROSE", Material.POPPY);
renamedLegacy.put("DOUBLE_STEP", Material.SMOOTH_STONE);
renamedLegacy.put("STEP", Material.SMOOTH_STONE_SLAB);
renamedLegacy.put("MOB_SPAWNER", Material.SPAWNER);
renamedLegacy.put("WOOD_STAIRS", Material.OAK_STAIRS);
renamedLegacy.put("WORKBENCH", Material.CRAFTING_TABLE);
renamedLegacy.put("CROPS", Material.WHEAT_SEEDS);
renamedLegacy.put("SEEDS", Material.WHEAT_SEEDS);
renamedLegacy.put("SOIL", Material.FARMLAND);
renamedLegacy.put("BURNING_FURNACE", Material.FURNACE);
renamedLegacy.put("SIGN_POST", Material.OAK_SIGN);
renamedLegacy.put("SIGN", Material.OAK_SIGN);
renamedLegacy.put("WOODEN_DOOR", Material.OAK_DOOR);
renamedLegacy.put("WOOD_DOOR", Material.OAK_DOOR);
renamedLegacy.put("RAILS", Material.RAIL);
renamedLegacy.put("WALL_SIGN", Material.OAK_WALL_SIGN);
renamedLegacy.put("STONE_PLATE", Material.STONE_PRESSURE_PLATE);
renamedLegacy.put("WOOD_PLATE", Material.OAK_PRESSURE_PLATE);
renamedLegacy.put("GLOWING_REDSTONE_ORE", Material.REDSTONE_ORE);
renamedLegacy.put("REDSTONE_TORCH_OFF", Material.REDSTONE_TORCH);
renamedLegacy.put("REDSTONE_TORCH_ON", Material.REDSTONE_TORCH);
renamedLegacy.put("IRON_DOOR_BLOCK", Material.IRON_DOOR);
renamedLegacy.put("SUGAR_CANE_BLOCK", Material.SUGAR_CANE);
renamedLegacy.put("CAKE_BLOCK", Material.CAKE);
renamedLegacy.put("MELON_BLOCK", Material.MELON);
renamedLegacy.put("BEETROOT_BLOCK", Material.BEETROOT);
renamedLegacy.put("FENCE", Material.OAK_FENCE);
renamedLegacy.put("PORTAL", Material.NETHER_PORTAL);
renamedLegacy.put("DIODE_BLOCK_OFF", Material.REPEATER);
renamedLegacy.put("DIODE_BLOCK_ON", Material.REPEATER);
renamedLegacy.put("DIODE", Material.REPEATER);
renamedLegacy.put("STAINED_GLASS", Material.WHITE_STAINED_GLASS);
renamedLegacy.put("TRAP_DOOR", Material.OAK_TRAPDOOR);
renamedLegacy.put("MONSTER_EGGS", Material.SKELETON_SPAWN_EGG);
renamedLegacy.put("MONSTER_EGG", Material.SKELETON_SPAWN_EGG);
renamedLegacy.put("SMOOTH_BRICK", Material.STONE_BRICKS);
renamedLegacy.put("HUGE_MUSHROOM_1", Material.MUSHROOM_STEM);
renamedLegacy.put("HUGE_MUSHROOM_2", Material.RED_MUSHROOM);
renamedLegacy.put("IRON_FENCE", Material.IRON_BARS);
renamedLegacy.put("THIN_GLASS", Material.GLASS_PANE);
renamedLegacy.put("FENCE_GATE", Material.OAK_FENCE_GATE);
renamedLegacy.put("SMOOTH_STAIRS", Material.STONE_BRICK_STAIRS);
renamedLegacy.put("MYCEL", Material.MYCELIUM);
renamedLegacy.put("WATER_LILY", Material.LILY_PAD);
renamedLegacy.put("NETHER_FENCE", Material.NETHER_BRICK_FENCE);
renamedLegacy.put("NETHER_WARTS", Material.NETHER_WART);
renamedLegacy.put("NETHER_STALK", Material.NETHER_WART);
renamedLegacy.put("ENCHANTMENT_TABLE", Material.ENCHANTING_TABLE);
renamedLegacy.put("ENDER_PORTAL", Material.END_PORTAL);
renamedLegacy.put("ENDER_PORTAL_FRAME", Material.END_PORTAL_FRAME);
renamedLegacy.put("ENDER_STONE", Material.END_STONE);
renamedLegacy.put("REDSTONE_LAMP_OFF", Material.REDSTONE_LAMP);
renamedLegacy.put("REDSTONE_LAMP_ON", Material.REDSTONE_LAMP);
renamedLegacy.put("WOOD_DOUBLE_STEP", Material.OAK_SLAB);
renamedLegacy.put("WOOD_STEP", Material.OAK_SLAB);
renamedLegacy.put("SPRUCE_WOOD_STAIRS", Material.SPRUCE_STAIRS);
renamedLegacy.put("BIRCH_WOOD_STAIRS", Material.BIRCH_STAIRS);
renamedLegacy.put("JUNGLE_WOOD_STAIRS", Material.JUNGLE_STAIRS);
renamedLegacy.put("COMMAND", Material.COMMAND_BLOCK);
renamedLegacy.put("COBBLE_WALL", Material.COBBLESTONE_WALL);
renamedLegacy.put("WOOD_BUTTON", Material.OAK_BUTTON);
renamedLegacy.put("SKULL", Material.SKELETON_SKULL);
renamedLegacy.put("SKULL_ITEM", Material.SKELETON_SKULL);
renamedLegacy.put("GOLD_PLATE", Material.LIGHT_WEIGHTED_PRESSURE_PLATE);
renamedLegacy.put("IRON_PLATE", Material.HEAVY_WEIGHTED_PRESSURE_PLATE);
renamedLegacy.put("REDSTONE_COMPARATOR_OFF", Material.COMPARATOR);
renamedLegacy.put("REDSTONE_COMPARATOR_ON", Material.COMPARATOR);
renamedLegacy.put("REDSTONE_COMPARATOR", Material.COMPARATOR);
renamedLegacy.put("QUARTZ_ORE", Material.QUARTZ);
renamedLegacy.put("STAINED_CLAY", Material.WHITE_TERRACOTTA);
renamedLegacy.put("STAINED_GLASS_PANE", Material.WHITE_STAINED_GLASS_PANE);
renamedLegacy.put("LEAVES_2", Material.ACACIA_LEAVES);
renamedLegacy.put("LOG_2", Material.ACACIA_LOG);
renamedLegacy.put("CARPET", Material.WHITE_CARPET);
renamedLegacy.put("HARD_CLAY", Material.TERRACOTTA);
renamedLegacy.put("DOUBLE_PLANT", Material.SUNFLOWER);
renamedLegacy.put("STANDING_BANNER", Material.WHITE_BANNER);
renamedLegacy.put("BANNER", Material.WHITE_BANNER);
renamedLegacy.put("WALL_BANNER", Material.WHITE_WALL_BANNER);
renamedLegacy.put("DAYLIGHT_DETECTOR_INVERTED", Material.DAYLIGHT_DETECTOR);
renamedLegacy.put("DOUBLE_STONE_SLAB2", Material.RED_SANDSTONE_SLAB);
renamedLegacy.put("STONE_SLAB2", Material.RED_SANDSTONE_SLAB);
renamedLegacy.put("PURPUR_DOUBLE_SLAB", Material.PURPUR_SLAB);
renamedLegacy.put("END_BRICKS", Material.END_STONE_BRICKS);
renamedLegacy.put("COMMAND_REPEATING", Material.REPEATING_COMMAND_BLOCK);
renamedLegacy.put("COMMAND_CHAIN", Material.CHAIN_COMMAND_BLOCK);
renamedLegacy.put("MAGMA", Material.MAGMA_BLOCK);
renamedLegacy.put("RED_NETHER_BRICK", Material.RED_NETHER_BRICKS);
renamedLegacy.put("SILVER_SHULKER_BOX", Material.LIGHT_GRAY_SHULKER_BOX);
renamedLegacy.put("SILVER_GLAZED_TERRACOTTA", Material.LIGHT_GRAY_TERRACOTTA);
renamedLegacy.put("CONCRETE", Material.WHITE_CONCRETE);
renamedLegacy.put("CONCRETE_POWDER", Material.WHITE_CONCRETE_POWDER);
renamedLegacy.put("IRON_SPADE", Material.IRON_SHOVEL);
renamedLegacy.put("WOOD_SWORD", Material.WOODEN_SWORD);
renamedLegacy.put("WOOD_SPADE", Material.WOODEN_SHOVEL);
renamedLegacy.put("WOOD_PICKAXE", Material.WOODEN_PICKAXE);
renamedLegacy.put("WOOD_AXE", Material.WOODEN_AXE);
renamedLegacy.put("STONE_SPADE", Material.STONE_SHOVEL);
renamedLegacy.put("DIAMOND_SPADE", Material.DIAMOND_SHOVEL);
renamedLegacy.put("MUSHROOM_SOUP", Material.MUSHROOM_STEW);
renamedLegacy.put("GOLD_SWORD", Material.GOLDEN_SWORD);
renamedLegacy.put("GOLD_SPADE", Material.GOLDEN_SHOVEL);
renamedLegacy.put("GOLD_PICKAXE", Material.GOLDEN_PICKAXE);
renamedLegacy.put("GOLD_AXE", Material.GOLDEN_AXE);
renamedLegacy.put("SULPHUR", Material.GUNPOWDER);
renamedLegacy.put("WOOD_HOE", Material.WOODEN_HOE);
renamedLegacy.put("GOLD_HOE", Material.GOLDEN_HOE);
renamedLegacy.put("GOLD_HELMET", Material.GOLDEN_HELMET);
renamedLegacy.put("GOLD_CHESTPLATE", Material.GOLDEN_CHESTPLATE);
renamedLegacy.put("GOLD_LEGGINGS", Material.GOLDEN_LEGGINGS);
renamedLegacy.put("GOLD_BOOTS", Material.GOLDEN_BOOTS);
renamedLegacy.put("PORK", Material.PORKCHOP);
renamedLegacy.put("GRILLED_PORK", Material.COOKED_PORKCHOP);
renamedLegacy.put("SNOW_BALL", Material.SNOWBALL);
renamedLegacy.put("BOAT", Material.OAK_BOAT);
renamedLegacy.put("CLAY_BRICK", Material.BRICKS);
renamedLegacy.put("STORAGE_MINECART", Material.CHEST_MINECART);
renamedLegacy.put("POWERED_MINECART", Material.FURNACE_MINECART);
renamedLegacy.put("WATCH", Material.CLOCK);
renamedLegacy.put("RAW_FISH", Material.SALMON);
renamedLegacy.put("COOKED_FISH", Material.COOKED_SALMON);
renamedLegacy.put("INK_SACK", Material.INK_SAC);
renamedLegacy.put("RAW_BEEF", Material.BEEF);
renamedLegacy.put("RAW_CHICKEN", Material.CHICKEN);
renamedLegacy.put("EYE_OF_ENDER", Material.ENDER_EYE);
renamedLegacy.put("SPECKLED_MELON", Material.GLISTERING_MELON_SLICE);
renamedLegacy.put("EXP_BOTTLE", Material.EXPERIENCE_BOTTLE);
renamedLegacy.put("FIREBALL", Material.FIRE_CHARGE);
renamedLegacy.put("BOOK_AND_QUILL", Material.WRITABLE_BOOK);
renamedLegacy.put("FLOWER_POT_ITEM", Material.FLOWER_POT);
renamedLegacy.put("EMPTY_MAP", Material.MAP);
renamedLegacy.put("BREWING_STAND_ITEM", Material.BREWING_STAND);
renamedLegacy.put("CAULDRON_ITEM", Material.CAULDRON);
renamedLegacy.put("CARROT_ITEM", Material.CARROT);
renamedLegacy.put("POTATO_ITEM", Material.POTATO);
renamedLegacy.put("SPRUCE_DOOR_ITEM", Material.SPRUCE_DOOR);
renamedLegacy.put("BIRCH_DOOR_ITEM", Material.BIRCH_DOOR);
renamedLegacy.put("JUNGLE_DOOR_ITEM", Material.JUNGLE_DOOR);
renamedLegacy.put("ACACIA_DOOR_ITEM", Material.ACACIA_DOOR);
renamedLegacy.put("DARK_OAK_DOOR_ITEM", Material.DARK_OAK_DOOR);
renamedLegacy.put("CARROT_STICK", Material.CARROT_ON_A_STICK);
renamedLegacy.put("FIREWORK", Material.FIREWORK_ROCKET);
renamedLegacy.put("FIREWORK_CHARGE", Material.FIREWORK_STAR);
renamedLegacy.put("NETHER_BRICK_ITEM", Material.NETHER_BRICKS);
renamedLegacy.put("EXPLOSIVE_MINECART", Material.TNT_MINECART);
renamedLegacy.put("IRON_BARDING", Material.IRON_HORSE_ARMOR);
renamedLegacy.put("GOLD_BARDING", Material.GOLDEN_HORSE_ARMOR);
renamedLegacy.put("DIAMOND_BARDING", Material.DIAMOND_HORSE_ARMOR);
renamedLegacy.put("LEASH", Material.LEAD);
renamedLegacy.put("COMMAND_MINECART", Material.COMMAND_BLOCK_MINECART);
renamedLegacy.put("CHORUS_FRUIT_POPPED", Material.POPPED_CHORUS_FRUIT);
renamedLegacy.put("DRAGONS_BREATH", Material.DRAGON_BREATH);
renamedLegacy.put("BOAT_SPRUCE", Material.SPRUCE_BOAT);
renamedLegacy.put("BOAT_BIRCH", Material.BIRCH_BOAT);
renamedLegacy.put("BOAT_JUNGLE", Material.JUNGLE_BOAT);
renamedLegacy.put("BOAT_ACACIA", Material.ACACIA_BOAT);
renamedLegacy.put("BOAT_DARK_OAK", Material.DARK_OAK_BOAT);
renamedLegacy.put("TOTEM", Material.TOTEM_OF_UNDYING);
renamedLegacy.put("GOLD_RECORD", Material.MUSIC_DISC_13);
renamedLegacy.put("GREEN_RECORD", Material.MUSIC_DISC_CAT);
renamedLegacy.put("RECORD_3", Material.MUSIC_DISC_BLOCKS);
renamedLegacy.put("RECORD_4", Material.MUSIC_DISC_CHIRP);
renamedLegacy.put("RECORD_5", Material.MUSIC_DISC_FAR);
renamedLegacy.put("RECORD_6", Material.MUSIC_DISC_MALL);
renamedLegacy.put("RECORD_7", Material.MUSIC_DISC_MELLOHI);
renamedLegacy.put("RECORD_8", Material.MUSIC_DISC_STAL);
renamedLegacy.put("RECORD_9", Material.MUSIC_DISC_STRAD);
renamedLegacy.put("RECORD_10", Material.MUSIC_DISC_WARD);
renamedLegacy.put("RECORD_11", Material.MUSIC_DISC_11);
renamedLegacy.put("RECORD_12", Material.MUSIC_DISC_WAIT);
}
Material getMaterial(String material);
Material getDye(int colorCode);
ItemStack setSkullOwner(String player);
private static final Reflection.Field<?> scoreboardName = Reflection.getField(ClientboundSetObjectivePacket.class, Reflection.getClass("net.minecraft.network.chat.Component"), 0);
public void setScoreboardTitle(Object packet, String title) {
scoreboardName.set(packet, ChatWrapper.impl.stringToChatComponent(title));
}
Object getPose(EntityPose pose);
void setNamedSpawnPacketDataWatcher(Object packet);
Object formatDisplayName(String displayName);
private static final Class<?> scoreActionEnum = Core.getVersion() < 21 ? Reflection.getClass("net.minecraft.server.ServerScoreboard$Method") : null;
private static final Reflection.Field<?> scoreAction = Core.getVersion() < 21 ? Reflection.getField(ClientboundSetScorePacket.class, scoreActionEnum, 0) : null;
private static final Object scoreActionChange = Core.getVersion() < 21 ? scoreActionEnum.getEnumConstants()[0] : null;
void setSpawnPacketType(Object packet, EntityType type);
public void setScoreAction(Object packet) {
scoreAction.set(packet, scoreActionChange);
}
int getViewDistance(Player player);
public Material getMaterial(String material) {
try{
return Material.valueOf(material);
}catch(IllegalArgumentException e){
return renamedLegacy.get(material);
}
}
void syncSave(World world);
public Material getDye(int colorCode) {
switch(colorCode){
case 1:
return Material.RED_DYE;
case 2:
return Material.GREEN_DYE;
case 3:
return Material.BROWN_DYE;
case 4:
return Material.LAPIS_LAZULI;
case 5:
return Material.PURPLE_DYE;
case 6:
return Material.CYAN_DYE;
case 7:
return Material.LIGHT_GRAY_DYE;
case 8:
return Material.GRAY_DYE;
case 9:
return Material.PINK_DYE;
case 10:
return Material.LIME_DYE;
case 11:
return Material.YELLOW_DYE;
case 12:
return Material.LIGHT_BLUE_DYE;
case 13:
return Material.MAGENTA_DYE;
case 14:
return Material.ORANGE_DYE;
case 15:
return Material.WHITE_DYE;
default:
return Material.BLACK_DYE;
}
}
public ItemStack setSkullOwner(String player) {
ItemStack head = new ItemStack(Material.PLAYER_HEAD, 1);
head.editMeta(SkullMeta.class, skullMeta -> {
try {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(player.startsWith(".") ? player.substring(1) : player);
PlayerProfile playerProfile = offlinePlayer.getPlayerProfile();
playerProfile.complete();
skullMeta.setPlayerProfile(playerProfile);
} catch (Exception e) {
// Ignore
}
});
return head;
}
protected static final Class<?> entityPose = Reflection.getClass("net.minecraft.world.entity.Pose");
protected static final Object shooting = entityPose.getEnumConstants()[16];
protected static final Object standing = entityPose.getEnumConstants()[0];
protected static final Object swimming = entityPose.getEnumConstants()[3];
protected static final Object sneaking = entityPose.getEnumConstants()[5];
public Object getPose(FlatteningWrapper.EntityPose pose) {
switch (pose) {
case SHOOTING:
return shooting;
case SNEAKING:
return sneaking;
case SWIMMING:
return swimming;
case NORMAL:
default:
return standing;
}
}
public void setNamedSpawnPacketDataWatcher(Object packet) {
// field not present
}
public Object formatDisplayName(String displayName) {
return displayName != null ? Optional.of(ChatWrapper.impl.stringToChatComponent(displayName)) : Optional.empty();
}
private static final Class<?> registryBlocks = Reflection.getClass("net.minecraft.core.DefaultedRegistry");
private static final Class<?> entityTypes = Reflection.getClass("net.minecraft.world.entity.EntityType");
private static final Object entityTypesRegistry = Reflection.getField(Reflection.getClass(Core.getVersion() > 18 ? "net.minecraft.core.registries.BuiltInRegistries" : "net.minecraft.core.IRegistry"), registryBlocks, 0, entityTypes).get(null);
private static final Reflection.Method get = Reflection.getMethod(registryBlocks, null, Reflection.getClass("net.minecraft.resources.ResourceLocation"));
private static final Reflection.Field<?> spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, entityTypes, 0);
private static final Reflection.Field<?> spawnLivingType = Core.getVersion() > 18 ? spawnType : Reflection.getField(ProtocolWrapper.spawnLivingPacket, int.class, 1);
private static final Reflection.Method toMinecraft = Reflection.getMethod("org.bukkit.craftbukkit.util.CraftNamespacedKey", "toMinecraft", NamespacedKey.class);
private static final Map<EntityType, Object> types = new HashMap<>();
static {
types.put(EntityType.ARMOR_STAND, 1);
}
public void setSpawnPacketType(Object packet, EntityType type) {
if(type.isAlive()) {
spawnLivingType.set(packet, Core.getVersion() > 18 ? get.invoke(entityTypesRegistry, toMinecraft.invoke(null, type.getKey())) : types.get(type));
} else {
spawnType.set(packet, get.invoke(entityTypesRegistry, toMinecraft.invoke(null, type.getKey())));
}
}
public int getViewDistance(Player player) {
return player.getClientViewDistance();
}
private static final Reflection.Method getHandle = Reflection.getMethod("org.bukkit.craftbukkit.CraftWorld", "getHandle");
private static final Reflection.Method save = Reflection.getMethod("net.minecraft.server.level.ServerLevel", null, Reflection.getClass("net.minecraft.util.ProgressListener"), boolean.class, boolean.class);
public void syncSave(World world) {
save.invoke(getHandle.invoke(world), null, true, false);
}
public enum EntityPose {
@@ -19,8 +19,17 @@
package de.steamwar.core;
import de.steamwar.sql.SteamwarUser;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLocaleChangeEvent;
public interface LocaleChangeWrapper extends Listener {
LocaleChangeWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
import java.util.Locale;
public class LocaleChangeWrapper implements Listener {
@EventHandler
private void onLocale(PlayerLocaleChangeEvent event) {
SteamwarUser.get(event.getPlayer().getUniqueId()).setLocale(Locale.forLanguageTag(event.getLocale()), false);
}
}
@@ -19,37 +19,50 @@
package de.steamwar.core;
import de.steamwar.Reflection;
import com.mojang.authlib.GameProfile;
import com.mojang.datafixers.util.Pair;
import de.steamwar.Reflection;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameType;
import org.bukkit.GameMode;
import java.util.function.LongSupplier;
import java.util.Collections;
import java.util.EnumSet;
public interface ProtocolWrapper {
public class ProtocolWrapper {
Class<?> itemStack = Reflection.getClass("net.minecraft.world.item.ItemStack");
Class<?> spawnPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundAddEntityPacket");
Class<?> spawnLivingPacket = Core.getVersion() > 18 ? ProtocolWrapper.spawnPacket : Reflection.getClass("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving");
Class<?> equipmentPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket");
public static final Class<?> itemStack = Reflection.getClass("net.minecraft.world.item.ItemStack");
public static final Class<?> spawnPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundAddEntityPacket");
public static final Class<?> spawnLivingPacket = Core.getVersion() > 18 ? ProtocolWrapper.spawnPacket : Reflection.getClass("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving");
public static final Class<?> equipmentPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket");
Class<?> enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "net.minecraft.world.level.GameType" : "net.minecraft.WorldSettings$EnumGamemode");
Reflection.Method getGameModeById = Reflection.getTypedMethod(enumGamemode, null, enumGamemode, int.class);
public static final Class<?> enumGamemode = Reflection.getClass(Core.getVersion() > 9 ? "net.minecraft.world.level.GameType" : "net.minecraft.WorldSettings$EnumGamemode");
public static final Reflection.Method getGameModeById = Reflection.getTypedMethod(enumGamemode, null, enumGamemode, int.class);
// 0: hand, 1: offhand, 2: feet, 3: legs, 4: chest, 5: head
Object[] itemSlots = Core.getVersion() > 8 ? Reflection.getClass("net.minecraft.world.entity.EnumItemSlot").getEnumConstants() : new Integer[]{0, 0, 1, 2, 3, 4};
public static final Object[] itemSlots = Core.getVersion() > 8 ? Reflection.getClass("net.minecraft.world.entity.EnumItemSlot").getEnumConstants() : new Integer[]{0, 0, 1, 2, 3, 4};
ProtocolWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
public static final ProtocolWrapper impl = new ProtocolWrapper();
void setEquipmentPacketStack(Object packet, Object slot, Object stack);
Object playerInfoPacketConstructor(PlayerInfoAction action, GameProfile profile, GameMode mode);
default void initTPSWarp(LongSupplier longSupplier) {
Class<?> systemUtils = Reflection.getClass("net.minecraft.Util");
Reflection.getField(systemUtils, LongSupplier.class, 0).set(systemUtils, (LongSupplier) () -> System.nanoTime() + longSupplier.getAsLong());
public void setEquipmentPacketStack(Object packet, Object slot, Object stack) {
ClientboundSetEquipmentPacket setEquipmentPacket = (ClientboundSetEquipmentPacket) packet;
setEquipmentPacket.getSlots().add(Pair.of((EquipmentSlot) slot, (ItemStack) stack));
}
enum PlayerInfoAction {
public Object playerInfoPacketConstructor(PlayerInfoAction action, GameProfile profile, GameMode mode) {
if(action == PlayerInfoAction.REMOVE)
return new ClientboundPlayerInfoRemovePacket(Collections.singletonList(profile.getId()));
return new ClientboundPlayerInfoUpdatePacket(action == PlayerInfoAction.ADD ?
EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE) : EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE),
Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.Entry(profile.getId(), profile, false, 0, GameType.byId(mode.getValue()), null, false, 0, null)));
}
public enum PlayerInfoAction {
ADD,
GAMEMODE,
REMOVE
@@ -19,8 +19,15 @@
package de.steamwar.core;
import de.steamwar.linkage.Linked;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerRecipeDiscoverEvent;
public interface RecipeDiscoverWrapper extends Listener {
RecipeDiscoverWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
@Linked
public class RecipeDiscoverWrapper implements Listener {
@EventHandler
public void onRecipeDiscover(PlayerRecipeDiscoverEvent e) {
e.setCancelled(true);
}
}
@@ -1,52 +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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitTask;
@UtilityClass
public class TPSWarpUtils {
private static long nanoOffset = 0;
private static long nanoDOffset = 0;
private static BukkitTask bukkitTask = null;
static {
ProtocolWrapper.impl.initTPSWarp(() -> nanoOffset);
}
public static void warp(double tps) {
double d = 50 - (50 / (tps / 20.0));
nanoDOffset = Math.max(0, (long) (d * 1000000));
if (nanoDOffset == 0) {
if (bukkitTask == null) return;
bukkitTask.cancel();
bukkitTask = null;
} else if (bukkitTask == null) {
bukkitTask = Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> nanoOffset += nanoDOffset, 1, 1);
}
}
public static boolean isWarping() {
return nanoDOffset > 0;
}
}
@@ -21,8 +21,10 @@ package de.steamwar.core;
import org.bukkit.Particle;
public interface TrickyParticleWrapper {
public TrickyParticleWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
public class TrickyParticleWrapper {
public static final TrickyParticleWrapper impl = new TrickyParticleWrapper();
Particle getVillagerHappy();
public Particle getVillagerHappy() {
return Particle.HAPPY_VILLAGER;
}
}
@@ -24,14 +24,22 @@ import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.EntityType;
public interface TrickyTrialsWrapper {
TrickyTrialsWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
public class TrickyTrialsWrapper {
public static final TrickyTrialsWrapper impl = new TrickyTrialsWrapper();
EntityType getTntEntityType();
public EntityType getTntEntityType() {
return EntityType.TNT;
}
Enchantment getUnbreakingEnchantment();
public Enchantment getUnbreakingEnchantment() {
return Enchantment.UNBREAKING;
}
Material getTurtleScute();
public Material getTurtleScute() {
return Material.TURTLE_SCUTE;
}
String getValue(Property property);
public String getValue(Property property) {
return property.value();
}
}
@@ -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
@@ -23,6 +23,7 @@ import org.bukkit.plugin.Plugin;
import java.lang.reflect.InvocationTargetException;
@Deprecated
public class VersionDependent {
private VersionDependent() {}
@@ -0,0 +1,79 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public class WorldEditRendererFallback {
private static final int VIEW_DISTANCE = 64;
private static final int SQ_VIEW_DISTANCE = VIEW_DISTANCE * VIEW_DISTANCE;
private static final double STEP_SIZE = 0.5;
private static final Vector ONES = new Vector(1, 1, 1);
private static final Vector STEPS = new Vector(STEP_SIZE, STEP_SIZE, STEP_SIZE);
public void draw(Player player, boolean scheduled, boolean clipboard, Vector min, Vector max) {
if (!scheduled) return;
max = max.clone().add(ONES);
drawLine(player, clipboard, new Vector(min.getX(), min.getY(), min.getZ()), new Vector(max.getX(), min.getY(), min.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), max.getY(), min.getZ()), new Vector(max.getX(), max.getY(), min.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), min.getY(), max.getZ()), new Vector(max.getX(), min.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), max.getY(), max.getZ()), new Vector(max.getX(), max.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), min.getY(), min.getZ()), new Vector(min.getX(), max.getY(), min.getZ()));
drawLine(player, clipboard, new Vector(max.getX(), min.getY(), min.getZ()), new Vector(max.getX(), max.getY(), min.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), min.getY(), max.getZ()), new Vector(min.getX(), max.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(max.getX(), min.getY(), max.getZ()), new Vector(max.getX(), max.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), min.getY(), min.getZ()), new Vector(min.getX(), min.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(max.getX(), min.getY(), min.getZ()), new Vector(max.getX(), min.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(min.getX(), max.getY(), min.getZ()), new Vector(min.getX(), max.getY(), max.getZ()));
drawLine(player, clipboard, new Vector(max.getX(), max.getY(), min.getZ()), new Vector(max.getX(), max.getY(), max.getZ()));
}
private void drawLine(Player player, boolean clipboard, Vector min, Vector max) {
Particle particle;
if (clipboard) {
particle = TrickyParticleWrapper.impl.getVillagerHappy();
} else {
particle = Particle.DRAGON_BREATH;
}
Vector stepSize = max.clone().subtract(min).normalize().multiply(STEPS);
while (min.getX() <= max.getX() && min.getY() <= max.getY() && min.getZ() <= max.getZ()) {
Location location = player.getLocation();
double dx = min.getX() - location.getX();
double dy = min.getY() - location.getY();
double dz = min.getZ() - location.getZ();
if (dx * dx + dy * dy + dz * dz > SQ_VIEW_DISTANCE) {
min.add(stepSize);
continue;
}
player.spawnParticle(particle, min.getX(), min.getY(), min.getZ(), 1, 0.0, 0.0, 0.0, 0.0);
min.add(stepSize);
}
}
}
@@ -19,12 +19,19 @@
package de.steamwar.core;
import de.steamwar.entity.CWireframe;
import de.steamwar.entity.REntityServer;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public interface WorldEditRendererWrapper {
WorldEditRendererWrapper fallback = VersionDependent.getVersionImpl(Core.getInstance(), 9);
WorldEditRendererWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
import java.util.HashMap;
import java.util.Map;
public class WorldEditRendererWrapper {
public static final WorldEditRendererFallback fallback = new WorldEditRendererFallback();
public static final WorldEditRendererWrapper impl = new WorldEditRendererWrapper();
static void safeDraw(Player player, boolean scheduled, boolean clipboard, Vector pos1, Vector pos2) {
if (PlayerVersion.isBedrock(player) || PlayerVersion.getVersion(player) < 20) {
@@ -34,11 +41,83 @@ public interface WorldEditRendererWrapper {
}
}
void draw(Player player, boolean scheduled, boolean clipboard, Vector pos1, Vector pos2);
private static final class BoxPair {
private CWireframe regionBox;
private CWireframe clipboardBox;
void tick(Player player);
public CWireframe get(boolean clipboard) {
if (clipboard) {
return clipboardBox;
} else {
return regionBox;
}
}
void hide(Player player, boolean clipboard, boolean hide);
public void set(boolean clipboard, CWireframe box) {
if (clipboard) {
this.clipboardBox = box;
} else {
this.regionBox = box;
}
}
void remove(Player player);
public void die() {
if (clipboardBox != null) {
clipboardBox.die();
}
if (regionBox != null) {
regionBox.die();
}
}
}
private static final Map<Player, REntityServer> servers = new HashMap<>();
private static final Map<Player, BoxPair> boxes = new HashMap<>();
public void draw(Player player, boolean scheduled, boolean clipboard, Vector pos1, Vector pos2) {
REntityServer server = servers.computeIfAbsent(player, __ -> {
REntityServer entityServer = new REntityServer();
entityServer.addPlayer(player);
return entityServer;
});
WorldEditRendererCUIEditor.Type type = clipboard ? WorldEditRendererCUIEditor.Type.CLIPBOARD : WorldEditRendererCUIEditor.Type.SELECTION;
float width = type.getWidth(player).value;
Material material = type.getMaterial(player);
if (material == Material.BARRIER) {
hide(player, clipboard, true);
return;
}
BlockData block = material.createBlockData();
BoxPair boxPair = boxes.computeIfAbsent(player, __ -> new BoxPair());
CWireframe box = boxPair.get(clipboard);
if (box == null) {
box = new CWireframe(server);
boxPair.set(clipboard, box);
}
box.setPos1And2(pos1.toLocation(player.getWorld()), pos2.toLocation(player.getWorld()));
box.setWidth(width);
box.setBlock(block);
}
public void tick(Player player) {
REntityServer server = servers.get(player);
if (server != null) server.tick();
}
public void hide(Player player, boolean clipboard, boolean hide) {
BoxPair boxPair = boxes.get(player);
if (boxPair == null) return;
CWireframe box = boxPair.get(clipboard);
if (box == null) return;
box.hide(hide);
}
public void remove(Player player) {
BoxPair boxPair = boxes.remove(player);
if (boxPair != null) boxPair.die();
REntityServer server = servers.remove(player);
if (server != null) server.close();
}
}
@@ -19,9 +19,18 @@
package de.steamwar.core;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV2;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV3;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.worldedit.EmptyClipboardException;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.*;
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV1Reader;
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV2Reader;
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV3Reader;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder;
@@ -30,25 +39,140 @@ import de.steamwar.sql.NodeData;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import org.enginehub.linbus.stream.LinBinaryIO;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.logging.Level;
public interface WorldEditWrapper {
WorldEditWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
public class WorldEditWrapper {
public static final WorldEditWrapper impl = new WorldEditWrapper();
InputStream getPlayerClipboard(Player player);
public InputStream getPlayerClipboard(Player player) {
return WorldEditWrapper.getPlayerClipboard(player, (outputStream, clipboard, clipboardHolder) -> {
ClipboardWriter writer = BuiltInClipboardFormat.FAST_V3.getWriter(outputStream);
writer.write(clipboard);
writer.close();
});
}
void setPlayerClipboard(Player player, Clipboard clipboard);
Clipboard getClipboard(NodeData data) throws IOException;
Clipboard getClipboard(InputStream inputStream) throws IOException;
public void setPlayerClipboard(Player player, Clipboard clipboard) {
Actor actor = WorldEditWrapper.getWorldEditPlugin().wrapCommandSender(player);
WorldEditWrapper.getWorldEditPlugin().getWorldEdit().getSessionManager().get(actor).setClipboard(new ClipboardHolder(clipboard));
}
Vector getOrigin(Clipboard clipboard);
Vector getMinimum(Region region);
Vector getMaximum(Region region);
Vector applyTransform(Vector vector, Transform transform);
public Clipboard getClipboard(NodeData data) throws IOException {
ResetableInputStream is = new ResetableInputStream(data.schemData(false));
for (ClipboardFormat clipboardFormat : ClipboardFormats.getAll()) {
FilterInputStream fis = new FilterInputStream(is) {
@Override
public void close() throws IOException {
// Ignore close call!
}
};
boolean canBeRead = clipboardFormat.isFormat(fis);
is.reset();
if (!canBeRead) continue;
return clipboardFormat.load(is);
}
throw new IOException("No clipboard found");
}
NodeData.SchematicFormat getNativeFormat();
private static final Function<InputStream, ClipboardReader> FastV3 = FastSchematicReaderV3::new;
@SuppressWarnings("removal")
private static final Function<InputStream, ClipboardReader> FastV2 = inputStream -> new FastSchematicReaderV2(new NBTInputStream(inputStream));
@SuppressWarnings("removal")
private static final Function<InputStream, ClipboardReader> McEdit = inputStream -> new MCEditSchematicReader(new NBTInputStream(inputStream));
private static final Function<InputStream, ClipboardReader> SpongeV3 = inputStream -> new SpongeSchematicV3Reader(LinBinaryIO.read(new DataInputStream(inputStream)));
private static final Function<InputStream, ClipboardReader> SpongeV2 = inputStream -> new SpongeSchematicV2Reader(LinBinaryIO.read(new DataInputStream(inputStream)));
private static final Function<InputStream, ClipboardReader> SpongeV1 = inputStream -> new SpongeSchematicV1Reader(LinBinaryIO.read(new DataInputStream(inputStream)));
private static final Function<InputStream, ClipboardReader>[] READERS = new Function[]{
FastV3,
FastV2,
SpongeV3,
SpongeV2,
SpongeV1,
McEdit
};
public Clipboard getClipboard(InputStream inputStream) throws IOException {
ResetableInputStream is = new ResetableInputStream(inputStream);
for (Function<InputStream, ClipboardReader> reader : READERS) {
FilterInputStream fis = new FilterInputStream(is) {
@Override
public void close() throws IOException {
// Ignore close call!
}
};
try {
return reader.apply(fis).read();
} catch (Exception e) {
is.reset();
}
}
is.close();
throw new IOException("No clipboard found");
}
private static class ResetableInputStream extends InputStream {
private InputStream inputStream;
private int pointer = 0;
private List<Integer> list = new ArrayList<>();
public ResetableInputStream(InputStream in) {
this.inputStream = in;
}
@Override
public int read() throws IOException {
if (pointer >= list.size()) {
int data = inputStream.read();
list.add(data);
pointer++;
return data;
}
int data = list.get(pointer);
pointer++;
return data;
}
@Override
public void reset() throws IOException {
pointer = 0;
}
@Override
public void close() throws IOException {
list.clear();
pointer = -1;
}
}
public org.bukkit.util.Vector getOrigin(Clipboard clipboard) {
return new org.bukkit.util.Vector(clipboard.getOrigin().x(), clipboard.getOrigin().y(), clipboard.getOrigin().z());
}
public Vector getMinimum(Region region) {
return new Vector(region.getMinimumPoint().x(), region.getMinimumPoint().y(), region.getMinimumPoint().z());
}
public Vector getMaximum(Region region) {
return new Vector(region.getMaximumPoint().x(), region.getMaximumPoint().y(), region.getMaximumPoint().z());
}
public Vector applyTransform(Vector vector, Transform transform) {
Vector3 v = Vector3.at(vector.getX(), vector.getY(), vector.getZ());
v = transform.apply(v);
return new org.bukkit.util.Vector(v.x(), v.y(), v.z());
}
public NodeData.SchematicFormat getNativeFormat() {
return NodeData.SchematicFormat.SPONGE_V3;
}
static WorldEditPlugin getWorldEditPlugin() {
return (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit");
@@ -19,15 +19,56 @@
package de.steamwar.core;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.linkage.Linked;
import net.minecraft.network.protocol.game.ClientboundLoginPacket;
import net.minecraft.network.protocol.game.CommonPlayerSpawnInfo;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level;
@Linked
public class WorldIdentifier {
private static final IWorldIdentifier impl = VersionDependent.getVersionImpl(Core.getInstance());
private static ResourceKey<Level> resourceKey = null;
private static final Class<?> resourceKeyClass = Reflection.getClass("net.minecraft.resources.ResourceKey");
private static final Class<?> minecraftKeyClass = Reflection.getClass("net.minecraft.resources.MinecraftKey");
private static final Reflection.Constructor resourceKeyConstructor = Reflection.getConstructor(resourceKeyClass, minecraftKeyClass, minecraftKeyClass);
private static final Reflection.Constructor minecraftKeyConstructor = Reflection.getConstructor(minecraftKeyClass, String.class, String.class);
public static void set(String name) {
impl.setResourceKey(name);
resourceKey = (ResourceKey<Level>) resourceKeyConstructor.invoke(minecraftKeyConstructor.invoke("minecraft", "dimension"), minecraftKeyConstructor.invoke("steamwar", name));
}
protected interface IWorldIdentifier {
void setResourceKey(String name);
public WorldIdentifier() {
TinyProtocol.instance.addFilter(ClientboundLoginPacket.class, (player, o) -> {
if (resourceKey == null) return o;
ClientboundLoginPacket packet = (ClientboundLoginPacket) o;
return new ClientboundLoginPacket(packet.playerId(),
packet.hardcore(),
packet.levels(),
packet.maxPlayers(),
packet.chunkRadius(),
packet.simulationDistance(),
packet.reducedDebugInfo(),
packet.showDeathScreen(),
packet.doLimitedCrafting(),
new CommonPlayerSpawnInfo(
packet.commonPlayerSpawnInfo().dimensionType(),
resourceKey,
packet.commonPlayerSpawnInfo().seed(),
packet.commonPlayerSpawnInfo().gameType(),
packet.commonPlayerSpawnInfo().previousGameType(),
packet.commonPlayerSpawnInfo().isDebug(),
packet.commonPlayerSpawnInfo().isFlat(),
packet.commonPlayerSpawnInfo().lastDeathLocation(),
packet.commonPlayerSpawnInfo().portalCooldown(),
packet.commonPlayerSpawnInfo().seaLevel()
),
packet.enforcesSecureChat()
);
});
}
}
@@ -19,12 +19,56 @@
package de.steamwar.core.authlib;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.GameProfileRepository;
import de.steamwar.core.Core;
import de.steamwar.core.VersionDependent;
import com.mojang.authlib.ProfileLookupCallback;
import de.steamwar.Reflection;
import de.steamwar.sql.SteamwarUser;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.Services;
public abstract class SteamwarGameProfileRepository implements GameProfileRepository {
public static final SteamwarGameProfileRepository impl = VersionDependent.getVersionImpl(Core.getInstance());
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public abstract void inject();
public class SteamwarGameProfileRepository implements GameProfileRepository {
public static final SteamwarGameProfileRepository impl = new SteamwarGameProfileRepository();
private static final GameProfileRepository fallback;
private static final Reflection.Field<Services> field;
private static final Services current;
static {
Class<?> clazz = MinecraftServer.getServer().getClass();
field = Reflection.getField(clazz, Services.class, 0);
current = field.get(MinecraftServer.getServer());
fallback = current.profileRepository();
}
@Override
public void findProfilesByNames(String[] strings, ProfileLookupCallback profileLookupCallback) {
List<String> unknownNames = new ArrayList<>();
for (String name:strings) {
SteamwarUser user = SteamwarUser.get(name);
if(user == null) {
unknownNames.add(name);
continue;
}
profileLookupCallback.onProfileLookupSucceeded(new GameProfile(user.getUUID(), user.getUserName()));
}
if(!unknownNames.isEmpty()) {
fallback.findProfilesByNames(unknownNames.toArray(new String[0]), profileLookupCallback);
}
}
@Override
public Optional<GameProfile> findProfileByName(String s) {
return fallback.findProfileByName(s);
}
public void inject() {
Services newServices = new Services(current.sessionService(), current.servicesKeySet(), this, current.profileCache(), current.paperConfigurations());
field.set(MinecraftServer.getServer(), newServices);
}
}
@@ -19,12 +19,35 @@
package de.steamwar.entity;
import de.steamwar.core.Core;
import de.steamwar.core.VersionDependent;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.PositionMoveRotation;
import net.minecraft.world.phys.Vec3;
public interface PacketConstructor {
PacketConstructor impl = VersionDependent.getVersionImpl(Core.getInstance());
import java.util.Collections;
Object teleportPacket(int entityId, double x, double y, double z, float yaw, float pitch);
Object createRPlayerSpawn(RPlayer player);
public class PacketConstructor {
public static final PacketConstructor impl = new PacketConstructor();
public Object teleportPacket(int entityId, double x, double y, double z, float yaw, float pitch) {
PositionMoveRotation rot = new PositionMoveRotation(new Vec3(x, y, z), Vec3.ZERO, pitch, yaw);
return new ClientboundTeleportEntityPacket(entityId, rot, Collections.emptySet(), false);
}
public Object createRPlayerSpawn(RPlayer player) {
return new ClientboundAddEntityPacket(
player.entityId,
player.uuid,
player.x,
player.y,
player.z,
player.yaw,
player.pitch,
EntityType.PLAYER,
0,
Vec3.ZERO,
player.headYaw
);
}
}
@@ -21,11 +21,54 @@ package de.steamwar.scoreboard;
import de.steamwar.core.Core;
import de.steamwar.core.VersionDependent;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.Scoreboard;
public interface SWScoreboard {
public static final SWScoreboard impl = VersionDependent.getVersionImpl(Core.getInstance());
import java.util.HashMap;
import java.util.Map;
boolean createScoreboard(Player player, ScoreboardCallback callback);
void removeScoreboard(Player player);
public class SWScoreboard {
public static final SWScoreboard impl = new SWScoreboard();
private static final HashMap<Player, ScoreboardCallback> playerBoards = new HashMap<>();
private static final String SIDEBAR = "sw-sidebar";
static {
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> {
for(Map.Entry<Player, ScoreboardCallback> scoreboard : playerBoards.entrySet()) {
render(scoreboard.getKey(), scoreboard.getValue());
}
}, 10, 5);
}
private static void render(Player player, ScoreboardCallback callback) {
if (player.getScoreboard().getObjective(SIDEBAR) != null) {
player.getScoreboard().getObjective(SIDEBAR).unregister();
}
Objective objective = player.getScoreboard().registerNewObjective(SIDEBAR, "dummy", Component.text(callback.getTitle()));
objective.setAutoUpdateDisplay(true);
objective.setDisplaySlot(DisplaySlot.SIDEBAR);
callback.getData().forEach((text, score) -> objective.getScore(text).setScore(score));
}
public boolean createScoreboard(Player player, ScoreboardCallback callback) {
Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
player.setScoreboard(scoreboard);
render(player, callback);
playerBoards.put(player, callback);
return true;
}
public void removeScoreboard(Player player) {
player.getScoreboard().getObjective(SIDEBAR).unregister();
playerBoards.remove(player);
}
}
@@ -19,19 +19,55 @@
package de.steamwar.techhider;
import com.google.common.collect.ImmutableList;
import de.steamwar.Reflection;
import de.steamwar.core.Core;
import de.steamwar.core.VersionDependent;
import org.bukkit.Material;
import java.util.HashSet;
import java.util.Set;
public interface BlockIds {
BlockIds impl = VersionDependent.getVersionImpl(Core.getInstance());
public class BlockIds {
public static final BlockIds impl = new BlockIds();
Reflection.Method getCombinedId = Reflection.getTypedMethod(TechHider.block, null, int.class, TechHider.iBlockData);
int getCombinedId(Object iBlockData);
int materialToId(Material material);
Set<Integer> materialToAllIds(Material material);
private static final Class<?> blockStateList = Reflection.getClass("net.minecraft.world.level.block.state.StateDefinition");
private static final Class<?> fluidTypeFlowing = Reflection.getClass("net.minecraft.world.level.material.FlowingFluid");
private static final Class<?> fluid = Reflection.getClass("net.minecraft.world.level.material.FluidState");
private static final Reflection.Method getBlockData = Reflection.getTypedMethod(TechHider.block, null, TechHider.iBlockData);
public int materialToId(Material material) {
return getCombinedId(getBlockData.invoke(getBlock(material)));
}
private static final Reflection.Method getStates = Reflection.getTypedMethod(TechHider.block, null, blockStateList);
private static final Reflection.Method getStateList = Reflection.getTypedMethod(blockStateList, null, ImmutableList.class);
private static final Object water = Reflection.getTypedMethod(fluidTypeFlowing, null, fluid, boolean.class).invoke(Reflection.getField(Reflection.getClass("net.minecraft.world.level.material.Fluids"), fluidTypeFlowing, 1).get(null), false);
private static final Iterable<?> registryBlockId = (Iterable<?>) Reflection.getField(TechHider.block, Reflection.getClass("net.minecraft.core.IdMapper"), 0).get(null);
private static final Reflection.Method getFluid = Reflection.getTypedMethod(TechHider.iBlockData, null, fluid);
public Set<Integer> materialToAllIds(Material material) {
Set<Integer> ids = new HashSet<>();
for(Object data : (ImmutableList<?>) getStateList.invoke(getStates.invoke(getBlock(material)))) {
ids.add(getCombinedId(data));
}
if(material == Material.WATER) {
for(Object data : registryBlockId) {
if(getFluid.invoke(data) == water) {
ids.add(getCombinedId(data));
}
}
}
return ids;
}
private static final Reflection.Method getBlock = Reflection.getTypedMethod(TechHider.craftMagicNumbers, "getBlock", TechHider.block, Material.class);
private Object getBlock(Material material) {
return getBlock.invoke(null, material);
}
public int getCombinedId(Object blockData) {
return (int) getCombinedId.invoke(null, blockData);
}
}
@@ -19,21 +19,147 @@
package de.steamwar.techhider;
import de.steamwar.core.Core;
import de.steamwar.core.VersionDependent;
import de.steamwar.Reflection;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.Getter;
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.util.SimpleBitStorage;
import net.minecraft.world.level.block.entity.BlockEntityType;
import org.bukkit.entity.Player;
import java.util.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 interface ChunkHider {
ChunkHider impl = VersionDependent.getVersionImpl(Core.getInstance());
public class ChunkHider {
public static final ChunkHider impl = new ChunkHider();
Class<?> mapChunkPacket();
BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider techHider);
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.Field<Integer> chunkXField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0);
private static final Reflection.Field<Integer> chunkZField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1);
private static final Reflection.Field<ClientboundLevelChunkPacketData> chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0);
private static final Reflection.Field<byte[]> dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0);
private static final Reflection.Field<List> tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0);
public BiFunction<Player, Object, Object> 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<String> hiddenBlockEntities = techHider.getHiddenBlockEntities();
tileEntities.set(dataWrapper, ((List<?>)tileEntities.get(dataWrapper)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList()));
ByteBuf in = Unpooled.wrappedBuffer(dataField.get(dataWrapper));
ByteBuf out = Unpooled.buffer(in.readableBytes() + 64);
for(int yOffset = p.getWorld().getMinHeight(); yOffset < p.getWorld().getMaxHeight(); yOffset += 16) {
SectionHider section = new SectionHider(p, techHider, in, out, chunkX, yOffset/16, chunkZ);
section.copyBlockCount();
blocks(section);
biomes(section);
}
if (in.readableBytes() != 0) {
throw new IllegalStateException("ChunkHider21: Incomplete chunk data, " + in.readableBytes() + " bytes left");
}
byte[] data = new byte[out.readableBytes()];
out.readBytes(data);
dataField.set(dataWrapper, data);
chunkData.set(packet, dataWrapper);
return packet;
};
}
public static final Class<?> tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo");
protected static final Reflection.Field<BlockEntityType> entityType = Reflection.getField(tileEntity, BlockEntityType.class, 0);
private static final Class<?> builtInRegestries = Reflection.getClass("net.minecraft.core.registries.BuiltInRegistries");
private static final Class<?> registry = Reflection.getClass("net.minecraft.core.Registry");
private static final Reflection.Field<?> nameField = Reflection.getField(builtInRegestries, "BLOCK_ENTITY_TYPE", registry);
private static final Class<?> resourceLocation = Reflection.getClass("net.minecraft.resources.ResourceLocation");
private static final Reflection.Method getKey = Reflection.getTypedMethod(registry, "getKey", resourceLocation, Object.class);
private static final Reflection.Method getName = Reflection.getTypedMethod(resourceLocation, "getPath", String.class);
protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) {
return !hiddenBlockEntities.contains(getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile))));
}
private void blocks(SectionHider section) {
section.copyBitsPerBlock();
boolean singleValued = section.getBitsPerBlock() == 0;
if (singleValued) {
int value = ProtocolUtils.readVarInt(section.getIn());
ProtocolUtils.writeVarInt(section.getOut(), !section.isSkipSection() && section.getObfuscate().contains(value) ? section.getTarget() : value);
return;
} else 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 {
@@ -19,19 +19,74 @@
package de.steamwar.techhider;
import de.steamwar.core.Core;
import de.steamwar.core.VersionDependent;
import de.steamwar.Reflection;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.SignBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.function.BiFunction;
public interface ProtocolWrapper {
ProtocolWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
public class ProtocolWrapper {
public static final ProtocolWrapper impl = new ProtocolWrapper();
private static final Reflection.Field<SectionPos> multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPos.class, 0);
private static final Reflection.Field<short[]> multiBlockChangePos = Reflection.getField(TechHider.multiBlockChangePacket, short[].class, 0);
private static final Reflection.Field<BlockState[]> multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, BlockState[].class, 0);
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider) {
return (p, packet) -> {
TechHider.LocationEvaluator locationEvaluator = techHider.getLocationEvaluator();
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;
boolean unfilteredTileEntityDataAction(Object packet);
packet = TechHider.multiBlockChangeCloner.apply(packet);
final short[] oldPos = multiBlockChangePos.get(packet);
final BlockState[] oldBlocks = multiBlockChangeBlocks.get(packet);
ArrayList<Short> poss = new ArrayList<>(oldPos.length);
ArrayList<BlockState> blocks = new ArrayList<>(oldPos.length);
for(int i = 0; i < oldPos.length; i++) {
short pos = oldPos[i];
BlockState 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(block) ? (BlockState) techHider.getObfuscationTarget() : block);
break;
default:
break;
}
}
BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider);
if(blocks.isEmpty())
return null;
BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider);
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 BlockState[0]));
return packet;
};
}
private static final Reflection.Field<BlockEntityType> tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, BlockEntityType.class, 0);
private static final BlockEntityType<?> signType = Reflection.getField(BlockEntityType.class, BlockEntityType.class, 0, SignBlockEntity.class).get(null);
public boolean unfilteredTileEntityDataAction(Object packet) {
return tileEntityType.get(packet) != signType;
}
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) {
return null;
}
}