From 786257ad0e5c0af6253a5caafde7191c04f1f822 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 12:22:44 +0200 Subject: [PATCH 01/19] Add initial AccessWidener --- AccessWidener/build.gradle.kts | 45 ++++++ .../java/de/steamwar/AccessWidenerEntry.java | 49 ++++++ .../java/de/steamwar/AccessWidenerParser.java | 104 ++++++++++++ .../de/steamwar/AccessWidenerScanner.java | 145 +++++++++++++++++ .../src/main/java/de/steamwar/Agent.java | 67 ++++++++ .../main/java/de/steamwar/ClassPatcher.java | 63 ++++++++ .../java/de/steamwar/ClassTransformer.java | 98 ++++++++++++ .../src/main/java/de/steamwar/Main.java | 125 +++++++++++++++ .../java/de/steamwar/WideningTransformer.java | 149 ++++++++++++++++++ .../src/bausystem.accesswidener | 3 + .../features/util/NoClipCommand.java | 6 +- BauSystem/build.gradle.kts | 9 ++ .../steamwar/core/AccessWidenerAttacher.java | 117 ++++++++++++++ buildSrc/src/steamwar.devserver.gradle | 1 - settings.gradle.kts | 4 + 15 files changed, 979 insertions(+), 6 deletions(-) create mode 100644 AccessWidener/build.gradle.kts create mode 100644 AccessWidener/src/main/java/de/steamwar/AccessWidenerEntry.java create mode 100644 AccessWidener/src/main/java/de/steamwar/AccessWidenerParser.java create mode 100644 AccessWidener/src/main/java/de/steamwar/AccessWidenerScanner.java create mode 100644 AccessWidener/src/main/java/de/steamwar/Agent.java create mode 100644 AccessWidener/src/main/java/de/steamwar/ClassPatcher.java create mode 100644 AccessWidener/src/main/java/de/steamwar/ClassTransformer.java create mode 100644 AccessWidener/src/main/java/de/steamwar/Main.java create mode 100644 AccessWidener/src/main/java/de/steamwar/WideningTransformer.java create mode 100644 BauSystem/BauSystem_Main/src/bausystem.accesswidener create mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/core/AccessWidenerAttacher.java diff --git a/AccessWidener/build.gradle.kts b/AccessWidener/build.gradle.kts new file mode 100644 index 00000000..87a25b4d --- /dev/null +++ b/AccessWidener/build.gradle.kts @@ -0,0 +1,45 @@ +/* + * 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 . + */ + +plugins { + `java-library` + id("com.github.johnrengelman.shadow") version "8.1.1" +} + +dependencies { + implementation("org.ow2.asm:asm:9.7") + implementation("org.ow2.asm:asm-commons:9.7") +} + +tasks.shadowJar { + manifest { + attributes( + "Manifest-Version" to "1.0", + "Build-Jdk-Spec" to "21", + "Main-Class" to "de.steamwar.Main", + "Premain-Class" to "de.steamwar.Agent", + "Can-Retransform-Classes" to "true", + "Can-Redefine-Classes" to "true", + ) + } +} + +tasks.build { + dependsOn(tasks.shadowJar) +} diff --git a/AccessWidener/src/main/java/de/steamwar/AccessWidenerEntry.java b/AccessWidener/src/main/java/de/steamwar/AccessWidenerEntry.java new file mode 100644 index 00000000..5b5ff9a0 --- /dev/null +++ b/AccessWidener/src/main/java/de/steamwar/AccessWidenerEntry.java @@ -0,0 +1,49 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar; + +/** + * A single parsed line from a .accesswidener file. + *

+ * Examples: + * accessible class net/minecraft/server/level/ServerPlayer + * accessible method net/minecraft/server/level/ServerPlayer getStats ()V + * mutable field net/minecraft/world/entity/Entity id I + * extendable class net/minecraft/world/level/chunk/LevelChunk + */ +public record AccessWidenerEntry( + /** accessible | mutable | extendable (may have "transitive-" prefix) */ + String directive, + /** class | method | field */ + String memberType, + /** Internal class name, e.g. net/minecraft/server/level/ServerPlayer */ + String target, + /** Method/field name, null for class entries */ + String name, + /** Descriptor, null for class entries */ + String descriptor) { + /** + * Returns true if this entry targets the class with the given internal name. + */ + public boolean targets(String internalName) { + return target.equals(internalName); + } +} + diff --git a/AccessWidener/src/main/java/de/steamwar/AccessWidenerParser.java b/AccessWidener/src/main/java/de/steamwar/AccessWidenerParser.java new file mode 100644 index 00000000..d962e0dc --- /dev/null +++ b/AccessWidener/src/main/java/de/steamwar/AccessWidenerParser.java @@ -0,0 +1,104 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * Parses Fabric-compatible .accesswidener files. + *

+ * Supported format: + *

+ *   accessWidener v2 named
+ *
+ *   # comments are supported
+ *   accessible class  net/minecraft/Foo
+ *   accessible method net/minecraft/Foo someMethod ()V
+ *   accessible field  net/minecraft/Foo someField I
+ *   mutable    field  net/minecraft/Foo someField I
+ *   extendable class  net/minecraft/Foo
+ *   extendable method net/minecraft/Foo someMethod ()V
+ *
+ *   # transitive variants (expose widening to dependents)
+ *   transitive-accessible class net/minecraft/Foo
+ * 
+ */ +public final class AccessWidenerParser { + + private AccessWidenerParser() { + } + + public static List parse(InputStream in) throws IOException { + List entries = new ArrayList<>(); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { + + String line; + boolean headerSeen = false; + + while ((line = reader.readLine()) != null) { + // Strip inline comments + int commentIdx = line.indexOf('#'); + if (commentIdx >= 0) line = line.substring(0, commentIdx); + line = line.strip(); + + if (line.isEmpty()) continue; + + if (!headerSeen) { + // First non-blank, non-comment line must be the header + if (!line.startsWith("accessWidener")) { + throw new IOException("Missing accessWidener header, got: " + line); + } + headerSeen = true; + continue; + } + + AccessWidenerEntry entry = parseLine(line); + if (entry != null) entries.add(entry); + } + } + + return entries; + } + + private static AccessWidenerEntry parseLine(String line) { + String[] parts = line.split("\\s+"); + if (parts.length < 3) return null; + + String directive = parts[0]; // accessible / mutable / extendable / transitive-* + String memberType = parts[1]; // class / method / field + String target = parts[2]; // internal class name + + return switch (memberType) { + case "class" -> new AccessWidenerEntry(directive, "class", target, null, null); + case "method", "field" -> { + if (parts.length < 5) yield null; + yield new AccessWidenerEntry(directive, memberType, target, parts[3], parts[4]); + } + default -> null; + }; + } +} \ No newline at end of file diff --git a/AccessWidener/src/main/java/de/steamwar/AccessWidenerScanner.java b/AccessWidener/src/main/java/de/steamwar/AccessWidenerScanner.java new file mode 100644 index 00000000..70d2c0c9 --- /dev/null +++ b/AccessWidener/src/main/java/de/steamwar/AccessWidenerScanner.java @@ -0,0 +1,145 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Field; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.*; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * Scans all plugin ClassLoaders for "plugin.accesswidener" resources + * and returns the parsed entries. + */ +public final class AccessWidenerScanner { + + private static final Logger LOG = Logger.getLogger("AccessWidenerScanner"); + + private AccessWidenerScanner() { + } + + /** + * Scans every ClassLoader visible from the already-loaded classes for + */ + public static List scanAll(Instrumentation inst) { + List allEntries = new ArrayList<>(); + Set seen = Collections.newSetFromMap(new IdentityHashMap<>()); + + for (Class clazz : inst.getAllLoadedClasses()) { + ClassLoader cl = clazz.getClassLoader(); + if (cl == null) continue; // bootstrap — skip + if (!seen.add(cl)) continue; // already processed + if (!isPluginClassLoader(cl)) continue; + + allEntries.addAll(scanClassLoader(cl)); + } + + return allEntries; + } + + /** + * Uses getResources() so it finds ALL matching files on the loader's classpath. + */ + public static List scanClassLoader(ClassLoader cl) { + List entries = new ArrayList<>(); + + try { + for (URL url : findAccessWideners(cl)) { + try (InputStream in = url.openStream()) { + List parsed = AccessWidenerParser.parse(in); + LOG.info("[AccessWidener] Loaded " + parsed.size() + " entries from " + url); + entries.addAll(parsed); + } catch (IOException e) { + LOG.warning("[AccessWidener] Failed to read " + url + ": " + e.getMessage()); + } + } + } catch (IOException e) { + LOG.warning("[AccessWidener] Failed to scan " + cl + ": " + e.getMessage()); + } + + return entries; + } + + public static List findAccessWideners(ClassLoader cl) throws IOException { + List results = new ArrayList<>(); + + // Standard path — works on most Paper versions + if (cl instanceof URLClassLoader urlCl) { + return scanUrls(urlCl.getURLs()); + } + + // Newer Paper — PluginClassLoader stores the jar as a field + try { + // Try common field names used across Paper versions + for (String fieldName : List.of("file", "jarFile", "pluginJar", "source")) { + try { + Field f = cl.getClass().getDeclaredField(fieldName); + f.setAccessible(true); + Object value = f.get(cl); + if (value instanceof File file) { + return scanUrls(new URL[]{ file.toURI().toURL() }); + } + if (value instanceof Path path) { + return scanUrls(new URL[]{ path.toUri().toURL() }); + } + } catch (NoSuchFieldException ignored) {} + } + } catch (Exception e) { + LOG.warning("[AccessWidener] Could not extract JAR path from " + cl + ": " + e.getMessage()); + } + + return results; + } + + private static List scanUrls(URL[] urls) throws IOException { + List results = new ArrayList<>(); + for (URL url : urls) { + if (!url.getPath().endsWith(".jar")) continue; + try (ZipInputStream zip = new ZipInputStream(url.openStream())) { + ZipEntry entry; + while ((entry = zip.getNextEntry()) != null) { + if (entry.getName().endsWith(".accesswidener")) { + results.add(new URL("jar:" + url + "!/" + entry.getName())); + } + } + } + } + return results; + } + + /** + * Returns true if the classloader looks like a Paper plugin classloader. + * Checks by name so we don't need a direct dependency on Paper internals. + */ + public static boolean isPluginClassLoader(ClassLoader cl) { + String name = cl.getClass().getName(); + return name.contains("PluginClassLoader") // legacy Paper / Spigot + || name.contains("PaperPluginLoader") // Paper 1.20.5+ + || name.contains("PaperSimplePluginManager"); // just in case + } +} diff --git a/AccessWidener/src/main/java/de/steamwar/Agent.java b/AccessWidener/src/main/java/de/steamwar/Agent.java new file mode 100644 index 00000000..a786fed8 --- /dev/null +++ b/AccessWidener/src/main/java/de/steamwar/Agent.java @@ -0,0 +1,67 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar; + +import java.lang.instrument.Instrumentation; +import java.util.logging.Logger; + +/** + * Java agent entry point. + * + * Can be used two ways: + * 1. At JVM startup: java -javaagent:paper-access-widener-agent.jar -jar server.jar + * 2. Late attach: from inside a Paper plugin via the Attach API + * + * On attach the agent: + * 1. Scans all existing plugin ClassLoaders for "plugin.accesswidener" resources + * 2. Registers a ClassFileTransformer that: + * a. Applies widening to every class as it loads + * b. Detects new plugin ClassLoaders and scans them automatically + * 3. Retransforms any Minecraft/server classes that are already loaded + */ +public class Agent { + private Agent() { + /* This utility class should not be instantiated */ + } + + private static final Logger LOG = Logger.getLogger("AccessWidenerAgent"); + + // Exposed so tests or other code can inspect the live transformer + static volatile WideningTransformer transformer; + + // -javaagent: startup + public static void premain(String args, Instrumentation inst) { + init(inst); + } + + private static void init(Instrumentation inst) { + LOG.info("[AccessWidener] Agent initialising."); + + WideningTransformer t = new WideningTransformer(inst); + transformer = t; + + // --- Phase 2: register transformer for future class loads --- + // canRetransform=true so we can call retransformClasses() later + inst.addTransformer(t, true); + + LOG.info("[AccessWidener] Agent ready."); + } +} + diff --git a/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java b/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java new file mode 100644 index 00000000..3e640694 --- /dev/null +++ b/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java @@ -0,0 +1,63 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar; + +import org.objectweb.asm.*; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Uses ASM to patch class bytecode according to a list of access widener entries. + * + * Returns {@code null} if the class is not targeted by any entry (no-op signal + * to the caller so it can skip the write). + */ +public class ClassPatcher { + + private final List entries; + + /** Pre-computed set of targeted internal names for fast filtering. */ + private final Set targets; + + public ClassPatcher(List entries) { + this.entries = entries; + this.targets = entries.stream() + .map(AccessWidenerEntry::target) + .collect(Collectors.toSet()); + } + + /** + * Patches {@code classBytes} if {@code internalName} is targeted. + * + * @return patched bytes, or {@code null} if no changes were needed + */ + public byte[] patch(String internalName, byte[] classBytes) { + if (!targets.contains(internalName)) return null; + + ClassReader cr = new ClassReader(classBytes); + // COMPUTE_FRAMES would require the full classpath; we only touch flags so 0 is fine + ClassWriter cw = new ClassWriter(cr, 0); + cr.accept(new ClassTransformer(cw, internalName, entries), 0); + return cw.toByteArray(); + } +} + diff --git a/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java b/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java new file mode 100644 index 00000000..c329b966 --- /dev/null +++ b/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java @@ -0,0 +1,98 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.util.List; + +public class ClassTransformer extends ClassVisitor { + + private final String internalName; + private final List entries; + + public ClassTransformer(ClassVisitor cv, String internalName, List entries) { + super(Opcodes.ASM9, cv); + this.internalName = internalName; + this.entries = entries; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + int newAccess = access; + for (AccessWidenerEntry e : entries) { + if (!e.targets(internalName) || !"class".equals(e.memberType())) continue; + newAccess = applyDirective(e.directive(), newAccess, false); + } + super.visit(version, newAccess, name, signature, superName, interfaces); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + int newAccess = access; + for (AccessWidenerEntry e : entries) { + if (!e.targets(internalName) || !"method".equals(e.memberType())) continue; + if (!name.equals(e.name()) || !descriptor.equals(e.descriptor())) continue; + newAccess = applyDirective(e.directive(), newAccess, false); + } + return super.visitMethod(newAccess, name, descriptor, signature, exceptions); + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + int newAccess = access; + for (AccessWidenerEntry e : entries) { + if (!e.targets(internalName) || !"field".equals(e.memberType())) continue; + if (!name.equals(e.name())) continue; + newAccess = applyDirective(e.directive(), newAccess, true); + } + return super.visitField(newAccess, name, descriptor, signature, value); + } + + /** + * Apply a directive to an access bitmask. + * + * @param directive accessible / mutable / extendable (with optional "transitive-" prefix) + * @param access current access flags + * @param isField true when processing a field (mutable removes final) + */ + private static int applyDirective(String directive, int access, boolean isField) { + // Strip transitive- prefix — the widening itself is the same + String effective = directive.startsWith("transitive-") ? directive.substring("transitive-".length()) : directive; + + return switch (effective) { + case "accessible" -> makePublic(access); + case "extendable" -> makePublic(removeFinal(access)); + case "mutable" -> isField ? removeFinal(access) : access; + default -> access; + }; + } + + private static int makePublic(int access) { + return (access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) | Opcodes.ACC_PUBLIC; + } + + private static int removeFinal(int access) { + return access & ~Opcodes.ACC_FINAL; + } +} diff --git a/AccessWidener/src/main/java/de/steamwar/Main.java b/AccessWidener/src/main/java/de/steamwar/Main.java new file mode 100644 index 00000000..8dc81875 --- /dev/null +++ b/AccessWidener/src/main/java/de/steamwar/Main.java @@ -0,0 +1,125 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Command-line tool that produces a widened copy of a JAR for use as a + * compile-time stub in IntelliJ / Gradle. + * + * Usage: + * java -jar jar-widener.jar [file.accesswidener ...] + * + * The output JAR is identical to the input JAR except that every class + * targeted by the access widener entries has its access flags patched: + * accessible → public + * extendable → public + non-final + * mutable → non-final field + * + * Intended for use as a Gradle task so IntelliJ sees the already-widened + * class when you Ctrl+click NMS code, and javac compiles without complaints. + */ +public class Main { + + public static void main(String[] args) throws Exception { + if (args.length < 2) { + System.err.println("Usage: jar-widener [*.accesswidener ...]"); + System.exit(1); + } + + Path inputJar = Path.of(args[0]); + Path outputJar = Path.of(args[1]); + + if (!Files.exists(inputJar)) { + System.err.println("Input JAR not found: " + inputJar); + System.exit(1); + } + + // --- Collect all access widener entries --- + List entries = new ArrayList<>(); + + if (args.length > 2) { + for (int i = 2; i < args.length; i++) { + Path awFile = Path.of(args[i]); + if (!Files.exists(awFile)) { + System.err.println("Warning: access widener file not found, skipping: " + awFile); + continue; + } + try (InputStream in = Files.newInputStream(awFile)) { + List parsed = AccessWidenerParser.parse(in); + System.out.println("Loaded " + parsed.size() + " entries from " + awFile.getFileName()); + entries.addAll(parsed); + } + } + } + + if (entries.isEmpty()) { + System.out.println("No access widener entries found — copying JAR unchanged."); + Files.createDirectories(outputJar.getParent()); + Files.copy(inputJar, outputJar, StandardCopyOption.REPLACE_EXISTING); + return; + } + + System.out.println("Widening " + inputJar.getFileName() + + " with " + entries.size() + " total entr" + + (entries.size() == 1 ? "y" : "ies") + "..."); + + // --- Copy input → output, transforming .class files in place --- + Files.createDirectories(outputJar.getParent()); + Files.copy(inputJar, outputJar, StandardCopyOption.REPLACE_EXISTING); + + ClassPatcher patcher = new ClassPatcher(entries); + + try (FileSystem fs = FileSystems.newFileSystem(outputJar)) { + // Walk every .class entry in the JAR + try (var stream = Files.walk(fs.getPath("/"))) { + stream.filter(p -> p.toString().endsWith(".class")) + .forEach(classPath -> patchClass(fs, classPath, patcher)); + } + } + + System.out.println("Done. Widened JAR written to " + outputJar); + } + + private static void patchClass(FileSystem fs, Path classPath, ClassPatcher patcher) { + // Derive internal class name from path e.g. /net/minecraft/Foo.class → net/minecraft/Foo + String internalName = classPath.toString() + .replaceFirst("^/", "") + .replace(".class", ""); + + try { + byte[] original = Files.readAllBytes(classPath); + byte[] patched = patcher.patch(internalName, original); + + if (patched != null) { + Files.write(classPath, patched); + System.out.println(" Widened: " + internalName); + } + } catch (IOException e) { + System.err.println(" Warning: failed to patch " + internalName + ": " + e.getMessage()); + } + } +} + diff --git a/AccessWidener/src/main/java/de/steamwar/WideningTransformer.java b/AccessWidener/src/main/java/de/steamwar/WideningTransformer.java new file mode 100644 index 00000000..dd14a9a0 --- /dev/null +++ b/AccessWidener/src/main/java/de/steamwar/WideningTransformer.java @@ -0,0 +1,149 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.lang.instrument.UnmodifiableClassException; +import java.security.ProtectionDomain; +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +/** + * Transforms class bytecode to apply access widening rules. + *

+ * Also monitors for new plugin ClassLoaders appearing (when plugins load after + * the agent attaches) and automatically picks up their .accesswidener files. + */ +public class WideningTransformer implements ClassFileTransformer { + + private static final Logger LOG = Logger.getLogger("WidenerTransformer"); + + private final Instrumentation instrumentation; + + /** + * All entries collected across all plugins. Thread-safe for concurrent plugin loads. + */ + private final List entries = new CopyOnWriteArrayList<>(); + + /** + * ClassLoaders we have already scanned, to avoid re-scanning. + */ + private final Set scannedLoaders = Collections.synchronizedSet(Collections.newSetFromMap(new IdentityHashMap<>())); + + public WideningTransformer(Instrumentation instrumentation) { + this.instrumentation = instrumentation; + } + + /** + * Add entries — used during initial scan before the transformer is registered. + */ + public void addEntries(List newEntries) { + entries.addAll(newEntries); + } + + // ------------------------------------------------------------------------- + // ClassFileTransformer + // ------------------------------------------------------------------------- + + @Override + public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { + + // Check for new plugin classloaders we haven't seen yet + if (loader != null && AccessWidenerScanner.isPluginClassLoader(loader) && scannedLoaders.add(loader)) { + onNewPluginClassLoader(loader); + } + + // No entries for this class — skip transformation entirely + if (className == null) return classfileBuffer; + boolean relevant = entries.stream().anyMatch(e -> e.targets(className)); + if (!relevant) return classfileBuffer; + + // Apply widening via ASM + try { + ClassReader cr = new ClassReader(classfileBuffer); + ClassWriter cw = new ClassWriter(cr, 0); + cr.accept(new ClassTransformer(cw, className, entries), 0); + return cw.toByteArray(); + } catch (Exception e) { + LOG.warning("[AccessWidener] Failed to transform " + className + ": " + e.getMessage()); + return classfileBuffer; + } + } + + // ------------------------------------------------------------------------- + // New ClassLoader detection + // ------------------------------------------------------------------------- + + private void onNewPluginClassLoader(ClassLoader loader) { + List newEntries = AccessWidenerScanner.scanClassLoader(loader); + if (newEntries.isEmpty()) return; + + entries.addAll(newEntries); + LOG.info("[AccessWidener] Picked up " + newEntries.size() + " new entr" + (newEntries.size() == 1 ? "y" : "ies") + " from " + loader); + + // Retransform already-loaded classes that are now targeted + scheduleRetransform(newEntries); + } + + /** + * Retransformation cannot be called from within a transform() call, + * so we dispatch it to a short-lived daemon thread. + */ + private void scheduleRetransform(List newEntries) { + Set targets = newEntries.stream().map(e -> e.target().replace('/', '.')).collect(Collectors.toSet()); + + Thread t = new Thread(() -> retransform(targets), "access-widener-retransform"); + t.setDaemon(true); + t.start(); + } + + private void retransform(Set dotNames) { + List toRetransform = Arrays.stream(instrumentation.getAllLoadedClasses()) + .filter(c -> dotNames.contains(c.getName())) + .filter(instrumentation::isModifiableClass) + .collect(Collectors.toList()); + + if (toRetransform.isEmpty()) return; + + LOG.info("[AccessWidener] Retransforming " + toRetransform.size() + " class(es)."); + List failed = new ArrayList<>(); + + for (Class clazz : toRetransform) { + try { + instrumentation.retransformClasses(clazz); + } catch (UnmodifiableClassException | UnsupportedOperationException e) { + failed.add(clazz.getName()); + } + } + + if (!failed.isEmpty()) { + LOG.warning("[AccessWidener] The following classes were already loaded before the agent" + + " and cannot be retransformed. Add -javaagent: to your start script" + + " to fix this:"); + failed.forEach(name -> LOG.warning(" - " + name)); + } + } +} diff --git a/BauSystem/BauSystem_Main/src/bausystem.accesswidener b/BauSystem/BauSystem_Main/src/bausystem.accesswidener new file mode 100644 index 00000000..7a0f195d --- /dev/null +++ b/BauSystem/BauSystem_Main/src/bausystem.accesswidener @@ -0,0 +1,3 @@ +accessWidener v2 named + +accessible field net/minecraft/server/level/ServerPlayerGameMode gameModeForPlayer Lnet/minecraft/world/level/GameType; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java index 0b10e163..6ead52d4 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java @@ -21,7 +21,6 @@ package de.steamwar.bausystem.features.util; import com.comphenix.tinyprotocol.TinyProtocol; import com.mojang.authlib.GameProfile; -import de.steamwar.Reflection; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.features.tpslimit.TPSUtils; import de.steamwar.bausystem.utils.BauMemberUpdateEvent; @@ -30,7 +29,6 @@ import de.steamwar.core.ProtocolWrapper; import de.steamwar.core.SWPlayer; import de.steamwar.linkage.Linked; import net.minecraft.network.protocol.game.*; -import net.minecraft.server.level.ServerPlayerGameMode; import net.minecraft.world.entity.player.Abilities; import net.minecraft.world.level.GameType; import org.bukkit.Bukkit; @@ -103,10 +101,8 @@ public class NoClipCommand extends SWCommand implements Listener { TinyProtocol.instance.addFilter(ServerboundSetCreativeModeSlotPacket.class, third); } - private static final Reflection.Field playerGameMode = Reflection.getField(ServerPlayerGameMode.class, GameType.class, 0); - private void setInternalGameMode(Player player, GameMode gameMode) { - playerGameMode.set(((CraftPlayer) player).getHandle().gameMode, GameType.byId(gameMode.getValue())); + // ((CraftPlayer) player).getHandle().gameMode.gameModeForPlayer = GameType.byId(gameMode.getValue()); } @Register(help = true) diff --git a/BauSystem/build.gradle.kts b/BauSystem/build.gradle.kts index de9fe1b8..b6180f3f 100644 --- a/BauSystem/build.gradle.kts +++ b/BauSystem/build.gradle.kts @@ -20,15 +20,22 @@ plugins { `java-library` alias(libs.plugins.shadow) + id("io.papermc.paperweight.userdev") version "1.7.1" } tasks.build { finalizedBy(tasks.shadowJar) } +repositories { + maven("https://repo.papermc.io/repository/maven-public/") +} + dependencies { implementation(project(":BauSystem:BauSystem_RegionFixed")) implementation(project(":BauSystem:BauSystem_Main")) + + paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT") } tasks.register("DevBau21") { @@ -37,5 +44,7 @@ tasks.register("DevBau21") { dependsOn(":SpigotCore:shadowJar") dependsOn(":BauSystem:shadowJar") dependsOn(":SchematicSystem:shadowJar") + dependsOn(":AccessWidener:shadowJar") template = "Bau21" + jvmArgs = "-javaagent:/home/yoyonow/Bau21/plugins/AccessWidener.jar=start" } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/AccessWidenerAttacher.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/AccessWidenerAttacher.java new file mode 100644 index 00000000..ccf61c40 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/AccessWidenerAttacher.java @@ -0,0 +1,117 @@ +package de.steamwar.core; + +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.logging.Logger; + +/** + * Public API for Paper plugins that want to attach the access widener agent. + * + *

Usage

+ * Call {@link #ensureAttached(Path)} from your plugin's {@code onLoad()} method + * (not {@code onEnable()} — onLoad fires before classes start loading): + * + *
{@code
+ * public class MyPlugin extends JavaPlugin {
+ *     @Override
+ *     public void onLoad() {
+ *         try {
+ *             AccessWidenerAttacher.ensureAttached(getDataFolder());
+ *         } catch (Exception e) {
+ *             getSLF4JLogger().error("Failed to attach access widener agent", e);
+ *         }
+ *     }
+ * }
+ * }
+ * + *

Resource file

+ * Drop a {@code plugin.accesswidener} file in your plugin's resources: + * + *
+ *   accessWidener v2 named
+ *
+ *   accessible method net/minecraft/server/level/ServerPlayer getStats ()V
+ *   mutable    field  net/minecraft/world/entity/Entity id I
+ * 
+ * + * The agent discovers it automatically via your plugin's ClassLoader. + */ +public final class AccessWidenerAttacher { + + private static final Logger LOG = Logger.getLogger("AccessWidenerAttacher"); + private static final Object LOCK = new Object(); + + private static volatile boolean attached = false; + + private AccessWidenerAttacher() {} + + /** + * Attaches the access widener agent to the running JVM if it has not already + * been attached. Safe to call from multiple plugins — the agent is only + * attached once. + * + * @param agentJar the agentJar + * @throws AccessWidenerException if the agent could not be attached + */ + public static void ensureAttached(Path agentJar) throws AccessWidenerException { + if (attached) return; // fast path — no locking needed after first attach + + synchronized (LOCK) { + if (attached) return; // double-checked + + try { + attachAgent(agentJar); + attached = true; + LOG.info("[AccessWidener] Agent attached successfully."); + } catch (Exception e) { + throw new AccessWidenerException("Failed to attach access widener agent", e); + } + } + } + + /** Returns true if the agent has been successfully attached. */ + public static boolean isAttached() { + return attached; + } + + private static void attachAgent(Path agentJar) throws Exception { + // Verify the Attach API is available before trying + try { + Class.forName("com.sun.tools.attach.VirtualMachine"); + } catch (ClassNotFoundException e) { + throw new AccessWidenerException( + "The JDK Attach API is not available. " + + "Make sure you are running on a JDK (not a JRE) " + + "and add '--add-opens jdk.attach/sun.tools.attach=ALL-UNNAMED' " + + "to your JVM flags if on Java 9+.", e); + } + + String pid = String.valueOf(ProcessHandle.current().pid()); + + // Use reflection so the Attach API is not a hard compile-time dependency + Class vmClass = Class.forName("com.sun.tools.attach.VirtualMachine"); + Method attachMethod = vmClass.getMethod("attach", String.class); + Method loadAgentMethod = vmClass.getMethod("loadAgent", String.class); + Method detachMethod = vmClass.getMethod("detach"); + + Object vm = attachMethod.invoke(null, pid); + try { + loadAgentMethod.invoke(vm, agentJar.toAbsolutePath().toString()); + } finally { + detachMethod.invoke(vm); + } + } + + // ------------------------------------------------------------------------- + // Exception type + // ------------------------------------------------------------------------- + + public static class AccessWidenerException extends Exception { + public AccessWidenerException(String message, Throwable cause) { + super(message, cause); + } + public AccessWidenerException(String message) { + super(message); + } + } +} diff --git a/buildSrc/src/steamwar.devserver.gradle b/buildSrc/src/steamwar.devserver.gradle index 59e6cfbb..af6d224e 100644 --- a/buildSrc/src/steamwar.devserver.gradle +++ b/buildSrc/src/steamwar.devserver.gradle @@ -27,7 +27,6 @@ plugins { class DevServer extends DefaultTask { @Input - @Optional boolean debug = false @Input diff --git a/settings.gradle.kts b/settings.gradle.kts index b84535d4..c3da5d8d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -162,6 +162,10 @@ dependencyResolutionManagement { } } +include( + "AccessWidener" +) + include( "BauSystem", "BauSystem:BauSystem_Main", From 961331f029edfee17c585d83a2b84c7ca50869ec Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 13:19:04 +0200 Subject: [PATCH 02/19] Fix Agent and WideningTransformer and ClassPatcher --- .../de/steamwar/AccessWidenerScanner.java | 145 ------------------ .../src/main/java/de/steamwar/Agent.java | 30 ++-- .../main/java/de/steamwar/ClassPatcher.java | 26 ++-- .../src/main/java/de/steamwar/Utils.java | 60 ++++++++ .../java/de/steamwar/WideningTransformer.java | 116 +------------- BauSystem/build.gradle.kts | 7 - 6 files changed, 105 insertions(+), 279 deletions(-) delete mode 100644 AccessWidener/src/main/java/de/steamwar/AccessWidenerScanner.java create mode 100644 AccessWidener/src/main/java/de/steamwar/Utils.java diff --git a/AccessWidener/src/main/java/de/steamwar/AccessWidenerScanner.java b/AccessWidener/src/main/java/de/steamwar/AccessWidenerScanner.java deleted file mode 100644 index 70d2c0c9..00000000 --- a/AccessWidener/src/main/java/de/steamwar/AccessWidenerScanner.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2026 SteamWar.de-Serverteam - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package de.steamwar; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.lang.instrument.Instrumentation; -import java.lang.reflect.Field; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Path; -import java.util.*; -import java.util.logging.Logger; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -/** - * Scans all plugin ClassLoaders for "plugin.accesswidener" resources - * and returns the parsed entries. - */ -public final class AccessWidenerScanner { - - private static final Logger LOG = Logger.getLogger("AccessWidenerScanner"); - - private AccessWidenerScanner() { - } - - /** - * Scans every ClassLoader visible from the already-loaded classes for - */ - public static List scanAll(Instrumentation inst) { - List allEntries = new ArrayList<>(); - Set seen = Collections.newSetFromMap(new IdentityHashMap<>()); - - for (Class clazz : inst.getAllLoadedClasses()) { - ClassLoader cl = clazz.getClassLoader(); - if (cl == null) continue; // bootstrap — skip - if (!seen.add(cl)) continue; // already processed - if (!isPluginClassLoader(cl)) continue; - - allEntries.addAll(scanClassLoader(cl)); - } - - return allEntries; - } - - /** - * Uses getResources() so it finds ALL matching files on the loader's classpath. - */ - public static List scanClassLoader(ClassLoader cl) { - List entries = new ArrayList<>(); - - try { - for (URL url : findAccessWideners(cl)) { - try (InputStream in = url.openStream()) { - List parsed = AccessWidenerParser.parse(in); - LOG.info("[AccessWidener] Loaded " + parsed.size() + " entries from " + url); - entries.addAll(parsed); - } catch (IOException e) { - LOG.warning("[AccessWidener] Failed to read " + url + ": " + e.getMessage()); - } - } - } catch (IOException e) { - LOG.warning("[AccessWidener] Failed to scan " + cl + ": " + e.getMessage()); - } - - return entries; - } - - public static List findAccessWideners(ClassLoader cl) throws IOException { - List results = new ArrayList<>(); - - // Standard path — works on most Paper versions - if (cl instanceof URLClassLoader urlCl) { - return scanUrls(urlCl.getURLs()); - } - - // Newer Paper — PluginClassLoader stores the jar as a field - try { - // Try common field names used across Paper versions - for (String fieldName : List.of("file", "jarFile", "pluginJar", "source")) { - try { - Field f = cl.getClass().getDeclaredField(fieldName); - f.setAccessible(true); - Object value = f.get(cl); - if (value instanceof File file) { - return scanUrls(new URL[]{ file.toURI().toURL() }); - } - if (value instanceof Path path) { - return scanUrls(new URL[]{ path.toUri().toURL() }); - } - } catch (NoSuchFieldException ignored) {} - } - } catch (Exception e) { - LOG.warning("[AccessWidener] Could not extract JAR path from " + cl + ": " + e.getMessage()); - } - - return results; - } - - private static List scanUrls(URL[] urls) throws IOException { - List results = new ArrayList<>(); - for (URL url : urls) { - if (!url.getPath().endsWith(".jar")) continue; - try (ZipInputStream zip = new ZipInputStream(url.openStream())) { - ZipEntry entry; - while ((entry = zip.getNextEntry()) != null) { - if (entry.getName().endsWith(".accesswidener")) { - results.add(new URL("jar:" + url + "!/" + entry.getName())); - } - } - } - } - return results; - } - - /** - * Returns true if the classloader looks like a Paper plugin classloader. - * Checks by name so we don't need a direct dependency on Paper internals. - */ - public static boolean isPluginClassLoader(ClassLoader cl) { - String name = cl.getClass().getName(); - return name.contains("PluginClassLoader") // legacy Paper / Spigot - || name.contains("PaperPluginLoader") // Paper 1.20.5+ - || name.contains("PaperSimplePluginManager"); // just in case - } -} diff --git a/AccessWidener/src/main/java/de/steamwar/Agent.java b/AccessWidener/src/main/java/de/steamwar/Agent.java index a786fed8..6558508c 100644 --- a/AccessWidener/src/main/java/de/steamwar/Agent.java +++ b/AccessWidener/src/main/java/de/steamwar/Agent.java @@ -19,7 +19,11 @@ package de.steamwar; +import java.io.File; +import java.io.IOException; import java.lang.instrument.Instrumentation; +import java.util.ArrayList; +import java.util.List; import java.util.logging.Logger; /** @@ -38,14 +42,11 @@ import java.util.logging.Logger; */ public class Agent { private Agent() { - /* This utility class should not be instantiated */ + throw new IllegalStateException("Utility class"); } private static final Logger LOG = Logger.getLogger("AccessWidenerAgent"); - // Exposed so tests or other code can inspect the live transformer - static volatile WideningTransformer transformer; - // -javaagent: startup public static void premain(String args, Instrumentation inst) { init(inst); @@ -54,13 +55,22 @@ public class Agent { private static void init(Instrumentation inst) { LOG.info("[AccessWidener] Agent initialising."); - WideningTransformer t = new WideningTransformer(inst); - transformer = t; - - // --- Phase 2: register transformer for future class loads --- - // canRetransform=true so we can call retransformClasses() later - inst.addTransformer(t, true); + List entries = new ArrayList<>(); + File file = new File(new File(".").getAbsoluteFile(), "plugins/"); + File[] files = file.listFiles(); + if (files == null) files = new File[0]; + for (File jarFile : files) { + if (!jarFile.isFile()) continue; + if (!jarFile.getName().endsWith(".jar")) continue; + try { + entries.addAll(Utils.findAndParseAccessWideners(jarFile.toPath())); + } catch (IOException e) { + LOG.warning("Failed to parse access wideners from " + jarFile.getAbsolutePath()); + } + } + LOG.info("[AccessWidener] Loaded " + entries.size() + " access wideners."); + inst.addTransformer(new WideningTransformer(entries), false); LOG.info("[AccessWidener] Agent ready."); } } diff --git a/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java b/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java index 3e640694..a5549762 100644 --- a/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java +++ b/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java @@ -19,10 +19,12 @@ package de.steamwar; -import org.objectweb.asm.*; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; import java.util.List; import java.util.Set; +import java.util.logging.Logger; import java.util.stream.Collectors; /** @@ -33,6 +35,8 @@ import java.util.stream.Collectors; */ public class ClassPatcher { + private static final Logger LOG = Logger.getLogger("ClassPatcher"); + private final List entries; /** Pre-computed set of targeted internal names for fast filtering. */ @@ -46,18 +50,22 @@ public class ClassPatcher { } /** - * Patches {@code classBytes} if {@code internalName} is targeted. + * Patches {@code classBytes} if {@code className} is targeted. * * @return patched bytes, or {@code null} if no changes were needed */ - public byte[] patch(String internalName, byte[] classBytes) { - if (!targets.contains(internalName)) return null; + public byte[] patch(String className, byte[] classBytes) { + if (!targets.contains(className)) return null; - ClassReader cr = new ClassReader(classBytes); - // COMPUTE_FRAMES would require the full classpath; we only touch flags so 0 is fine - ClassWriter cw = new ClassWriter(cr, 0); - cr.accept(new ClassTransformer(cw, internalName, entries), 0); - return cw.toByteArray(); + try { + ClassReader cr = new ClassReader(classBytes); + ClassWriter cw = new ClassWriter(cr, 0); + cr.accept(new ClassTransformer(cw, className, entries), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + return cw.toByteArray(); + } catch (Exception e) { + LOG.warning("[AccessWidener] Failed to transform " + className + ": " + e.getMessage()); + return null; + } } } diff --git a/AccessWidener/src/main/java/de/steamwar/Utils.java b/AccessWidener/src/main/java/de/steamwar/Utils.java new file mode 100644 index 00000000..1a5bc541 --- /dev/null +++ b/AccessWidener/src/main/java/de/steamwar/Utils.java @@ -0,0 +1,60 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2026 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public class Utils { + + private static final Logger LOG = Logger.getLogger("AccessWidenerAgent"); + + private Utils() { + throw new IllegalStateException("Utility class"); + } + + public static List findAndParseAccessWideners(Path jarPath) throws IOException { + List results = new ArrayList<>(); + + try (ZipFile zip = new ZipFile(jarPath.toFile())) { + Enumeration entries = zip.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + if (entry.isDirectory() || !entry.getName().endsWith(".accesswidener")) continue; + + try (InputStream in = zip.getInputStream(entry)) { + results.addAll(AccessWidenerParser.parse(in)); + } catch (IOException e) { + LOG.warning("[AccessWidener] Failed to parse " + entry.getName() + + " in " + jarPath.getFileName() + ": " + e.getMessage()); + } + } + } + + return results; + } +} diff --git a/AccessWidener/src/main/java/de/steamwar/WideningTransformer.java b/AccessWidener/src/main/java/de/steamwar/WideningTransformer.java index dd14a9a0..2215c769 100644 --- a/AccessWidener/src/main/java/de/steamwar/WideningTransformer.java +++ b/AccessWidener/src/main/java/de/steamwar/WideningTransformer.java @@ -19,17 +19,9 @@ package de.steamwar; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; - import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.Instrumentation; -import java.lang.instrument.UnmodifiableClassException; import java.security.ProtectionDomain; -import java.util.*; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.logging.Logger; -import java.util.stream.Collectors; +import java.util.List; /** * Transforms class bytecode to apply access widening rules. @@ -39,111 +31,19 @@ import java.util.stream.Collectors; */ public class WideningTransformer implements ClassFileTransformer { - private static final Logger LOG = Logger.getLogger("WidenerTransformer"); + private final ClassPatcher patcher; - private final Instrumentation instrumentation; - - /** - * All entries collected across all plugins. Thread-safe for concurrent plugin loads. - */ - private final List entries = new CopyOnWriteArrayList<>(); - - /** - * ClassLoaders we have already scanned, to avoid re-scanning. - */ - private final Set scannedLoaders = Collections.synchronizedSet(Collections.newSetFromMap(new IdentityHashMap<>())); - - public WideningTransformer(Instrumentation instrumentation) { - this.instrumentation = instrumentation; + public WideningTransformer(List entries) { + patcher = new ClassPatcher(entries); } - /** - * Add entries — used during initial scan before the transformer is registered. - */ - public void addEntries(List newEntries) { - entries.addAll(newEntries); - } - - // ------------------------------------------------------------------------- - // ClassFileTransformer - // ------------------------------------------------------------------------- - @Override public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { - - // Check for new plugin classloaders we haven't seen yet - if (loader != null && AccessWidenerScanner.isPluginClassLoader(loader) && scannedLoaders.add(loader)) { - onNewPluginClassLoader(loader); - } - - // No entries for this class — skip transformation entirely - if (className == null) return classfileBuffer; - boolean relevant = entries.stream().anyMatch(e -> e.targets(className)); - if (!relevant) return classfileBuffer; - - // Apply widening via ASM - try { - ClassReader cr = new ClassReader(classfileBuffer); - ClassWriter cw = new ClassWriter(cr, 0); - cr.accept(new ClassTransformer(cw, className, entries), 0); - return cw.toByteArray(); - } catch (Exception e) { - LOG.warning("[AccessWidener] Failed to transform " + className + ": " + e.getMessage()); + byte[] result = patcher.patch(className, classfileBuffer); + if (result == null) { return classfileBuffer; - } - } - - // ------------------------------------------------------------------------- - // New ClassLoader detection - // ------------------------------------------------------------------------- - - private void onNewPluginClassLoader(ClassLoader loader) { - List newEntries = AccessWidenerScanner.scanClassLoader(loader); - if (newEntries.isEmpty()) return; - - entries.addAll(newEntries); - LOG.info("[AccessWidener] Picked up " + newEntries.size() + " new entr" + (newEntries.size() == 1 ? "y" : "ies") + " from " + loader); - - // Retransform already-loaded classes that are now targeted - scheduleRetransform(newEntries); - } - - /** - * Retransformation cannot be called from within a transform() call, - * so we dispatch it to a short-lived daemon thread. - */ - private void scheduleRetransform(List newEntries) { - Set targets = newEntries.stream().map(e -> e.target().replace('/', '.')).collect(Collectors.toSet()); - - Thread t = new Thread(() -> retransform(targets), "access-widener-retransform"); - t.setDaemon(true); - t.start(); - } - - private void retransform(Set dotNames) { - List toRetransform = Arrays.stream(instrumentation.getAllLoadedClasses()) - .filter(c -> dotNames.contains(c.getName())) - .filter(instrumentation::isModifiableClass) - .collect(Collectors.toList()); - - if (toRetransform.isEmpty()) return; - - LOG.info("[AccessWidener] Retransforming " + toRetransform.size() + " class(es)."); - List failed = new ArrayList<>(); - - for (Class clazz : toRetransform) { - try { - instrumentation.retransformClasses(clazz); - } catch (UnmodifiableClassException | UnsupportedOperationException e) { - failed.add(clazz.getName()); - } - } - - if (!failed.isEmpty()) { - LOG.warning("[AccessWidener] The following classes were already loaded before the agent" - + " and cannot be retransformed. Add -javaagent: to your start script" - + " to fix this:"); - failed.forEach(name -> LOG.warning(" - " + name)); + } else { + return result; } } } diff --git a/BauSystem/build.gradle.kts b/BauSystem/build.gradle.kts index b6180f3f..aa03f184 100644 --- a/BauSystem/build.gradle.kts +++ b/BauSystem/build.gradle.kts @@ -20,22 +20,15 @@ plugins { `java-library` alias(libs.plugins.shadow) - id("io.papermc.paperweight.userdev") version "1.7.1" } tasks.build { finalizedBy(tasks.shadowJar) } -repositories { - maven("https://repo.papermc.io/repository/maven-public/") -} - dependencies { implementation(project(":BauSystem:BauSystem_RegionFixed")) implementation(project(":BauSystem:BauSystem_Main")) - - paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT") } tasks.register("DevBau21") { From eb866125ea7a8247cc1a434405063927efc846b2 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 13:45:49 +0200 Subject: [PATCH 03/19] Remove reflection from BauSystem --- .../src/main/java/de/steamwar/Agent.java | 18 ++++---- BauSystem/BauSystem_Main/build.gradle.kts | 44 +++++++++++++++++++ .../src/bausystem.accesswidener | 5 +++ .../features/util/NoClipCommand.java | 2 +- .../bausystem/utils/PlaceItemUtils.java | 9 ++-- .../steamwar/bausystem/utils/TickManager.java | 4 +- 6 files changed, 62 insertions(+), 20 deletions(-) diff --git a/AccessWidener/src/main/java/de/steamwar/Agent.java b/AccessWidener/src/main/java/de/steamwar/Agent.java index 6558508c..e078d357 100644 --- a/AccessWidener/src/main/java/de/steamwar/Agent.java +++ b/AccessWidener/src/main/java/de/steamwar/Agent.java @@ -28,17 +28,15 @@ import java.util.logging.Logger; /** * Java agent entry point. - * - * Can be used two ways: - * 1. At JVM startup: java -javaagent:paper-access-widener-agent.jar -jar server.jar - * 2. Late attach: from inside a Paper plugin via the Attach API - * + *

+ * At JVM startup: java -javaagent:paper-access-widener-agent.jar -jar server.jar + *

* On attach the agent: - * 1. Scans all existing plugin ClassLoaders for "plugin.accesswidener" resources - * 2. Registers a ClassFileTransformer that: - * a. Applies widening to every class as it loads - * b. Detects new plugin ClassLoaders and scans them automatically - * 3. Retransforms any Minecraft/server classes that are already loaded + *

    + *
  1. Find all .jar files inside the plugins folder
  2. + *
  3. Scan all found jars for *.accesswidener resources
  4. + *
  5. Transform any class during loading
  6. + *
*/ public class Agent { private Agent() { diff --git a/BauSystem/BauSystem_Main/build.gradle.kts b/BauSystem/BauSystem_Main/build.gradle.kts index f51b33cd..f81b7614 100644 --- a/BauSystem/BauSystem_Main/build.gradle.kts +++ b/BauSystem/BauSystem_Main/build.gradle.kts @@ -30,6 +30,45 @@ java { targetCompatibility = JavaVersion.VERSION_21 } +// ─── Collect all .accesswidener files from this plugin's resources ──────────── +val accessWidenerFiles: FileCollection = fileTree("src/") { + include("**/*.accesswidener") +} + +val paperJarProvider: Provider = provider { + val dep = libs.nms.get() + configurations["compileClasspath"].resolvedConfiguration.resolvedArtifacts.first { artifact -> + artifact.moduleVersion.id.module.group == dep.module.group && artifact.moduleVersion.id.module.name == dep.module.name + }.file +} + +// ─── Widen the Paper dev JAR so the IDE / javac see the patched access ──────── +val widenedJar by tasks.registering(JavaExec::class) { + description = "Produces a widened copy of the Paper dev JAR for compile-time use." + group = "widener" + + // Re-run whenever the .accesswidener files change + inputs.file(paperJarProvider) + inputs.files(accessWidenerFiles) + + val output = layout.buildDirectory.file("widened/paper-widened.jar") + outputs.file(output) + + classpath = project(":AccessWidener").tasks.named("shadowJar").get().outputs.files + + mainClass.set("de.steamwar.Main") + + doFirst { + args = buildList { + add(paperJarProvider.get().absolutePath) + add(output.get().asFile.absolutePath) + addAll(accessWidenerFiles.map { it.absolutePath }) + } + } + + dependsOn(":AccessWidener:shadowJar") +} + dependencies { compileOnly(libs.classindex) annotationProcessor(libs.classindex) @@ -40,6 +79,7 @@ dependencies { compileOnly(libs.paperapi) compileOnly(libs.nms) + compileOnly(files(widenedJar)) compileOnly(libs.fawe) compileOnly(libs.netty) @@ -47,3 +87,7 @@ dependencies { implementation(libs.luaj) implementation(files("$projectDir/../libs/YAPION-SNAPSHOT.jar")) } + +tasks.compileJava { + dependsOn(widenedJar) +} diff --git a/BauSystem/BauSystem_Main/src/bausystem.accesswidener b/BauSystem/BauSystem_Main/src/bausystem.accesswidener index 7a0f195d..921ce434 100644 --- a/BauSystem/BauSystem_Main/src/bausystem.accesswidener +++ b/BauSystem/BauSystem_Main/src/bausystem.accesswidener @@ -1,3 +1,8 @@ accessWidener v2 named accessible field net/minecraft/server/level/ServerPlayerGameMode gameModeForPlayer Lnet/minecraft/world/level/GameType; +accessible field org/bukkit/craftbukkit/block/CraftBlockState position Lnet/minecraft/core/BlockPos; +accessible field org/bukkit/craftbukkit/block/CraftBlockState world Lorg/bukkit/craftbukkit/CraftWorld; +mutable field org/bukkit/craftbukkit/block/CraftBlockState position Lnet/minecraft/core/BlockPos; +mutable field org/bukkit/craftbukkit/block/CraftBlockState world Lorg/bukkit/craftbukkit/CraftWorld; +accessible field net/minecraft/server/ServerTickRateManager remainingSprintTicks J \ No newline at end of file diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java index 6ead52d4..19bffd3f 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/NoClipCommand.java @@ -102,7 +102,7 @@ public class NoClipCommand extends SWCommand implements Listener { } private void setInternalGameMode(Player player, GameMode gameMode) { - // ((CraftPlayer) player).getHandle().gameMode.gameModeForPlayer = GameType.byId(gameMode.getValue()); + ((CraftPlayer) player).getHandle().gameMode.gameModeForPlayer = GameType.byId(gameMode.getValue()); } @Register(help = true) diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/PlaceItemUtils.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/PlaceItemUtils.java index c4ab9a54..71f42cbb 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/PlaceItemUtils.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/PlaceItemUtils.java @@ -19,7 +19,6 @@ package de.steamwar.bausystem.utils; -import de.steamwar.Reflection; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.experimental.UtilityClass; @@ -86,9 +85,6 @@ public class PlaceItemUtils { .collect(Collectors.toSet()); } - private static final Reflection.Field positionAccessor = Reflection.getField(CraftBlockState.class, BlockPos.class, 0); - private static final Reflection.Field worldAccessor = Reflection.getField(CraftBlockState.class, CraftWorld.class, 0); - /** * Attempt to place an {@link ItemStack} the {@link Player} is holding against a {@link Block} inside the World. * This can be easily used inside the {@link org.bukkit.event.player.PlayerInteractEvent} to mimik placing a @@ -288,8 +284,9 @@ public class PlaceItemUtils { } else { // If a BlockState is present set the Position and World to the Block you want to place Location blockLocation = block.getLocation(); - positionAccessor.set(blockState, new BlockPos(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ())); - worldAccessor.set(blockState, blockLocation.getWorld()); + CraftBlockState craftBlockState = (CraftBlockState) blockState; + craftBlockState.position = new BlockPos(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ()); + craftBlockState.world = (CraftWorld) blockLocation.getWorld(); } if (blockData.getMaterial().isSolid()) { diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickManager.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickManager.java index 59d12df2..613b3ea4 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickManager.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/TickManager.java @@ -20,7 +20,6 @@ package de.steamwar.bausystem.utils; import com.comphenix.tinyprotocol.TinyProtocol; -import de.steamwar.Reflection; import de.steamwar.bausystem.BauSystem; import net.minecraft.network.protocol.game.ClientboundTickingStatePacket; import net.minecraft.server.MinecraftServer; @@ -33,7 +32,6 @@ public class TickManager implements Listener { public static final TickManager impl = new TickManager(); private static final ServerTickRateManager manager = MinecraftServer.getServer().tickRateManager(); - private static final Reflection.Field remainingSprintTicks = Reflection.getField(ServerTickRateManager.class, long.class, 0); private boolean blockTpsPacket = true; private int totalSteps; @@ -121,7 +119,7 @@ public class TickManager implements Listener { public long getRemainingTicks() { if (isSprinting()) { - return remainingSprintTicks.get(manager); + return manager.remainingSprintTicks; } else { return manager.frozenTicksToRun(); } From 641cefad01a7897d1f3f8ea9aaf35516e0726933 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 13:46:55 +0200 Subject: [PATCH 04/19] Add comments to bausystem.accesswidener --- BauSystem/BauSystem_Main/src/bausystem.accesswidener | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/BauSystem/BauSystem_Main/src/bausystem.accesswidener b/BauSystem/BauSystem_Main/src/bausystem.accesswidener index 921ce434..5dd89fdf 100644 --- a/BauSystem/BauSystem_Main/src/bausystem.accesswidener +++ b/BauSystem/BauSystem_Main/src/bausystem.accesswidener @@ -1,8 +1,13 @@ accessWidener v2 named +# For NoClipCommand accessible field net/minecraft/server/level/ServerPlayerGameMode gameModeForPlayer Lnet/minecraft/world/level/GameType; + +# For PlaceItemUtils accessible field org/bukkit/craftbukkit/block/CraftBlockState position Lnet/minecraft/core/BlockPos; -accessible field org/bukkit/craftbukkit/block/CraftBlockState world Lorg/bukkit/craftbukkit/CraftWorld; mutable field org/bukkit/craftbukkit/block/CraftBlockState position Lnet/minecraft/core/BlockPos; +accessible field org/bukkit/craftbukkit/block/CraftBlockState world Lorg/bukkit/craftbukkit/CraftWorld; mutable field org/bukkit/craftbukkit/block/CraftBlockState world Lorg/bukkit/craftbukkit/CraftWorld; -accessible field net/minecraft/server/ServerTickRateManager remainingSprintTicks J \ No newline at end of file + +# For TickManager +accessible field net/minecraft/server/ServerTickRateManager remainingSprintTicks J From 36b31fac77049d941278cd65a3d6c581b42e6155 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 13:49:35 +0200 Subject: [PATCH 05/19] Remove unneeded code Cleanup build.gradle.kts --- AccessWidener/build.gradle.kts | 2 +- .../steamwar/core/AccessWidenerAttacher.java | 117 ------------------ 2 files changed, 1 insertion(+), 118 deletions(-) delete mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/core/AccessWidenerAttacher.java diff --git a/AccessWidener/build.gradle.kts b/AccessWidener/build.gradle.kts index 87a25b4d..a6632cf1 100644 --- a/AccessWidener/build.gradle.kts +++ b/AccessWidener/build.gradle.kts @@ -19,7 +19,7 @@ plugins { `java-library` - id("com.github.johnrengelman.shadow") version "8.1.1" + alias(libs.plugins.shadow) } dependencies { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/AccessWidenerAttacher.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/AccessWidenerAttacher.java deleted file mode 100644 index ccf61c40..00000000 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/AccessWidenerAttacher.java +++ /dev/null @@ -1,117 +0,0 @@ -package de.steamwar.core; - -import java.lang.reflect.Method; -import java.nio.file.Path; -import java.util.logging.Logger; - -/** - * Public API for Paper plugins that want to attach the access widener agent. - * - *

Usage

- * Call {@link #ensureAttached(Path)} from your plugin's {@code onLoad()} method - * (not {@code onEnable()} — onLoad fires before classes start loading): - * - *
{@code
- * public class MyPlugin extends JavaPlugin {
- *     @Override
- *     public void onLoad() {
- *         try {
- *             AccessWidenerAttacher.ensureAttached(getDataFolder());
- *         } catch (Exception e) {
- *             getSLF4JLogger().error("Failed to attach access widener agent", e);
- *         }
- *     }
- * }
- * }
- * - *

Resource file

- * Drop a {@code plugin.accesswidener} file in your plugin's resources: - * - *
- *   accessWidener v2 named
- *
- *   accessible method net/minecraft/server/level/ServerPlayer getStats ()V
- *   mutable    field  net/minecraft/world/entity/Entity id I
- * 
- * - * The agent discovers it automatically via your plugin's ClassLoader. - */ -public final class AccessWidenerAttacher { - - private static final Logger LOG = Logger.getLogger("AccessWidenerAttacher"); - private static final Object LOCK = new Object(); - - private static volatile boolean attached = false; - - private AccessWidenerAttacher() {} - - /** - * Attaches the access widener agent to the running JVM if it has not already - * been attached. Safe to call from multiple plugins — the agent is only - * attached once. - * - * @param agentJar the agentJar - * @throws AccessWidenerException if the agent could not be attached - */ - public static void ensureAttached(Path agentJar) throws AccessWidenerException { - if (attached) return; // fast path — no locking needed after first attach - - synchronized (LOCK) { - if (attached) return; // double-checked - - try { - attachAgent(agentJar); - attached = true; - LOG.info("[AccessWidener] Agent attached successfully."); - } catch (Exception e) { - throw new AccessWidenerException("Failed to attach access widener agent", e); - } - } - } - - /** Returns true if the agent has been successfully attached. */ - public static boolean isAttached() { - return attached; - } - - private static void attachAgent(Path agentJar) throws Exception { - // Verify the Attach API is available before trying - try { - Class.forName("com.sun.tools.attach.VirtualMachine"); - } catch (ClassNotFoundException e) { - throw new AccessWidenerException( - "The JDK Attach API is not available. " - + "Make sure you are running on a JDK (not a JRE) " - + "and add '--add-opens jdk.attach/sun.tools.attach=ALL-UNNAMED' " - + "to your JVM flags if on Java 9+.", e); - } - - String pid = String.valueOf(ProcessHandle.current().pid()); - - // Use reflection so the Attach API is not a hard compile-time dependency - Class vmClass = Class.forName("com.sun.tools.attach.VirtualMachine"); - Method attachMethod = vmClass.getMethod("attach", String.class); - Method loadAgentMethod = vmClass.getMethod("loadAgent", String.class); - Method detachMethod = vmClass.getMethod("detach"); - - Object vm = attachMethod.invoke(null, pid); - try { - loadAgentMethod.invoke(vm, agentJar.toAbsolutePath().toString()); - } finally { - detachMethod.invoke(vm); - } - } - - // ------------------------------------------------------------------------- - // Exception type - // ------------------------------------------------------------------------- - - public static class AccessWidenerException extends Exception { - public AccessWidenerException(String message, Throwable cause) { - super(message, cause); - } - public AccessWidenerException(String message) { - super(message); - } - } -} From f5d9c6e1758fce566876de03c638c128571a4a26 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 18:26:15 +0200 Subject: [PATCH 06/19] Unreflect everything --- .../main/java/de/steamwar/ClassPatcher.java | 6 ++ .../java/de/steamwar/ClassTransformer.java | 10 +++ .../fightsystem/listener/Recording.java | 15 ++-- SpigotCore/SpigotCore_Main/build.gradle.kts | 44 ++++++++++++ .../comphenix/tinyprotocol/TinyProtocol.java | 21 ++---- .../de/steamwar/core/BountifulWrapper.java | 49 ------------- .../de/steamwar/core/CheckpointUtilsJ9.java | 14 ++-- .../src/de/steamwar/core/ErrorHandler.java | 5 +- .../src/de/steamwar/core/WorldIdentifier.java | 8 +-- .../SteamwarGameProfileRepository.java | 9 +-- .../src/de/steamwar/entity/REntity.java | 70 ++++--------------- .../src/de/steamwar/techhider/ChunkHider.java | 44 ++++-------- .../de/steamwar/techhider/ProtocolUtils.java | 20 ++---- .../src/de/steamwar/techhider/TechHider.java | 23 ++---- .../steamwar/techhider/legacy/ChunkHider.java | 40 ++++------- .../techhider/legacy/ProtocolWrapper.java | 33 +++------ .../steamwar/techhider/legacy/TechHider.java | 54 ++++++-------- .../src/spigotcore.accesswidener | 57 +++++++++++++++ 18 files changed, 217 insertions(+), 305 deletions(-) create mode 100644 SpigotCore/SpigotCore_Main/src/spigotcore.accesswidener diff --git a/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java b/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java index a5549762..2cce0c77 100644 --- a/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java +++ b/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Set; import java.util.logging.Logger; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Uses ASM to patch class bytecode according to a list of access widener entries. @@ -46,6 +47,11 @@ public class ClassPatcher { this.entries = entries; this.targets = entries.stream() .map(AccessWidenerEntry::target) + .flatMap(s -> { + if (!s.contains("$")) return Stream.of(s); + int index = s.lastIndexOf('$'); + return Stream.of(s, s.substring(0, index)); + }) .collect(Collectors.toSet()); } diff --git a/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java b/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java index c329b966..ccc3df9d 100644 --- a/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java +++ b/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java @@ -47,6 +47,16 @@ public class ClassTransformer extends ClassVisitor { super.visit(version, newAccess, name, signature, superName, interfaces); } + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + int newAccess = access; + for (AccessWidenerEntry e : entries) { + if (!e.target().equals(name) || !"class".equals(e.memberType())) continue; + newAccess = applyDirective(e.directive(), newAccess, false); + } + super.visitInnerClass(name, outerName, innerName, newAccess); + } + @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { int newAccess = access; diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java index 0187d089..d11c98db 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java @@ -115,18 +115,18 @@ public class Recording implements Listener { }.register(); new StateDependent(ArenaMode.AntiReplay, FightState.Ingame) { private final BiFunction place = Recording.this::blockPlace; - private final BiFunction dig = Recording.this::blockDig; + private final BiFunction dig = Recording.this::blockDig; @Override public void enable() { TinyProtocol.instance.addFilter(ServerboundUseItemPacket.class, place); - TinyProtocol.instance.addFilter(blockDigPacket, dig); + TinyProtocol.instance.addFilter(ServerboundPlayerActionPacket.class, dig); } @Override public void disable() { TinyProtocol.instance.removeFilter(ServerboundUseItemPacket.class, place); - TinyProtocol.instance.removeFilter(blockDigPacket, dig); + TinyProtocol.instance.removeFilter(ServerboundPlayerActionPacket.class, dig); } }.register(); new StateDependentTask(ArenaMode.AntiReplay, FightState.All, () -> { @@ -143,13 +143,8 @@ public class Recording implements Listener { GlobalRecorder.getInstance().entitySpeed(entity); } - private static final Class> blockDigPacket = ServerboundPlayerActionPacket.class; - private static final Class playerDigType = blockDigPacket.getDeclaredClasses()[0]; - private static final Reflection.Field blockDigType = Reflection.getField(blockDigPacket, playerDigType, 0); - private static final Object releaseUseItem = playerDigType.getEnumConstants()[5]; - - private Object blockDig(Player p, Object packet) { - if (!isNotSent(p) && blockDigType.get(packet) == releaseUseItem) { + private Object blockDig(Player p, ServerboundPlayerActionPacket packet) { + if (!isNotSent(p) && packet.getAction() == ServerboundPlayerActionPacket.Action.RELEASE_USE_ITEM) { GlobalRecorder.getInstance().bowSpan(p, false, false); } return packet; diff --git a/SpigotCore/SpigotCore_Main/build.gradle.kts b/SpigotCore/SpigotCore_Main/build.gradle.kts index a5e3227d..34bc1d49 100644 --- a/SpigotCore/SpigotCore_Main/build.gradle.kts +++ b/SpigotCore/SpigotCore_Main/build.gradle.kts @@ -42,6 +42,45 @@ tasks.withType().configureEach { jvmArgs("--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED") } +// ─── Collect all .accesswidener files from this plugin's resources ──────────── +val accessWidenerFiles: FileCollection = fileTree("src/") { + include("**/*.accesswidener") +} + +val paperJarProvider: Provider = provider { + val dep = libs.nms.get() + configurations["compileClasspath"].resolvedConfiguration.resolvedArtifacts.first { artifact -> + artifact.moduleVersion.id.module.group == dep.module.group && artifact.moduleVersion.id.module.name == dep.module.name + }.file +} + +// ─── Widen the Paper dev JAR so the IDE / javac see the patched access ──────── +val widenedJar by tasks.registering(JavaExec::class) { + description = "Produces a widened copy of the Paper dev JAR for compile-time use." + group = "widener" + + // Re-run whenever the .accesswidener files change + inputs.file(paperJarProvider) + inputs.files(accessWidenerFiles) + + val output = layout.buildDirectory.file("widened/paper-widened.jar") + outputs.file(output) + + classpath = project(":AccessWidener").tasks.named("shadowJar").get().outputs.files + + mainClass.set("de.steamwar.Main") + + doFirst { + args = buildList { + add(paperJarProvider.get().absolutePath) + add(output.get().asFile.absolutePath) + addAll(accessWidenerFiles.map { it.absolutePath }) + } + } + + dependsOn(":AccessWidener:shadowJar") +} + dependencies { compileOnly(libs.classindex) annotationProcessor(libs.classindex) @@ -53,6 +92,7 @@ dependencies { compileOnly(libs.paperapi) compileOnly(libs.nms) + compileOnly(files(widenedJar)) compileOnly(libs.authlib) compileOnly(libs.datafixer) compileOnly(libs.netty) @@ -61,3 +101,7 @@ dependencies { implementation(libs.anvilgui) } + +tasks.compileJava { + dependsOn(widenedJar) +} diff --git a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java index f7df16c1..5391f59b 100644 --- a/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java +++ b/SpigotCore/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java @@ -1,7 +1,6 @@ package com.comphenix.tinyprotocol; import com.google.common.collect.MapMaker; -import de.steamwar.Reflection; import de.steamwar.core.CRIUWakeupEvent; import de.steamwar.core.Core; import io.netty.channel.*; @@ -180,22 +179,12 @@ public class TinyProtocol { networkManagers = serverConnection.getConnections(); // We need to synchronize against this list createServerChannelHandler(); + for (ChannelFuture item : serverConnection.channels) { + // Channel future that contains the server connection + Channel serverChannel = item.channel(); - // Find the correct list, or implicitly throw an exception - boolean looking = true; - for (int i = 0; looking; i++) { - List list = Reflection.getField(serverConnection.getClass(), List.class, i).get(serverConnection); - - for (Object item : list) { - if (!(item instanceof ChannelFuture)) break; - - // Channel future that contains the server connection - Channel serverChannel = ((ChannelFuture) item).channel(); - - serverChannels.add(serverChannel); - serverChannel.pipeline().addFirst(serverChannelHandler); - looking = false; - } + serverChannels.add(serverChannel); + serverChannel.pipeline().addFirst(serverChannelHandler); } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java index e5a69d9f..64bef9b5 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java @@ -26,13 +26,9 @@ import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializer; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; -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 { public static final BountifulWrapper impl = new BountifulWrapper(); @@ -55,49 +51,4 @@ public class BountifulWrapper { public Object getDataWatcherItem(Object dwo, Object value) { return new SynchedEntityData.DataItem<>((EntityDataAccessor) dwo, value); } - - public BountifulWrapper.PositionSetter getPositionSetter(Class packetClass, int fieldOffset) { - try { - Reflection.Field field = Reflection.getField(packetClass, PositionMoveRotation.class, 0); - - return (packet, x, y, z, pitch, yaw) -> { - field.set(packet, new PositionMoveRotation(new Vec3(x, y, z), field.get(packet).deltaMovement(), yaw, pitch)); - }; - } catch (IllegalArgumentException e) { - Reflection.Field posX = Reflection.getField(packetClass, double.class, fieldOffset); - Reflection.Field posY = Reflection.getField(packetClass, double.class, fieldOffset + 1); - Reflection.Field 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.UUIDSetter getUUIDSetter(Class packetClass) { - Reflection.Field uuidField = Reflection.getField(packetClass, UUID.class, 0); - - return uuidField::set; - } - - public interface PositionSetter { - void set(Object packet, double x, double y, double z, float pitch, float yaw); - } - - public interface UUIDSetter { - void set(Object packet, UUID uuid); - } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/CheckpointUtilsJ9.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/CheckpointUtilsJ9.java index 4ccbaaa7..2849b5a4 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/CheckpointUtilsJ9.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/CheckpointUtilsJ9.java @@ -20,7 +20,6 @@ package de.steamwar.core; import com.comphenix.tinyprotocol.TinyProtocol; -import de.steamwar.Reflection; import de.steamwar.sql.internal.Statement; import io.netty.channel.ChannelFuture; import net.minecraft.server.MinecraftServer; @@ -96,9 +95,6 @@ class CheckpointUtilsJ9 { } } - - private static final Reflection.Field channelFutures = Reflection.getField(ServerConnectionListener.class, List.class, 0, ChannelFuture.class); - private static void freezeInternal(Path path) throws Exception { Bukkit.getPluginManager().callEvent(new CRIUSleepEvent()); @@ -109,9 +105,9 @@ class CheckpointUtilsJ9 { // Close socket ServerConnectionListener serverConnection = MinecraftServer.getServer().getConnection(); - List channels = channelFutures.get(serverConnection); - for (Object future : channels) { - ((ChannelFuture) future).channel().close().syncUninterruptibly(); + List channels = serverConnection.channels; + for (ChannelFuture future : channels) { + future.channel().close().syncUninterruptibly(); } channels.clear(); @@ -145,8 +141,8 @@ class CheckpointUtilsJ9 { // Reopen socket serverConnection.startTcpServerListener(InetAddress.getLoopbackAddress(), port); - for (Object future : channels) { - ((ChannelFuture) future).channel().config().setAutoRead(true); + for (ChannelFuture future : channels) { + future.channel().config().setAutoRead(true); } Bukkit.getPluginManager().callEvent(new CRIUWakeupEvent()); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/ErrorHandler.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/ErrorHandler.java index f60eb406..5995a3c1 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/ErrorHandler.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/ErrorHandler.java @@ -19,7 +19,6 @@ package de.steamwar.core; -import de.steamwar.Reflection; import de.steamwar.sql.SWException; import org.spigotmc.WatchdogThread; @@ -39,9 +38,7 @@ public class ErrorHandler extends Handler { public ErrorHandler() { Logger.getLogger("").addHandler(this); - - Reflection.Field getInstance = Reflection.getField(WatchdogThread.class, WatchdogThread.class, 0); - watchdogThreadId = getInstance.get(null).getId(); + watchdogThreadId = WatchdogThread.instance.threadId(); } void unregister() { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldIdentifier.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldIdentifier.java index 2ca9d085..2fa27b1e 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldIdentifier.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/WorldIdentifier.java @@ -20,7 +20,6 @@ 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; @@ -33,13 +32,8 @@ public class WorldIdentifier { private static ResourceKey resourceKey = null; - private static final Class resourceKeyClass = ResourceKey.class; - private static final Class minecraftKeyClass = ResourceLocation.class; - 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) { - resourceKey = (ResourceKey) resourceKeyConstructor.invoke(minecraftKeyConstructor.invoke("minecraft", "dimension"), minecraftKeyConstructor.invoke("steamwar", name)); + resourceKey = new ResourceKey<>(new ResourceLocation("minecraft", "dimension"), new ResourceLocation("steamwar", name)); } public WorldIdentifier() { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/authlib/SteamwarGameProfileRepository.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/authlib/SteamwarGameProfileRepository.java index 32e394e7..dd7540c6 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/authlib/SteamwarGameProfileRepository.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/authlib/SteamwarGameProfileRepository.java @@ -22,7 +22,6 @@ package de.steamwar.core.authlib; import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfileRepository; import com.mojang.authlib.ProfileLookupCallback; -import de.steamwar.Reflection; import de.steamwar.sql.SteamwarUser; import net.minecraft.server.MinecraftServer; import net.minecraft.server.Services; @@ -35,13 +34,10 @@ public class SteamwarGameProfileRepository implements GameProfileRepository { public static final SteamwarGameProfileRepository impl = new SteamwarGameProfileRepository(); private static final GameProfileRepository fallback; - private static final Reflection.Field field; private static final Services current; static { - Class clazz = MinecraftServer.getServer().getClass(); - field = Reflection.getField(clazz, Services.class, 0); - current = field.get(MinecraftServer.getServer()); + current = MinecraftServer.getServer().services; fallback = current.profileRepository(); } @@ -68,7 +64,6 @@ public class SteamwarGameProfileRepository implements GameProfileRepository { } public void inject() { - Services newServices = new Services(current.sessionService(), current.servicesKeySet(), this, current.profileCache(), current.paperConfigurations()); - field.set(MinecraftServer.getServer(), newServices); + MinecraftServer.getServer().services = new Services(current.sessionService(), current.servicesKeySet(), this, current.profileCache(), current.paperConfigurations()); } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java index 8ac0c01b..2d867cc1 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java @@ -44,7 +44,6 @@ import org.bukkit.inventory.ItemStack; import java.util.*; import java.util.function.Consumer; -import java.util.function.Function; public class REntity { @@ -174,14 +173,10 @@ public class REntity { server.postEntityMove(this, fromX, fromZ); } - private static final Class animationPacket = ClientboundAnimatePacket.class; - private static final Reflection.Field animationEntity = Reflection.getField(animationPacket, int.class, 5); - private static final Reflection.Field animationAnimation = Reflection.getField(animationPacket, int.class, 6); - public void showAnimation(byte animation) { - Object packet = Reflection.newInstance(animationPacket); - animationEntity.set(packet, entityId); - animationAnimation.set(packet, (int) animation); + ClientboundAnimatePacket packet = (ClientboundAnimatePacket) Reflection.newInstance(ClientboundAnimatePacket.class); + packet.id = entityId; + packet.action = animation; server.updateEntity(this, packet); } @@ -189,14 +184,10 @@ public class REntity { server.updateEntity(this, new ClientboundSetEntityMotionPacket(entityId, new Vec3(calcVelocity(dX), calcVelocity(dY), calcVelocity(dZ)))); } - private static final Class statusPacket = ClientboundEntityEventPacket.class; - private static final Reflection.Field statusEntity = Reflection.getField(statusPacket, int.class, 0); - private static final Reflection.Field statusStatus = Reflection.getField(statusPacket, byte.class, 0); - public void showDamage() { - Object packet = Reflection.newInstance(statusPacket); - statusEntity.set(packet, entityId); - statusStatus.set(packet, (byte) 2); + ClientboundEntityEventPacket packet = (ClientboundEntityEventPacket) Reflection.newInstance(ClientboundEntityEventPacket.class); + packet.entityId = entityId; + packet.eventId = (byte) 2; server.updateEntity(this, packet); } @@ -251,14 +242,10 @@ public class REntity { server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); } - private static final Function spawnPacketGenerator = entitySpawnPacketGenerator(ClientboundAddEntityPacket.class, 2); - - private static final Reflection.Field additionalData = Reflection.getField(ClientboundAddEntityPacket.class, int.class, 4); - - private Object spawnPacketGenerator() { - Object packet = spawnPacketGenerator.apply(this); - additionalData.set(packet, objectData); - return packet; + private ClientboundAddEntityPacket spawnPacketGenerator() { + ResourceLocation key = CraftNamespacedKey.toMinecraft(entityType.getKey()); + net.minecraft.world.entity.EntityType entityType = BuiltInRegistries.ENTITY_TYPE.get(key).get().value(); + return new ClientboundAddEntityPacket(entityId, uuid, x, y, z, pitch, yaw, entityType, objectData, Vec3.ZERO, 0); } void list(Consumer packetSink) { @@ -359,14 +346,10 @@ public class REntity { } } - private static final Class headRotationPacket = ClientboundRotateHeadPacket.class; - private static final Reflection.Field headRotationEntity = Reflection.getField(headRotationPacket, int.class, 0); - private static final Reflection.Field headRotationYaw = Reflection.getField(headRotationPacket, byte.class, 0); - private Object getHeadRotationPacket() { - Object packet = Reflection.newInstance(headRotationPacket); - headRotationEntity.set(packet, entityId); - headRotationYaw.set(packet, headYaw); + ClientboundRotateHeadPacket packet = (ClientboundRotateHeadPacket) Reflection.newInstance(ClientboundRotateHeadPacket.class); + packet.entityId = entityId; + packet.yHeadRot = headYaw; return packet; } @@ -374,33 +357,6 @@ public class REntity { return new ClientboundSetEquipmentPacket(entityId, Collections.singletonList(Pair.of((EquipmentSlot) slot, CraftItemStack.asNMSCopy(stack)))); } - private static final Reflection.Field spawnType = Reflection.getField(ClientboundAddEntityPacket.class, net.minecraft.world.entity.EntityType.class, 0); - - private static Function entitySpawnPacketGenerator(Class spawnPacket, int posOffset) { - BountifulWrapper.UUIDSetter uuid = BountifulWrapper.impl.getUUIDSetter(spawnPacket); - Function packetGenerator = spawnPacketGenerator(spawnPacket, posOffset); - - return entity -> { - Object packet = packetGenerator.apply(entity); - uuid.set(packet, entity.uuid); - ResourceLocation key = CraftNamespacedKey.toMinecraft(entity.entityType.getKey()); - spawnType.set(packet, BuiltInRegistries.ENTITY_TYPE.get(key).get().value()); - return packet; - }; - } - - protected static Function spawnPacketGenerator(Class spawnPacket, int posOffset) { - Reflection.Field entityId = Reflection.getField(spawnPacket, int.class, 0); - BountifulWrapper.PositionSetter position = BountifulWrapper.impl.getPositionSetter(spawnPacket, posOffset); - - return entity -> { - Object packet = Reflection.newInstance(spawnPacket); - entityId.set(packet, entity.entityId); - position.set(packet, entity.x, entity.y, entity.z, entity.pitch, entity.yaw); - return packet; - }; - } - private byte rotToByte(float rot) { return (byte) ((int) (rot * 256.0F / 360.0F)); } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index 3d7fa97c..471cdc4a 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -19,10 +19,8 @@ package de.steamwar.techhider; -import de.steamwar.Reflection; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import net.minecraft.core.SectionPos; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.util.SimpleBitStorage; @@ -35,13 +33,8 @@ import java.util.List; import java.util.function.UnaryOperator; public class ChunkHider { - private static final UnaryOperator chunkPacketShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); - private static final UnaryOperator chunkDataShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); - - private static final Reflection.Field levelChunkPacketDataField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0); - - private static final Reflection.Field chunkBlockDataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0); - private static final Reflection.Field chunkBlockEntitiesDataField = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0); + private static final UnaryOperator chunkPacketShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); + private static final UnaryOperator chunkDataShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); private final int SECTION_SPAN_SIZE = 16; private final byte BIT_PER_BLOCK_INDIRECTION_LIMIT = 8; @@ -178,8 +171,8 @@ public class ChunkHider { byte[] data = new byte[out.readableBytes()]; out.readBytes(data); - List blockEntities = chunkBlockEntitiesDataField.get(chunkData); - List filteredBlockEntities = filterBlockEntities(player, blockEntities, chunkX, chunkZ); + List blockEntities = chunkData.blockEntitiesData; + List filteredBlockEntities = filterBlockEntities(player, blockEntities, chunkX, chunkZ); return buildNewChunkPacket(packet, data, filteredBlockEntities); @@ -237,33 +230,24 @@ public class ChunkHider { return reEncodedData.getRaw(); } - private ClientboundLevelChunkWithLightPacket buildNewChunkPacket(ClientboundLevelChunkWithLightPacket originalPacket, byte[] newBlockDataBuffer, List newBlockEntities) { - ClientboundLevelChunkWithLightPacket clonedPacket = (ClientboundLevelChunkWithLightPacket) chunkPacketShallowCloner.apply(originalPacket); - ClientboundLevelChunkPacketData clonedPacketChunkData = (ClientboundLevelChunkPacketData) chunkDataShallowCloner.apply(originalPacket.getChunkData()); + private ClientboundLevelChunkWithLightPacket buildNewChunkPacket(ClientboundLevelChunkWithLightPacket originalPacket, byte[] newBlockDataBuffer, List newBlockEntities) { + ClientboundLevelChunkWithLightPacket clonedPacket = chunkPacketShallowCloner.apply(originalPacket); + ClientboundLevelChunkPacketData clonedPacketChunkData = chunkDataShallowCloner.apply(originalPacket.getChunkData()); - chunkBlockDataField.set(clonedPacketChunkData, newBlockDataBuffer); - chunkBlockEntitiesDataField.set(clonedPacketChunkData, newBlockEntities); - - levelChunkPacketDataField.set(clonedPacket, clonedPacketChunkData); + clonedPacketChunkData.buffer = newBlockDataBuffer; + clonedPacketChunkData.blockEntitiesData = newBlockEntities; + clonedPacket.chunkData = clonedPacketChunkData; return clonedPacket; } - - private static final Class blockEntitiyInfoClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo"); - - private static final Reflection.Field blockEntityInfoTypeField = Reflection.getField(blockEntitiyInfoClass, BlockEntityType.class, 0); - private static final Reflection.Field packedXZField = Reflection.getField(blockEntitiyInfoClass, int.class, 0); - private static final Reflection.Field yField = Reflection.getField(blockEntitiyInfoClass, int.class, 1); - - private List filterBlockEntities(Player player, List blockEntities, int chunkX, int chunkZ) { + private List filterBlockEntities(Player player, List blockEntities, int chunkX, int chunkZ) { int fourBitBitmask = 0b0000_1111; return blockEntities.stream() .filter((blockEntityInfo) -> { - BlockEntityType type = blockEntityInfoTypeField.get(blockEntityInfo); - - int packedXZ = packedXZField.get(blockEntityInfo); + BlockEntityType type = blockEntityInfo.type; + int packedXZ = blockEntityInfo.packedXZ; int localX = (packedXZ >> 4) & fourBitBitmask; int localZ = packedXZ & fourBitBitmask; @@ -271,7 +255,7 @@ public class ChunkHider { int worldX = (chunkX * SECTION_SPAN_SIZE) + localX; int worldZ = (chunkZ * SECTION_SPAN_SIZE) + localZ; - int worldY = yField.get(blockEntityInfo); + int worldY = blockEntityInfo.y; return accessPrivilegeProvider.isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlockEntity(player, worldX, worldY, worldZ, type); }).toList(); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java index e71b522a..b97fe828 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java @@ -50,33 +50,23 @@ public class ProtocolUtils { }; } - public static UnaryOperator shallowCloneGenerator(Class clazz) { - BiConsumer filler = shallowFill(clazz); + public static UnaryOperator shallowCloneGenerator(Class clazz) { + BiConsumer filler = shallowFill(clazz); return source -> { - Object clone = Reflection.newInstance(clazz); + T clone = (T) Reflection.newInstance(clazz); filler.accept(source, clone); return clone; }; } - public static UnaryOperator shallowTypedCloneGenerator(Class clazz) { - BiConsumer filler = shallowFill(clazz); - - return source -> { - Object clone = Reflection.newInstance(clazz); - filler.accept(source, clone); - return (T) clone; - }; - } - - private static BiConsumer shallowFill(Class clazz) { + private static BiConsumer shallowFill(Class clazz) { if (clazz == null) { return (source, clone) -> { }; } - BiConsumer superFiller = shallowFill(clazz.getSuperclass()); + BiConsumer superFiller = shallowFill(clazz.getSuperclass()); Field[] fds = clazz.getDeclaredFields(); List fields = new ArrayList<>(); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java index 1539f16d..0fef6f1e 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/TechHider.java @@ -20,7 +20,6 @@ package de.steamwar.techhider; import com.comphenix.tinyprotocol.TinyProtocol; -import de.steamwar.Reflection; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.shorts.ShortArraySet; @@ -364,24 +363,16 @@ public class TechHider { }; } - private final Reflection.Field moveEntityPacketEntityIdField = Reflection.getField(ClientboundMoveEntityPacket.class, int.class, 0); - private Packet processMoveEntityPacket(Player player, ClientboundMoveEntityPacket packet) { - int entityId = moveEntityPacketEntityIdField.get(packet); - - if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) { + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, packet.entityId)) { return packet; } else { return null; } } - private final Reflection.Field rotateHeadPacketEntityIdField = Reflection.getField(ClientboundRotateHeadPacket.class, int.class, 0); - private Packet processRotateHeadPacket(Player player, ClientboundRotateHeadPacket packet) { - int entityId = rotateHeadPacketEntityIdField.get(packet); - - if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) { + if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, packet.entityId)) { return packet; } else { return null; @@ -463,14 +454,10 @@ public class TechHider { } } - private final Reflection.Field sectionPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, SectionPos.class, 0); - private final Reflection.Field oldPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, short[].class, 0); - private final Reflection.Field oldStatesField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, BlockState[].class, 0); - private ClientboundSectionBlocksUpdatePacket processSectionUpdate(Player p, ClientboundSectionBlocksUpdatePacket packet) { - SectionPos sectionPos = sectionPosField.get(packet); - short[] oldPos = oldPosField.get(packet); - BlockState[] oldStates = oldStatesField.get(packet); + SectionPos sectionPos = packet.sectionPos; + short[] oldPos = packet.positions; + BlockState[] oldStates = packet.states; List filteredPos = new ArrayList<>(oldPos.length); List filteredStates = new ArrayList<>(oldStates.length); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java index c3df8e6f..57bce2e2 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java @@ -34,7 +34,6 @@ 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; @@ -44,35 +43,26 @@ import java.util.stream.Collectors; public class ChunkHider { public static final ChunkHider impl = new ChunkHider(); - public Class mapChunkPacket() { - return ClientboundLevelChunkWithLightPacket.class; - } + private static final UnaryOperator chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); + private static final UnaryOperator chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); - private static final UnaryOperator chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); - private static final UnaryOperator chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); - - private static final Reflection.Field chunkXField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0); - private static final Reflection.Field chunkZField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1); - private static final Reflection.Field chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0); - - private static final Reflection.Field dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0); - private static final Reflection.Field tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0); - - public BiFunction chunkHiderGenerator(TechHider techHider) { + public BiFunction chunkHiderGenerator(TechHider techHider) { return (p, packet) -> { - int chunkX = chunkXField.get(packet); - int chunkZ = chunkZField.get(packet); + int chunkX = packet.getX(); + int chunkZ = packet.getZ(); if (techHider.getLocationEvaluator().skipChunk(p, chunkX, chunkZ)) { return packet; } packet = chunkPacketCloner.apply(packet); - Object dataWrapper = chunkDataCloner.apply(chunkData.get(packet)); + ClientboundLevelChunkPacketData dataWrapper = chunkDataCloner.apply(packet.getChunkData()); Set hiddenBlockEntities = techHider.getHiddenBlockEntities(); - tileEntities.set(dataWrapper, ((List) tileEntities.get(dataWrapper)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList())); + dataWrapper.blockEntitiesData = dataWrapper.blockEntitiesData.stream() + .filter(te -> tileEntityVisible(hiddenBlockEntities, te)) + .collect(Collectors.toList()); - ByteBuf in = Unpooled.wrappedBuffer(dataField.get(dataWrapper)); + ByteBuf in = Unpooled.wrappedBuffer(dataWrapper.buffer); 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); @@ -88,20 +78,18 @@ public class ChunkHider { byte[] data = new byte[out.readableBytes()]; out.readBytes(data); - dataField.set(dataWrapper, data); + dataWrapper.buffer = data; - chunkData.set(packet, dataWrapper); + packet.chunkData = dataWrapper; return packet; }; } private static final Registry> registry = Reflection.getField(BuiltInRegistries.class, "BLOCK_ENTITY_TYPE", Registry.class).get(null); private static final Reflection.Method getKey = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "getKey", ResourceLocation.class, Object.class); - public static final Class tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo"); - protected static final Reflection.Field entityType = Reflection.getField(tileEntity, BlockEntityType.class, 0); - protected boolean tileEntityVisible(Set hiddenBlockEntities, Object tile) { - BlockEntityType type = entityType.get(tile); + protected boolean tileEntityVisible(Set hiddenBlockEntities, ClientboundLevelChunkPacketData.BlockEntityInfo tile) { + BlockEntityType type = tile.type; String path = ((ResourceLocation) getKey.invoke(registry, type)).getPath(); return !hiddenBlockEntities.contains(path); } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java index 00e0dc31..cb7b8d05 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ProtocolWrapper.java @@ -19,10 +19,8 @@ package de.steamwar.techhider.legacy; -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.network.protocol.game.ClientboundSectionBlocksUpdatePacket; import net.minecraft.world.level.block.state.BlockState; import org.bukkit.entity.Player; @@ -33,24 +31,20 @@ import java.util.function.BiFunction; public class ProtocolWrapper { public static final ProtocolWrapper impl = new ProtocolWrapper(); - private static final Reflection.Field multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPos.class, 0); - private static final Reflection.Field multiBlockChangePos = Reflection.getField(TechHider.multiBlockChangePacket, short[].class, 0); - private static final Reflection.Field multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, BlockState[].class, 0); - - public BiFunction multiBlockChangeGenerator(TechHider techHider) { + public BiFunction 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); + SectionPos chunkCoords = packet.sectionPos; + int chunkX = chunkCoords.getX(); + int chunkY = chunkCoords.getY(); + int chunkZ = chunkCoords.getZ(); if (locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ)) { return packet; } packet = TechHider.multiBlockChangeCloner.apply(packet); - final short[] oldPos = multiBlockChangePos.get(packet); - final BlockState[] oldBlocks = multiBlockChangeBlocks.get(packet); + final short[] oldPos = packet.positions; + final BlockState[] oldBlocks = packet.states; ArrayList poss = new ArrayList<>(oldPos.length); ArrayList blocks = new ArrayList<>(oldPos.length); for (int i = 0; i < oldPos.length; i++) { @@ -77,16 +71,9 @@ public class ProtocolWrapper { newPos[i] = poss.get(i); } - multiBlockChangePos.set(packet, newPos); - multiBlockChangeBlocks.set(packet, blocks.toArray(new BlockState[0])); + packet.positions = newPos; + packet.states = blocks.toArray(BlockState[]::new); return packet; }; } - - private static final Reflection.Field 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; - } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java index 25b3f215..9a96771f 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java @@ -28,6 +28,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; import net.minecraft.network.protocol.game.*; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import org.bukkit.Material; import org.bukkit.craftbukkit.util.CraftMagicNumbers; @@ -37,7 +38,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.function.BiFunction; -import java.util.function.Function; import java.util.function.UnaryOperator; import java.util.stream.Collectors; @@ -45,10 +45,6 @@ import java.util.stream.Collectors; public class TechHider { public static final Class blockPosition = BlockPos.class; - private static final Class baseBlockPosition = Vec3i.class; - public static final Reflection.Field blockPositionX = Reflection.getField(baseBlockPosition, int.class, 0); - public static final Reflection.Field blockPositionY = Reflection.getField(baseBlockPosition, int.class, 1); - public static final Reflection.Field blockPositionZ = Reflection.getField(baseBlockPosition, int.class, 2); public static final Class iBlockData = BlockState.class; public static final Class block = Block.class; @@ -79,11 +75,11 @@ public class TechHider { this.obfuscationTarget = CraftMagicNumbers.getBlock(obfuscationTarget).defaultBlockState(); this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget); - techhiders.put(blockActionPacket, this::blockActionHider); - techhiders.put(blockChangePacket, this::blockChangeHider); - techhiders.put(tileEntityDataPacket, this::tileEntityDataHider); - techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this)); - techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(this)); + techhiders.put(ClientboundBlockEventPacket.class, (player, o) -> this.blockActionHider(player, (ClientboundBlockEventPacket) o)); + techhiders.put(ClientboundBlockUpdatePacket.class, (player, o) -> this.blockChangeHider(player, (ClientboundBlockUpdatePacket) o)); + techhiders.put(ClientboundBlockEntityDataPacket.class, (player, o) -> this.tileEntityDataHider(player, (ClientboundBlockEntityDataPacket) o)); + techhiders.put(ClientboundSectionBlocksUpdatePacket.class, (BiFunction) ProtocolWrapper.impl.multiBlockChangeGenerator(this)); + techhiders.put(ClientboundLevelChunkWithLightPacket.class, (BiFunction) ChunkHider.impl.chunkHiderGenerator(this)); techhiders.put(ServerboundUseItemOnPacket.class, (p, packet) -> locationEvaluator.suppressInteractions(p) ? null : packet); techhiders.put(ServerboundInteractPacket.class, (p, packet) -> locationEvaluator.suppressInteractions(p) ? null : packet); @@ -97,53 +93,43 @@ public class TechHider { techhiders.forEach(TinyProtocol.instance::removeFilter); } - public static final Class multiBlockChangePacket = ClientboundSectionBlocksUpdatePacket.class; - public static final UnaryOperator multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(TechHider.multiBlockChangePacket); + public static final UnaryOperator multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(ClientboundSectionBlocksUpdatePacket.class); - private static final Class blockChangePacket = ClientboundBlockUpdatePacket.class; - private static final Function blockChangeCloner = ProtocolUtils.shallowCloneGenerator(blockChangePacket); - private static final Reflection.Field blockChangePosition = Reflection.getField(blockChangePacket, blockPosition, 0); - private static final Reflection.Field blockChangeBlockData = Reflection.getField(blockChangePacket, iBlockData, 0); + private static final UnaryOperator blockChangeCloner = ProtocolUtils.shallowCloneGenerator(ClientboundBlockUpdatePacket.class); - private Object blockChangeHider(Player p, Object packet) { - switch (locationEvaluator.checkBlockPos(p, blockChangePosition.get(packet))) { + private ClientboundBlockUpdatePacket blockChangeHider(Player p, ClientboundBlockUpdatePacket packet) { + switch (locationEvaluator.checkBlockPos(p, packet.getPos())) { case SKIP: return packet; case CHECK: - if (!iBlockDataHidden((BlockState) blockChangeBlockData.get(packet))) { + if (!iBlockDataHidden(packet.blockState)) { return packet; } case HIDE: packet = blockChangeCloner.apply(packet); - blockChangeBlockData.set(packet, obfuscationTarget); + packet.blockState = (BlockState) obfuscationTarget; return packet; case HIDE_AIR: default: packet = blockChangeCloner.apply(packet); - blockChangeBlockData.set(packet, AIR); + packet.blockState = (BlockState) AIR; return packet; } } - private static final Class blockActionPacket = ClientboundBlockEventPacket.class; - private static final Reflection.Field blockActionPosition = Reflection.getField(blockActionPacket, blockPosition, 0); - - private Object blockActionHider(Player p, Object packet) { - if (locationEvaluator.checkBlockPos(p, blockActionPosition.get(packet)) == State.SKIP) { + private Object blockActionHider(Player p, ClientboundBlockEventPacket packet) { + if (locationEvaluator.checkBlockPos(p, packet.getPos()) == State.SKIP) { return packet; } return null; } - public static final Class tileEntityDataPacket = ClientboundBlockEntityDataPacket.class; - private static final Reflection.Field tileEntityDataPosition = Reflection.getField(tileEntityDataPacket, blockPosition, 0); - - private Object tileEntityDataHider(Player p, Object packet) { - switch (locationEvaluator.checkBlockPos(p, tileEntityDataPosition.get(packet))) { + private ClientboundBlockEntityDataPacket tileEntityDataHider(Player p, ClientboundBlockEntityDataPacket packet) { + switch (locationEvaluator.checkBlockPos(p, packet.getPos())) { case SKIP: return packet; case CHECK: - if (ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)) { + if (packet.getType() != BlockEntityType.SIGN) { return packet; } default: @@ -173,8 +159,8 @@ public class TechHider { return skipChunkSection(player, ProtocolUtils.posToChunk(x), ProtocolUtils.posToChunk(y), ProtocolUtils.posToChunk(z)) ? State.SKIP : State.CHECK; } - default State checkBlockPos(Player player, Object pos) { - return check(player, blockPositionX.get(pos), blockPositionY.get(pos), blockPositionZ.get(pos)); + default State checkBlockPos(Player player, Vec3i pos) { + return check(player, pos.getX(), pos.getY(), pos.getZ()); } default boolean blockPrecise(Player player, int x, int y, int z) { diff --git a/SpigotCore/SpigotCore_Main/src/spigotcore.accesswidener b/SpigotCore/SpigotCore_Main/src/spigotcore.accesswidener new file mode 100644 index 00000000..1fcaac48 --- /dev/null +++ b/SpigotCore/SpigotCore_Main/src/spigotcore.accesswidener @@ -0,0 +1,57 @@ +accessWidener v2 named + +# For TinyProtocol and CheckpointUtilsJ9 +accessible field net/minecraft/server/network/ServerConnectionListener channels Ljava/util/List; + +# For ErrorHandler +accessible field org/spigotmc/WatchdogThread instance Lorg/spigotmc/WatchdogThread; + +# For ResourceKey +accessible method net/minecraft/resources/ResourceKey (Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/resources/ResourceLocation;)V +accessible method net/minecraft/resources/ResourceLocation (Ljava/lang/String;Ljava/lang/String;)V + +# For SteamwarGameProfileRepository +accessible field net/minecraft/server/MinecraftServer services Lnet/minecraft/server/Services; +mutable field net/minecraft/server/MinecraftServer services Lnet/minecraft/server/Services; + +# REntity +accessible field net/minecraft/network/protocol/game/ClientboundAnimatePacket id I +mutable field net/minecraft/network/protocol/game/ClientboundAnimatePacket id I +accessible field net/minecraft/network/protocol/game/ClientboundAnimatePacket action I +mutable field net/minecraft/network/protocol/game/ClientboundAnimatePacket action I + +accessible field net/minecraft/network/protocol/game/ClientboundEntityEventPacket entityId I +mutable field net/minecraft/network/protocol/game/ClientboundEntityEventPacket entityId I +accessible field net/minecraft/network/protocol/game/ClientboundEntityEventPacket eventId B +mutable field net/minecraft/network/protocol/game/ClientboundEntityEventPacket eventId B + +accessible field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket yHeadRot B +mutable field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket yHeadRot B +## + TechHider +accessible field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket entityId I +mutable field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket entityId I + +# For ChunkHider +accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket chunkData Lnet/minecraft/network/protocol/game/ClientboundLevelChunkPacketData; +mutable field net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket chunkData Lnet/minecraft/network/protocol/game/ClientboundLevelChunkPacketData; + +accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData buffer [B +mutable field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData buffer [B +accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData blockEntitiesData Ljava/util/List; +mutable field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData blockEntitiesData Ljava/util/List; + +accessible class net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo +accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo type Lnet/minecraft/world/level/block/entity/BlockEntityType; +accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo packedXZ I +accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData$BlockEntityInfo y I + +# For TechHider +accessible field net/minecraft/network/protocol/game/ClientboundMoveEntityPacket entityId I +accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket sectionPos Lnet/minecraft/core/SectionPos; +accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket positions [S +mutable field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket positions [S +accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket states [Lnet/minecraft/world/level/block/state/BlockState; +mutable field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket states [Lnet/minecraft/world/level/block/state/BlockState; + +# For legacy/TechHider +mutable field net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket blockState Lnet/minecraft/world/level/block/state/BlockState; From aa66b3dc838136559e1e9611b7157e9c1df006e9 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 18:29:44 +0200 Subject: [PATCH 07/19] Optimize imports --- .../features/simulator/SimulatorCursor.java | 12 ++---------- .../blastresistance/BlastResistanceCommand.java | 1 - .../features/techhider/TechHiderCommand.java | 2 +- .../de/steamwar/bausystem/features/tracer/Trace.java | 1 - .../bausystem/features/world/BauMemberUpdate.java | 2 -- .../bausystem/features/world/SpectatorListener.java | 2 +- .../bausystem/features/xray/XrayCommand.java | 2 +- .../src/de/steamwar/fightsystem/FightSystem.java | 5 ++++- .../src/de/steamwar/fightsystem/fight/FightTeam.java | 4 +++- .../fightsystem/listener/InFightInventory.java | 1 - .../de/steamwar/fightsystem/listener/Recording.java | 2 -- .../steamwar/fightsystem/record/PacketProcessor.java | 4 +++- .../src/de/steamwar/core/events/AntiNocom.java | 4 ---- .../src/de/steamwar/cursor/CursorListener.java | 2 +- .../src/de/steamwar/entity/RFallingBlockEntity.java | 1 - .../src/de/steamwar/techhider/legacy/TechHider.java | 1 - .../de/steamwar/velocitycore/discord/DiscordBot.java | 7 +++---- 17 files changed, 19 insertions(+), 34 deletions(-) diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursor.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursor.java index 73bbfcd7..72178e23 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursor.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/simulator/SimulatorCursor.java @@ -55,19 +55,11 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.event.player.PlayerDropItemEvent; -import org.bukkit.event.player.PlayerItemHeldEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.player.PlayerToggleSneakEvent; +import org.bukkit.event.player.*; import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/blastresistance/BlastResistanceCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/blastresistance/BlastResistanceCommand.java index 7c9430a7..7afb0e5f 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/blastresistance/BlastResistanceCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/slaves/blastresistance/BlastResistanceCommand.java @@ -32,7 +32,6 @@ import de.steamwar.linkage.Linked; import org.bukkit.Material; import org.bukkit.entity.Player; -import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @Linked diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java index 4744e41d..2f401a59 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java @@ -31,6 +31,7 @@ import de.steamwar.command.TypeMapper; import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.linkage.Linked; import de.steamwar.linkage.LinkedInstance; +import de.steamwar.techhider.legacy.TechHider; import net.md_5.bungee.api.ChatMessageType; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; @@ -38,7 +39,6 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; -import de.steamwar.techhider.legacy.TechHider; import java.util.*; import java.util.stream.Collectors; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/Trace.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/Trace.java index 95107761..8eb49d79 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/Trace.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/tracer/Trace.java @@ -25,7 +25,6 @@ import de.steamwar.bausystem.features.tracer.rendering.TraceEntity; import de.steamwar.bausystem.features.tracer.rendering.ViewFlag; import de.steamwar.bausystem.region.Region; import de.steamwar.entity.REntity; -import de.steamwar.entity.REntityAction; import de.steamwar.entity.REntityServer; import lombok.Getter; import lombok.Setter; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java index ed5b068b..d3416e08 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java @@ -36,8 +36,6 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.player.PlayerItemConsumeEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java index 92a5c59e..645600cd 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java @@ -25,6 +25,7 @@ import de.steamwar.bausystem.config.BauServer; import de.steamwar.bausystem.utils.BauMemberUpdateEvent; import de.steamwar.linkage.Linked; import de.steamwar.sql.BauweltMember; +import de.steamwar.techhider.legacy.TechHider; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -39,7 +40,6 @@ import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.player.*; import org.bukkit.util.Vector; -import de.steamwar.techhider.legacy.TechHider; import java.util.HashSet; import java.util.Set; diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java index 55bcbeeb..aa90081c 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/xray/XrayCommand.java @@ -28,6 +28,7 @@ import de.steamwar.command.SWCommand; import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.linkage.Linked; import de.steamwar.linkage.LinkedInstance; +import de.steamwar.techhider.legacy.TechHider; import net.md_5.bungee.api.ChatMessageType; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import net.minecraft.server.level.ServerPlayer; @@ -40,7 +41,6 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerQuitEvent; -import de.steamwar.techhider.legacy.TechHider; import java.util.*; import java.util.function.BiFunction; diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java index d3339975..06ceac41 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/FightSystem.java @@ -32,7 +32,10 @@ import de.steamwar.fightsystem.record.GlobalRecorder; import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.OneShotStateDependent; import de.steamwar.fightsystem.states.StateDependentListener; -import de.steamwar.fightsystem.utils.*; +import de.steamwar.fightsystem.utils.FightUI; +import de.steamwar.fightsystem.utils.HullHider; +import de.steamwar.fightsystem.utils.SWSound; +import de.steamwar.fightsystem.utils.TechHiderWrapper; import de.steamwar.linkage.AbstractLinker; import de.steamwar.linkage.SpigotLinker; import de.steamwar.message.Message; diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java index a8cddb3a..0119bf40 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/fight/FightTeam.java @@ -35,7 +35,9 @@ import de.steamwar.fightsystem.listener.TeamArea; import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.OneShotStateDependent; import de.steamwar.fightsystem.states.StateDependent; -import de.steamwar.fightsystem.utils.*; +import de.steamwar.fightsystem.utils.FightUI; +import de.steamwar.fightsystem.utils.ItemBuilder; +import de.steamwar.fightsystem.utils.Region; import de.steamwar.fightsystem.winconditions.Wincondition; import de.steamwar.fightsystem.winconditions.Winconditions; import de.steamwar.inventory.SWItem; diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/InFightInventory.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/InFightInventory.java index 8d7d339c..c49b46c6 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/InFightInventory.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/InFightInventory.java @@ -25,7 +25,6 @@ import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.linkage.Linked; import org.bukkit.Material; -import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java index d11c98db..ecd333ec 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/listener/Recording.java @@ -20,7 +20,6 @@ package de.steamwar.fightsystem.listener; import com.comphenix.tinyprotocol.TinyProtocol; -import de.steamwar.Reflection; import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.FightSystem; @@ -37,7 +36,6 @@ import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.fightsystem.states.StateDependentTask; import de.steamwar.fightsystem.utils.SWSound; import de.steamwar.linkage.Linked; -import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.network.protocol.game.ServerboundUseItemPacket; import net.minecraft.world.InteractionHand; diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java index b62ef662..d7cf16e6 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/record/PacketProcessor.java @@ -34,7 +34,9 @@ import de.steamwar.fightsystem.fight.FightWorld; import de.steamwar.fightsystem.fight.FreezeWorld; import de.steamwar.fightsystem.listener.FightScoreboard; import de.steamwar.fightsystem.states.FightState; -import de.steamwar.fightsystem.utils.*; +import de.steamwar.fightsystem.utils.FightUI; +import de.steamwar.fightsystem.utils.Message; +import de.steamwar.fightsystem.utils.TechHiderWrapper; import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.Team; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java index b5276c00..d3625026 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java @@ -20,16 +20,13 @@ package de.steamwar.core.events; import com.comphenix.tinyprotocol.TinyProtocol; -import de.steamwar.Reflection; import de.steamwar.core.Core; import de.steamwar.linkage.Linked; import de.steamwar.sql.SWException; import de.steamwar.techhider.ProtocolUtils; -import de.steamwar.techhider.TechHider; import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket; -import net.minecraft.world.phys.BlockHitResult; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -38,7 +35,6 @@ import org.bukkit.event.player.PlayerQuitEvent; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; @Linked public class AntiNocom implements Listener { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java index 2d7b438f..99e286ca 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/cursor/CursorListener.java @@ -13,7 +13,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerInteractEvent; -import java.util.*; +import java.util.Optional; @Linked diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RFallingBlockEntity.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RFallingBlockEntity.java index e1ab5cba..50226dfa 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RFallingBlockEntity.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RFallingBlockEntity.java @@ -20,7 +20,6 @@ package de.steamwar.entity; import de.steamwar.techhider.BlockIds; -import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import org.bukkit.Location; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java index 9a96771f..f8e027be 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java @@ -20,7 +20,6 @@ package de.steamwar.techhider.legacy; import com.comphenix.tinyprotocol.TinyProtocol; -import de.steamwar.Reflection; import de.steamwar.techhider.BlockIds; import de.steamwar.techhider.ProtocolUtils; import lombok.Getter; diff --git a/VelocityCore/src/de/steamwar/velocitycore/discord/DiscordBot.java b/VelocityCore/src/de/steamwar/velocitycore/discord/DiscordBot.java index 9cbd4061..7e7dba1e 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/discord/DiscordBot.java +++ b/VelocityCore/src/de/steamwar/velocitycore/discord/DiscordBot.java @@ -22,7 +22,6 @@ package de.steamwar.velocitycore.discord; import de.steamwar.command.SWCommand; import de.steamwar.messages.Chatter; import de.steamwar.sql.Event; -import de.steamwar.sql.SteamwarUser; import de.steamwar.velocitycore.VelocityCore; import de.steamwar.velocitycore.discord.channels.*; import de.steamwar.velocitycore.discord.listeners.ChannelListener; @@ -53,12 +52,12 @@ import net.dv8tion.jda.api.utils.MemberCachePolicy; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import java.awt.*; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -import java.util.stream.Collectors; public class DiscordBot { public static final String ARGUMENT_NAME = "arguments"; From 2bc164c1b0f8e7cf9455d38230b4ffd662251ec2 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 20:38:33 +0200 Subject: [PATCH 08/19] Extract widener definition into a widener.gradle.kts plugin --- BauSystem/BauSystem_Main/build.gradle.kts | 45 +---------- SpigotCore/SpigotCore_Main/build.gradle.kts | 45 +---------- buildSrc/{build.gradle => build.gradle.kts} | 23 +++--- .../groovy}/steamwar.devserver.gradle | 0 .../{ => main/groovy}/steamwar.java.gradle | 0 .../{ => main/groovy}/steamwar.kotlin.gradle | 0 buildSrc/src/main/kotlin/WidenerExtension.kt | 52 +++++++++++++ buildSrc/src/main/kotlin/widener.gradle.kts | 78 +++++++++++++++++++ 8 files changed, 148 insertions(+), 95 deletions(-) rename buildSrc/{build.gradle => build.gradle.kts} (82%) rename buildSrc/src/{ => main/groovy}/steamwar.devserver.gradle (100%) rename buildSrc/src/{ => main/groovy}/steamwar.java.gradle (100%) rename buildSrc/src/{ => main/groovy}/steamwar.kotlin.gradle (100%) create mode 100644 buildSrc/src/main/kotlin/WidenerExtension.kt create mode 100644 buildSrc/src/main/kotlin/widener.gradle.kts diff --git a/BauSystem/BauSystem_Main/build.gradle.kts b/BauSystem/BauSystem_Main/build.gradle.kts index f81b7614..7f5f9b18 100644 --- a/BauSystem/BauSystem_Main/build.gradle.kts +++ b/BauSystem/BauSystem_Main/build.gradle.kts @@ -19,6 +19,7 @@ plugins { steamwar.java + widener } tasks.compileJava { @@ -30,45 +31,6 @@ java { targetCompatibility = JavaVersion.VERSION_21 } -// ─── Collect all .accesswidener files from this plugin's resources ──────────── -val accessWidenerFiles: FileCollection = fileTree("src/") { - include("**/*.accesswidener") -} - -val paperJarProvider: Provider = provider { - val dep = libs.nms.get() - configurations["compileClasspath"].resolvedConfiguration.resolvedArtifacts.first { artifact -> - artifact.moduleVersion.id.module.group == dep.module.group && artifact.moduleVersion.id.module.name == dep.module.name - }.file -} - -// ─── Widen the Paper dev JAR so the IDE / javac see the patched access ──────── -val widenedJar by tasks.registering(JavaExec::class) { - description = "Produces a widened copy of the Paper dev JAR for compile-time use." - group = "widener" - - // Re-run whenever the .accesswidener files change - inputs.file(paperJarProvider) - inputs.files(accessWidenerFiles) - - val output = layout.buildDirectory.file("widened/paper-widened.jar") - outputs.file(output) - - classpath = project(":AccessWidener").tasks.named("shadowJar").get().outputs.files - - mainClass.set("de.steamwar.Main") - - doFirst { - args = buildList { - add(paperJarProvider.get().absolutePath) - add(output.get().asFile.absolutePath) - addAll(accessWidenerFiles.map { it.absolutePath }) - } - } - - dependsOn(":AccessWidener:shadowJar") -} - dependencies { compileOnly(libs.classindex) annotationProcessor(libs.classindex) @@ -79,7 +41,6 @@ dependencies { compileOnly(libs.paperapi) compileOnly(libs.nms) - compileOnly(files(widenedJar)) compileOnly(libs.fawe) compileOnly(libs.netty) @@ -88,6 +49,6 @@ dependencies { implementation(files("$projectDir/../libs/YAPION-SNAPSHOT.jar")) } -tasks.compileJava { - dependsOn(widenedJar) +widener { + fromCatalog(libs.nms) } diff --git a/SpigotCore/SpigotCore_Main/build.gradle.kts b/SpigotCore/SpigotCore_Main/build.gradle.kts index 34bc1d49..322b1046 100644 --- a/SpigotCore/SpigotCore_Main/build.gradle.kts +++ b/SpigotCore/SpigotCore_Main/build.gradle.kts @@ -19,6 +19,7 @@ plugins { steamwar.java + widener } tasks.compileJava { @@ -42,45 +43,6 @@ tasks.withType().configureEach { jvmArgs("--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED") } -// ─── Collect all .accesswidener files from this plugin's resources ──────────── -val accessWidenerFiles: FileCollection = fileTree("src/") { - include("**/*.accesswidener") -} - -val paperJarProvider: Provider = provider { - val dep = libs.nms.get() - configurations["compileClasspath"].resolvedConfiguration.resolvedArtifacts.first { artifact -> - artifact.moduleVersion.id.module.group == dep.module.group && artifact.moduleVersion.id.module.name == dep.module.name - }.file -} - -// ─── Widen the Paper dev JAR so the IDE / javac see the patched access ──────── -val widenedJar by tasks.registering(JavaExec::class) { - description = "Produces a widened copy of the Paper dev JAR for compile-time use." - group = "widener" - - // Re-run whenever the .accesswidener files change - inputs.file(paperJarProvider) - inputs.files(accessWidenerFiles) - - val output = layout.buildDirectory.file("widened/paper-widened.jar") - outputs.file(output) - - classpath = project(":AccessWidener").tasks.named("shadowJar").get().outputs.files - - mainClass.set("de.steamwar.Main") - - doFirst { - args = buildList { - add(paperJarProvider.get().absolutePath) - add(output.get().asFile.absolutePath) - addAll(accessWidenerFiles.map { it.absolutePath }) - } - } - - dependsOn(":AccessWidener:shadowJar") -} - dependencies { compileOnly(libs.classindex) annotationProcessor(libs.classindex) @@ -92,7 +54,6 @@ dependencies { compileOnly(libs.paperapi) compileOnly(libs.nms) - compileOnly(files(widenedJar)) compileOnly(libs.authlib) compileOnly(libs.datafixer) compileOnly(libs.netty) @@ -102,6 +63,6 @@ dependencies { implementation(libs.anvilgui) } -tasks.compileJava { - dependsOn(widenedJar) +widener { + fromCatalog(libs.nms) } diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle.kts similarity index 82% rename from buildSrc/build.gradle rename to buildSrc/build.gradle.kts index e1ee5bb0..3df3ec61 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle.kts @@ -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 @@ -18,21 +18,22 @@ */ plugins { - id 'groovy-gradle-plugin' -} - -sourceSets { - main { - groovy { - srcDirs("src/") - } - } + `kotlin-dsl` + `groovy-gradle-plugin` } repositories { mavenCentral() + gradlePluginPortal() +} + +sourceSets { + main { + groovy.srcDirs("src/main/groovy") + kotlin.srcDirs("src/main/kotlin") + } } dependencies { implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.21") -} \ No newline at end of file +} diff --git a/buildSrc/src/steamwar.devserver.gradle b/buildSrc/src/main/groovy/steamwar.devserver.gradle similarity index 100% rename from buildSrc/src/steamwar.devserver.gradle rename to buildSrc/src/main/groovy/steamwar.devserver.gradle diff --git a/buildSrc/src/steamwar.java.gradle b/buildSrc/src/main/groovy/steamwar.java.gradle similarity index 100% rename from buildSrc/src/steamwar.java.gradle rename to buildSrc/src/main/groovy/steamwar.java.gradle diff --git a/buildSrc/src/steamwar.kotlin.gradle b/buildSrc/src/main/groovy/steamwar.kotlin.gradle similarity index 100% rename from buildSrc/src/steamwar.kotlin.gradle rename to buildSrc/src/main/groovy/steamwar.kotlin.gradle diff --git a/buildSrc/src/main/kotlin/WidenerExtension.kt b/buildSrc/src/main/kotlin/WidenerExtension.kt new file mode 100644 index 00000000..d0263567 --- /dev/null +++ b/buildSrc/src/main/kotlin/WidenerExtension.kt @@ -0,0 +1,52 @@ +/* + * 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 . + */ + +import org.gradle.api.Project +import org.gradle.api.artifacts.MinimalExternalModuleDependency +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.provider.Provider +import javax.inject.Inject + +abstract class WidenerExtension { + + @get:Inject + abstract val project: Project + + abstract val inputJars: ConfigurableFileCollection + abstract val accessWidenerFiles: ConfigurableFileCollection + + fun fromCatalog(vararg dependencies: Provider) { + val files = dependencies.map { dependency -> + project.provider { + val dep = dependency.get() + + project.configurations.getByName("compileClasspath") + .resolvedConfiguration + .resolvedArtifacts + .first { + it.moduleVersion.id.module.group == dep.module.group && + it.moduleVersion.id.module.name == dep.module.name + } + .file + } + } + + inputJars.from(files) + } +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/widener.gradle.kts b/buildSrc/src/main/kotlin/widener.gradle.kts new file mode 100644 index 00000000..71da2e8d --- /dev/null +++ b/buildSrc/src/main/kotlin/widener.gradle.kts @@ -0,0 +1,78 @@ +/* + * 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 . + */ + +val widener = extensions.create("widener") + +widener.accessWidenerFiles.setFrom( + fileTree("src/") { include("**/*.accesswidener") } +) + +// ─── Tasks (unchanged from before) ─────────────────────────────────────────── + +val jarWidenerClasspath = rootProject.project(":AccessWidener") + .tasks.named("shadowJar") + .get().outputs.files + +val allWidenedJars = objects.fileCollection() + +project.gradle.projectsEvaluated { + if (widener.inputJars.isEmpty) { + logger.warn("[widener] No input JARs configured for ${project.name} — widenedJar tasks will not be registered.") + return@projectsEvaluated + } + + widener.inputJars.forEachIndexed { index, inputJar -> + val taskName = if (index == 0) "widenedJar" else "widenedJar$index" + val outputFile = layout.buildDirectory + .file("widened/${inputJar.nameWithoutExtension}-widened.jar") + + val task = tasks.register(taskName) { + description = "Produces a widened copy of ${inputJar.name} for compile-time use." + group = "widener" + + inputs.file(inputJar) + inputs.files(widener.accessWidenerFiles) + outputs.file(outputFile) + + classpath = jarWidenerClasspath + mainClass.set("de.steamwar.Main") + + dependsOn(rootProject.project(":AccessWidener").tasks.named("shadowJar")) + + doFirst { + args = buildList { + add(inputJar.absolutePath) + add(outputFile.get().asFile.absolutePath) + addAll(widener.accessWidenerFiles.map { it.absolutePath }) + } + } + } + + allWidenedJars.from(task) + } + + tasks.named("compileJava") { + // dependsOn(allWidenedJars.buildDependencies) + dependsOn(allWidenedJars) + } +} + +project.dependencies { + add("compileOnly", project.fileTree("build/widened")) +} From 932732737d8c075efb39d77d98c0dafcab25ffc2 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 22:35:13 +0200 Subject: [PATCH 09/19] Unreflect more stuff --- .../java/de/steamwar/WideningTransformer.java | 7 +- .../src/bausystem.accesswidener | 3 + .../bausystem/utils/WorldEditUtils.java | 39 ++- BauSystem/BauSystem_Main/src/plugin.yml | 2 +- BauSystem/build.gradle.kts | 2 + .../fightsystem/utils/TechHiderWrapper.java | 16 +- .../src/de/steamwar/Reflection.java | 279 ------------------ .../de/steamwar/core/BountifulWrapper.java | 16 - .../src/de/steamwar/core/Core.java | 13 +- .../de/steamwar/core/FlatteningWrapper.java | 34 --- .../src/de/steamwar/entity/RArmorStand.java | 2 +- .../src/de/steamwar/entity/RBlockDisplay.java | 4 +- .../src/de/steamwar/entity/RDisplay.java | 67 ++--- .../src/de/steamwar/entity/REntity.java | 64 ++-- .../src/de/steamwar/entity/RInteraction.java | 38 +-- .../src/de/steamwar/entity/RItemDisplay.java | 8 +- .../src/de/steamwar/entity/RPlayer.java | 7 +- .../src/de/steamwar/entity/RTextDisplay.java | 20 +- .../steamwar/techhider/legacy/ChunkHider.java | 8 +- buildSrc/src/main/kotlin/WidenerExtension.kt | 6 +- buildSrc/src/main/kotlin/widener.gradle.kts | 3 - 21 files changed, 162 insertions(+), 476 deletions(-) delete mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java diff --git a/AccessWidener/src/main/java/de/steamwar/WideningTransformer.java b/AccessWidener/src/main/java/de/steamwar/WideningTransformer.java index 2215c769..8fcd33e2 100644 --- a/AccessWidener/src/main/java/de/steamwar/WideningTransformer.java +++ b/AccessWidener/src/main/java/de/steamwar/WideningTransformer.java @@ -39,11 +39,6 @@ public class WideningTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { - byte[] result = patcher.patch(className, classfileBuffer); - if (result == null) { - return classfileBuffer; - } else { - return result; - } + return patcher.patch(className, classfileBuffer); } } diff --git a/BauSystem/BauSystem_Main/src/bausystem.accesswidener b/BauSystem/BauSystem_Main/src/bausystem.accesswidener index 5dd89fdf..51fe2890 100644 --- a/BauSystem/BauSystem_Main/src/bausystem.accesswidener +++ b/BauSystem/BauSystem_Main/src/bausystem.accesswidener @@ -11,3 +11,6 @@ mutable field org/bukkit/craftbukkit/block/CraftBlockState world Lorg/bukkit/cra # For TickManager accessible field net/minecraft/server/ServerTickRateManager remainingSprintTicks J + +# Test +accessible field net/minecraft/core/registries/BuiltInRegistries BLOCK_ENTITY_TYPE Lnet/minecraft/core/Registry; \ No newline at end of file diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/WorldEditUtils.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/WorldEditUtils.java index 0bab1548..8c493707 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/WorldEditUtils.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/utils/WorldEditUtils.java @@ -19,6 +19,7 @@ package de.steamwar.bausystem.utils; +import com.fastasyncworldedit.core.regions.selector.PolyhedralRegionSelector; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalSession; @@ -32,16 +33,19 @@ import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionSelector; +import com.sk89q.worldedit.regions.selector.*; import com.sk89q.worldedit.regions.selector.limit.SelectorLimits; -import de.steamwar.Reflection; +import com.sk89q.worldedit.world.World; import de.steamwar.bausystem.shared.Pair; import lombok.SneakyThrows; import lombok.experimental.UtilityClass; import org.bukkit.Location; -import org.bukkit.World; import org.bukkit.entity.Player; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; @UtilityClass @@ -91,17 +95,36 @@ public class WorldEditUtils { .getRegionSelector(BukkitAdapter.adapt(player.getWorld())); return new Pair<>(regionSelector.getClass(), regionSelector.getVertices() .stream() - .map(blockVector3 -> blockVector3 == null ? null : adapt(player.getWorld(), blockVector3)) + .map(blockVector3 -> { + if (blockVector3 == null) { + return null; + } else { + return BukkitAdapter.adapt(player.getWorld(), blockVector3); + } + }) .collect(Collectors.toList())); } + private static final Map, Function> constructors = new HashMap<>(); + static { + constructors.put(CuboidRegionSelector.class, CuboidRegionSelector::new); + constructors.put(ExtendingCuboidRegionSelector.class, ExtendingCuboidRegionSelector::new); + constructors.put(Polygonal2DRegionSelector.class, Polygonal2DRegionSelector::new); + constructors.put(EllipsoidRegionSelector.class, EllipsoidRegionSelector::new); + constructors.put(SphereRegionSelector.class, SphereRegionSelector::new); + constructors.put(CylinderRegionSelector.class, CylinderRegionSelector::new); + constructors.put(ConvexPolyhedralRegionSelector.class, ConvexPolyhedralRegionSelector::new); + constructors.put(PolyhedralRegionSelector.class, PolyhedralRegionSelector::new); + } + public void setVertices(Player player, Class clazz, List vertices) { LocalSession localSession = WorldEdit.getInstance() .getSessionManager() .get(BukkitAdapter.adapt(player)); - Reflection.Constructor constructorInvoker = Reflection.getConstructor(clazz, com.sk89q.worldedit.world.World.class); - RegionSelector regionSelector = (RegionSelector) constructorInvoker.invoke(BukkitAdapter.adapt(player.getWorld())); + Function constructor = constructors.get(clazz); + if (constructor == null) return; + RegionSelector regionSelector = constructor.apply(BukkitAdapter.adapt(player.getWorld())); localSession.setRegionSelector(BukkitAdapter.adapt(player.getWorld()), regionSelector); if (vertices.isEmpty()) return; @@ -127,13 +150,9 @@ public class WorldEditUtils { try { BlockVector3 min = regionSelector.getRegion().getMinimumPoint(); BlockVector3 max = regionSelector.getRegion().getMaximumPoint(); - return new Pair<>(adapt(player.getWorld(), min), adapt(player.getWorld(), max)); + return new Pair<>(BukkitAdapter.adapt(player.getWorld(), min), BukkitAdapter.adapt(player.getWorld(), max)); } catch (IncompleteRegionException e) { return null; } } - - private Location adapt(World world, BlockVector3 blockVector3) { - return new Location(world, blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ()); - } } diff --git a/BauSystem/BauSystem_Main/src/plugin.yml b/BauSystem/BauSystem_Main/src/plugin.yml index 71b08d95..bbc550bc 100644 --- a/BauSystem/BauSystem_Main/src/plugin.yml +++ b/BauSystem/BauSystem_Main/src/plugin.yml @@ -4,7 +4,7 @@ version: "2.0" depend: [ WorldEdit, SpigotCore ] load: POSTWORLD main: de.steamwar.bausystem.BauSystem -api-version: "1.13" +api-version: "1.21" website: "https://steamwar.de" description: "So unseriös wie wir sind: BauSystem nur besser." diff --git a/BauSystem/build.gradle.kts b/BauSystem/build.gradle.kts index aa03f184..1d158e37 100644 --- a/BauSystem/build.gradle.kts +++ b/BauSystem/build.gradle.kts @@ -39,5 +39,7 @@ tasks.register("DevBau21") { dependsOn(":SchematicSystem:shadowJar") dependsOn(":AccessWidener:shadowJar") template = "Bau21" + // TODO: Add to every new server start! newer than 1.21 inclusive! jvmArgs = "-javaagent:/home/yoyonow/Bau21/plugins/AccessWidener.jar=start" + setdParams(mapOf("paper.disablePluginRemapping" to "true")) } diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java index 3fc4a364..7c4c9d77 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/TechHiderWrapper.java @@ -19,7 +19,6 @@ package de.steamwar.fightsystem.utils; -import de.steamwar.Reflection; import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.events.BoardingEvent; @@ -51,7 +50,7 @@ import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; import java.util.Collection; -import java.util.Optional; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -82,18 +81,13 @@ public class TechHiderWrapper extends StateDependent implements Listener { .map(CraftMagicNumbers::getBlock) .collect(Collectors.toUnmodifiableSet()); - Object blockEntityType; - try { - blockEntityType = BuiltInRegistries.class.getDeclaredField("BLOCK_ENTITY_TYPE").get(null); - } catch (Exception e) { - throw new IllegalStateException(e); - } - Reflection.Method method = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "get", Optional.class, ResourceLocation.class); Set> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream() - .map((id) -> { + .map(id -> { ResourceLocation loc = ResourceLocation.parse(id); - return ((Optional>>) method.invoke(blockEntityType, loc)).get().value(); + return BuiltInRegistries.BLOCK_ENTITY_TYPE.get(loc).orElse(null); }) + .filter(Objects::nonNull) + .map(Holder.Reference::value) .collect(Collectors.toUnmodifiableSet()); new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith), new AccessPrivilegeProvider() { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/Reflection.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/Reflection.java index d75fdae7..324d0e44 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/Reflection.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/Reflection.java @@ -19,292 +19,13 @@ package de.steamwar; -import de.steamwar.core.Core; import jdk.internal.misc.Unsafe; -import lombok.AllArgsConstructor; import lombok.experimental.UtilityClass; -import org.bukkit.Bukkit; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; @UtilityClass public final class Reflection { - public static final int MAJOR_VERSION; - public static final int MINOR_VERSION; - - static { - String[] version = Bukkit.getServer().getBukkitVersion().split("-")[0].split("\\."); - MAJOR_VERSION = Integer.parseInt(version[1]); - MINOR_VERSION = version.length > 2 ? Integer.parseInt(version[2]) : 0; - } - - private static final String ORG_BUKKIT_CRAFTBUKKIT = Bukkit.getServer().getClass().getPackage().getName(); - public static final String LEGACY_NET_MINECRAFT_SERVER = ORG_BUKKIT_CRAFTBUKKIT.replace("org.bukkit.craftbukkit", "net.minecraft.server"); - - private static final Map spigotClassnames = new HashMap<>(); - - static { - // See https://mappings.dev for complete mappings - spigotClassnames.put("net.minecraft.Util", "net.minecraft.SystemUtils"); - - spigotClassnames.put("net.minecraft.core.BlockPos", "net.minecraft.core.BlockPosition"); - spigotClassnames.put("net.minecraft.core.DefaultedRegistry", "net.minecraft.core.RegistryBlocks"); - spigotClassnames.put("net.minecraft.core.IdMapper", "net.minecraft.core.RegistryBlockID"); - spigotClassnames.put("net.minecraft.core.Vec3i", "net.minecraft.core.BaseBlockPosition"); - - spigotClassnames.put("net.minecraft.nbt.CompoundTag", "net.minecraft.nbt.NBTTagCompound"); - - spigotClassnames.put("net.minecraft.network.Connection", "net.minecraft.network.NetworkManager"); - - spigotClassnames.put("net.minecraft.network.chat.Component", "net.minecraft.network.chat.IChatBaseComponent"); - - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundAddEntityPacket", "net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundAddPlayerPacket", "net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawn"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundAnimatePacket", "net.minecraft.network.protocol.game.PacketPlayOutAnimation"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket", "net.minecraft.network.protocol.game.PacketPlayOutBlockBreak"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket", "net.minecraft.network.protocol.game.PacketPlayOutTileEntityData"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundBlockEventPacket", "net.minecraft.network.protocol.game.PacketPlayOutBlockAction"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket", "net.minecraft.network.protocol.game.PacketPlayOutBlockChange"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundContainerClosePacket", "net.minecraft.network.protocol.game.PacketPlayOutCloseWindow"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundEntityEventPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityStatus"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundExplodePacket", "net.minecraft.network.protocol.game.PacketPlayOutExplosion"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundGameEventPacket", "net.minecraft.network.protocol.game.PacketPlayOutGameStateChange"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo", "net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$a"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundLevelEventPacket", "net.minecraft.network.protocol.game.PacketPlayOutWorldEvent"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket", "net.minecraft.network.protocol.game.PacketPlayOutWorldParticles"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntity"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Pos", "net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutRelEntityMove"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$PosRot", "net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutRelEntityMoveLook"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot", "net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutEntityLook"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket", "net.minecraft.network.protocol.game.PacketPlayOutOpenSignEditor"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityDestroy"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundRotateHeadPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityHeadRotation"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket", "net.minecraft.network.protocol.game.PacketPlayOutMultiBlockChange"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityVelocity"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityEquipment"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetObjectivePacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSetScorePacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundSoundPacket", "net.minecraft.network.protocol.game.PacketPlayOutNamedSoundEffect"); - spigotClassnames.put("net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket", "net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport"); - spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundContainerClickPacket", "net.minecraft.network.protocol.game.PacketPlayInWindowClick"); - spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundInteractPacket", "net.minecraft.network.protocol.game.PacketPlayInUseEntity"); - spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundInteractPacket$Action", "net.minecraft.network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction"); - spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundInteractPacket$ActionType", "net.minecraft.network.protocol.game.PacketPlayInUseEntity$b"); - spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos", "net.minecraft.network.protocol.game.PacketPlayInFlying$PacketPlayInPosition"); - spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot", "net.minecraft.network.protocol.game.PacketPlayInFlying$PacketPlayInPositionLook"); - spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Rot", "net.minecraft.network.protocol.game.PacketPlayInFlying$PacketPlayInLook"); - spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundPlayerActionPacket", "net.minecraft.network.protocol.game.PacketPlayInBlockDig"); - spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket", "net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot"); - spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundSignUpdatePacket", "net.minecraft.network.protocol.game.PacketPlayInUpdateSign"); - spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundUseItemPacket", "net.minecraft.network.protocol.game.PacketPlayInBlockPlace"); - spigotClassnames.put("net.minecraft.network.protocol.game.ServerboundUseItemOnPacket", "net.minecraft.network.protocol.game.PacketPlayInUseItem"); - - spigotClassnames.put("net.minecraft.network.syncher.EntityDataAccessor", "net.minecraft.network.syncher.DataWatcherObject"); - spigotClassnames.put("net.minecraft.network.syncher.EntityDataSerializer", "net.minecraft.network.syncher.DataWatcherSerializer"); - spigotClassnames.put("net.minecraft.network.syncher.EntityDataSerializers", "net.minecraft.network.syncher.DataWatcherRegistry"); - spigotClassnames.put("net.minecraft.network.syncher.SynchedEntityData$DataItem", "net.minecraft.network.syncher.DataWatcher$Item"); - - spigotClassnames.put("net.minecraft.server.ServerScoreboard$Method", "net.minecraft.server.ScoreboardServer$Action"); - - spigotClassnames.put("net.minecraft.server.level.ChunkMap", "net.minecraft.server.level.PlayerChunkMap"); - spigotClassnames.put("net.minecraft.server.level.ChunkMap$TrackedEntity", "net.minecraft.server.level.PlayerChunkMap$EntityTracker"); - spigotClassnames.put("net.minecraft.server.level.ServerChunkCache", "net.minecraft.server.level.ChunkProviderServer"); - spigotClassnames.put("net.minecraft.server.level.ServerLevel", "net.minecraft.server.level.WorldServer"); - spigotClassnames.put("net.minecraft.server.level.ServerPlayer", "net.minecraft.server.level.EntityPlayer"); - - spigotClassnames.put("net.minecraft.server.network.ServerConnectionListener", "net.minecraft.server.network.ServerConnection"); - - spigotClassnames.put("net.minecraft.world.InteractionHand", "net.minecraft.world.EnumHand"); - - spigotClassnames.put("net.minecraft.world.entity.EntityType", "net.minecraft.world.entity.EntityTypes"); - spigotClassnames.put("net.minecraft.world.entity.Pose", "net.minecraft.world.entity.EntityPose"); - - spigotClassnames.put("net.minecraft.world.entity.item.PrimedTnt", "net.minecraft.world.entity.item.EntityTNTPrimed"); - - spigotClassnames.put("net.minecraft.world.entity.projectile.AbstractArrow", "net.minecraft.world.entity.projectile.EntityArrow"); - - spigotClassnames.put("net.minecraft.world.level.GameType", "net.minecraft.world.level.EnumGamemode"); - spigotClassnames.put("net.minecraft.world.level.LevelAccessor", "net.minecraft.world.level.GeneratorAccess"); - - spigotClassnames.put("net.minecraft.world.level.block.state.BlockState", "net.minecraft.world.level.block.state.IBlockData"); - spigotClassnames.put("net.minecraft.world.level.block.state.StateDefinition", "net.minecraft.world.level.block.state.BlockStateList"); - - spigotClassnames.put("net.minecraft.world.level.chunk.LevelChunk", "net.minecraft.world.level.chunk.Chunk"); - - spigotClassnames.put("net.minecraft.world.level.material.FlowingFluid", "net.minecraft.world.level.material.FluidTypeFlowing"); - spigotClassnames.put("net.minecraft.world.level.material.Fluids", "net.minecraft.world.level.material.FluidTypes"); - spigotClassnames.put("net.minecraft.world.level.material.FluidState", "net.minecraft.world.level.material.Fluid"); - - spigotClassnames.put("net.minecraft.world.phys.BlockHitResult", "net.minecraft.world.phys.MovingObjectPositionBlock"); - spigotClassnames.put("net.minecraft.world.phys.Vec3", "net.minecraft.world.phys.Vec3D"); - - spigotClassnames.put("net.minecraft.resources.ResourceLocation", "net.minecraft.resources.MinecraftKey"); - - spigotClassnames.put("net.minecraft.util.ProgressListener", "net.minecraft.util.IProgressUpdate"); - } - - public static Class getClass(String name) { - try { - if (name.startsWith("org.bukkit.craftbukkit")) { - return Class.forName(ORG_BUKKIT_CRAFTBUKKIT + name.substring(22)); - } else if (MAJOR_VERSION < 17 && name.startsWith("net.minecraft")) { - return Class.forName(LEGACY_NET_MINECRAFT_SERVER + "." + spigotClassnames.getOrDefault(name, name).split("[.](?=[^.]*$)")[1]); - } else if (MAJOR_VERSION < 21 || MINOR_VERSION < 4) { - return Class.forName(spigotClassnames.getOrDefault(name, name)); - } else { - Class clazz = null; - try { - clazz = Class.forName(name); - } catch (ClassNotFoundException e) { - } - if (clazz != null && clazz.getName().equals(name)) { - return clazz; - } - - try { - return Core.class.getClassLoader().getParent().loadClass(name); - } catch (ClassNotFoundException e) { - if (clazz == null) { - throw e; - } - - return clazz; - } - } - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException("Cannot find " + name, e); - } - } - - @AllArgsConstructor - public static class Field { - private final java.lang.reflect.Field f; - - @SuppressWarnings("unchecked") - public T get(Object target) { - try { - return (T) f.get(target); - } catch (IllegalAccessException e) { - throw new IllegalArgumentException("Cannot read field", e); - } - } - - public void set(Object target, Object value) { - try { - f.set(target, value); - } catch (IllegalAccessException e) { - throw new IllegalArgumentException("Cannot write field", e); - } - } - } - - public static Field getField(Class target, String name, Class fieldType) { - return getField(target, name, fieldType, 0); - } - - public static Field getField(Class target, Class fieldType, int index) { - return getField(target, null, fieldType, index); - } - - public static Field getField(Class target, Class fieldType, int index, Class... parameters) { - return getField(target, null, fieldType, index, parameters); - } - - private static Field getField(Class target, String name, Class fieldType, int index, Class... parameters) { - for (final java.lang.reflect.Field field : target.getDeclaredFields()) { - if (matching(field, name, fieldType, parameters) && index-- <= 0) { - field.setAccessible(true); - return new Field<>(field); - } - } - - // Search in parent classes - if (target.getSuperclass() != null) { - return getField(target.getSuperclass(), name, fieldType, index); - } - - throw new IllegalArgumentException("Cannot find field with type " + fieldType); - } - - private static boolean matching(java.lang.reflect.Field field, String name, Class fieldType, Class... parameters) { - if (name != null && !field.getName().equals(name)) return false; - - if (!fieldType.isAssignableFrom(field.getType())) return false; - - if (parameters.length > 0) { - Type[] arguments = ((ParameterizedType) field.getGenericType()).getActualTypeArguments(); - - for (int i = 0; i < parameters.length; i++) { - if (arguments[i] instanceof ParameterizedType ? ((ParameterizedType) arguments[i]).getRawType() != parameters[i] : arguments[i] != parameters[i]) { - return false; - } - } - } - return true; - } - - @AllArgsConstructor - public static class Method { - private final java.lang.reflect.Method m; - - public Object invoke(Object target, Object... arguments) { - try { - return m.invoke(target, arguments); - } catch (Exception e) { - throw new IllegalArgumentException("Cannot invoke method " + m, e); - } - } - } - - public static Method getTypedMethod(Class clazz, String methodName, Class returnType, Class... params) { - for (final java.lang.reflect.Method method : clazz.getDeclaredMethods()) { - if ((methodName == null || method.getName().equals(methodName)) - && (returnType == null || method.getReturnType().equals(returnType)) - && Arrays.equals(method.getParameterTypes(), params)) { - method.setAccessible(true); - return new Method(method); - } - } - - // Search in every superclass - if (clazz.getSuperclass() != null) { - return getTypedMethod(clazz.getSuperclass(), methodName, returnType, params); - } - - throw new IllegalArgumentException(String.format("Cannot find method %s (%s).", methodName, Arrays.asList(params))); - } - - @AllArgsConstructor - public static class Constructor { - private final java.lang.reflect.Constructor c; - - public Object invoke(Object... arguments) { - try { - return c.newInstance(arguments); - } catch (Exception e) { - throw new IllegalArgumentException("Cannot invoke constructor " + c, e); - } - } - } - - public static Constructor getConstructor(Class clazz, Class... params) { - for (final java.lang.reflect.Constructor constructor : clazz.getDeclaredConstructors()) { - if (Arrays.equals(constructor.getParameterTypes(), params)) { - constructor.setAccessible(true); - return new Constructor(constructor); - } - } - - throw new IllegalStateException(String.format("Unable to find constructor for %s (%s).", clazz, Arrays.asList(params))); - } - public static Object newInstance(Class clazz) { try { return Unsafe.getUnsafe().allocateInstance(clazz); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java index 64bef9b5..f02a59f7 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/BountifulWrapper.java @@ -19,13 +19,8 @@ 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.network.syncher.EntityDataAccessor; -import net.minecraft.network.syncher.EntityDataSerializer; -import net.minecraft.network.syncher.EntityDataSerializers; -import net.minecraft.network.syncher.SynchedEntityData; import org.bukkit.Sound; import org.bukkit.entity.Player; @@ -40,15 +35,4 @@ public class BountifulWrapper { if (type == ChatMessageType.CHAT) type = ChatMessageType.SYSTEM; player.spigot().sendMessage(type, msg); } - - private static final Class dataWatcherRegistry = EntityDataSerializers.class; - private static final Class dataWatcherSerializer = EntityDataSerializer.class; - - public Object getDataWatcherObject(int index, Class type) { - return new EntityDataAccessor<>(index, (EntityDataSerializer) Reflection.getField(dataWatcherRegistry, dataWatcherSerializer, 0, type).get(null)); - } - - public Object getDataWatcherItem(Object dwo, Object value) { - return new SynchedEntityData.DataItem<>((EntityDataAccessor) dwo, value); - } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/Core.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/Core.java index a1aefd55..8f076c0a 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/Core.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/Core.java @@ -5,8 +5,8 @@ * * 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. + * the Free Software Foundation, either versionStrings 3 of the License, or + * (at your option) any later versionStrings. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -20,7 +20,6 @@ package de.steamwar.core; import com.comphenix.tinyprotocol.TinyProtocol; -import de.steamwar.Reflection; import de.steamwar.command.SWCommandUtils; import de.steamwar.command.SWTypeMapperCreator; import de.steamwar.command.TabCompletionCache; @@ -49,9 +48,13 @@ public class Core extends JavaPlugin { public static final Message MESSAGE = new Message("SpigotCore", Core.class.getClassLoader()); + @Getter @Deprecated - public static int getVersion() { - return Reflection.MAJOR_VERSION; + private static final int version; + + static { + String[] versionStrings = Bukkit.getServer().getBukkitVersion().split("-")[0].split("\\."); + version = Integer.parseInt(versionStrings[1]); } @Getter diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java deleted file mode 100644 index 6d6d4d1b..00000000 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/core/FlatteningWrapper.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2025 SteamWar.de-Serverteam - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package de.steamwar.core; - -import lombok.experimental.UtilityClass; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.network.chat.contents.PlainTextContents; - -import java.util.Optional; - -@UtilityClass -public class FlatteningWrapper { - - public Object formatDisplayName(String displayName) { - return displayName != null ? Optional.of((Object) MutableComponent.create(PlainTextContents.create(displayName))) : Optional.empty(); - } -} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RArmorStand.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RArmorStand.java index 746a080e..be389fcb 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RArmorStand.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RArmorStand.java @@ -49,7 +49,7 @@ public class RArmorStand extends REntity implements RInteractableEntity blockWatcher = new EntityDataAccessor<>(23, EntityDataSerializers.BLOCK_STATE); - private void getBlock(boolean ignoreDefault, BiConsumer packetSink) { + private void getBlock(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || !block.getAsString(true).equals(DEFAULT_BLOCK.getAsString(true))) { - packetSink.accept(blockWatcher, ((CraftBlockData) block).getState()); + packetSink.add(blockWatcher, ((CraftBlockData) block).getState()); } } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RDisplay.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RDisplay.java index 4ec21947..d96d085c 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RDisplay.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RDisplay.java @@ -89,18 +89,13 @@ public abstract class RDisplay extends REntity { } @SafeVarargs - protected final void sendPacket(Consumer packetSink, BiConsumer>... dataSinkSinks) { - List keyValueData = new ArrayList<>(); + protected final void sendPacket(Consumer packetSink, BiConsumer... dataSinkSinks) { + EntityDataPacketBuilder builder = entityDataPacket(); boolean ignoreDefault = packetSink == updatePacketSink; - for (BiConsumer> dataSinkSink : dataSinkSinks) { - dataSinkSink.accept(ignoreDefault, (dataWatcher, value) -> { - keyValueData.add(dataWatcher); - keyValueData.add(value); - }); - } - if (!keyValueData.isEmpty()) { - packetSink.accept(getDataWatcherPacket(keyValueData.toArray())); + for (BiConsumer dataSinkSink : dataSinkSinks) { + dataSinkSink.accept(ignoreDefault, builder); } + if (!builder.isEmpty()) builder.send(packetSink); } public void setTransform(@NonNull Transformation transform) { @@ -113,12 +108,12 @@ public abstract class RDisplay extends REntity { private static final EntityDataAccessor scaleWatcher = new EntityDataAccessor<>(12, EntityDataSerializers.VECTOR3); private static final EntityDataAccessor rightRotationWatcher = new EntityDataAccessor<>(14, EntityDataSerializers.QUATERNION); - private void getTransformData(boolean ignoreDefault, BiConsumer dataSink) { + private void getTransformData(boolean ignoreDefault, EntityDataPacketBuilder dataSink) { if (ignoreDefault || !transform.equals(DEFAULT_TRANSFORM)) { - dataSink.accept(translationWatcher, transform.getTranslation()); - dataSink.accept(leftRotationWatcher, transform.getLeftRotation()); - dataSink.accept(scaleWatcher, transform.getScale()); - dataSink.accept(rightRotationWatcher, transform.getRightRotation()); + dataSink.add(translationWatcher, transform.getTranslation()); + dataSink.add(leftRotationWatcher, transform.getLeftRotation()); + dataSink.add(scaleWatcher, transform.getScale()); + dataSink.add(rightRotationWatcher, transform.getRightRotation()); } } @@ -130,10 +125,10 @@ public abstract class RDisplay extends REntity { private static final EntityDataAccessor transformationInterpolationDurationWatcher = new EntityDataAccessor<>(9, EntityDataSerializers.INT); private static final EntityDataAccessor positionOrRotationInterpolationDurationWatcher = new EntityDataAccessor<>(10, EntityDataSerializers.INT); - private void getInterpolationDuration(boolean ignoreDefault, BiConsumer packetSink) { + private void getInterpolationDuration(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || interpolationDelay != 0) { - packetSink.accept(transformationInterpolationDurationWatcher, interpolationDuration); - packetSink.accept(positionOrRotationInterpolationDurationWatcher, interpolationDuration); + packetSink.add(transformationInterpolationDurationWatcher, interpolationDuration); + packetSink.add(positionOrRotationInterpolationDurationWatcher, interpolationDuration); } } @@ -144,9 +139,9 @@ public abstract class RDisplay extends REntity { private static final EntityDataAccessor viewRangeWatcher = new EntityDataAccessor<>(17, EntityDataSerializers.FLOAT); - private void getViewRange(boolean ignoreDefault, BiConsumer packetSink) { + private void getViewRange(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || viewRange != 1.0F) { - packetSink.accept(viewRangeWatcher, viewRange); + packetSink.add(viewRangeWatcher, viewRange); } } @@ -157,9 +152,9 @@ public abstract class RDisplay extends REntity { private static final EntityDataAccessor shadowRadiusWatcher = new EntityDataAccessor<>(18, EntityDataSerializers.FLOAT); - private void getShadowRadius(boolean ignoreDefault, BiConsumer packetSink) { + private void getShadowRadius(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || shadowRadius != 0.0F) { - packetSink.accept(shadowRadiusWatcher, shadowRadius); + packetSink.add(shadowRadiusWatcher, shadowRadius); } } @@ -170,9 +165,9 @@ public abstract class RDisplay extends REntity { private static final EntityDataAccessor shadowStrengthWatcher = new EntityDataAccessor<>(19, EntityDataSerializers.FLOAT); - private void getShadowStrength(boolean ignoreDefault, BiConsumer packetSink) { + private void getShadowStrength(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || shadowStrength != 1.0F) { - packetSink.accept(shadowStrengthWatcher, shadowStrength); + packetSink.add(shadowStrengthWatcher, shadowStrength); } } @@ -183,9 +178,9 @@ public abstract class RDisplay extends REntity { private static final EntityDataAccessor displayWidthWatcher = new EntityDataAccessor<>(20, EntityDataSerializers.FLOAT); - private void getDisplayWidth(boolean ignoreDefault, BiConsumer packetSink) { + private void getDisplayWidth(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || displayWidth != 0.0F) { - packetSink.accept(displayWidthWatcher, displayWidth); + packetSink.add(displayWidthWatcher, displayWidth); } } @@ -196,9 +191,9 @@ public abstract class RDisplay extends REntity { private static final EntityDataAccessor displayHeightWatcher = new EntityDataAccessor<>(21, EntityDataSerializers.FLOAT); - private void getDisplayHeight(boolean ignoreDefault, BiConsumer packetSink) { + private void getDisplayHeight(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || displayHeight != 0.0F) { - packetSink.accept(displayHeightWatcher, displayHeight); + packetSink.add(displayHeightWatcher, displayHeight); } } @@ -209,9 +204,9 @@ public abstract class RDisplay extends REntity { private static final EntityDataAccessor interpolationDelayWatcher = new EntityDataAccessor<>(8, EntityDataSerializers.INT); - private void getInterpolationDelay(boolean ignoreDefault, BiConsumer packetSink) { + private void getInterpolationDelay(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || interpolationDelay != 0) { - packetSink.accept(interpolationDelayWatcher, interpolationDelay); + packetSink.add(interpolationDelayWatcher, interpolationDelay); } } @@ -222,9 +217,9 @@ public abstract class RDisplay extends REntity { private static final EntityDataAccessor billboardWatcher = new EntityDataAccessor<>(15, EntityDataSerializers.BYTE); - private void getBillboard(boolean ignoreDefault, BiConsumer packetSink) { + private void getBillboard(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || billboard != Display.Billboard.FIXED) { - packetSink.accept(billboardWatcher, (byte) billboard.ordinal()); + packetSink.add(billboardWatcher, (byte) billboard.ordinal()); } } @@ -235,9 +230,9 @@ public abstract class RDisplay extends REntity { private static final EntityDataAccessor glowColorOverrideWatcher = new EntityDataAccessor<>(22, EntityDataSerializers.INT); - private void getGlowColorOverride(boolean ignoreDefault, BiConsumer packetSink) { + private void getGlowColorOverride(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || glowColorOverride != null) { - packetSink.accept(glowColorOverrideWatcher, glowColorOverride == null ? -1 : glowColorOverride.asARGB()); + packetSink.add(glowColorOverrideWatcher, glowColorOverride == null ? -1 : glowColorOverride.asARGB()); } } @@ -248,9 +243,9 @@ public abstract class RDisplay extends REntity { private static final EntityDataAccessor brightnessWatcher = new EntityDataAccessor<>(16, EntityDataSerializers.INT); - private void getBrightness(boolean ignoreDefault, BiConsumer packetSink) { + private void getBrightness(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || brightness != null) { - packetSink.accept(brightnessWatcher, brightness == null ? -1 : brightness.getBlockLight() << 4 | brightness.getSkyLight() << 20); + packetSink.add(brightnessWatcher, brightness == null ? -1 : brightness.getBlockLight() << 4 | brightness.getSkyLight() << 20); } } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java index 2d867cc1..63411a0c 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java @@ -21,12 +21,12 @@ package de.steamwar.entity; import com.mojang.datafixers.util.Pair; import de.steamwar.Reflection; -import de.steamwar.core.BountifulWrapper; -import de.steamwar.core.FlatteningWrapper; import it.unimi.dsi.fastutil.ints.IntArrayList; import lombok.Getter; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.contents.PlainTextContents; import net.minecraft.network.protocol.game.*; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; @@ -193,12 +193,12 @@ public class REntity { public void setPose(Pose pose) { this.pose = pose; - server.updateEntity(this, getDataWatcherPacket(poseDataWatcher, pose)); + server.updateEntity(this, entityDataPacket().add(poseDataWatcher, pose).build()); } public void setOnFire(boolean perma) { fireTick = perma ? -1 : 21; - server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); + server.updateEntity(this, entityDataPacket().add(entityStatusWatcher, getEntityStatus()).build()); } public boolean isOnFire() { @@ -207,20 +207,18 @@ public class REntity { public void setInvisible(boolean invisible) { this.invisible = invisible; - server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); + server.updateEntity(this, entityDataPacket().add(entityStatusWatcher, getEntityStatus()).build()); } public void setBowDrawn(boolean drawn, boolean offHand) { bowDrawn = drawn; - server.updateEntity(this, getDataWatcherPacket(poseDataWatcher, Pose.SHOOTING)); + server.updateEntity(this, entityDataPacket().add(poseDataWatcher, Pose.SHOOTING).build()); } public void setDisplayName(String displayName) { this.displayName = displayName; - server.updateEntity(this, getDataWatcherPacket( - nameWatcher, FlatteningWrapper.formatDisplayName(displayName), - nameVisibleWatcher, displayName != null - )); + server.updateEntity(this, entityDataPacket().add(nameWatcher, formatDisplayName(displayName)) + .add(nameVisibleWatcher, displayName != null).build()); } public void setItem(Object slot, ItemStack stack) { @@ -234,12 +232,12 @@ public class REntity { public void setNoGravity(boolean noGravity) { this.noGravity = noGravity; - server.updateEntity(this, getDataWatcherPacket(noGravityDataWatcher, noGravity)); + server.updateEntity(this, entityDataPacket().add(noGravityDataWatcher, noGravity).build()); } public void setGlowing(boolean glowing) { this.isGlowing = glowing; - server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); + server.updateEntity(this, entityDataPacket().add(entityStatusWatcher, getEntityStatus()).build()); } private ClientboundAddEntityPacket spawnPacketGenerator() { @@ -263,20 +261,22 @@ public class REntity { } if (pose != Pose.STANDING) { - packetSink.accept(getDataWatcherPacket(poseDataWatcher, pose)); + entityDataPacket().add(poseDataWatcher, pose).send(packetSink); } byte status = getEntityStatus(); if (status != 0) { - packetSink.accept(getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); + entityDataPacket().add(entityStatusWatcher, getEntityStatus()).send(packetSink); } if (displayName != null) { - packetSink.accept(getDataWatcherPacket(nameWatcher, FlatteningWrapper.formatDisplayName(displayName), nameVisibleWatcher, true)); + entityDataPacket().add(nameWatcher, formatDisplayName(displayName)) + .add(nameVisibleWatcher, true) + .send(packetSink); } if (noGravity) { - packetSink.accept(getDataWatcherPacket(noGravityDataWatcher, true)); + entityDataPacket().add(noGravityDataWatcher, true).send(packetSink); } } @@ -284,7 +284,7 @@ public class REntity { if (fireTick > 0) { fireTick--; if (fireTick == 0) { - server.updateEntity(this, getDataWatcherPacket(entityStatusWatcher, getEntityStatus())); + server.updateEntity(this, entityDataPacket().add(entityStatusWatcher, getEntityStatus()).build()); } } } @@ -315,13 +315,29 @@ public class REntity { return status; } - protected Object getDataWatcherPacket(Object... dataWatcherKeyValues) { - ArrayList> 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()); + protected EntityDataPacketBuilder entityDataPacket() { + return new EntityDataPacketBuilder(); + } + + public class EntityDataPacketBuilder { + private List> values = new ArrayList<>(); + + public EntityDataPacketBuilder add(EntityDataAccessor accessor, T value) { + values.add(new SynchedEntityData.DataItem<>(accessor, value).value()); + return null; } - return new ClientboundSetEntityDataPacket(entityId, nativeWatchers); + public boolean isEmpty() { + return values.isEmpty(); + } + + public ClientboundSetEntityDataPacket build() { + return new ClientboundSetEntityDataPacket(entityId, values); + } + + public void send(Consumer packetSink) { + packetSink.accept(build()); + } } private Object getTeleportPacket() { @@ -364,4 +380,8 @@ public class REntity { private int calcVelocity(double value) { return (int) (Math.max(-3.9, Math.min(value, 3.9)) * 8000); } + + private static Optional formatDisplayName(String displayName) { + return displayName != null ? Optional.of(MutableComponent.create(PlainTextContents.create(displayName))) : Optional.empty(); + } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RInteraction.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RInteraction.java index 58514280..7d27063e 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RInteraction.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RInteraction.java @@ -19,14 +19,13 @@ package de.steamwar.entity; -import de.steamwar.core.BountifulWrapper; import lombok.Getter; import lombok.Setter; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; import org.bukkit.Location; import org.bukkit.entity.EntityType; -import java.util.ArrayList; -import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -61,18 +60,13 @@ public class RInteraction extends REntity implements RInteractableEntity packetSink, BiConsumer>... dataSinkSinks) { - List keyValueData = new ArrayList<>(); + protected final void sendPacket(Consumer packetSink, BiConsumer... dataSinkSinks) { + EntityDataPacketBuilder builder = entityDataPacket(); boolean ignoreDefault = packetSink == updatePacketSink; - for (BiConsumer> dataSinkSink : dataSinkSinks) { - dataSinkSink.accept(ignoreDefault, (dataWatcher, value) -> { - keyValueData.add(dataWatcher); - keyValueData.add(value); - }); - } - if (!keyValueData.isEmpty()) { - packetSink.accept(getDataWatcherPacket(keyValueData.toArray())); + for (BiConsumer dataSinkSink : dataSinkSinks) { + dataSinkSink.accept(ignoreDefault, builder); } + if (!builder.isEmpty()) builder.send(packetSink); } public void setInteractionWidth(float interactionWidth) { @@ -80,11 +74,11 @@ public class RInteraction extends REntity implements RInteractableEntity interactionWidthWatcher = new EntityDataAccessor<>(8, EntityDataSerializers.FLOAT); - private void getInteractionWidthData(boolean ignoreDefault, BiConsumer dataSink) { + private void getInteractionWidthData(boolean ignoreDefault, EntityDataPacketBuilder dataSink) { if (ignoreDefault || interactionWidth != 1.0) { - dataSink.accept(interactionWidthWatcher, interactionWidth); + dataSink.add(interactionWidthWatcher, interactionWidth); } } @@ -93,11 +87,11 @@ public class RInteraction extends REntity implements RInteractableEntity interactionHeightWatcher = new EntityDataAccessor<>(9, EntityDataSerializers.FLOAT); - private void getInteractionHeightData(boolean ignoreDefault, BiConsumer dataSink) { + private void getInteractionHeightData(boolean ignoreDefault, EntityDataPacketBuilder dataSink) { if (ignoreDefault || interactionHeight != 1.0) { - dataSink.accept(interactionHeightWatcher, interactionHeight); + dataSink.add(interactionHeightWatcher, interactionHeight); } } @@ -106,11 +100,11 @@ public class RInteraction extends REntity implements RInteractableEntity responsiveWatcher = new EntityDataAccessor<>(10, EntityDataSerializers.BOOLEAN); - private void getResponsiveData(boolean ignoreDefault, BiConsumer dataSink) { + private void getResponsiveData(boolean ignoreDefault, EntityDataPacketBuilder dataSink) { if (ignoreDefault || !responsive) { - dataSink.accept(responsiveWatcher, responsive); + dataSink.add(responsiveWatcher, responsive); } } } \ No newline at end of file diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RItemDisplay.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RItemDisplay.java index e10d2ec9..21453516 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RItemDisplay.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RItemDisplay.java @@ -63,9 +63,9 @@ public class RItemDisplay extends RDisplay { private static final EntityDataAccessor itemStackWatcher = new EntityDataAccessor<>(23, EntityDataSerializers.ITEM_STACK); - private void getItemStack(boolean ignoreDefault, BiConsumer packetSink) { + private void getItemStack(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || !itemStack.equals(DEFAULT_ITEM_STACK)) { - packetSink.accept(itemStackWatcher, CraftItemStack.asNMSCopy(itemStack)); + packetSink.add(itemStackWatcher, CraftItemStack.asNMSCopy(itemStack)); } } @@ -76,9 +76,9 @@ public class RItemDisplay extends RDisplay { sendPacket(updatePacketSink, this::getItemDisplayTransform); } - private void getItemDisplayTransform(boolean ignoreDefault, BiConsumer packetSink) { + private void getItemDisplayTransform(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || itemDisplayTransform != ItemDisplay.ItemDisplayTransform.NONE) { - packetSink.accept(itemDisplayTransformWatcher, (byte) itemDisplayTransform.ordinal()); + packetSink.add(itemDisplayTransformWatcher, (byte) itemDisplayTransform.ordinal()); } } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java index daa9a648..cfb6304d 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RPlayer.java @@ -21,7 +21,6 @@ package de.steamwar.entity; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; -import de.steamwar.core.BountifulWrapper; import de.steamwar.core.ProtocolWrapper; import de.steamwar.network.CoreNetworkHandler; import de.steamwar.network.NetworkSender; @@ -29,6 +28,8 @@ import de.steamwar.network.packets.common.PlayerSkinRequestPacket; import lombok.Getter; import lombok.Setter; import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.world.phys.Vec3; import org.bukkit.GameMode; import org.bukkit.Location; @@ -42,7 +43,7 @@ import java.util.function.Consumer; public class RPlayer extends REntity implements RInteractableEntity { - private static final Object skinPartsDataWatcher = BountifulWrapper.impl.getDataWatcherObject(17, Byte.class); + private static final EntityDataAccessor skinPartsDataWatcher = new EntityDataAccessor<>(17, EntityDataSerializers.BYTE); @Getter private final UUID actualUUID; @@ -85,7 +86,7 @@ public class RPlayer extends REntity implements RInteractableEntity { @Override void spawn(Consumer packetSink) { packetSink.accept(getNamedSpawnPacket()); - packetSink.accept(getDataWatcherPacket(skinPartsDataWatcher, (byte) 0x7F)); + entityDataPacket().add(skinPartsDataWatcher, (byte) 0x7F).send(packetSink); for (Map.Entry entry : itemSlots.entrySet()) { packetSink.accept(getEquipmentPacket(entry.getKey(), entry.getValue())); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RTextDisplay.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RTextDisplay.java index b6db865f..f30fbfd8 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RTextDisplay.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RTextDisplay.java @@ -77,9 +77,9 @@ public class RTextDisplay extends RDisplay { private static final EntityDataAccessor textWatcher = new EntityDataAccessor<>(23, EntityDataSerializers.COMPONENT); - private void getText(boolean ignoreDefault, BiConsumer packetSink) { + private void getText(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || !text.isEmpty()) { - packetSink.accept(textWatcher, MutableComponent.create(PlainTextContents.create(text))); + packetSink.add(textWatcher, MutableComponent.create(PlainTextContents.create(text))); } } @@ -90,9 +90,9 @@ public class RTextDisplay extends RDisplay { private static final EntityDataAccessor lineWidthWatcher = new EntityDataAccessor<>(24, EntityDataSerializers.INT); - private void getLineWidth(boolean ignoreDefault, BiConsumer packetSink) { + private void getLineWidth(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || lineWidth != 200) { - packetSink.accept(lineWidthWatcher, lineWidth); + packetSink.add(lineWidthWatcher, lineWidth); } } @@ -103,9 +103,9 @@ public class RTextDisplay extends RDisplay { private static final EntityDataAccessor textOpacityWatcher = new EntityDataAccessor<>(26, EntityDataSerializers.BYTE); - private void getTextOpacity(boolean ignoreDefault, BiConsumer packetSink) { + private void getTextOpacity(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || textOpacity != (byte) -1) { - packetSink.accept(textOpacityWatcher, textOpacity); + packetSink.add(textOpacityWatcher, textOpacity); } } @@ -126,9 +126,9 @@ public class RTextDisplay extends RDisplay { private static final EntityDataAccessor backgroundColorWatcher = new EntityDataAccessor<>(25, EntityDataSerializers.INT); - private void getBackgroundColor(boolean ignoreDefault, BiConsumer packetSink) { + private void getBackgroundColor(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { if (ignoreDefault || backgroundColor != null) { - packetSink.accept(backgroundColorWatcher, backgroundColor); + packetSink.add(backgroundColorWatcher, backgroundColor); } } @@ -146,7 +146,7 @@ public class RTextDisplay extends RDisplay { private static final EntityDataAccessor textStatusWatcher = new EntityDataAccessor<>(27, EntityDataSerializers.BYTE); - private void getTextStatus(boolean ignoreDefault, BiConsumer packetSink) { + private void getTextStatus(boolean ignoreDefault, EntityDataPacketBuilder packetSink) { byte status = 0; if (shadowed) { @@ -167,7 +167,7 @@ public class RTextDisplay extends RDisplay { } if (ignoreDefault || status != 0) { - packetSink.accept(textStatusWatcher, status); + packetSink.add(textStatusWatcher, status); } } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java index 57bce2e2..dc4e3c33 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java @@ -19,16 +19,13 @@ package de.steamwar.techhider.legacy; -import de.steamwar.Reflection; import de.steamwar.techhider.ProtocolUtils; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import lombok.Getter; -import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; -import net.minecraft.resources.ResourceLocation; import net.minecraft.util.SimpleBitStorage; import net.minecraft.world.level.block.entity.BlockEntityType; import org.bukkit.entity.Player; @@ -85,12 +82,9 @@ public class ChunkHider { }; } - private static final Registry> registry = Reflection.getField(BuiltInRegistries.class, "BLOCK_ENTITY_TYPE", Registry.class).get(null); - private static final Reflection.Method getKey = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "getKey", ResourceLocation.class, Object.class); - protected boolean tileEntityVisible(Set hiddenBlockEntities, ClientboundLevelChunkPacketData.BlockEntityInfo tile) { BlockEntityType type = tile.type; - String path = ((ResourceLocation) getKey.invoke(registry, type)).getPath(); + String path = BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(type).getPath(); return !hiddenBlockEntities.contains(path); } diff --git a/buildSrc/src/main/kotlin/WidenerExtension.kt b/buildSrc/src/main/kotlin/WidenerExtension.kt index d0263567..73ae4e5b 100644 --- a/buildSrc/src/main/kotlin/WidenerExtension.kt +++ b/buildSrc/src/main/kotlin/WidenerExtension.kt @@ -32,7 +32,7 @@ abstract class WidenerExtension { abstract val accessWidenerFiles: ConfigurableFileCollection fun fromCatalog(vararg dependencies: Provider) { - val files = dependencies.map { dependency -> + dependencies.map { dependency -> project.provider { val dep = dependency.get() @@ -45,8 +45,6 @@ abstract class WidenerExtension { } .file } - } - - inputJars.from(files) + }.let { inputJars.from(it) } } } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/widener.gradle.kts b/buildSrc/src/main/kotlin/widener.gradle.kts index 71da2e8d..c85ae3d5 100644 --- a/buildSrc/src/main/kotlin/widener.gradle.kts +++ b/buildSrc/src/main/kotlin/widener.gradle.kts @@ -23,8 +23,6 @@ widener.accessWidenerFiles.setFrom( fileTree("src/") { include("**/*.accesswidener") } ) -// ─── Tasks (unchanged from before) ─────────────────────────────────────────── - val jarWidenerClasspath = rootProject.project(":AccessWidener") .tasks.named("shadowJar") .get().outputs.files @@ -68,7 +66,6 @@ project.gradle.projectsEvaluated { } tasks.named("compileJava") { - // dependsOn(allWidenedJars.buildDependencies) dependsOn(allWidenedJars) } } From 1590f8f0ee9c6689483e666325908feb78cfc0f4 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 23:18:22 +0200 Subject: [PATCH 10/19] Remove Reflection --- .../main/java/de/steamwar/ClassPatcher.java | 8 ++++- .../java/de/steamwar/ClassTransformer.java | 11 +++++- .../src/bausystem.accesswidener | 3 -- .../bausystem/features/util/BindCommand.java | 16 ++------- .../src/de/steamwar/Reflection.java | 36 ------------------- .../steamwar/command/CommandRegistering.java | 23 +++--------- .../src/de/steamwar/entity/REntity.java | 7 ++-- .../src/de/steamwar/techhider/ChunkHider.java | 4 +-- .../de/steamwar/techhider/ProtocolUtils.java | 22 ++---------- .../steamwar/techhider/legacy/ChunkHider.java | 4 +-- .../steamwar/techhider/legacy/TechHider.java | 4 +-- .../src/spigotcore.accesswidener | 16 ++++++++- 12 files changed, 50 insertions(+), 104 deletions(-) delete mode 100644 SpigotCore/SpigotCore_Main/src/de/steamwar/Reflection.java diff --git a/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java b/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java index 2cce0c77..c54f69dd 100644 --- a/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java +++ b/AccessWidener/src/main/java/de/steamwar/ClassPatcher.java @@ -43,6 +43,8 @@ public class ClassPatcher { /** Pre-computed set of targeted internal names for fast filtering. */ private final Set targets; + private final Set targetsPublicConstructor; + public ClassPatcher(List entries) { this.entries = entries; this.targets = entries.stream() @@ -53,6 +55,10 @@ public class ClassPatcher { return Stream.of(s, s.substring(0, index)); }) .collect(Collectors.toSet()); + this.targetsPublicConstructor = entries.stream() + .filter(entry -> entry.directive().equals("transitive-extendable")) + .map(AccessWidenerEntry::target) + .collect(Collectors.toSet()); } /** @@ -66,7 +72,7 @@ public class ClassPatcher { try { ClassReader cr = new ClassReader(classBytes); ClassWriter cw = new ClassWriter(cr, 0); - cr.accept(new ClassTransformer(cw, className, entries), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + cr.accept(new ClassTransformer(cw, className, entries, targetsPublicConstructor.contains(className)), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); return cw.toByteArray(); } catch (Exception e) { LOG.warning("[AccessWidener] Failed to transform " + className + ": " + e.getMessage()); diff --git a/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java b/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java index ccc3df9d..534155b9 100644 --- a/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java +++ b/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java @@ -30,11 +30,13 @@ public class ClassTransformer extends ClassVisitor { private final String internalName; private final List entries; + private final boolean appendPublicConstructor; - public ClassTransformer(ClassVisitor cv, String internalName, List entries) { + public ClassTransformer(ClassVisitor cv, String internalName, List entries, boolean appendPublicConstructor) { super(Opcodes.ASM9, cv); this.internalName = internalName; this.entries = entries; + this.appendPublicConstructor = appendPublicConstructor; } @Override @@ -44,6 +46,13 @@ public class ClassTransformer extends ClassVisitor { if (!e.targets(internalName) || !"class".equals(e.memberType())) continue; newAccess = applyDirective(e.directive(), newAccess, false); } + if (appendPublicConstructor) { + MethodVisitor methodVisitor = visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null); + methodVisitor.visitMaxs(0, 0); + methodVisitor.visitCode(); + methodVisitor.visitInsn(Opcodes.RETURN); + methodVisitor.visitEnd(); + } super.visit(version, newAccess, name, signature, superName, interfaces); } diff --git a/BauSystem/BauSystem_Main/src/bausystem.accesswidener b/BauSystem/BauSystem_Main/src/bausystem.accesswidener index 51fe2890..5dd89fdf 100644 --- a/BauSystem/BauSystem_Main/src/bausystem.accesswidener +++ b/BauSystem/BauSystem_Main/src/bausystem.accesswidener @@ -11,6 +11,3 @@ mutable field org/bukkit/craftbukkit/block/CraftBlockState world Lorg/bukkit/cra # For TickManager accessible field net/minecraft/server/ServerTickRateManager remainingSprintTicks J - -# Test -accessible field net/minecraft/core/registries/BuiltInRegistries BLOCK_ENTITY_TYPE Lnet/minecraft/core/Registry; \ No newline at end of file diff --git a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/BindCommand.java b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/BindCommand.java index bde7967e..5db75a81 100644 --- a/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/BindCommand.java +++ b/BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/util/BindCommand.java @@ -38,6 +38,7 @@ import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; import org.bukkit.command.CommandMap; import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.CraftServer; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -47,7 +48,6 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.persistence.PersistentDataType; -import java.lang.reflect.Field; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -73,19 +73,7 @@ public class BindCommand extends SWCommand implements Listener { } } - private static final CommandMap commandMap; - - static { - Field knownCommandsField; - try { - knownCommandsField = Bukkit.getServer().getClass().getDeclaredField("commandMap"); - knownCommandsField.setAccessible(true); - commandMap = (CommandMap) knownCommandsField.get(Bukkit.getServer()); - } catch (IllegalAccessException | NoSuchFieldException var2) { - Bukkit.shutdown(); - throw new SecurityException("Oh shit. Commands cannot be registered.", var2); - } - } + private static final CommandMap commandMap = ((CraftServer) Bukkit.getServer()).getCommandMap(); private static final NamespacedKey KEY = SWUtils.getNamespaceKey("command"); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/Reflection.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/Reflection.java deleted file mode 100644 index 324d0e44..00000000 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/Reflection.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2025 SteamWar.de-Serverteam - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package de.steamwar; - -import jdk.internal.misc.Unsafe; -import lombok.experimental.UtilityClass; - - -@UtilityClass -public final class Reflection { - - public static Object newInstance(Class clazz) { - try { - return Unsafe.getUnsafe().allocateInstance(clazz); - } catch (InstantiationException e) { - throw new SecurityException("Could not create object", e); - } - } -} diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/command/CommandRegistering.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/command/CommandRegistering.java index 4adffb58..90026343 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/command/CommandRegistering.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/command/CommandRegistering.java @@ -22,35 +22,20 @@ package de.steamwar.command; import lombok.experimental.UtilityClass; import org.bukkit.Bukkit; import org.bukkit.command.Command; -import org.bukkit.command.CommandMap; import org.bukkit.command.SimpleCommandMap; +import org.bukkit.craftbukkit.CraftServer; -import java.lang.reflect.Field; import java.util.Map; @UtilityClass class CommandRegistering { - private static final CommandMap commandMap; + private static final SimpleCommandMap commandMap; private static final Map knownCommandMap; static { - try { - final Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap"); - commandMapField.setAccessible(true); - commandMap = (CommandMap) commandMapField.get(Bukkit.getServer()); - } catch (NoSuchFieldException | IllegalAccessException exception) { - Bukkit.shutdown(); - throw new SecurityException("Oh shit. Commands cannot be registered.", exception); - } - try { - final Field knownCommandsField = SimpleCommandMap.class.getDeclaredField("knownCommands"); - knownCommandsField.setAccessible(true); - knownCommandMap = (Map) knownCommandsField.get(commandMap); - } catch (NoSuchFieldException | IllegalAccessException exception) { - Bukkit.shutdown(); - throw new SecurityException("Oh shit. Commands cannot be registered.", exception); - } + commandMap = ((CraftServer) Bukkit.getServer()).getCommandMap(); + knownCommandMap = commandMap.getKnownCommands(); } static void unregister(Command command) { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java index 63411a0c..1c7cce4d 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java @@ -20,7 +20,6 @@ package de.steamwar.entity; import com.mojang.datafixers.util.Pair; -import de.steamwar.Reflection; import it.unimi.dsi.fastutil.ints.IntArrayList; import lombok.Getter; import net.minecraft.core.registries.BuiltInRegistries; @@ -174,7 +173,7 @@ public class REntity { } public void showAnimation(byte animation) { - ClientboundAnimatePacket packet = (ClientboundAnimatePacket) Reflection.newInstance(ClientboundAnimatePacket.class); + ClientboundAnimatePacket packet = new ClientboundAnimatePacket(); packet.id = entityId; packet.action = animation; server.updateEntity(this, packet); @@ -185,7 +184,7 @@ public class REntity { } public void showDamage() { - ClientboundEntityEventPacket packet = (ClientboundEntityEventPacket) Reflection.newInstance(ClientboundEntityEventPacket.class); + ClientboundEntityEventPacket packet = new ClientboundEntityEventPacket(); packet.entityId = entityId; packet.eventId = (byte) 2; server.updateEntity(this, packet); @@ -363,7 +362,7 @@ public class REntity { } private Object getHeadRotationPacket() { - ClientboundRotateHeadPacket packet = (ClientboundRotateHeadPacket) Reflection.newInstance(ClientboundRotateHeadPacket.class); + ClientboundRotateHeadPacket packet = new ClientboundRotateHeadPacket(); packet.entityId = entityId; packet.yHeadRot = headYaw; return packet; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java index 471cdc4a..bd21f357 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ChunkHider.java @@ -33,8 +33,8 @@ import java.util.List; import java.util.function.UnaryOperator; public class ChunkHider { - private static final UnaryOperator chunkPacketShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); - private static final UnaryOperator chunkDataShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); + private static final UnaryOperator chunkPacketShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkWithLightPacket::new); + private static final UnaryOperator chunkDataShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class, ClientboundLevelChunkPacketData::new); private final int SECTION_SPAN_SIZE = 16; private final byte BIT_PER_BLOCK_INDIRECTION_LIMIT = 8; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java index b97fe828..18997565 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java @@ -20,41 +20,25 @@ package de.steamwar.techhider; import com.google.common.primitives.Bytes; -import de.steamwar.Reflection; import io.netty.buffer.ByteBuf; -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.Supplier; import java.util.function.UnaryOperator; public class ProtocolUtils { private ProtocolUtils() { } - @Deprecated - 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) { + public static UnaryOperator shallowCloneGenerator(Class clazz, Supplier supplier) { BiConsumer filler = shallowFill(clazz); return source -> { - T clone = (T) Reflection.newInstance(clazz); + T clone = supplier.get(); filler.accept(source, clone); return clone; }; diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java index dc4e3c33..9aaa9b40 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/ChunkHider.java @@ -40,8 +40,8 @@ import java.util.stream.Collectors; public class ChunkHider { public static final ChunkHider impl = new ChunkHider(); - private static final UnaryOperator chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class); - private static final UnaryOperator chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class); + private static final UnaryOperator chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkWithLightPacket::new); + private static final UnaryOperator chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class, ClientboundLevelChunkPacketData::new); public BiFunction chunkHiderGenerator(TechHider techHider) { return (p, packet) -> { diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java index f8e027be..dfa0a5f3 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/legacy/TechHider.java @@ -92,9 +92,9 @@ public class TechHider { techhiders.forEach(TinyProtocol.instance::removeFilter); } - public static final UnaryOperator multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(ClientboundSectionBlocksUpdatePacket.class); + public static final UnaryOperator multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(ClientboundSectionBlocksUpdatePacket.class, ClientboundSectionBlocksUpdatePacket::new); - private static final UnaryOperator blockChangeCloner = ProtocolUtils.shallowCloneGenerator(ClientboundBlockUpdatePacket.class); + private static final UnaryOperator blockChangeCloner = ProtocolUtils.shallowCloneGenerator(ClientboundBlockUpdatePacket.class, ClientboundBlockUpdatePacket::new); private ClientboundBlockUpdatePacket blockChangeHider(Player p, ClientboundBlockUpdatePacket packet) { switch (locationEvaluator.checkBlockPos(p, packet.getPos())) { diff --git a/SpigotCore/SpigotCore_Main/src/spigotcore.accesswidener b/SpigotCore/SpigotCore_Main/src/spigotcore.accesswidener index 1fcaac48..68d0aa82 100644 --- a/SpigotCore/SpigotCore_Main/src/spigotcore.accesswidener +++ b/SpigotCore/SpigotCore_Main/src/spigotcore.accesswidener @@ -15,16 +15,22 @@ accessible field net/minecraft/server/MinecraftServer services Lnet/minecraft/se mutable field net/minecraft/server/MinecraftServer services Lnet/minecraft/server/Services; # REntity +## transitive-extendable means that a public no args constructor is added without any super initialization +transitive-extendable class net/minecraft/network/protocol/game/ClientboundAnimatePacket accessible field net/minecraft/network/protocol/game/ClientboundAnimatePacket id I mutable field net/minecraft/network/protocol/game/ClientboundAnimatePacket id I accessible field net/minecraft/network/protocol/game/ClientboundAnimatePacket action I mutable field net/minecraft/network/protocol/game/ClientboundAnimatePacket action I +## transitive-extendable means that a public no args constructor is added without any super initialization +transitive-extendable class net/minecraft/network/protocol/game/ClientboundEntityEventPacket accessible field net/minecraft/network/protocol/game/ClientboundEntityEventPacket entityId I mutable field net/minecraft/network/protocol/game/ClientboundEntityEventPacket entityId I accessible field net/minecraft/network/protocol/game/ClientboundEntityEventPacket eventId B mutable field net/minecraft/network/protocol/game/ClientboundEntityEventPacket eventId B +## transitive-extendable means that a public no args constructor is added without any super initialization +transitive-extendable class net/minecraft/network/protocol/game/ClientboundRotateHeadPacket accessible field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket yHeadRot B mutable field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket yHeadRot B ## + TechHider @@ -32,9 +38,13 @@ accessible field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket mutable field net/minecraft/network/protocol/game/ClientboundRotateHeadPacket entityId I # For ChunkHider +## transitive-extendable means that a public no args constructor is added without any super initialization +transitive-extendable class net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket chunkData Lnet/minecraft/network/protocol/game/ClientboundLevelChunkPacketData; mutable field net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket chunkData Lnet/minecraft/network/protocol/game/ClientboundLevelChunkPacketData; +## transitive-extendable means that a public no args constructor is added without any super initialization +transitive-extendable class net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData buffer [B mutable field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData buffer [B accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData blockEntitiesData Ljava/util/List; @@ -47,6 +57,8 @@ accessible field net/minecraft/network/protocol/game/ClientboundLevelChunkPacket # For TechHider accessible field net/minecraft/network/protocol/game/ClientboundMoveEntityPacket entityId I +## transitive-extendable means that a public no args constructor is added without any super initialization +transitive-extendable class net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket sectionPos Lnet/minecraft/core/SectionPos; accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket positions [S mutable field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket positions [S @@ -54,4 +66,6 @@ accessible field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpd mutable field net/minecraft/network/protocol/game/ClientboundSectionBlocksUpdatePacket states [Lnet/minecraft/world/level/block/state/BlockState; # For legacy/TechHider -mutable field net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket blockState Lnet/minecraft/world/level/block/state/BlockState; +## transitive-extendable means that a public no args constructor is added without any super initialization +transitive-extendable class net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket +mutable field net/minecraft/network/protocol/game/ClientboundBlockUpdatePacket blockState Lnet/minecraft/world/level/block/state/BlockState; \ No newline at end of file From 2b43af885165e4f9836c972108049140d1c81a58 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 23:22:05 +0200 Subject: [PATCH 11/19] Remove unneded reflection --- .../misslewars/slowmo/SlowMoRunner.java | 9 ++- .../misslewars/slowmo/SlowMoUtils.java | 74 ------------------- 2 files changed, 6 insertions(+), 77 deletions(-) delete mode 100644 MissileWars/src/de/steamwar/misslewars/slowmo/SlowMoUtils.java diff --git a/MissileWars/src/de/steamwar/misslewars/slowmo/SlowMoRunner.java b/MissileWars/src/de/steamwar/misslewars/slowmo/SlowMoRunner.java index 0d0363f0..84df082c 100644 --- a/MissileWars/src/de/steamwar/misslewars/slowmo/SlowMoRunner.java +++ b/MissileWars/src/de/steamwar/misslewars/slowmo/SlowMoRunner.java @@ -20,9 +20,12 @@ package de.steamwar.misslewars.slowmo; import de.steamwar.misslewars.MissileWars; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.TickRateManager; import org.bukkit.Bukkit; public class SlowMoRunner { + private static TickRateManager tickRateManager = MinecraftServer.getServer().tickRateManager(); private static long currentTime = 0; private static long current = 0; @@ -40,14 +43,14 @@ public class SlowMoRunner { if (currentTime > 0) { current += 1; if (current % 5 == 0) { - SlowMoUtils.unfreeze(); + tickRateManager.setFrozen(false); current = 0; } else { - SlowMoUtils.freeze(); + tickRateManager.setFrozen(true); } currentTime--; } else { - SlowMoUtils.unfreeze(); + tickRateManager.setFrozen(false); } }, 0, 1); } diff --git a/MissileWars/src/de/steamwar/misslewars/slowmo/SlowMoUtils.java b/MissileWars/src/de/steamwar/misslewars/slowmo/SlowMoUtils.java deleted file mode 100644 index c5e01cc4..00000000 --- a/MissileWars/src/de/steamwar/misslewars/slowmo/SlowMoUtils.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2025 SteamWar.de-Serverteam - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package de.steamwar.misslewars.slowmo; - -import net.minecraft.server.level.ServerLevel; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.craftbukkit.CraftWorld; - -import java.lang.reflect.Field; - -public class SlowMoUtils { - - private static final Field field; - public static final boolean freezeEnabled; - - private static boolean frozen = false; - - private static final World world; - - static { - Field temp; - try { - temp = ServerLevel.class.getField("freezed"); - } catch (NoSuchFieldException e) { - temp = null; - } - field = temp; - if (field != null) field.setAccessible(true); - freezeEnabled = field != null; - world = Bukkit.getWorlds().get(0); - } - - public static void freeze() { - setFreeze(world, true); - } - - public static void unfreeze() { - setFreeze(world, false); - } - - public static boolean frozen() { - return freezeEnabled && frozen; - } - - private static void setFreeze(World world, boolean state) { - if (freezeEnabled) { - if (frozen == state) return; - try { - field.set(((CraftWorld) world).getHandle(), state); - frozen = state; - } catch (IllegalAccessException e) { - // Ignored; - } - } - } -} From c362a3e1f0dcd6e6f1b07e552cff488f9bea4db4 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 23:24:11 +0200 Subject: [PATCH 12/19] Remove unused code --- .../countdown/EnternCountdown.java | 4 -- .../de/steamwar/techhider/ProtocolUtils.java | 69 ------------------- 2 files changed, 73 deletions(-) diff --git a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/EnternCountdown.java b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/EnternCountdown.java index 87a45b4a..e5a62ac6 100644 --- a/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/EnternCountdown.java +++ b/FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/countdown/EnternCountdown.java @@ -27,12 +27,9 @@ import de.steamwar.fightsystem.fight.FightPlayer; import de.steamwar.fightsystem.utils.Message; import de.steamwar.fightsystem.utils.Region; import de.steamwar.fightsystem.utils.SWSound; -import de.steamwar.techhider.ProtocolUtils; import net.md_5.bungee.api.ChatMessageType; import org.bukkit.Bukkit; -import java.util.List; - public class EnternCountdown extends Countdown { private static int calcTime(FightPlayer fp, Countdown countdown) { @@ -47,7 +44,6 @@ public class EnternCountdown extends Countdown { } private final FightPlayer fightPlayer; - private List chunkPos; public EnternCountdown(FightPlayer fp, Countdown countdown) { super(calcTime(fp, countdown), new Message("ENTERN_COUNTDOWN"), SWSound.BLOCK_NOTE_PLING, false); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java index 18997565..70d86008 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/techhider/ProtocolUtils.java @@ -19,7 +19,6 @@ package de.steamwar.techhider; -import com.google.common.primitives.Bytes; import io.netty.buffer.ByteBuf; import java.lang.reflect.Field; @@ -80,25 +79,6 @@ public class ProtocolUtils { return chunk; } - @Deprecated - 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 readVarInt(ByteBuf buf) { int numRead = 0; int result = 0; @@ -141,53 +121,4 @@ public class ProtocolUtils { writeVarInt(buf, varInt); } } - - @Deprecated - 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; - } - - @Deprecated - 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); - } - - @Deprecated - 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; - } - } } From 414bfbfe3c259df4162d88025bc03427d59ecc23 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 23:48:33 +0200 Subject: [PATCH 13/19] Fix ClassTransformer public constructor --- .../src/main/java/de/steamwar/ClassTransformer.java | 10 +++++++++- BauSystem/build.gradle.kts | 3 +-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java b/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java index 534155b9..4467bd48 100644 --- a/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java +++ b/AccessWidener/src/main/java/de/steamwar/ClassTransformer.java @@ -48,9 +48,17 @@ public class ClassTransformer extends ClassVisitor { } if (appendPublicConstructor) { MethodVisitor methodVisitor = visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null); - methodVisitor.visitMaxs(0, 0); methodVisitor.visitCode(); + methodVisitor.visitVarInsn(Opcodes.ALOAD, 0); + methodVisitor.visitMethodInsn( + Opcodes.INVOKESPECIAL, + "java/lang/Object", + "", + "()V", + false + ); methodVisitor.visitInsn(Opcodes.RETURN); + methodVisitor.visitMaxs(1, 1); methodVisitor.visitEnd(); } super.visit(version, newAccess, name, signature, superName, interfaces); diff --git a/BauSystem/build.gradle.kts b/BauSystem/build.gradle.kts index 1d158e37..aebbc9bb 100644 --- a/BauSystem/build.gradle.kts +++ b/BauSystem/build.gradle.kts @@ -37,9 +37,8 @@ tasks.register("DevBau21") { dependsOn(":SpigotCore:shadowJar") dependsOn(":BauSystem:shadowJar") dependsOn(":SchematicSystem:shadowJar") - dependsOn(":AccessWidener:shadowJar") template = "Bau21" // TODO: Add to every new server start! newer than 1.21 inclusive! - jvmArgs = "-javaagent:/home/yoyonow/Bau21/plugins/AccessWidener.jar=start" + jvmArgs = "-javaagent:/jars/AccessWidener.jar=start" setdParams(mapOf("paper.disablePluginRemapping" to "true")) } From f5ac006c0cb6bf9e10d7173c74eaa5cd1cb26e33 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 23:49:38 +0200 Subject: [PATCH 14/19] Fix REntity.EntityDataPacketBuilder --- SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java index 1c7cce4d..a5be3c41 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java @@ -323,7 +323,7 @@ public class REntity { public EntityDataPacketBuilder add(EntityDataAccessor accessor, T value) { values.add(new SynchedEntityData.DataItem<>(accessor, value).value()); - return null; + return this; } public boolean isEmpty() { From b36464e69e3e285b3fd3d7b627fc9eb7a39036ce Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 23:50:34 +0200 Subject: [PATCH 15/19] Implement Velocity changes --- .../src/de/steamwar/velocitycore/Node.java | 17 +++++++++++------ .../de/steamwar/velocitycore/ServerStarter.java | 2 +- .../de/steamwar/velocitycore/ServerVersion.java | 8 +++++++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/VelocityCore/src/de/steamwar/velocitycore/Node.java b/VelocityCore/src/de/steamwar/velocitycore/Node.java index c7f68c70..82eec3d1 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/Node.java +++ b/VelocityCore/src/de/steamwar/velocitycore/Node.java @@ -63,7 +63,7 @@ public abstract class Node { nodes.forEach(consumer); } - public abstract ProcessBuilder startServer(String serverJar, File directory, String worldDir, String levelName, int port, String... dParams); + public abstract ProcessBuilder startServer(ServerVersion version, File directory, String worldDir, String levelName, int port, String... dParams); protected abstract ProcessBuilder prepareExecution(String... command); @@ -97,7 +97,8 @@ public abstract class Node { return hostname; } - protected void constructServerstart(File directory, List cmd, String serverJar, String worldDir, String levelName, int port, String... dParams) { + protected void constructServerstart(File directory, List cmd, ServerVersion version, String worldDir, String levelName, int port, String... dParams) { + String serverJar = version.getServerJar(); if (JAVA_8.contains(serverJar)) { cmd.add("/usr/lib/jvm/openj9-8/bin/java"); } else { @@ -107,6 +108,10 @@ public abstract class Node { for (String param : dParams) { cmd.add("-D" + param); } + if (version.isExtendedStartup()) { + cmd.add("-Dpaper.disablePluginRemapping=true"); + cmd.add("-javaagent:/jars/AccessWidener.jar=start"); + } cmd.add("-Xshareclasses:nonfatal,name=" + directory.getName()); cmd.add("-Xmx768M"); cmd.addAll(OPENJ9_ARGS); @@ -145,9 +150,9 @@ public abstract class Node { } @Override - public ProcessBuilder startServer(String serverJar, File directory, String worldDir, String levelName, int port, String... dParams) { + public ProcessBuilder startServer(ServerVersion version, File directory, String worldDir, String levelName, int port, String... dParams) { List cmd = new ArrayList<>(); - constructServerstart(directory, cmd, serverJar, worldDir, levelName, port, dParams); + constructServerstart(directory, cmd, version, worldDir, levelName, port, dParams); ProcessBuilder builder = new ProcessBuilder(cmd); builder.directory(directory); return builder; @@ -177,7 +182,7 @@ public abstract class Node { } @Override - public ProcessBuilder startServer(String serverJar, File directory, String worldDir, String levelName, int port, String... dParams) { + public ProcessBuilder startServer(ServerVersion version, File directory, String worldDir, String levelName, int port, String... dParams) { List cmd = new ArrayList<>(); cmd.add("ssh"); cmd.add("-L"); @@ -186,7 +191,7 @@ public abstract class Node { cmd.add("cd"); cmd.add(directory.getPath()); cmd.add(";"); - constructServerstart(directory, cmd, serverJar, worldDir, levelName, port, dParams); + constructServerstart(directory, cmd, version, worldDir, levelName, port, dParams); return new ProcessBuilder(cmd); } diff --git a/VelocityCore/src/de/steamwar/velocitycore/ServerStarter.java b/VelocityCore/src/de/steamwar/velocitycore/ServerStarter.java index 55bf4d36..85a537f8 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/ServerStarter.java +++ b/VelocityCore/src/de/steamwar/velocitycore/ServerStarter.java @@ -325,7 +325,7 @@ public class ServerStarter { private void regularStart(String serverName, int port) { postStart(constructor.construct(serverName, port, node.startServer( - version.getServerJar(), directory, worldDir, worldName, port, arguments.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).toArray(String[]::new) + version, directory, worldDir, worldName, port, arguments.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).toArray(String[]::new) ), worldCleanup, null)); } diff --git a/VelocityCore/src/de/steamwar/velocitycore/ServerVersion.java b/VelocityCore/src/de/steamwar/velocitycore/ServerVersion.java index 59f52f0f..f7b2dff9 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/ServerVersion.java +++ b/VelocityCore/src/de/steamwar/velocitycore/ServerVersion.java @@ -23,6 +23,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import de.steamwar.sql.GameModeConfig; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.RequiredArgsConstructor; import java.io.File; import java.util.HashMap; @@ -33,6 +34,7 @@ import java.util.regex.Pattern; @Getter @AllArgsConstructor +@RequiredArgsConstructor public enum ServerVersion { SPIGOT_8("spigot-1.8.8.jar", 8, ProtocolVersion.MINECRAFT_1_8), SPIGOT_9("spigot-1.9.4.jar", 9, ProtocolVersion.MINECRAFT_1_9), @@ -47,7 +49,7 @@ public enum ServerVersion { PAPER_18("paper-1.18.2.jar", 15, ProtocolVersion.MINECRAFT_1_18_2), PAPER_19("paper-1.19.3.jar", 19, ProtocolVersion.MINECRAFT_1_19_3), PAPER_20("paper-1.20.1.jar", 20, ProtocolVersion.MINECRAFT_1_20), - PAPER_21("paper-1.21.6.jar", 21, ProtocolVersion.MINECRAFT_1_21_6); + PAPER_21("paper-1.21.6.jar", 21, ProtocolVersion.MINECRAFT_1_21_6, true); private static final Map chatMap = new HashMap<>(); @@ -104,6 +106,10 @@ public enum ServerVersion { private final String serverJar; private final int versionSuffix; private final ProtocolVersion protocolVersion; + /** + * Adding AccessWidener agent and setting System Property (paper.disablePluginRemapping) to true + */ + private boolean extendedStartup; public String getWorldFolder(String base) { return base + versionSuffix + "/"; From 117dbd08ae898e865c8b7ac83df526e8c0d930d0 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Thu, 11 Jun 2026 23:54:42 +0200 Subject: [PATCH 16/19] Implement startup arguments into steamwar.devserver.gradle --- BauSystem/build.gradle.kts | 3 --- buildSrc/src/main/groovy/steamwar.devserver.gradle | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/BauSystem/build.gradle.kts b/BauSystem/build.gradle.kts index aebbc9bb..de9fe1b8 100644 --- a/BauSystem/build.gradle.kts +++ b/BauSystem/build.gradle.kts @@ -38,7 +38,4 @@ tasks.register("DevBau21") { dependsOn(":BauSystem:shadowJar") dependsOn(":SchematicSystem:shadowJar") template = "Bau21" - // TODO: Add to every new server start! newer than 1.21 inclusive! - jvmArgs = "-javaagent:/jars/AccessWidener.jar=start" - setdParams(mapOf("paper.disablePluginRemapping" to "true")) } diff --git a/buildSrc/src/main/groovy/steamwar.devserver.gradle b/buildSrc/src/main/groovy/steamwar.devserver.gradle index af6d224e..ce3b8765 100644 --- a/buildSrc/src/main/groovy/steamwar.devserver.gradle +++ b/buildSrc/src/main/groovy/steamwar.devserver.gradle @@ -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 @@ -245,8 +245,10 @@ class DevServer extends DefaultTask { for (Map.Entry dParam : dParams.entrySet()) { devPy.append(" -D${dParam.key}=${dParam.value}") } + devPy.append(" -Dpaper.disablePluginRemapping=true") devPy.append(" $template") if (debug) devPy.append(" -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:$debugPort") + devPy.append(" -javaagent:/jars/AccessWidener.jar=start") if (jvmArgs != null) devPy.append(" $jvmArgs") println("Starting $template with command ${devPy.toString()}") From 2d59eb81c77a5886207ae435608a330467d06af0 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Fri, 12 Jun 2026 09:49:32 +0200 Subject: [PATCH 17/19] Fix ColorInit and CustomMap deprecation warnings --- LobbySystem/build.gradle.kts | 6 +++++ .../src/de/steamwar/lobby/map/ColorInit.java | 24 ++++++++++++++++++- .../src/de/steamwar/lobby/map/CustomMap.java | 3 ++- LobbySystem/src/lobby.accesswidener | 6 +++++ 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 LobbySystem/src/lobby.accesswidener diff --git a/LobbySystem/build.gradle.kts b/LobbySystem/build.gradle.kts index a97d8394..7dd7d27f 100644 --- a/LobbySystem/build.gradle.kts +++ b/LobbySystem/build.gradle.kts @@ -19,6 +19,7 @@ plugins { steamwar.java + widener } dependencies { @@ -32,6 +33,11 @@ dependencies { compileOnly(libs.fawe) } +widener { + fromCatalog(libs.nms) + fromCatalog(libs.paperapi) +} + tasks.register("DevLobby20") { group = "run" description = "Run a 1.20 Dev Lobby" diff --git a/LobbySystem/src/de/steamwar/lobby/map/ColorInit.java b/LobbySystem/src/de/steamwar/lobby/map/ColorInit.java index ed5e0457..d5ffca23 100644 --- a/LobbySystem/src/de/steamwar/lobby/map/ColorInit.java +++ b/LobbySystem/src/de/steamwar/lobby/map/ColorInit.java @@ -42,7 +42,7 @@ public class ColorInit { if (inputStream == null) { colors = new byte[256 * 256 * 256]; for (int i = 0; i < colors.length; i++) { - colors[i] = MapPalette.matchColor(new Color(i)); + colors[i] = matchColor(new Color(i)); } } else { try { @@ -57,4 +57,26 @@ public class ColorInit { } System.out.println("[ColorInit] Initialization took " + (System.currentTimeMillis() - time) + "ms"); } + + public static byte matchColor(Color color) { + if (color.getAlpha() < 128) return 0; + + if (MapPalette.mapColorCache != null && MapPalette.mapColorCache.isCached()) { + return MapPalette.mapColorCache.matchColor(color); + } + + int index = 0; + double best = -1; + + for (int i = 4; i < MapPalette.colors.length; i++) { + double distance = MapPalette.getDistance(color, MapPalette.colors[i]); + if (distance < best || best == -1) { + best = distance; + index = i; + } + } + + // Minecraft has 248 colors, some of which have negative byte representations + return (byte) (index < 128 ? index : -129 + (index - 127)); + } } diff --git a/LobbySystem/src/de/steamwar/lobby/map/CustomMap.java b/LobbySystem/src/de/steamwar/lobby/map/CustomMap.java index 5c140ae9..ee0ebb5a 100644 --- a/LobbySystem/src/de/steamwar/lobby/map/CustomMap.java +++ b/LobbySystem/src/de/steamwar/lobby/map/CustomMap.java @@ -254,7 +254,8 @@ public class CustomMap implements Listener { int green = pixels[i2]; int i3 = (y * width + x) * numBands + 2; int blue = pixels[i3]; - Color nearest = MapPalette.getColor(ColorInit.getColorByte(red, green, blue)); + int colorIndex = ColorInit.getColorByte(red, green, blue); + Color nearest = MapPalette.colors[colorIndex >= 0 ? colorIndex : colorIndex + 256]; pixels[(y * width + x) * numBands] = nearest.getRed(); pixels[i2] = nearest.getGreen(); diff --git a/LobbySystem/src/lobby.accesswidener b/LobbySystem/src/lobby.accesswidener new file mode 100644 index 00000000..75a3fca5 --- /dev/null +++ b/LobbySystem/src/lobby.accesswidener @@ -0,0 +1,6 @@ +accessWidener v2 named + +# For CustomMap and ColorInit +accessible field org/bukkit/map/MapPalette colors [Ljava/awt/Color; +accessible method org/bukkit/map/MapPalette getDistance (Ljava/awt/Color;Ljava/awt/Color;)D +accessible field org/bukkit/map/MapPalette mapColorCache Lorg/bukkit/map/MapPalette$MapColorCache; \ No newline at end of file From 8c8da355bbe49bb93b195bb92f1c686fd5af6374 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Fri, 12 Jun 2026 10:02:34 +0200 Subject: [PATCH 18/19] Fix build.yml --- .gitea/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index e9f4d842..6aced9ef 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -51,6 +51,7 @@ jobs: rm -rf deploy mkdir -p deploy + cp "AccessWidener/build/libs/AccessWidener-all.jar" "deploy/AccessWidener.jar" cp "BauSystem/build/libs/BauSystem-all.jar" "deploy/BauSystem.jar" cp "FightSystem/build/libs/FightSystem-all.jar" "deploy/FightSystem.jar" cp "KotlinCore/build/libs/KotlinCore-all.jar" "deploy/KotlinCore.jar" From 5511a1f08ebbed1a35588ba8effbd9dbef552d31 Mon Sep 17 00:00:00 2001 From: YoyoNow Date: Fri, 12 Jun 2026 10:41:18 +0200 Subject: [PATCH 19/19] Fix steamwar.devserver.gradle --- .../src/main/groovy/steamwar.devserver.gradle | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/buildSrc/src/main/groovy/steamwar.devserver.gradle b/buildSrc/src/main/groovy/steamwar.devserver.gradle index ce3b8765..71b0027d 100644 --- a/buildSrc/src/main/groovy/steamwar.devserver.gradle +++ b/buildSrc/src/main/groovy/steamwar.devserver.gradle @@ -64,6 +64,10 @@ class DevServer extends DefaultTask { @Optional Boolean forceUpgrade = null + @Input + @Optional + String worldName = null + DevServer() { super() doFirst { @@ -83,13 +87,16 @@ class DevServer extends DefaultTask { } } - worldName = properties.get("worldName") + if (template.startsWith("Bau")) { + if (properties.containsKey("worldName")) { + worldName = properties.get("worldName") + } else { + throw new GradleException("Please supply the 'worldName' in a 'steamwar.properties' files either in this project dir or any parent project!") + } + } host = properties.get("host") debugPort = new Random().nextInt(5001, 10000) - if (worldName == null) { - throw new GradleException("Please supply the 'worldName' in a 'steamwar.properties' files either in this project dir or any parent project!") - } if (host == null) { throw new GradleException("Please supply the 'host' in a 'steamwar.properties' files either in this project dir or any parent project!") } @@ -115,9 +122,6 @@ class DevServer extends DefaultTask { @Internal Boolean running = true - @Internal - String worldName = null - class Finalizer extends DefaultTask { Finalizer() {