diff --git a/.gitignore b/.gitignore index 5e8db8e..cd0165c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,6 @@ # Rust resources rust/resources/jassets/* -!rust/resources/jassets/j4rs-* - -rust/resources/deps/* # Gradle cache .gradle/ diff --git a/java/patchbukkit/build.gradle.kts b/java/patchbukkit/build.gradle.kts index bc55001..7f39025 100644 --- a/java/patchbukkit/build.gradle.kts +++ b/java/patchbukkit/build.gradle.kts @@ -47,9 +47,8 @@ repositories { } dependencies { - paperweight.paperDevBundle("26.2.build.111-stable") + paperweight.paperDevBundle("26.2.build.112-stable") implementation("net.sf.jopt-simple:jopt-simple:6.0-alpha-3") - implementation("io.github.astonbitecode:j4rs:0.25.2") implementation("org.apache.maven:maven-resolver-provider:3.9.6") implementation("org.apache.maven.resolver:maven-resolver-impl:1.9.18") implementation("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18") diff --git a/java/patchbukkit/src/main/java/org/bukkit/command/SimpleCommandMap.java b/java/patchbukkit/src/main/java/org/bukkit/command/SimpleCommandMap.java index 60b54bb..572e793 100644 --- a/java/patchbukkit/src/main/java/org/bukkit/command/SimpleCommandMap.java +++ b/java/patchbukkit/src/main/java/org/bukkit/command/SimpleCommandMap.java @@ -42,6 +42,8 @@ public class SimpleCommandMap implements CommandMap { knownCommands.put(label, command); registeredDirectly = true; } + + command.register(this); knownCommands.put(fallbackPrefix + ":" + label, command); if (command.getAliases() != null) { diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitInternalAPIBridge.java b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitInternalAPIBridge.java index 7d371cb..be4cc1b 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitInternalAPIBridge.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitInternalAPIBridge.java @@ -72,7 +72,7 @@ public class PatchBukkitInternalAPIBridge implements InternalAPIBridge { JavaPlugin plugin, BooleanSupplier registrationCheck ) { - return null; + return new org.patchbukkit.events.PatchBukkitLifecycleEventManager(plugin, registrationCheck); } public String getStatisticCriteriaKey(Statistic statistic) { diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitPluginLoader.java b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitPluginLoader.java index 4f0fa0e..ccedcb5 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitPluginLoader.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitPluginLoader.java @@ -8,6 +8,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; +import java.util.logging.Level; +import java.util.logging.Logger; import org.bukkit.event.Event; import org.bukkit.event.Listener; import org.bukkit.plugin.EventExecutor; @@ -24,6 +26,8 @@ import org.patchbukkit.PatchBukkitPluginManager; @SuppressWarnings({ "deprecation", "removal" }) public class PatchBukkitPluginLoader implements PluginLoader { + private static final Logger LOGGER = Logger.getLogger("PatchBukkitPluginLoader"); + public static Plugin createPlugin( String jarPath, String mainClass, @@ -33,7 +37,7 @@ public class PatchBukkitPluginLoader implements PluginLoader { try { File jarFile = new File(jarPath); if (!jarFile.exists()) { - System.err.println( + LOGGER.severe( "[PatchBukkit] Plugin file does not exist: " + jarPath ); return null; @@ -87,16 +91,17 @@ public class PatchBukkitPluginLoader implements PluginLoader { pm.registerPlugin(plugin); } } catch (Throwable ignored) {} + try { + PatchBukkitBootstrap.registerPluginCommands(plugin, classLoader.getDescription()); + } catch (Throwable ignored) {} try { plugin.onLoad(); } catch (Throwable t) { - System.err.println("[PatchBukkit] Error during onLoad() for " + mainClass + ": " + t.getMessage()); - t.printStackTrace(); + LOGGER.log(Level.SEVERE, "[PatchBukkit] Error during onLoad() for " + mainClass, t); } return plugin; } catch (Throwable e) { - System.err.println("[PatchBukkit] Failed to instantiate plugin class " + mainClass + ": " + e.getMessage()); - e.printStackTrace(); + LOGGER.log(Level.SEVERE, "[PatchBukkit] Failed to instantiate plugin class " + mainClass, e); return null; } } @@ -110,11 +115,11 @@ public class PatchBukkitPluginLoader implements PluginLoader { return true; } } + return false; } catch (Throwable t) { - System.err.println("[PatchBukkit] Failed to enable plugin " + pluginName + ": " + t.getMessage()); - t.printStackTrace(); + LOGGER.log(Level.SEVERE, "[PatchBukkit] Failed to enable plugin " + pluginName, t); + return false; } - return false; } public static boolean disablePlugin(String pluginName) { @@ -126,11 +131,11 @@ public class PatchBukkitPluginLoader implements PluginLoader { return true; } } + return false; } catch (Throwable t) { - System.err.println("[PatchBukkit] Failed to disable plugin " + pluginName + ": " + t.getMessage()); - t.printStackTrace(); + LOGGER.log(Level.SEVERE, "[PatchBukkit] Failed to disable plugin " + pluginName, t); + return false; } - return false; } @Override diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitPluginManager.java b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitPluginManager.java index 185048a..8e7a11d 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitPluginManager.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitPluginManager.java @@ -107,11 +107,13 @@ public class PatchBukkitPluginManager implements PluginManager { Class pluginClass = jarClass.asSubclass(Plugin.class); Plugin plugin = pluginClass.getDeclaredConstructor().newInstance(); loader.init((org.bukkit.plugin.java.JavaPlugin) plugin); + try { + org.patchbukkit.loader.PatchBukkitBootstrap.registerPluginCommands(plugin, description); + } catch (Throwable ignored) {} try { plugin.onLoad(); } catch (Throwable t) { - server.getLogger().severe("Error loading " + plugin.getName() + ": " + t.getMessage()); - t.printStackTrace(); + server.getLogger().log(Level.SEVERE, "Error loading " + plugin.getName(), t); } registerPlugin(plugin); return plugin; @@ -145,7 +147,7 @@ public class PatchBukkitPluginManager implements PluginManager { result.add(plugin); } } catch (Exception e) { - server.getLogger().severe("Could not load plugin '" + file.getName() + "' in folder '" + file.getParent() + "': " + e.getMessage()); + server.getLogger().log(Level.SEVERE, "Could not load plugin '" + file.getName() + "' in folder '" + file.getParent() + "'", e); } } } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java index 051e39f..6514369 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/PatchBukkitServer.java @@ -12,9 +12,15 @@ import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler; import io.papermc.paper.threadedregions.scheduler.RegionScheduler; import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; import java.net.InetAddress; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -175,6 +181,10 @@ public class PatchBukkitServer implements Server { } } + private static final PrintStream ORIGINAL_OUT = System.out; + private static final PrintStream ORIGINAL_ERR = System.err; + private static final ThreadLocal IN_LOGGING = ThreadLocal.withInitial(() -> Boolean.FALSE); + private static final Logger logger = Logger.getLogger("Minecraft"); static { @@ -201,20 +211,136 @@ public class PatchBukkitServer implements Server { root.addHandler(new java.util.logging.Handler() { @Override public void publish(java.util.logging.LogRecord record) { + if (record == null) return; + if (IN_LOGGING.get()) return; + IN_LOGGING.set(true); try { LogLevel logLevel = LEVEL_MAP.getOrDefault(record.getLevel(), LogLevel.INFO); - NativeBridgeFfi.sendLog( - SendLogRequest.newBuilder() - .setLevel(logLevel) - .setMessage(record.getMessage() != null ? record.getMessage() : "") - .setLoggerName(record.getLoggerName() != null ? record.getLoggerName() : "") - .build() - ); - } catch (Throwable ignored) {} + String message = formatLogRecord(record); + String loggerName = record.getLoggerName() != null ? record.getLoggerName() : ""; + if (loggerName.startsWith("jdk.") || loggerName.startsWith("sun.") || loggerName.startsWith("java.") || loggerName.startsWith("javax.")) { + if (record.getLevel().intValue() < Level.WARNING.intValue()) { + return; + } + } + try { + NativeBridgeFfi.sendLog( + SendLogRequest.newBuilder() + .setLevel(logLevel) + .setMessage(message) + .setLoggerName(loggerName) + .build() + ); + } catch (Throwable t) { + ORIGINAL_ERR.println("[" + record.getLevel() + "][" + loggerName + "] " + message); + } + } catch (Throwable t) { + ORIGINAL_ERR.println("[RootLogger Error] Failed to publish record: " + t.getMessage()); + } finally { + IN_LOGGING.set(false); + } } @Override public void flush() {} @Override public void close() {} }); + + redirectSystemStreams(); + } + + public static String formatLogRecord(java.util.logging.LogRecord record) { + String msg = record.getMessage(); + if (msg == null) { + msg = ""; + } else if (record.getResourceBundle() != null) { + try { + msg = record.getResourceBundle().getString(msg); + } catch (Exception ignored) {} + } + Object[] params = record.getParameters(); + if (params != null && params.length > 0 && msg.contains("{0}")) { + try { + msg = MessageFormat.format(msg, params); + } catch (Exception ignored) {} + } + if (record.getThrown() != null) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + if (!msg.isEmpty()) { + pw.println(msg); + } + record.getThrown().printStackTrace(pw); + pw.flush(); + msg = sw.toString(); + } + return msg; + } + + private static void redirectSystemStreams() { + try { + System.setOut(new LoggingPrintStream(ORIGINAL_OUT, Level.INFO, "System.out")); + System.setErr(new LoggingPrintStream(ORIGINAL_ERR, Level.SEVERE, "System.err")); + } catch (Throwable t) { + ORIGINAL_ERR.println("[PatchBukkit] Failed to redirect System streams: " + t.getMessage()); + } + } + + private static class LoggingPrintStream extends PrintStream { + private final PrintStream delegate; + private final Level level; + private final String loggerName; + private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + public LoggingPrintStream(PrintStream delegate, Level level, String loggerName) { + super(delegate, true); + this.delegate = delegate; + this.level = level; + this.loggerName = loggerName; + } + + @Override + public void write(int b) { + delegate.write(b); + if (b == '\n') { + flushBuffer(); + } else if (b != '\r') { + buffer.write(b); + } + } + + @Override + public void write(byte[] buf, int off, int len) { + delegate.write(buf, off, len); + for (int i = off; i < off + len; i++) { + byte b = buf[i]; + if (b == '\n') { + flushBuffer(); + } else if (b != '\r') { + buffer.write(b); + } + } + } + + private synchronized void flushBuffer() { + if (buffer.size() == 0) return; + String line = buffer.toString(StandardCharsets.UTF_8); + buffer.reset(); + if (line.isEmpty() || IN_LOGGING.get()) return; + IN_LOGGING.set(true); + try { + LogLevel logLevel = LEVEL_MAP.getOrDefault(level, LogLevel.INFO); + try { + NativeBridgeFfi.sendLog( + SendLogRequest.newBuilder() + .setLevel(logLevel) + .setMessage(line) + .setLoggerName(loggerName) + .build() + ); + } catch (Throwable ignored) {} + } finally { + IN_LOGGING.set(false); + } + } } /** @@ -225,6 +351,21 @@ public class PatchBukkitServer implements Server { this.onlinePlayersByName.put(player.getName().toLowerCase(), player); } + public static void registerPlayer(String uuidStr, String name, boolean isOp) { + try { + UUID uuid = UUID.fromString(uuidStr); + org.patchbukkit.entity.PatchBukkitPlayer player = new org.patchbukkit.entity.PatchBukkitPlayer(uuid, name); + if (isOp) { + player.setOp(true); + } + if (org.bukkit.Bukkit.getServer() instanceof PatchBukkitServer server) { + server.registerPlayer(player); + } + } catch (Throwable t) { + logger.log(Level.SEVERE, "Failed to register player: " + name, t); + } + } + public PatchBukkitEventManager getEventManager() { return this.pluginManager.getEventManager(); } @@ -312,10 +453,11 @@ public class PatchBukkitServer implements Server { @Override public @NotNull String getMinecraftVersion() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getMinecraftVersion'" - ); + String version = this.bukkitVersion; + if (version != null && version.contains("-")) { + return version.split("-")[0]; + } + return version != null && !version.equals("Unknown-Version") ? version : "1.21.4"; } @Override @@ -325,56 +467,36 @@ public class PatchBukkitServer implements Server { @Override public int getMaxPlayers() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getMaxPlayers'" - ); + return 20; } @Override public void setMaxPlayers(int maxPlayers) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'setMaxPlayers'" - ); } @Override public int getPort() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getPort'" - ); + return 25565; } @Override public int getViewDistance() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getViewDistance'" - ); + return 10; } @Override public int getSimulationDistance() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getSimulationDistance'" - ); + return 10; } @Override public @NotNull String getIp() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getIp'"); + return "127.0.0.1"; } @Override public @NotNull String getWorldType() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException( - "Unimplemented method 'getWorldType'" - ); + return "DEFAULT"; } @Override diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/command/PatchBukkitCommandMap.java b/java/patchbukkit/src/main/java/org/patchbukkit/command/PatchBukkitCommandMap.java index f764b0a..0980c9a 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/command/PatchBukkitCommandMap.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/command/PatchBukkitCommandMap.java @@ -79,6 +79,8 @@ public class PatchBukkitCommandMap extends SimpleCommandMap { registeredDirectly = true; } + command.register(this); + registerVariants(label, command); registerVariants(fallbackPrefix + ":" + label, command); if (!clean.isEmpty()) { @@ -105,7 +107,8 @@ public class PatchBukkitCommandMap extends SimpleCommandMap { .setPluginName(fallbackPrefix) .build(); NativeBridgeFfi.registerCommand(request); - } catch (Throwable ignored) { + } catch (Throwable t) { + server.getLogger().log(java.util.logging.Level.WARNING, "Failed to register command to native bridge: " + label, t); } return registeredDirectly; @@ -140,12 +143,66 @@ public class PatchBukkitCommandMap extends SimpleCommandMap { try { String[] args = Arrays.copyOfRange(split, 1, split.length); - return command.execute(sender, rawLabel, args); + String executedLabel = rawLabel.startsWith("//") ? rawLabel : cleanLabel(rawLabel); + return command.execute(sender, executedLabel, args); } catch (Exception ex) { throw new CommandException("Unhandled exception executing '" + cmdLine + "'", ex); } } + public static boolean dispatchRaw(String senderUuid, String senderName, boolean isOp, String commandLine) { + try { + org.bukkit.command.CommandSender sender; + if (senderUuid != null && !senderUuid.isBlank()) { + java.util.UUID uuid = java.util.UUID.fromString(senderUuid); + sender = org.bukkit.Bukkit.getPlayer(uuid); + if (sender == null) { + sender = new org.patchbukkit.entity.PatchBukkitPlayer(uuid, senderName != null ? senderName : "Player"); + } + if (sender instanceof org.patchbukkit.entity.PatchBukkitPlayer p) { + p.setOp(isOp); + } + } else { + sender = org.bukkit.Bukkit.getConsoleSender(); + } + return org.bukkit.Bukkit.dispatchCommand(sender, commandLine); + } catch (Throwable t) { + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Error dispatching command: " + commandLine, t); + return false; + } + } + + public static String[] tabCompleteRaw(String senderUuid, String senderName, boolean isOp, String fullCommand, String worldName, double x, double y, double z) { + try { + org.bukkit.command.CommandSender sender; + if (senderUuid != null && !senderUuid.isBlank()) { + java.util.UUID uuid = java.util.UUID.fromString(senderUuid); + sender = org.bukkit.Bukkit.getPlayer(uuid); + if (sender == null) { + sender = new org.patchbukkit.entity.PatchBukkitPlayer(uuid, senderName != null ? senderName : "Player"); + } + if (sender instanceof org.patchbukkit.entity.PatchBukkitPlayer p) { + p.setOp(isOp); + } + } else { + sender = org.bukkit.Bukkit.getConsoleSender(); + } + Location loc = null; + if (worldName != null && !worldName.isBlank()) { + org.bukkit.World world = org.patchbukkit.world.PatchBukkitWorld.getOrCreate(worldName); + loc = new Location(world, x, y, z); + } + List list = org.bukkit.Bukkit.getServer().getCommandMap().tabComplete(sender, fullCommand, loc); + if (list == null || list.isEmpty()) { + return new String[0]; + } + return list.toArray(new String[0]); + } catch (Throwable t) { + org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Error during tab completion: " + fullCommand, t); + return new String[0]; + } + } + @Override public void clearCommands() { knownCommands.clear(); diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java index c097ec1..fc0d29a 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/entity/PatchBukkitPlayer.java @@ -1240,15 +1240,18 @@ public class PatchBukkitPlayer @Override public boolean getAllowFlight() { - return NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)).getAllowFlying(); + var abilities = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)); + return abilities != null && abilities.getAllowFlying(); } @Override public void setAllowFlight(boolean flight) { - var abilities = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)).toBuilder(); - abilities.setAllowFlying(flight); - if (abilities.getFlying()) abilities.setFlying(false); - NativeBridgeFfi.setAbilities(SetAbilitiesRequest.newBuilder().setAbilities(abilities).setUuid(BridgeUtils.convertUuid(this.uuid)).build()); + var playerUuid = BridgeUtils.convertUuid(this.uuid); + var abilities = NativeBridgeFfi.getAbilities(playerUuid); + var builder = (abilities != null ? abilities.toBuilder() : patchbukkit.abilities.Abilities.newBuilder()); + builder.setAllowFlying(flight); + if (builder.getFlying()) builder.setFlying(false); + NativeBridgeFfi.setAbilities(SetAbilitiesRequest.newBuilder().setAbilities(builder.build()).setUuid(playerUuid).build()); } @Override @@ -1335,14 +1338,17 @@ public class PatchBukkitPlayer @Override public boolean isFlying() { - return NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)).getFlying(); + var abilities = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)); + return abilities != null && abilities.getFlying(); } @Override public void setFlying(boolean value) { var playerUuid = BridgeUtils.convertUuid(this.getUniqueId()); - var abilities = NativeBridgeFfi.getAbilities(playerUuid).toBuilder().setFlying(value).build(); - NativeBridgeFfi.setAbilities(SetAbilitiesRequest.newBuilder().setAbilities(abilities).setUuid(playerUuid).build()); + var abilities = NativeBridgeFfi.getAbilities(playerUuid); + var builder = (abilities != null ? abilities.toBuilder() : patchbukkit.abilities.Abilities.newBuilder()); + builder.setFlying(value); + NativeBridgeFfi.setAbilities(SetAbilitiesRequest.newBuilder().setAbilities(builder.build()).setUuid(playerUuid).build()); } @Override @@ -1351,8 +1357,10 @@ public class PatchBukkitPlayer throw new IllegalArgumentException("Speed value " + value + " is not between -1.0 and 1.0"); } var playerUuid = BridgeUtils.convertUuid(this.getUniqueId()); - var abilities = NativeBridgeFfi.getAbilities(playerUuid).toBuilder().setFlySpeed(value).build(); - NativeBridgeFfi.setAbilities(SetAbilitiesRequest.newBuilder().setAbilities(abilities).setUuid(playerUuid).build()); + var abilities = NativeBridgeFfi.getAbilities(playerUuid); + var builder = (abilities != null ? abilities.toBuilder() : patchbukkit.abilities.Abilities.newBuilder()); + builder.setFlySpeed(value); + NativeBridgeFfi.setAbilities(SetAbilitiesRequest.newBuilder().setAbilities(builder.build()).setUuid(playerUuid).build()); } @Override @@ -1361,18 +1369,22 @@ public class PatchBukkitPlayer throw new IllegalArgumentException("Speed value " + value + " is not between -1.0 and 1.0"); } var playerUuid = BridgeUtils.convertUuid(this.getUniqueId()); - var abilities = NativeBridgeFfi.getAbilities(playerUuid).toBuilder().setWalkSpeed(value).build(); - NativeBridgeFfi.setAbilities(SetAbilitiesRequest.newBuilder().setAbilities(abilities).setUuid(playerUuid).build()); + var abilities = NativeBridgeFfi.getAbilities(playerUuid); + var builder = (abilities != null ? abilities.toBuilder() : patchbukkit.abilities.Abilities.newBuilder()); + builder.setWalkSpeed(value); + NativeBridgeFfi.setAbilities(SetAbilitiesRequest.newBuilder().setAbilities(builder.build()).setUuid(playerUuid).build()); } @Override public float getFlySpeed() { - return NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)).getFlySpeed(); + var abilities = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)); + return abilities != null ? abilities.getFlySpeed() : 0.05f; } @Override public float getWalkSpeed() { - return NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)).getWalkSpeed(); + var abilities = NativeBridgeFfi.getAbilities(BridgeUtils.convertUuid(this.uuid)); + return abilities != null ? abilities.getWalkSpeed() : 0.1f; } @Override diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventFactory.java b/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventFactory.java index 31f0b37..5c3f408 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventFactory.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventFactory.java @@ -33,6 +33,7 @@ import patchbukkit.events.BlockIgniteEvent; import patchbukkit.events.ServerCommandEvent; import patchbukkit.events.SignChangeEvent; +import java.util.logging.Level; import java.util.logging.Logger; public class PatchBukkitEventFactory { @@ -44,11 +45,22 @@ public class PatchBukkitEventFactory { Event event = Event.parseFrom(data); return createEvent(event); } catch (InvalidProtocolBufferException e) { - LOGGER.severe("Failed to parse Event: " + e.getMessage()); + LOGGER.log(Level.SEVERE, "Failed to parse Event", e); return null; } } + public static byte[] fireEventFromBytes(byte[] data, String pluginName) { + org.bukkit.event.Event event = createEventFromBytes(data); + if (event == null) { + return FireEventResponse.newBuilder().setCancelled(false).build().toByteArray(); + } + if (Bukkit.getServer() instanceof org.patchbukkit.PatchBukkitServer server) { + server.getEventManager().fireEvent(event, pluginName); + } + return toFireEventResponse(event); + } + @Nullable public static org.bukkit.event.Event createEvent(@NotNull Event event) { try { @@ -427,7 +439,10 @@ public class PatchBukkitEventFactory { java.util.UUID uuid = java.util.UUID.fromString(uuidStr); Player player = Bukkit.getServer().getPlayer(uuid); if (player == null) { - LOGGER.warning("EventFactory: Player not found for UUID " + uuidStr); + player = new org.patchbukkit.entity.PatchBukkitPlayer(uuid, "Player"); + if (Bukkit.getServer() instanceof org.patchbukkit.PatchBukkitServer server) { + server.registerPlayer(player); + } } return player; } catch (IllegalArgumentException e) { diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventManager.java b/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventManager.java index d4c6a55..02caa92 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventManager.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/events/PatchBukkitEventManager.java @@ -53,6 +53,8 @@ public class PatchBukkitEventManager { this.server = server; } + private final Set registeredBridgeEvents = java.util.concurrent.ConcurrentHashMap.newKeySet(); + public void callEvent(@NotNull Event event) throws IllegalStateException { if (event.isAsynchronous() && this.server.isPrimaryThread()) { throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously."); @@ -60,285 +62,7 @@ public class PatchBukkitEventManager { throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously."); } - var request = CallEventRequest.newBuilder(); - switch (event.getEventName()) { - case "org.bukkit.event.player.PlayerJoinEvent": - var castedEvent = (org.bukkit.event.player.PlayerJoinEvent) event; - request.setEvent( - patchbukkit.events.Event.newBuilder().setPlayerJoin( - PlayerJoinEvent.newBuilder() - .setJoinMessage(castedEvent.joinMessage().toString()) - .setPlayerUuid(BridgeUtils.convertUuid(castedEvent.getPlayer().getUniqueId())).build() - ).build() - ); - break; - case "org.bukkit.event.player.PlayerQuitEvent": - var castedQuit = (org.bukkit.event.player.PlayerQuitEvent) event; - String qMsg = castedQuit.quitMessage() != null ? castedQuit.quitMessage().toString() : ""; - request.setEvent( - patchbukkit.events.Event.newBuilder().setPlayerQuit( - PlayerQuitEvent.newBuilder() - .setQuitMessage(qMsg) - .setPlayerUuid(BridgeUtils.convertUuid(castedQuit.getPlayer().getUniqueId())).build() - ).build() - ); - break; - case "org.bukkit.event.player.PlayerGameModeChangeEvent": - var castedGM = (org.bukkit.event.player.PlayerGameModeChangeEvent) event; - request.setEvent( - patchbukkit.events.Event.newBuilder().setPlayerGamemodeChange( - PlayerGameModeChangeEvent.newBuilder() - .setNewGamemode(castedGM.getNewGameMode().name()) - .setPlayerUuid(BridgeUtils.convertUuid(castedGM.getPlayer().getUniqueId())).build() - ).build() - ); - break; - case "org.bukkit.event.player.PlayerInteractEvent": - var castedInteract = (org.bukkit.event.player.PlayerInteractEvent) event; - var block = castedInteract.getClickedBlock(); - int x = block != null ? block.getX() : 0; - int y = block != null ? block.getY() : 0; - int z = block != null ? block.getZ() : 0; - request.setEvent( - patchbukkit.events.Event.newBuilder().setPlayerInteract( - PlayerInteractEvent.newBuilder() - .setAction(castedInteract.getAction().name()) - .setClickedX(x) - .setClickedY(y) - .setClickedZ(z) - .setBlockFace(castedInteract.getBlockFace().name()) - .setHand(castedInteract.getHand() != null ? castedInteract.getHand().name() : "HAND") - .setPlayerUuid(BridgeUtils.convertUuid(castedInteract.getPlayer().getUniqueId())).build() - ).build() - ); - break; - case "org.bukkit.event.server.PluginEnableEvent": - var castedEnable = (org.bukkit.event.server.PluginEnableEvent) event; - request.setEvent( - patchbukkit.events.Event.newBuilder().setPluginEnable( - PluginEnableEvent.newBuilder() - .setPluginName(castedEnable.getPlugin().getName()).build() - ).build() - ); - break; - case "org.bukkit.event.block.SignChangeEvent": - var castedSign = (org.bukkit.event.block.SignChangeEvent) event; - var signBlock = castedSign.getBlock(); - var signBuilder = patchbukkit.events.SignChangeEvent.newBuilder() - .setBlockX(signBlock.getX()) - .setBlockY(signBlock.getY()) - .setBlockZ(signBlock.getZ()); - if (castedSign.getPlayer() != null) { - signBuilder.setPlayerUuid(BridgeUtils.convertUuid(castedSign.getPlayer().getUniqueId())); - } - for (String line : castedSign.getLines()) { - signBuilder.addLines(line != null ? line : ""); - } - request.setEvent(patchbukkit.events.Event.newBuilder().setSignChange(signBuilder.build()).build()); - break; - case "org.bukkit.event.block.BlockDamageEvent": - var castedDamage = (org.bukkit.event.block.BlockDamageEvent) event; - var dmgBlock = castedDamage.getBlock(); - var dmgBuilder = patchbukkit.events.BlockDamageEvent.newBuilder() - .setBlockX(dmgBlock.getX()) - .setBlockY(dmgBlock.getY()) - .setBlockZ(dmgBlock.getZ()) - .setInstaBreak(castedDamage.getInstaBreak()); - if (castedDamage.getPlayer() != null) { - dmgBuilder.setPlayerUuid(BridgeUtils.convertUuid(castedDamage.getPlayer().getUniqueId())); - } - request.setEvent(patchbukkit.events.Event.newBuilder().setBlockDamage(dmgBuilder.build()).build()); - break; - case "org.bukkit.event.block.BlockIgniteEvent": - var castedIgnite = (org.bukkit.event.block.BlockIgniteEvent) event; - var igniteBlock = castedIgnite.getBlock(); - var igniteBuilder = patchbukkit.events.BlockIgniteEvent.newBuilder() - .setBlockX(igniteBlock.getX()) - .setBlockY(igniteBlock.getY()) - .setBlockZ(igniteBlock.getZ()) - .setIgniteCause(castedIgnite.getCause() != null ? castedIgnite.getCause().name() : "FLINT_AND_STEEL"); - if (castedIgnite.getPlayer() != null) { - igniteBuilder.setPlayerUuid(BridgeUtils.convertUuid(castedIgnite.getPlayer().getUniqueId())); - } - request.setEvent(patchbukkit.events.Event.newBuilder().setBlockIgnite(igniteBuilder.build()).build()); - break; - case "org.bukkit.event.block.BlockGrowEvent": - var castedGrow = (org.bukkit.event.block.BlockGrowEvent) event; - var growBlock = castedGrow.getBlock(); - request.setEvent(patchbukkit.events.Event.newBuilder().setBlockGrow( - patchbukkit.events.BlockGrowEvent.newBuilder() - .setBlockX(growBlock.getX()) - .setBlockY(growBlock.getY()) - .setBlockZ(growBlock.getZ()) - .setNewBlockType(castedGrow.getNewState().getType().name()).build() - ).build()); - break; - case "org.bukkit.event.block.BlockFormEvent": - var castedForm = (org.bukkit.event.block.BlockFormEvent) event; - var formBlock = castedForm.getBlock(); - request.setEvent(patchbukkit.events.Event.newBuilder().setBlockForm( - patchbukkit.events.BlockFormEvent.newBuilder() - .setBlockX(formBlock.getX()) - .setBlockY(formBlock.getY()) - .setBlockZ(formBlock.getZ()) - .setNewBlockType(castedForm.getNewState().getType().name()).build() - ).build()); - break; - case "org.bukkit.event.block.BlockFadeEvent": - var castedFade = (org.bukkit.event.block.BlockFadeEvent) event; - var fadeBlock = castedFade.getBlock(); - request.setEvent(patchbukkit.events.Event.newBuilder().setBlockFade( - patchbukkit.events.BlockFadeEvent.newBuilder() - .setBlockX(fadeBlock.getX()) - .setBlockY(fadeBlock.getY()) - .setBlockZ(fadeBlock.getZ()) - .setNewBlockType(castedFade.getNewState().getType().name()).build() - ).build()); - break; - case "org.bukkit.event.server.ServerCommandEvent": - var castedCmd = (org.bukkit.event.server.ServerCommandEvent) event; - request.setEvent( - patchbukkit.events.Event.newBuilder().setServerCommand( - patchbukkit.events.ServerCommandEvent.newBuilder() - .setSenderName(castedCmd.getSender().getName()) - .setCommand(castedCmd.getCommand()).build() - ).build() - ); - break; - case "org.bukkit.event.server.PluginDisableEvent": - var castedDisable = (org.bukkit.event.server.PluginDisableEvent) event; - request.setEvent( - patchbukkit.events.Event.newBuilder().setPluginDisable( - PluginDisableEvent.newBuilder() - .setPluginName(castedDisable.getPlugin().getName()).build() - ).build() - ); - break; - case "org.bukkit.event.block.BlockBreakEvent": - var castedBreak = (org.bukkit.event.block.BlockBreakEvent) event; - var breakBlock = castedBreak.getBlock(); - var bBuilder = BlockBreakEvent.newBuilder() - .setBlockX(breakBlock.getX()) - .setBlockY(breakBlock.getY()) - .setBlockZ(breakBlock.getZ()) - .setBlockType(breakBlock.getType().name()) - .setExp(castedBreak.getExpToDrop()) - .setDropItems(castedBreak.isDropItems()); - if (castedBreak.getPlayer() != null) { - bBuilder.setPlayerUuid(BridgeUtils.convertUuid(castedBreak.getPlayer().getUniqueId())); - } - request.setEvent(patchbukkit.events.Event.newBuilder().setBlockBreak(bBuilder.build()).build()); - break; - case "org.bukkit.event.block.BlockPlaceEvent": - var castedPlace = (org.bukkit.event.block.BlockPlaceEvent) event; - var placedBlock = castedPlace.getBlockPlaced(); - var againstBlock = castedPlace.getBlockAgainst(); - request.setEvent( - patchbukkit.events.Event.newBuilder().setBlockPlace( - BlockPlaceEvent.newBuilder() - .setBlockX(placedBlock.getX()) - .setBlockY(placedBlock.getY()) - .setBlockZ(placedBlock.getZ()) - .setBlockPlacedType(placedBlock.getType().name()) - .setBlockAgainstType(againstBlock.getType().name()) - .setCanBuild(castedPlace.canBuild()) - .setPlayerUuid(BridgeUtils.convertUuid(castedPlace.getPlayer().getUniqueId())).build() - ).build() - ); - break; - case "org.bukkit.event.player.PlayerToggleSneakEvent": - var castedSneak = (org.bukkit.event.player.PlayerToggleSneakEvent) event; - request.setEvent( - patchbukkit.events.Event.newBuilder().setPlayerToggleSneak( - PlayerToggleSneakEvent.newBuilder() - .setIsSneaking(castedSneak.isSneaking()) - .setPlayerUuid(BridgeUtils.convertUuid(castedSneak.getPlayer().getUniqueId())).build() - ).build() - ); - break; - case "org.bukkit.event.player.PlayerToggleSprintEvent": - var castedSprint = (org.bukkit.event.player.PlayerToggleSprintEvent) event; - request.setEvent( - patchbukkit.events.Event.newBuilder().setPlayerToggleSprint( - PlayerToggleSprintEvent.newBuilder() - .setIsSprinting(castedSprint.isSprinting()) - .setPlayerUuid(BridgeUtils.convertUuid(castedSprint.getPlayer().getUniqueId())).build() - ).build() - ); - break; - case "org.bukkit.event.player.PlayerToggleFlightEvent": - var castedFlight = (org.bukkit.event.player.PlayerToggleFlightEvent) event; - request.setEvent( - patchbukkit.events.Event.newBuilder().setPlayerToggleFlight( - PlayerToggleFlightEvent.newBuilder() - .setIsFlying(castedFlight.isFlying()) - .setPlayerUuid(BridgeUtils.convertUuid(castedFlight.getPlayer().getUniqueId())).build() - ).build() - ); - break; - case "org.bukkit.event.player.PlayerMoveEvent": - var castedMove = (org.bukkit.event.player.PlayerMoveEvent) event; - request.setEvent( - patchbukkit.events.Event.newBuilder().setPlayerMove( - PlayerMoveEvent.newBuilder() - .setPlayerUuid(BridgeUtils.convertUuid(castedMove.getPlayer().getUniqueId())) - .setFrom(BridgeUtils.convertLocation(castedMove.getFrom())) - .setTo(BridgeUtils.convertLocation(castedMove.getTo())).build() - ).build() - ); - break; - case "org.bukkit.event.player.PlayerInteractEntityEvent": - case "org.bukkit.event.player.PlayerInteractAtEntityEvent": - var castedInteractEntity = (org.bukkit.event.player.PlayerInteractEntityEvent) event; - request.setEvent( - patchbukkit.events.Event.newBuilder().setPlayerInteractEntity( - PlayerInteractEntityEvent.newBuilder() - .setPlayerUuid(BridgeUtils.convertUuid(castedInteractEntity.getPlayer().getUniqueId())) - .setTargetUuid(BridgeUtils.convertUuid(castedInteractEntity.getRightClicked().getUniqueId())) - .setAction("INTERACT") - .setIsSneaking(castedInteractEntity.getPlayer().isSneaking()) - .build() - ).build() - ); - break; - case "org.bukkit.event.entity.EntityDamageByEntityEvent": - var castedDamageByEntity = (org.bukkit.event.entity.EntityDamageByEntityEvent) event; - if (castedDamageByEntity.getDamager() instanceof org.bukkit.entity.Player damagerPlayer) { - request.setEvent( - patchbukkit.events.Event.newBuilder().setPlayerInteractEntity( - PlayerInteractEntityEvent.newBuilder() - .setPlayerUuid(BridgeUtils.convertUuid(damagerPlayer.getUniqueId())) - .setTargetUuid(BridgeUtils.convertUuid(castedDamageByEntity.getEntity().getUniqueId())) - .setAction("ATTACK") - .setIsSneaking(damagerPlayer.isSneaking()) - .build() - ).build() - ); - } - break; - case "org.bukkit.event.player.AsyncPlayerChatEvent": - case "org.bukkit.event.player.PlayerChatEvent": - var castedChat = (org.bukkit.event.player.AsyncPlayerChatEvent) event; - request.setEvent( - patchbukkit.events.Event.newBuilder().setPlayerChat( - PlayerChatEvent.newBuilder() - .setMessage(castedChat.getMessage()) - .setFormat(castedChat.getFormat()) - .setPlayerUuid(BridgeUtils.convertUuid(castedChat.getPlayer().getUniqueId())).build() - ).build() - ); - break; - } - var response = NativeBridgeFfi.callEvent(request.build()); - - boolean handledByPumpkin; - if (response == null) handledByPumpkin = false; - else handledByPumpkin = response.getHandled(); - - if (!handledByPumpkin) { - // Pumpkin doesn't know this event type, dispatch Java-only - callEventJavaOnly(event); - } + callEventJavaOnly(event); } /** @@ -418,9 +142,19 @@ public class PatchBukkitEventManager { for (Map.Entry, Set> entry : this.createRegisteredListeners(listener, plugin).entrySet()) { this.getEventListeners(this.getRegistrationClass(entry.getKey())).registerAll(entry.getValue()); - for (RegisteredListener rl : entry.getValue()) { - int priorityOrdinal = Math.min(rl.getPriority().ordinal(), 4); - var request = RegisterEventRequest.newBuilder().setEventType(entry.getKey().getName()).setPluginName(plugin.getName()).setPriority(priorityOrdinal).setBlocking(true).build(); + String eventName = entry.getKey().getName(); + String key = plugin.getName() + ":" + eventName; + if (registeredBridgeEvents.add(key)) { + int maxPriority = 0; + for (RegisteredListener rl : entry.getValue()) { + maxPriority = Math.max(maxPriority, Math.min(rl.getPriority().ordinal(), 4)); + } + var request = RegisterEventRequest.newBuilder() + .setEventType(eventName) + .setPluginName(plugin.getName()) + .setPriority(maxPriority) + .setBlocking(true) + .build(); NativeBridgeFfi.registerEvent(request); } } @@ -438,10 +172,17 @@ public class PatchBukkitEventManager { executor = new TimedEventExecutor(executor, plugin, null, event); this.getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); - int priorityOrdinal = Math.min(priority.ordinal(), 4); - var request = RegisterEventRequest.newBuilder().setEventType(event.getName()).setPluginName(plugin.getName()).setPriority(priorityOrdinal).setBlocking(true).build(); - NativeBridgeFfi.registerEvent(request); - + String key = plugin.getName() + ":" + event.getName(); + if (registeredBridgeEvents.add(key)) { + int priorityOrdinal = Math.min(priority.ordinal(), 4); + var request = RegisterEventRequest.newBuilder() + .setEventType(event.getName()) + .setPluginName(plugin.getName()) + .setPriority(priorityOrdinal) + .setBlocking(true) + .build(); + NativeBridgeFfi.registerEvent(request); + } } @NotNull @@ -534,5 +275,6 @@ public class PatchBukkitEventManager { public void clearEvents() { HandlerList.unregisterAll(); + registeredBridgeEvents.clear(); } } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/loader/PatchBukkitBootstrap.java b/java/patchbukkit/src/main/java/org/patchbukkit/loader/PatchBukkitBootstrap.java index 77c24ea..b67bdd0 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/loader/PatchBukkitBootstrap.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/loader/PatchBukkitBootstrap.java @@ -6,6 +6,8 @@ import java.net.URL; import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.logging.Level; +import java.util.logging.Logger; import org.bukkit.Bukkit; import org.bukkit.command.PluginCommand; import org.bukkit.plugin.Plugin; @@ -17,11 +19,13 @@ import org.patchbukkit.command.PatchBukkitCommandMap; public class PatchBukkitBootstrap { + private static final Logger LOGGER = Logger.getLogger("PatchBukkitBootstrap"); + public static boolean bootstrapPlugins(String pluginsDirPath) { try { File pluginsDir = new File(pluginsDirPath); if (!pluginsDir.exists() || !pluginsDir.isDirectory()) { - System.out.println("[PatchBukkit] Plugins directory does not exist: " + pluginsDirPath); + LOGGER.info("[PatchBukkit] Plugins directory does not exist: " + pluginsDirPath); return true; } @@ -29,7 +33,7 @@ public class PatchBukkitBootstrap { findJarsRecursive(pluginsDir, jarFiles); if (jarFiles.isEmpty()) { - System.out.println("[PatchBukkit] No plugin JAR files found in: " + pluginsDirPath); + LOGGER.info("[PatchBukkit] No plugin JAR files found in: " + pluginsDirPath); return true; } @@ -42,8 +46,7 @@ public class PatchBukkitBootstrap { holders.put(holder.name.toLowerCase(Locale.ENGLISH), holder); } } catch (Throwable t) { - System.err.println("[PatchBukkit] Failed to parse plugin JAR " + jarFile.getName() + ": " + t.getMessage()); - t.printStackTrace(); + LOGGER.log(Level.SEVERE, "[PatchBukkit] Failed to parse plugin JAR " + jarFile.getName(), t); } } @@ -53,8 +56,7 @@ public class PatchBukkitBootstrap { try { loadAndRegisterPlugin(holder, holders); } catch (Throwable t) { - System.err.println("[PatchBukkit] Failed to load plugin " + holder.name + ": " + t.getMessage()); - t.printStackTrace(); + LOGGER.log(Level.SEVERE, "[PatchBukkit] Failed to load plugin " + holder.name, t); } } @@ -64,10 +66,9 @@ public class PatchBukkitBootstrap { if (holder.pluginInstance != null) { try { pm.enablePlugin(holder.pluginInstance); - System.out.println("[PatchBukkit] Enabled plugin: " + holder.name); + LOGGER.info("[PatchBukkit] Enabled plugin: " + holder.name); } catch (Throwable t) { - System.err.println("[PatchBukkit] Error enabling plugin " + holder.name + ": " + t.getMessage()); - t.printStackTrace(); + LOGGER.log(Level.SEVERE, "[PatchBukkit] Error enabling plugin " + holder.name, t); } } } @@ -75,8 +76,7 @@ public class PatchBukkitBootstrap { return true; } catch (Throwable t) { - System.err.println("[PatchBukkit] Fatal error during bootstrap: " + t.getMessage()); - t.printStackTrace(); + LOGGER.log(Level.SEVERE, "[PatchBukkit] Fatal error during bootstrap", t); return false; } } @@ -210,14 +210,17 @@ public class PatchBukkitBootstrap { try { plugin.onLoad(); } catch (Throwable t) { - System.err.println("[PatchBukkit] Error during onLoad() for " + holder.name + ": " + t.getMessage()); - t.printStackTrace(); + LOGGER.log(Level.SEVERE, "[PatchBukkit] Error during onLoad() for " + holder.name, t); } holder.pluginInstance = plugin; + registerPluginCommands(plugin, holder.description); + } + public static void registerPluginCommands(Plugin plugin, PluginDescriptionFile description) { + if (plugin == null || description == null) return; if (Bukkit.getCommandMap() instanceof PatchBukkitCommandMap commandMap) { - Map> commands = holder.description.getCommands(); + Map> commands = description.getCommands(); if (commands != null) { for (Map.Entry> entry : commands.entrySet()) { String cmdName = entry.getKey(); @@ -247,8 +250,8 @@ public class PatchBukkitBootstrap { } } - commandMap.register(cmdName, holder.name, pluginCmd); - System.out.println("[PatchBukkit] Registered command: /" + cmdName + " for plugin " + holder.name); + commandMap.register(cmdName, description.getName(), pluginCmd); + System.out.println("[PatchBukkit] Registered command: /" + cmdName + " for plugin " + description.getName()); } } } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/registry/PatchBukkitRegistry.java b/java/patchbukkit/src/main/java/org/patchbukkit/registry/PatchBukkitRegistry.java index 9d56429..5998f53 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/registry/PatchBukkitRegistry.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/registry/PatchBukkitRegistry.java @@ -30,7 +30,10 @@ public class PatchBukkitRegistry implements Registry { private final ThreadLocal> inFallback = ThreadLocal.withInitial(HashSet::new); private final RegistryKey registryKey; - private boolean initialized = false; + private final RegistryType registryType; + private final Function> extractor; + private final Function factory; + private volatile boolean initialized = false; public PatchBukkitRegistry(RegistryKey registryKey) { this(null, null, null, registryKey); @@ -51,19 +54,49 @@ public class PatchBukkitRegistry implements Registry { RegistryKey registryKey ) { this.registryKey = registryKey; - if (registryType != null || extractor != null || factory != null) { - initialize(registryType, extractor, factory); + this.registryType = registryType; + this.extractor = extractor; + this.factory = factory; + } + + public void ensureInitialized() { + if (initialized) return; + synchronized (this) { + if (initialized) return; + initialized = true; + initializeInternal(); } } @SuppressWarnings("unchecked") - public synchronized void initialize( - RegistryType registryType, - Function> extractor, - Function factory - ) { - if (initialized) return; - initialized = true; + private void initializeInternal() { + if (registryType != null) { + try { + GetRegistryDataRequest request = GetRegistryDataRequest.newBuilder() + .setRegistry(registryType) + .build(); + + GetRegistryDataResponse response = NativeBridgeFfi.getRegistryData(request); + if (response != null && extractor != null) { + List

protoEntries = extractor.apply(response); + if (protoEntries != null) { + for (P protoEntry : protoEntries) { + if (protoEntry == null) continue; + try { + B value = factory != null ? factory.apply(protoEntry) : null; + if (value != null && value.getKey() != null) { + entries.put(value.getKey(), value); + } + } catch (Throwable t) { + // Skip invalid entry safely + } + } + } + } + } catch (Throwable t) { + // Ignore FFI or RPC failure safely + } + } // Auto-discover pre-registered Paper/Bukkit constants for this registry if (registryKey != null) { @@ -89,6 +122,8 @@ public class PatchBukkitRegistry implements Registry { } } catch (Throwable ignored) {} } + } else if (RegistryKey.SOUND_EVENT.equals(registryKey) || "sound_event".equalsIgnoreCase(registryKey.key().value())) { + // Do not reflectively autoDiscover Sound.class because Sound. depends on Registry.SOUNDS! } else { Class valueClass = (Class) LegacyRegistryIdentifiers.KEY_TO_CLASS_MAP.get(registryKey); if (valueClass != null) { @@ -97,34 +132,6 @@ public class PatchBukkitRegistry implements Registry { } } } - - if (registryType != null) { - try { - GetRegistryDataRequest request = GetRegistryDataRequest.newBuilder() - .setRegistry(registryType) - .build(); - - GetRegistryDataResponse response = NativeBridgeFfi.getRegistryData(request); - if (response != null && extractor != null) { - List

protoEntries = extractor.apply(response); - if (protoEntries != null) { - for (P protoEntry : protoEntries) { - if (protoEntry == null) continue; - try { - B value = factory.apply(protoEntry); - if (value != null && value.getKey() != null) { - entries.put(value.getKey(), value); - } - } catch (Throwable t) { - // Skip invalid entry safely - } - } - } - } - } catch (Throwable t) { - // Ignore FFI or RPC failure safely - } - } } @SuppressWarnings("unchecked") @@ -162,6 +169,7 @@ public class PatchBukkitRegistry implements Registry { if (key == null) { return null; } + ensureInitialized(); B value = entries.get(key); if (value != null) { return value; @@ -228,11 +236,13 @@ public class PatchBukkitRegistry implements Registry { @Override public boolean hasTag(TagKey key) { + ensureInitialized(); return key != null && tags.containsKey(key.key().asString()); } @Override public @NonNull Tag getTag(TagKey key) { + ensureInitialized(); PatchBukkitTag tag = tags.get(key.key().asString()); if (tag == null) { throw new NoSuchElementException("Unknown tag: " + key.key().asString()); @@ -246,25 +256,30 @@ public class PatchBukkitRegistry implements Registry { @Override public @NonNull Collection> getTags() { + ensureInitialized(); return Collections.unmodifiableCollection(tags.values()); } @Override public @NonNull Stream stream() { + ensureInitialized(); return entries.values().stream(); } public @NonNull Stream keyStream() { + ensureInitialized(); return entries.keySet().stream(); } @Override public int size() { + ensureInitialized(); return entries.size(); } @Override public @NonNull Iterator iterator() { + ensureInitialized(); return Collections.unmodifiableCollection(entries.values()).iterator(); } } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/registry/PatchBukkitRegistryAccess.java b/java/patchbukkit/src/main/java/org/patchbukkit/registry/PatchBukkitRegistryAccess.java index 98a6279..aaba928 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/registry/PatchBukkitRegistryAccess.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/registry/PatchBukkitRegistryAccess.java @@ -53,24 +53,16 @@ public class PatchBukkitRegistryAccess extends io.papermc.paper.registry.PaperRe return (Registry) existing; } - PatchBukkitRegistry reg = new PatchBukkitRegistry(registryKey); + PatchBukkitRegistry reg; + RegistryFactory factoryEntry = FACTORIES.get(registryKey); + if (factoryEntry != null) { + reg = new PatchBukkitRegistry(factoryEntry.registryType(), factoryEntry.extractor(), factoryEntry.factory(), registryKey); + } else { + reg = new PatchBukkitRegistry(registryKey); + } + Registry winner = instances.putIfAbsent(registryKey, reg); - if (winner != null) { - return (Registry) winner; - } - - try { - RegistryFactory factoryEntry = FACTORIES.get(registryKey); - if (factoryEntry != null) { - reg.initialize(factoryEntry.registryType(), factoryEntry.extractor(), factoryEntry.factory()); - } else { - reg.initialize(null, null, null); - } - } catch (Throwable t) { - // Ignore failure during registry initialization safely - } - - return (Registry) reg; + return (Registry) (winner != null ? winner : reg); } @SuppressWarnings({"unchecked", "deprecation"}) diff --git a/java/patchbukkit/src/test/java/org/patchbukkit/registry/PatchBukkitRegistryAccessTest.java b/java/patchbukkit/src/test/java/org/patchbukkit/registry/PatchBukkitRegistryAccessTest.java index f43e2cd..4a8511f 100644 --- a/java/patchbukkit/src/test/java/org/patchbukkit/registry/PatchBukkitRegistryAccessTest.java +++ b/java/patchbukkit/src/test/java/org/patchbukkit/registry/PatchBukkitRegistryAccessTest.java @@ -55,6 +55,15 @@ public class PatchBukkitRegistryAccessTest { }); } + @Test + public void testSoundClassInitialization() { + assertDoesNotThrow(() -> { + org.bukkit.Sound sound = org.bukkit.Sound.ENTITY_EXPERIENCE_ORB_PICKUP; + assertNotNull(sound, "Sound constant must initialize successfully without ExceptionInInitializerError"); + assertNotNull(sound.getKey(), "Sound key must not be null"); + }); + } + @Test public void testInspectBlockTypeAndDataMethods() { System.out.println("=== BLOCK TYPE METHODS ==="); diff --git a/rust/Cargo.lock b/rust/Cargo.lock index e00bf3a..eb85558 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -178,7 +178,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" dependencies = [ - "asn1-rs-derive", + "asn1-rs-derive 0.5.1", "asn1-rs-impl", "displaydoc", "nom", @@ -188,6 +188,22 @@ dependencies = [ "time", ] +[[package]] +name = "asn1-rs" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8" +dependencies = [ + "asn1-rs-derive 0.6.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 2.0.20", + "time", +] + [[package]] name = "asn1-rs-derive" version = "0.5.1" @@ -200,6 +216,18 @@ dependencies = [ "synstructure", ] +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + [[package]] name = "asn1-rs-impl" version = "0.2.0" @@ -211,6 +239,30 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-compression" version = "0.4.43" @@ -331,6 +383,15 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -489,9 +550,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" +checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -633,6 +694,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "compression-codecs" version = "0.4.38" @@ -649,6 +720,15 @@ version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -669,9 +749,9 @@ checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" [[package]] name = "cookie" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" dependencies = [ "percent-encoding", "time", @@ -900,6 +980,15 @@ dependencies = [ "spin 0.10.1", ] +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -1190,7 +1279,21 @@ version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" dependencies = [ - "asn1-rs", + "asn1-rs 0.6.2", + "displaydoc", + "nom", + "num-bigint 0.4.8", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs 0.7.2", "displaydoc", "nom", "num-bigint 0.4.8", @@ -1291,48 +1394,6 @@ dependencies = [ "litrs", ] -[[package]] -name = "dtls" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a431e87fc386bd5e02deb554a013f97bc406e47a7d8e97efb7c6366b980e9c" -dependencies = [ - "aes 0.8.4", - "aes-gcm", - "async-trait", - "bytecheck", - "byteorder", - "cbc", - "ccm", - "chacha20poly1305", - "der-parser", - "hmac 0.12.1", - "log", - "p256", - "p384 0.13.1", - "portable-atomic", - "rand 0.9.5", - "rand_core 0.6.4", - "rcgen", - "ring", - "rkyv", - "rustls", - "sec1 0.7.3", - "sha1 0.10.7", - "sha2 0.10.9", - "thiserror 1.0.69", - "tokio", - "webrtc-util", - "x25519-dalek", - "x509-parser", -] - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - [[package]] name = "ecdsa" version = "0.16.9" @@ -1501,9 +1562,29 @@ dependencies = [ [[package]] name = "error-code" -version = "3.3.2" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" +checksum = "0b5343afd4a8365a643ac588dab4cf234a190c7f6c88c9f6dd6ffe00837661b7" + +[[package]] +name = "event-listener" +version = "5.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" +dependencies = [ + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] [[package]] name = "fastrand" @@ -1554,9 +1635,9 @@ checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "find-msvc-tools" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "fixedbitset" @@ -1613,12 +1694,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "fsevent-sys" version = "4.1.0" @@ -1630,9 +1705,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -1645,9 +1720,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -1655,15 +1730,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -1672,38 +1747,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-macro" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.3", ] [[package]] name = "futures-sink" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -1738,18 +1813,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi 5.3.0", - "wasip2", -] - [[package]] name = "getrandom" version = "0.4.3" @@ -1759,7 +1822,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi 6.0.0", + "r-efi", "rand_core 0.10.1", "wasm-bindgen", ] @@ -1826,9 +1889,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.15" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +checksum = "9f877e75f39e9827ec50a572dd592684ac28c029578726c85f1b2aa6ab807449" dependencies = [ "atomic-waker", "bytes", @@ -1959,9 +2022,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ "bytes", "futures-core", @@ -2032,9 +2095,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" dependencies = [ "displaydoc", "potential_utf", @@ -2046,9 +2109,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", "litemap", @@ -2059,9 +2122,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -2073,16 +2136,17 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" [[package]] name = "icu_properties" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -2093,15 +2157,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" dependencies = [ "displaydoc", "icu_locale_core", @@ -2166,9 +2230,9 @@ dependencies = [ [[package]] name = "inotify" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "153be1941a183ec9ccd095ddbe17a8b8d435ef6c76e9e02451b933c3999af2c8" +checksum = "4cc00ea907cab49550b7da656f80ebb97be1b997d931fbcd28d39734e17ce592" dependencies = [ "bitflags 2.13.1", "inotify-sys", @@ -2203,27 +2267,6 @@ dependencies = [ "hybrid-array", ] -[[package]] -name = "interceptor" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c11a956a48159f7fe539b8198f12b4db9b709ae5f94385b840db38f97fed74" -dependencies = [ - "async-trait", - "bytes", - "futures", - "log", - "portable-atomic", - "rand 0.9.5", - "rtcp", - "rtp", - "thiserror 1.0.69", - "tokio", - "waitgroup", - "webrtc-srtp", - "webrtc-util", -] - [[package]] name = "io-extras" version = "0.19.0" @@ -2282,25 +2325,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" -[[package]] -name = "j4rs" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c26fb1429997a5d8f98777df359f0132f62905ecce1d3bc2836372045c67de89" -dependencies = [ - "cesu8", - "dunce", - "fs_extra", - "futures", - "java-locator", - "jni-sys", - "libc", - "libloading 0.8.9", - "log", - "serde", - "serde_json", -] - [[package]] name = "java-locator" version = "0.1.9" @@ -2346,6 +2370,38 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "java-locator", + "jni-macros", + "jni-sys", + "libloading 0.8.9", + "log", + "simd_cesu8", + "thiserror 2.0.20", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.119", +] + [[package]] name = "jni-sys" version = "0.4.1" @@ -2458,9 +2514,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2026a5056764a10b2bf5d56488cba40da507f5493a6a429340e2004d9ed085fa" +checksum = "28d0a00925a9f930d679b6789b721e3a7f9ed110f41b86d2497caa780c3a070a" dependencies = [ "libc", ] @@ -2473,9 +2529,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "litrs" @@ -2583,9 +2639,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.7.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -2670,19 +2726,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset", - "pin-utils", -] - [[package]] name = "nix" version = "0.31.3" @@ -2693,6 +2736,7 @@ dependencies = [ "cfg-if", "cfg_aliases", "libc", + "memoffset", ] [[package]] @@ -2795,9 +2839,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" dependencies = [ "num-traits", ] @@ -2857,7 +2901,16 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" dependencies = [ - "asn1-rs", + "asn1-rs 0.6.2", +] + +[[package]] +name = "oid-registry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs 0.7.2", ] [[package]] @@ -2931,6 +2984,12 @@ dependencies = [ "sha2 0.11.0", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.5" @@ -2961,7 +3020,7 @@ dependencies = [ "anyhow", "env_logger", "glob", - "j4rs", + "jni", "log", "prost", "prost-build", @@ -3088,12 +3147,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pkcs1" version = "0.8.0-rc.4" @@ -3126,9 +3179,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "png" @@ -3174,9 +3227,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" [[package]] name = "portable-atomic-util" @@ -3201,9 +3254,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "zerovec", ] @@ -3220,15 +3273,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24" -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "prettyplease" version = "0.2.37" @@ -3466,9 +3510,11 @@ dependencies = [ [[package]] name = "pumpkin" version = "0.1.0-dev+26.2-26.40" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#50fe9f7cd7182c13a856d0a41fd59a5ddcb37f6f" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#c196aa541190ba2c83f8d1ceea670cf660645147" dependencies = [ + "aes 0.9.2", "arc-swap", + "async-trait", "axum", "base64 0.23.1", "bitflags 2.13.1", @@ -3519,8 +3565,8 @@ dependencies = [ "tracing-subscriber", "ureq", "uuid", - "wasm-encoder 0.255.0", - "wasmparser 0.255.0", + "wasm-encoder 0.256.0", + "wasmparser 0.256.0", "wasmtime", "wasmtime-wasi", "wasmtime-wasi-http", @@ -3530,7 +3576,7 @@ dependencies = [ [[package]] name = "pumpkin-api-macros" version = "0.1.0-dev+26.2-26.40" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#50fe9f7cd7182c13a856d0a41fd59a5ddcb37f6f" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#c196aa541190ba2c83f8d1ceea670cf660645147" dependencies = [ "proc-macro-error2", "proc-macro2", @@ -3541,7 +3587,7 @@ dependencies = [ [[package]] name = "pumpkin-codecs" version = "0.1.0-dev+26.2-26.40" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#50fe9f7cd7182c13a856d0a41fd59a5ddcb37f6f" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#c196aa541190ba2c83f8d1ceea670cf660645147" dependencies = [ "serde_json", "tracing", @@ -3550,7 +3596,7 @@ dependencies = [ [[package]] name = "pumpkin-config" version = "0.1.0-dev+26.2-26.40" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#50fe9f7cd7182c13a856d0a41fd59a5ddcb37f6f" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#c196aa541190ba2c83f8d1ceea670cf660645147" dependencies = [ "pumpkin-util", "serde", @@ -3562,7 +3608,7 @@ dependencies = [ [[package]] name = "pumpkin-data" version = "0.1.0-dev+26.2-26.40" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#50fe9f7cd7182c13a856d0a41fd59a5ddcb37f6f" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#c196aa541190ba2c83f8d1ceea670cf660645147" dependencies = [ "crc-fast", "phf", @@ -3575,7 +3621,7 @@ dependencies = [ [[package]] name = "pumpkin-inventory" version = "0.1.0-dev+26.2-26.40" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#50fe9f7cd7182c13a856d0a41fd59a5ddcb37f6f" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#c196aa541190ba2c83f8d1ceea670cf660645147" dependencies = [ "pumpkin-data", "pumpkin-protocol", @@ -3590,10 +3636,11 @@ dependencies = [ [[package]] name = "pumpkin-macros" version = "0.1.0-dev+26.2-26.40" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#50fe9f7cd7182c13a856d0a41fd59a5ddcb37f6f" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#c196aa541190ba2c83f8d1ceea670cf660645147" dependencies = [ "heck", "proc-macro-error2", + "proc-macro2", "pumpkin-data", "quote", "syn 3.0.3", @@ -3602,7 +3649,7 @@ dependencies = [ [[package]] name = "pumpkin-nbt" version = "0.1.0-dev+26.2-26.40" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#50fe9f7cd7182c13a856d0a41fd59a5ddcb37f6f" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#c196aa541190ba2c83f8d1ceea670cf660645147" dependencies = [ "bytes", "cesu8", @@ -3616,7 +3663,7 @@ dependencies = [ [[package]] name = "pumpkin-protocol" version = "0.1.0-dev+26.2-26.40" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#50fe9f7cd7182c13a856d0a41fd59a5ddcb37f6f" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#c196aa541190ba2c83f8d1ceea670cf660645147" dependencies = [ "aes 0.9.2", "async-compression", @@ -3631,20 +3678,23 @@ dependencies = [ "pumpkin-util", "pumpkin-world", "serde", + "serde_json", "thiserror 2.0.20", "tokio", "uuid", + "xxhash-rust", ] [[package]] name = "pumpkin-util" version = "0.1.0-dev+26.2-26.40" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#50fe9f7cd7182c13a856d0a41fd59a5ddcb37f6f" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#c196aa541190ba2c83f8d1ceea670cf660645147" dependencies = [ "base64 0.23.1", "bytes", "colored", "crypto-bigint 0.7.5", + "dashmap", "ecdsa 0.17.0", "md5", "num-derive", @@ -3657,7 +3707,6 @@ dependencies = [ "serde_json", "sha2 0.11.0", "thiserror 2.0.20", - "tokio", "ureq", "uuid", ] @@ -3665,7 +3714,7 @@ dependencies = [ [[package]] name = "pumpkin-world" version = "0.1.0-dev+26.2-26.40" -source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#50fe9f7cd7182c13a856d0a41fd59a5ddcb37f6f" +source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#c196aa541190ba2c83f8d1ceea670cf660645147" dependencies = [ "arc-swap", "bitflags 2.13.1", @@ -3703,6 +3752,19 @@ version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" +[[package]] +name = "quinn-udp" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76150b617afc75e6e21ac5f39bc196e80b65415ae48d62dbef8e2519d040ce42" +dependencies = [ + "cfg_aliases", + "libc", + "log", + "socket2", + "windows-sys 0.61.2", +] + [[package]] name = "quote" version = "1.0.47" @@ -3712,12 +3774,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - [[package]] name = "r-efi" version = "6.0.0" @@ -3733,16 +3789,6 @@ dependencies = [ "ptr_meta", ] -[[package]] -name = "rand" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" -dependencies = [ - "rand_chacha", - "rand_core 0.9.5", -] - [[package]] name = "rand" version = "0.10.2" @@ -3764,16 +3810,6 @@ dependencies = [ "rand_core 0.10.1", ] -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - [[package]] name = "rand_core" version = "0.6.4" @@ -3783,15 +3819,6 @@ dependencies = [ "getrandom 0.2.17", ] -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - [[package]] name = "rand_core" version = "0.10.1" @@ -3820,15 +3847,15 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.13.2" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" +checksum = "091e7a8e7d86e6feb87a27ce8e2cba29d49eff9507afeebefab7eeb2ca667fb4" dependencies = [ "pem", "ring", "rustls-pki-types", "time", - "x509-parser", + "x509-parser 0.18.1", "yasna", ] @@ -3999,29 +4026,279 @@ dependencies = [ ] [[package]] -name = "rtcp" -version = "0.17.2" +name = "rtc" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad7f6a501162881032fc84b4bc78ae11f1b30748180b9f5cbe8810bf613aab" +checksum = "e55da8e478486106f04db1ccba177ae3260fb12899982cd5cddf2ac0e1cdcfe9" dependencies = [ "bytes", - "thiserror 1.0.69", - "webrtc-util", + "hex", + "log", + "rand 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rcgen", + "ring", + "rtc-datachannel", + "rtc-dtls", + "rtc-ice", + "rtc-interceptor", + "rtc-mdns", + "rtc-media", + "rtc-rtcp", + "rtc-rtp", + "rtc-sctp", + "rtc-sdp", + "rtc-shared", + "rtc-srtp", + "rtc-stun", + "rtc-turn", + "rustls", + "sansio", + "serde", + "serde_json", + "sha2 0.10.9", + "unicase", + "url", ] [[package]] -name = "rtp" -version = "0.17.2" +name = "rtc-datachannel" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "149329e78ada26b5e174a4c281a7a7e5c8bda6008adc90daddd6f98fce56db29" +checksum = "7bef39b017b5cf0a3511eab456505556b320e0aa04d4547b015ecba9f844fe43" +dependencies = [ + "bytes", + "log", + "rtc-sctp", + "rtc-shared", + "sansio", +] + +[[package]] +name = "rtc-dtls" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a1be8a58a72a257c233ad9a2f92130c15994e02f7a1981379fdebbe0d8761a" +dependencies = [ + "aes 0.8.4", + "bytecheck", + "byteorder", + "bytes", + "cbc", + "ccm", + "chacha20poly1305", + "der-parser 9.0.0", + "hmac 0.12.1", + "log", + "p256", + "p384 0.13.1", + "rand 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.6.4", + "rcgen", + "ring", + "rkyv", + "rtc-shared", + "rustls", + "sec1 0.7.3", + "sha1 0.10.7", + "sha2 0.10.9", + "subtle", + "x25519-dalek", + "x509-parser 0.16.0", +] + +[[package]] +name = "rtc-ice" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "640c7ecb9a09d21c0d4ba27b3ff22adb07cce90d23a7bce7c82bcbe424844e31" +dependencies = [ + "bytes", + "crc", + "log", + "rand 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rtc-mdns", + "rtc-shared", + "rtc-stun", + "sansio", + "serde", + "url", + "uuid", +] + +[[package]] +name = "rtc-interceptor" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9436633af4a6beff6fee08c99464012017fcd1e0a4c0559b1f0d733e8812217a" +dependencies = [ + "log", + "rand 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rtc-interceptor-derive", + "rtc-rtcp", + "rtc-rtp", + "rtc-shared", + "sansio", +] + +[[package]] +name = "rtc-interceptor-derive" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3563dafffb11e99264cbfa87c45d806c349c701a96ea3cf3c4778ae3c5da63b4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "rtc-mdns" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4db343dbcd1445783ac9f832e350775e4e655b2c890bead7d13dca1219e0ac5" +dependencies = [ + "bytes", + "log", + "rtc-shared", + "sansio", + "socket2", +] + +[[package]] +name = "rtc-media" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709a571ed5d88854f3f8c0b92e4a445d77ee54fd33dd45f9297a3d91e3e96d67" +dependencies = [ + "byteorder", + "bytes", + "rand 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rtc-rtp", + "rtc-shared", + "thiserror 2.0.20", +] + +[[package]] +name = "rtc-rtcp" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8817201df4a87008528804073d2dddfefd08eecb44afe6f6d61f7fb1beed72d0" +dependencies = [ + "bytes", + "rtc-shared", +] + +[[package]] +name = "rtc-rtp" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa84012c4a3d855586878e9f891af5062bd5690278b2c54bfcfb0a0ddd9098" dependencies = [ "bytes", "memchr", - "portable-atomic", - "rand 0.9.5", + "rand 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rtc-shared", "serde", - "thiserror 1.0.69", - "webrtc-util", +] + +[[package]] +name = "rtc-sctp" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78317cf685b6cde3dbd1cd8e7c9f373e74f00dce00000a25c14c6ddb3378da86" +dependencies = [ + "bytes", + "crc32c", + "log", + "rand 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rtc-shared", + "rustc-hash", + "slab", + "thiserror 2.0.20", +] + +[[package]] +name = "rtc-sdp" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ab04410496c8ea47350490298fb135f5432f8fa1aa88edf5dadc81c855a324" +dependencies = [ + "rand 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rtc-shared", + "url", +] + +[[package]] +name = "rtc-shared" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae32667b8c9f15223172ce27471e02791e2b401de559b3ea02a6ec93ff280e0" +dependencies = [ + "aes 0.8.4", + "aes-gcm", + "bitflags 1.3.2", + "bytes", + "nix", + "p256", + "rand 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rcgen", + "sec1 0.7.3", + "serde", + "substring", + "thiserror 2.0.20", + "url", + "winapi", +] + +[[package]] +name = "rtc-srtp" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413d48daef797c98c12ff91988abac36d44c81974f0a4b0658549174230819cd" +dependencies = [ + "aes 0.8.4", + "byteorder", + "bytes", + "ctr", + "hmac 0.12.1", + "ring", + "rtc-rtcp", + "rtc-rtp", + "rtc-shared", + "sha1 0.10.7", + "subtle", +] + +[[package]] +name = "rtc-stun" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4b4540b2b8ff7155ef9e95a3b492a7830e974e2fa9ce4b76ec315c4a6b4953d" +dependencies = [ + "base64 0.22.1", + "bytes", + "crc", + "lazy_static", + "md-5", + "rand 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "rtc-shared", + "sansio", + "subtle", + "url", +] + +[[package]] +name = "rtc-turn" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449365ff0b87488d5f9532409813e76e1f0f5965ef31ce6fcde9aa786c852c6c" +dependencies = [ + "bytes", + "log", + "rtc-shared", + "rtc-stun", + "sansio", ] [[package]] @@ -4149,9 +4426,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" dependencies = [ "ring", "rustls-pki-types", @@ -4176,7 +4453,7 @@ dependencies = [ "libc", "log", "memchr", - "nix 0.31.3", + "nix", "unicode-segmentation", "unicode-width", "utf8parse", @@ -4198,24 +4475,18 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sansio" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62751faa8bc286982334a082fe125184a29fc89d17775766e4f891b7d726980" + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sdp" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b6eecfa5151edef84d544ff3885dff98e5b5fe97586757a4b5118bde51c958" -dependencies = [ - "rand 0.9.5", - "substring", - "thiserror 1.0.69", - "url", -] - [[package]] name = "sec1" version = "0.7.3" @@ -4448,6 +4719,16 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -4484,25 +4765,6 @@ dependencies = [ "serde", ] -[[package]] -name = "smol_str" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" -dependencies = [ - "serde", -] - -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.5" @@ -4560,25 +4822,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "stun" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e3b8d5ad1dd4c8cd0595440f26521a71dd593cb5873ee8e06b91510b7d97269" -dependencies = [ - "base64 0.22.1", - "crc", - "lazy_static", - "md-5", - "rand 0.9.5", - "ring", - "subtle", - "thiserror 1.0.69", - "tokio", - "url", - "webrtc-util", -] - [[package]] name = "substring" version = "1.4.5" @@ -4758,9 +5001,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "zerovec", @@ -4790,10 +5033,9 @@ dependencies = [ "bytes", "libc", "mio", - "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.5", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -4990,27 +5232,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "turn" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d99249a493335eb44c4d7943a8751b22561a82c9903d8eb2d29780b3927880a7" -dependencies = [ - "async-trait", - "base64 0.22.1", - "futures", - "log", - "md-5", - "portable-atomic", - "rand 0.9.5", - "ring", - "stun", - "thiserror 1.0.69", - "tokio", - "tokio-util", - "webrtc-util", -] - [[package]] name = "twox-hash" version = "1.6.3" @@ -5075,11 +5296,11 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" +checksum = "972d7902c8735f2695410b8aed7df6ed12a47394aa1c8d7af49f0497b731a94d" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "cookie_store", "log", "percent-encoding", @@ -5094,11 +5315,11 @@ dependencies = [ [[package]] name = "ureq-proto" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c" +checksum = "da5f78b09e6941e1a0f2e30e695e4b120377b54d5e0aec11b594bb57b3971613" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "http", "httparse", "log", @@ -5153,15 +5374,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "waitgroup" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1f50000a783467e6c0200f9d10642f4bc424e39efc1b770203e88b488f79292" -dependencies = [ - "atomic-waker", -] - [[package]] name = "walkdir" version = "2.5.0" @@ -5187,15 +5399,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasip2" -version = "1.0.4+wasi-0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "wasm-bindgen" version = "0.2.127" @@ -5253,12 +5456,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.255.0" +version = "0.256.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b524283fb5df62eec102ed0574838961bdd7ba5ac9c50d38e2756c51c971a42" +checksum = "ec1492381bfd5ea51c2a99a919b676662559925cb8d7490547ec2e14c1ad3eb1" dependencies = [ "leb128fmt", - "wasmparser 0.255.0", + "wasmparser 0.256.0", ] [[package]] @@ -5276,9 +5479,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.255.0" +version = "0.256.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8e329ef4b5d46e73b91d3ac6924417cad55a8cbbf869c199283383427c3320b" +checksum = "60bd825ffedc6cba8a642924ba7ae424afbc47811cffbcb7b92031ec24e59b4c" dependencies = [ "bitflags 2.13.1", "hashbrown 0.17.1", @@ -5308,6 +5511,7 @@ dependencies = [ "async-trait", "bitflags 2.13.1", "bumpalo", + "bytes", "cc", "cfg-if", "encoding_rs", @@ -5566,6 +5770,7 @@ dependencies = [ "rustls", "tokio", "tokio-rustls", + "tokio-util", "tracing", "wasmtime", "wasmtime-wasi", @@ -5597,171 +5802,20 @@ dependencies = [ [[package]] name = "webrtc" -version = "0.17.2" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaacdf9d96224d7b6e2872ba065578f38775f7634367e3ac2cc87c7271da433" +checksum = "7330857e1e10fcbfa5f358f152cc01f22a05bb523875c0547677db0f24538dcd" dependencies = [ - "arc-swap", + "async-broadcast", + "async-channel", "async-trait", "bytes", - "dtls", - "hex", - "interceptor", - "lazy_static", + "event-listener", + "futures", "log", - "portable-atomic", - "rand 0.9.5", - "rcgen", - "regex", - "ring", - "rtcp", - "rtp", - "sdp", - "serde", - "serde_json", - "sha2 0.10.9", - "smol_str", - "stun", - "thiserror 1.0.69", + "quinn-udp", + "rtc", "tokio", - "turn", - "unicase", - "url", - "waitgroup", - "webrtc-data", - "webrtc-ice", - "webrtc-mdns", - "webrtc-media", - "webrtc-sctp", - "webrtc-srtp", - "webrtc-util", -] - -[[package]] -name = "webrtc-data" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd470286275809f2fcfcdb1e73ef5f1500be82eff7fe98150ce81b20aad5a2a4" -dependencies = [ - "bytes", - "log", - "portable-atomic", - "thiserror 1.0.69", - "tokio", - "webrtc-sctp", - "webrtc-util", -] - -[[package]] -name = "webrtc-ice" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7fd30f52e6fda8664779b84b7904b2553b76fee24d9ca665e774ae32b13f53" -dependencies = [ - "arc-swap", - "async-trait", - "crc", - "log", - "portable-atomic", - "rand 0.9.5", - "serde", - "serde_json", - "stun", - "thiserror 1.0.69", - "tokio", - "turn", - "url", - "uuid", - "waitgroup", - "webrtc-mdns", - "webrtc-util", -] - -[[package]] -name = "webrtc-mdns" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ffa0ea00c0fae979aafa5db285fec5eeb360e1ea246ebec529ba4e09f8e03e" -dependencies = [ - "log", - "socket2 0.5.10", - "thiserror 1.0.69", - "tokio", - "webrtc-util", -] - -[[package]] -name = "webrtc-media" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a6c7335bdd03dc023cb9a3bd7966866d969c92c86987b73aa16dfa39c7ca29" -dependencies = [ - "byteorder", - "bytes", - "rand 0.9.5", - "rtp", - "thiserror 1.0.69", -] - -[[package]] -name = "webrtc-sctp" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4b637f0d8eb96d900ac0f79b3060ddd21ca88dfefa467e96e67ab0016f2574" -dependencies = [ - "arc-swap", - "async-trait", - "bytes", - "crc", - "log", - "portable-atomic", - "rand 0.9.5", - "thiserror 1.0.69", - "tokio", - "webrtc-util", -] - -[[package]] -name = "webrtc-srtp" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45d2b667a0b5d04eebcb7cb22fd51b2af3721f418097b496e72de16386c8d0c3" -dependencies = [ - "aead", - "aes 0.8.4", - "aes-gcm", - "byteorder", - "bytes", - "ctr", - "hmac 0.12.1", - "log", - "rtcp", - "rtp", - "sha1 0.10.7", - "subtle", - "thiserror 1.0.69", - "tokio", - "webrtc-util", -] - -[[package]] -name = "webrtc-util" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1beae0b4f24969741c26ca282a257dc5823bd9cbe608f7e3ca3775102d323ad9" -dependencies = [ - "async-trait", - "bitflags 1.3.2", - "bytes", - "ipnet", - "lazy_static", - "log", - "nix 0.26.4", - "portable-atomic", - "rand 0.9.5", - "thiserror 1.0.69", - "tokio", - "winapi", ] [[package]] @@ -6092,12 +6146,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - [[package]] name = "wit-parser" version = "0.252.0" @@ -6130,9 +6178,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "x25519-dalek" @@ -6152,18 +6200,35 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" dependencies = [ - "asn1-rs", + "asn1-rs 0.6.2", "data-encoding", - "der-parser", + "der-parser 9.0.0", "lazy_static", "nom", - "oid-registry", - "ring", + "oid-registry 0.7.1", "rusticata-macros", "thiserror 1.0.69", "time", ] +[[package]] +name = "x509-parser" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" +dependencies = [ + "asn1-rs 0.7.2", + "data-encoding", + "der-parser 10.0.0", + "lazy_static", + "nom", + "oid-registry 0.8.1", + "ring", + "rusticata-macros", + "thiserror 2.0.20", + "time", +] + [[package]] name = "xxhash-rust" version = "0.8.18" @@ -6172,10 +6237,11 @@ checksum = "aee1b19627c7c60102ab80d3a9cbe18de90bfe03bfa6c3715447681f0e8c8af6" [[package]] name = "yasna" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282" dependencies = [ + "bit-vec", "time", ] @@ -6202,26 +6268,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zerocopy" -version = "0.8.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "zerofrom" version = "0.1.8" @@ -6265,9 +6311,9 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" dependencies = [ "displaydoc", "yoke", @@ -6276,9 +6322,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "94b5c6b5976d66c1d703c4fd17d3f5e43c8cedaacf604961b171adc7130896d8" dependencies = [ "yoke", "zerofrom", @@ -6287,13 +6333,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +checksum = "9f212a141d820099d57ffafb9569be9617a6f27d3dc881fbee8fb56642f917a9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.3", ] [[package]] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index c31f9ea..40e063e 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -21,7 +21,7 @@ pumpkin-api-macros = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch tokio = "1" tracing = "0.1" glob = "0.3.4" -j4rs = { version = "0.25.2" } +jni = { version = "0.22.4", features = ["invocation"] } rust-embed = { version = "8.12.0", features = ["interpolate-folder-path"] } walkdir = "2" zip = "8.6" @@ -39,7 +39,6 @@ prost = "0.14" toml = "1.1" [build-dependencies] -j4rs = { version = "0.25.2", artifact = "cdylib", lib = true } env_logger = "0.11.11" log = "0.4" prost-build = "0.14" diff --git a/rust/build/java.rs b/rust/build/java.rs index acef8f1..9be4f70 100644 --- a/rust/build/java.rs +++ b/rust/build/java.rs @@ -1,12 +1,10 @@ use std::{fs, path::PathBuf}; -use j4rs::{JvmBuilder, MavenArtifactRepo, MavenSettings}; - pub fn setup_java(base: PathBuf) { let resources = base.join("resources"); - let deps = resources.join("deps"); + let jassets = resources.join("jassets"); - let mut java_path = base.clone(); + let mut java_path = base; java_path.pop(); let java_path = java_path.join("java"); @@ -16,72 +14,15 @@ pub fn setup_java(base: PathBuf) { .join("libs") .join("patchbukkit.jar"); - let jassets = resources.join("jassets"); - let mut classpath_entries = Vec::new(); - - if let Ok(entries) = fs::read_dir(&jassets) { - for entry in entries.flatten() { - let path = entry.path(); - if let Some(filename) = path.file_name().and_then(|n| n.to_str()) - && filename.starts_with("j4rs") - && filename.ends_with(".jar") - { - classpath_entries.push(j4rs::ClasspathEntry::new(&path)); - } - } - } - - let mut jvm_builder = JvmBuilder::new(); - let mut jvm_builder = jvm_builder - .with_maven_settings(MavenSettings::new(vec![MavenArtifactRepo::from( - "papermc::https://repo.papermc.io/repository/maven-public/", - )])) - .skip_setting_native_lib() - .with_base_path(&resources) - .java_opts(vec![ - j4rs::JavaOpt::new("--enable-native-access=ALL-UNNAMED"), - j4rs::JavaOpt::new("--enable-final-field-mutation=ALL-UNNAMED"), - j4rs::JavaOpt::new("--add-opens=java.base/java.lang=ALL-UNNAMED"), - j4rs::JavaOpt::new("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED"), - j4rs::JavaOpt::new("-Dcom.google.protobuf.useUnsafe=false"), - ]); - - for entry in classpath_entries { - jvm_builder = jvm_builder.classpath_entry(entry); - } - - let _jvm = jvm_builder - .build() - .map_err(|err| format!("jvm failed to init: {err:?}")) - .unwrap(); - - if !&patchbukkit_jar.exists() { + if !patchbukkit_jar.exists() { panic!( - "Failed to find patchbukkit.jar, build the java library first by running `gradle build` in the java directory!" + "Failed to find patchbukkit.jar, build the java library first by running `./gradlew build` in the java directory!" ); } fs::create_dir_all(&jassets).unwrap(); - - let cdylib = std::env::var("CARGO_CDYLIB_FILE_J4RS").unwrap(); - let cdylib = PathBuf::from(cdylib); - - let mut cdylib_to = deps; - fs::create_dir_all(&cdylib_to).unwrap(); - - let original_name = cdylib.file_name().unwrap().to_string_lossy(); - let mut target_name = original_name.split('-').next().unwrap().to_string(); - if !target_name.ends_with(".dll") - && !target_name.ends_with(".so") - && !target_name.ends_with(".dylib") - && let Some(ext) = cdylib.extension() - { - target_name = format!("{target_name}.{}", ext.to_string_lossy()); - } - - cdylib_to.push(target_name); - - fs::copy(&cdylib, &cdylib_to) - .map_err(|err| format!("Failed to copy j4rs native lib: {err:?}")) + let dest_patchbukkit = jassets.join("patchbukkit.jar"); + fs::copy(&patchbukkit_jar, &dest_patchbukkit) + .map_err(|err| format!("Failed to copy patchbukkit.jar to resources/jassets: {err:?}")) .unwrap(); } diff --git a/rust/build/protobufs.rs b/rust/build/protobufs.rs index 326410c..656fc11 100644 --- a/rust/build/protobufs.rs +++ b/rust/build/protobufs.rs @@ -106,7 +106,6 @@ pub unsafe extern "C" fn {fn_name}( output_len: *mut usize, ) -> *mut u8 {{ use prost::Message; - tracing::debug!("{fn_name}: called with input_ptr={{:?}}, input_len={{}}", input_ptr, input_len); if input_ptr.is_null() {{ tracing::warn!("{fn_name}: null input (ptr={{:?}}, len={{}})", input_ptr, input_len); unsafe {{ *output_len = 0 }}; @@ -123,16 +122,13 @@ pub unsafe extern "C" fn {fn_name}( return std::ptr::null_mut(); }}; let Some(response) = {0}::{fn_name}_impl(request) else {{ - tracing::debug!("{fn_name}: impl returned None"); unsafe {{ *output_len = 0 }}; return std::ptr::null_mut(); }}; let encoded = response.encode_to_vec().into_boxed_slice(); let len = encoded.len(); unsafe {{ *output_len = len }}; - let ptr = Box::into_raw(encoded) as *mut u8; - tracing::debug!("{fn_name}: returning ptr={{:?}}, len={{}}", ptr, len); - ptr + Box::into_raw(encoded) as *mut u8 }} "#, self.impl_module @@ -214,47 +210,61 @@ pub unsafe extern "C" fn ffi_free_bytes(ptr: *mut u8, len: usize) {{ }} }} -pub fn initialize_ffi_callbacks(jvm: &j4rs::Jvm) -> anyhow::Result<()> {{ - use j4rs::InvocationArg;"# +pub fn initialize_ffi_callbacks(env: &mut jni::Env) -> anyhow::Result<()> {{"# ) .unwrap(); for service in &state.services { + let class_slash = service.java_class.replace('.', "/"); + let sig = format!("({})V", "J".repeat(service.methods.len())); + writeln!( file, r#" - // Initialize {} - if let Err(e) = jvm.invoke_static( - "{}", - "init", - &["#, - service.java_class, service.java_class + // Initialize {class_slash} + {{ + let args = ["#, ) .unwrap(); for method in &service.methods { - writeln!(file, - " InvocationArg::try_from(crate::proto::{}::{} as *const () as i64)?.into_primitive()?,", + writeln!( + file, + " jni::objects::JValue::Long(crate::proto::{}::{} as *const () as i64),", service.module_path, method.fn_name - ).unwrap(); + ) + .unwrap(); } writeln!( file, - r#" ], - ) {{ - tracing::error!("Failed to initialize FFI service: {{:?}}", e); - }} + r#" ]; + if let Err(e) = env.call_static_method( + jni::jni_str!("{class_slash}"), + jni::jni_str!("init"), + jni::jni_sig!("{sig}"), + &args, + ) {{ + env.exception_describe(); + env.exception_clear(); + tracing::error!("Failed to initialize FFI service {class_slash}: {{:?}}", e); + return Err(anyhow::anyhow!("Failed to initialize FFI service {class_slash}: {{e:?}}")); + }} - if let Err(e) = jvm.invoke_static( - "{}", - "initFree", - &[InvocationArg::try_from(ffi_free_bytes as *const () as i64)?.into_primitive()?], - ) {{ - tracing::error!("Failed to initialize FFI service initFree: {{:?}}", e); + if let Err(e) = env.call_static_method( + jni::jni_str!("{class_slash}"), + jni::jni_str!("initFree"), + jni::jni_sig!("(J)V"), + &[jni::objects::JValue::Long(ffi_free_bytes as *const () as i64)], + ) {{ + env.exception_describe(); + env.exception_clear(); + tracing::error!("Failed to initialize FFI service initFree on {class_slash}: {{:?}}", e); + return Err(anyhow::anyhow!("Failed to initialize FFI service initFree on {class_slash}: {{e:?}}")); + }} }}"#, - service.java_class ) + .unwrap(); } diff --git a/rust/resources/jassets/j4rs-0.25.3-SNAPSHOT-jar-with-dependencies.jar b/rust/resources/jassets/j4rs-0.25.3-SNAPSHOT-jar-with-dependencies.jar deleted file mode 100644 index 2771fb5..0000000 Binary files a/rust/resources/jassets/j4rs-0.25.3-SNAPSHOT-jar-with-dependencies.jar and /dev/null differ diff --git a/rust/src/commands/mod.rs b/rust/src/commands/mod.rs index 413d190..c2a2ab0 100644 --- a/rust/src/commands/mod.rs +++ b/rust/src/commands/mod.rs @@ -25,8 +25,8 @@ pub struct JavaCommandExecutor { #[derive(Clone)] pub enum SimpleCommandSender { Console, - /// UUID, Name - Player(String, String), + /// UUID, Name, is_op + Player(String, String, bool), } pub struct AnyCommandNode { @@ -94,7 +94,8 @@ impl ArgumentConsumer for AnyCommandNode { Box::pin(async move { let (tx, rx) = oneshot::channel(); - self.command_tx + if let Err(e) = self + .command_tx .send(JvmCommand::GetCommandTabComplete { command_sender, full_command: input.to_string(), @@ -102,9 +103,15 @@ impl ArgumentConsumer for AnyCommandNode { location, }) .await - .unwrap(); + { + tracing::warn!("Failed to send tab complete to JVM worker: {e}"); + return Ok(None); + } - rx.await.unwrap() + match rx.await { + Ok(res) => res, + Err(_) => Ok(None), + } }) } } @@ -113,10 +120,15 @@ impl From<&CommandSender> for SimpleCommandSender { fn from(val: &CommandSender) -> Self { match val { CommandSender::Console | CommandSender::Rcon(_) | CommandSender::Dummy => Self::Console, - CommandSender::Player(player) => Self::Player( - player.gameprofile.id.to_string(), - player.gameprofile.name.clone(), - ), + CommandSender::Player(player) => { + let is_op = + player.permission_lvl.load() >= pumpkin_util::permission::PermissionLvl::Two; + Self::Player( + player.gameprofile.id.to_string(), + player.gameprofile.name.clone(), + is_op, + ) + } CommandSender::CommandBlock(_block_entity, _world) => Self::Console, } } @@ -129,6 +141,10 @@ impl CommandExecutor for JavaCommandExecutor { _server: &'a pumpkin::server::Server, args: &'a pumpkin::command::args::ConsumedArgs<'a>, ) -> pumpkin::command::CommandResult<'a> { + if let CommandSender::Player(player) = sender { + crate::java::native_callbacks::utils::cache_player(player.clone()); + } + Box::pin(async move { let full_command = match args.get(ARG_ANY) { Some(Arg::Msg(msg)) => { @@ -147,15 +163,21 @@ impl CommandExecutor for JavaCommandExecutor { } }; - let (tx, _rx) = oneshot::channel(); - self.command_tx + let (tx, rx) = oneshot::channel(); + if let Err(e) = self + .command_tx .send(JvmCommand::TriggerCommand { full_command, respond_to: tx, command_sender: sender.into(), }) .await - .unwrap(); + { + tracing::error!("Failed to send command to JVM worker: {e}"); + return Ok(0); + } + + let _ = rx.await; Ok(1) }) } @@ -186,3 +208,23 @@ pub fn init_java_command( }), ) } + +#[cfg(test)] +mod tests { + use super::*; + use pumpkin::command::dispatcher::CommandDispatcher; + + #[test] + fn test_init_java_command_registration() { + let mut dispatcher = CommandDispatcher::default(); + + let (tx, _rx) = mpsc::channel(100); + let names = vec!["fly".to_string(), "/fly".to_string(), "//fly".to_string()]; + let tree = init_java_command("fly", tx, names, "Fly command"); + dispatcher.register(tree, "patchbukkit:command.fly"); + + assert!(dispatcher.get_tree("fly").is_ok()); + assert!(dispatcher.get_tree("/fly").is_ok()); + assert!(dispatcher.get_tree("//fly").is_ok()); + } +} diff --git a/rust/src/directories.rs b/rust/src/directories.rs index 4045ec9..6254a87 100644 --- a/rust/src/directories.rs +++ b/rust/src/directories.rs @@ -6,7 +6,6 @@ pub struct PatchBukkitDirectories { pub base: PathBuf, pub plugins: PathBuf, pub plugin_updates: PathBuf, - pub j4rs: PathBuf, pub jassets: PathBuf, } @@ -20,8 +19,7 @@ pub fn setup_directories(server: &Context) -> Result Result, data: Data) -> Option<()> { - if let Data::PlayerJoin(event) = data { - self.join_message = serde_json::from_str(&event.join_message).ok()?; - server.get_player_by_uuid(uuid::Uuid::from_str(&event.player_uuid?.value).ok()?)?; + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerJoin(event) = data + && let Ok(msg) = serde_json::from_str(&event.join_message) + { + self.join_message = msg; } Some(()) @@ -80,10 +79,11 @@ impl PatchBukkitEvent for pumpkin::plugin::player::player_leave::PlayerLeaveEven } } - fn apply_modifications(&mut self, server: &Arc, data: Data) -> Option<()> { - if let Data::PlayerQuit(event) = data { - self.leave_message = serde_json::from_str(&event.quit_message).ok()?; - server.get_player_by_uuid(uuid::Uuid::from_str(&event.player_uuid?.value).ok()?)?; + fn apply_modifications(&mut self, _server: &Arc, data: Data) -> Option<()> { + if let Data::PlayerQuit(event) = data + && let Ok(msg) = serde_json::from_str(&event.quit_message) + { + self.leave_message = msg; } Some(()) @@ -603,23 +603,51 @@ impl PatchBukkitEventHandler { } } -#[with_runtime(global)] impl EventHandler for PatchBukkitEventHandler where E: PatchBukkitEvent + Payload + Cancellable + 'static, { + fn handle<'a>(&'a self, server: &'a Arc, event: &'a E) -> BoxFuture<'a, ()> { + let command_tx = self.command_tx.clone(); + let payload = event.to_payload(server.clone()); + if let Some(player) = &payload.context.player { + crate::java::native_callbacks::utils::cache_player(player.clone()); + } + + Box::pin(async move { + let (tx, rx) = oneshot::channel(); + if let Err(e) = command_tx + .send(JvmCommand::FireEvent { + payload, + respond_to: tx, + plugin: self.plugin_name.clone(), + }) + .await + { + tracing::error!("Failed to send event to JVM worker: {e}"); + return; + } + + let _ = rx.await; + }) + } + fn handle_blocking<'a>( &'a self, server: &'a Arc, event: &'a mut E, ) -> BoxFuture<'a, ()> { let command_tx = self.command_tx.clone(); + let payload = event.to_payload(server.clone()); + if let Some(player) = &payload.context.player { + crate::java::native_callbacks::utils::cache_player(player.clone()); + } Box::pin(async move { let (tx, rx) = oneshot::channel(); if let Err(e) = command_tx .send(JvmCommand::FireEvent { - payload: event.to_payload(server.clone()), + payload, respond_to: tx, plugin: self.plugin_name.clone(), }) @@ -632,7 +660,9 @@ where match rx.await { Ok(response) => { event.set_cancelled(response.cancelled); - event.apply_modifications(server, response.data.unwrap().data.unwrap()); + if let Some(event_data) = response.data.and_then(|d| d.data) { + let _ = event.apply_modifications(server, event_data); + } } Err(_) => { tracing::warn!("JVM worker dropped response channel for event"); diff --git a/rust/src/java/jvm/commands.rs b/rust/src/java/jvm/commands.rs index d042d97..b22ec94 100644 --- a/rust/src/java/jvm/commands.rs +++ b/rust/src/java/jvm/commands.rs @@ -22,9 +22,10 @@ pub enum LoadPluginResult { pub enum JvmCommand { Initialize { - j4rs_path: PathBuf, + jassets_path: PathBuf, respond_to: oneshot::Sender>, context: Arc, + runtime_handle: tokio::runtime::Handle, command_tx: mpsc::Sender, config: PatchBukkitConfig, }, diff --git a/rust/src/java/jvm/worker.rs b/rust/src/java/jvm/worker.rs index 2361ae4..907c6ec 100644 --- a/rust/src/java/jvm/worker.rs +++ b/rust/src/java/jvm/worker.rs @@ -1,6 +1,6 @@ use std::{path::PathBuf, sync::Arc}; -use j4rs::{InvocationArg, Jvm, JvmBuilder}; +use jni::{Env, InitArgsBuilder, JNIVersion, JavaVM}; use pumpkin::plugin::Context; use tokio::sync::mpsc; @@ -21,7 +21,7 @@ pub struct JvmWorker { pub plugin_manager: PluginManager, pub event_manager: EventManager, pub command_manager: CommandManager, - jvm: Option, + jvm: Option>, context: Option>, } @@ -44,21 +44,22 @@ impl JvmWorker { while let Some(command) = self.command_rx.recv().await { match command { JvmCommand::Initialize { - j4rs_path, + jassets_path, respond_to, context, + runtime_handle, command_tx, config, } => { init_callback_context( context.clone(), - tokio::runtime::Handle::current(), + runtime_handle, command_tx.clone(), config, ) .unwrap(); self.context = Some(context); - let result = self.initialize_jvm(&j4rs_path); + let result = self.initialize_jvm(&jassets_path); let _ = respond_to.send(result); } JvmCommand::LoadPlugin { @@ -105,38 +106,49 @@ impl JvmWorker { server, command_tx, } => { - let jvm = match self.jvm { - Some(ref jvm) => jvm, - None => &Jvm::attach_thread().unwrap(), - }; - - let _ = respond_to.send( - self.plugin_manager - .instantiate_all_plugins( - jvm, + let res = if let Some(ref jvm) = self.jvm { + let plugins_dir = plugins_dir.clone(); + let server = server.clone(); + let command_tx = command_tx.clone(); + jvm.attach_current_thread(|env| -> anyhow::Result<()> { + self.plugin_manager.instantiate_all_plugins( + env, &plugins_dir, &server, command_tx, &mut self.command_manager, ) - .await, - ); + }) + .map_err(|e| anyhow::anyhow!("Failed to instantiate plugins: {e}")) + } else { + Err(anyhow::anyhow!("JVM is not initialized")) + }; + + let _ = respond_to.send(res); } JvmCommand::EnableAllPlugins { respond_to } => { - let jvm = match self.jvm { - Some(ref jvm) => jvm, - None => &Jvm::attach_thread().unwrap(), + let res = if let Some(ref jvm) = self.jvm { + jvm.attach_current_thread(|env| -> anyhow::Result<()> { + self.plugin_manager.enable_all_plugins(env) + }) + .map_err(|e| anyhow::anyhow!("Failed to enable plugins: {e}")) + } else { + Err(anyhow::anyhow!("JVM is not initialized")) }; - let _ = respond_to.send(self.plugin_manager.enable_all_plugins(jvm)); + let _ = respond_to.send(res); } JvmCommand::DisableAllPlugins { respond_to } => { - let jvm = match self.jvm { - Some(ref jvm) => jvm, - None => &Jvm::attach_thread().unwrap(), + let res = if let Some(ref jvm) = self.jvm { + jvm.attach_current_thread(|env| -> anyhow::Result<()> { + self.plugin_manager.disable_all_plugins(env) + }) + .map_err(|e| anyhow::anyhow!("Failed to disable plugins: {e}")) + } else { + Err(anyhow::anyhow!("JVM is not initialized")) }; - let _ = respond_to.send(self.plugin_manager.disable_all_plugins(jvm)); + let _ = respond_to.send(res); } JvmCommand::Shutdown { respond_to } => { let _ = respond_to.send(self.plugin_manager.unload_all_plugins()); @@ -147,40 +159,47 @@ impl JvmWorker { plugin, payload, } => { - let jvm = match self.jvm { - Some(ref jvm) => jvm, - None => &Jvm::attach_thread().unwrap(), - }; - let original_event = payload.event.clone(); - let cancelled = match self.event_manager.fire_event(jvm, payload, plugin) { - Ok(c) => c, - Err(e) => { - tracing::error!("Failed to fire event: {e}"); - FireEventResponse { - cancelled: false, - data: Some(original_event), + let response = if let Some(ref jvm) = self.jvm { + match jvm.attach_current_thread( + |env| -> anyhow::Result { + self.event_manager.fire_event(env, payload, plugin) + }, + ) { + Ok(resp) => resp, + Err(e) => { + tracing::error!("Failed to fire event: {e}"); + FireEventResponse { + cancelled: false, + data: Some(original_event), + } } } + } else { + FireEventResponse { + cancelled: false, + data: Some(original_event), + } }; - let _ = respond_to.send(cancelled); + let _ = respond_to.send(response); } JvmCommand::TriggerCommand { full_command, command_sender, respond_to, } => { - let jvm = match self.jvm { - Some(ref jvm) => jvm, - None => &Jvm::attach_thread().unwrap(), + let res = if let Some(ref jvm) = self.jvm { + jvm.attach_current_thread(|env| -> anyhow::Result<()> { + self.command_manager + .trigger_command(env, full_command, command_sender) + }) + .map_err(|e| anyhow::anyhow!("Failed to trigger command: {e}")) + } else { + Err(anyhow::anyhow!("JVM is not initialized")) }; - let result = - self.command_manager - .trigger_command(jvm, full_command, command_sender); - - let _ = respond_to.send(result); + let _ = respond_to.send(res); } JvmCommand::GetCommandTabComplete { command_sender, @@ -188,19 +207,26 @@ impl JvmWorker { respond_to, location, } => { - let jvm = match self.jvm { - Some(ref jvm) => jvm, - None => &Jvm::attach_thread().unwrap(), + let res = if let Some(ref jvm) = self.jvm { + match jvm.attach_current_thread(|env| -> anyhow::Result<_> { + Ok(self.command_manager.get_tab_complete( + env, + command_sender, + full_command, + location, + )) + }) { + Ok(inner_res) => inner_res, + Err(e) => { + tracing::error!("Failed to get tab complete: {e}"); + Ok(None) + } + } + } else { + Ok(None) }; - let result = self.command_manager.get_tab_complete( - jvm, - command_sender, - full_command, - location, - ); - - let _ = respond_to.send(result); + let _ = respond_to.send(res); } } } @@ -208,41 +234,85 @@ impl JvmWorker { tracing::info!("JVM worker thread exited"); } - fn initialize_jvm(&mut self, j4rs_path: &PathBuf) -> anyhow::Result<()> { - tracing::info!("Initializing JVM with path: {j4rs_path:?}"); + fn initialize_jvm(&mut self, jassets_path: &PathBuf) -> anyhow::Result<()> { + tracing::info!("Initializing JVM with assets path: {jassets_path:?}"); - let jassets = j4rs_path.join("jassets"); - let mut classpath_entries = Vec::new(); + let mut jar_paths = Vec::new(); - if let Ok(entries) = std::fs::read_dir(&jassets) { - for entry in entries.flatten() { - let path = entry.path(); - if let Some(filename) = path.file_name().and_then(|n| n.to_str()) - && filename.ends_with(".jar") - { - classpath_entries.push(j4rs::ClasspathEntry::new(&path)); - } + for entry in walkdir::WalkDir::new(jassets_path) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().is_file()) + { + let path = entry.path().to_path_buf(); + if let Some(filename) = path.file_name().and_then(|n| n.to_str()) + && filename.ends_with(".jar") + { + jar_paths.push(path); } } - let mut jvm_builder = JvmBuilder::new(); - let mut jvm_builder = jvm_builder.with_base_path(j4rs_path).java_opts(vec![ - j4rs::JavaOpt::new("--enable-native-access=ALL-UNNAMED"), - j4rs::JavaOpt::new("--enable-final-field-mutation=ALL-UNNAMED"), - j4rs::JavaOpt::new("--add-opens=java.base/java.lang=ALL-UNNAMED"), - j4rs::JavaOpt::new("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED"), - j4rs::JavaOpt::new("-Dcom.google.protobuf.useUnsafe=false"), - ]); - - for entry in classpath_entries { - jvm_builder = jvm_builder.classpath_entry(entry); + if jar_paths.is_empty() { + tracing::warn!( + "No JAR files found in jassets directory ({:?})", + jassets_path + ); + } else { + tracing::info!( + "Found {} JAR entries in jassets: {:?}", + jar_paths.len(), + jar_paths + ); } - let jvm = jvm_builder.build()?; + let has_patchbukkit = jar_paths.iter().any(|p| { + p.file_name() + .and_then(|n| n.to_str()) + .is_some_and(|n| n.contains("patchbukkit")) + }); + if !has_patchbukkit { + tracing::warn!( + "patchbukkit.jar was not found in {:?}. Server classes may fail to load!", + jassets_path + ); + } - initialize_callbacks(&jvm)?; + let separator = if cfg!(windows) { ";" } else { ":" }; + let classpath = jar_paths + .iter() + .map(|p| p.to_string_lossy()) + .collect::>() + .join(separator); - setup_patchbukkit_server(&jvm)?; + let jvm_args = InitArgsBuilder::new() + .version(JNIVersion::V21) + .option(format!("-Djava.class.path={classpath}")) + .option("--enable-native-access=ALL-UNNAMED") + .option("--enable-final-field-mutation=ALL-UNNAMED") + .option("--add-opens=java.base/java.lang=ALL-UNNAMED") + .option("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED") + .option("-Dcom.google.protobuf.useUnsafe=false") + .build() + .map_err(|e| anyhow::anyhow!("Failed to build JVM init args: {e:?}"))?; + + let jvm = + JavaVM::new(jvm_args).map_err(|e| anyhow::anyhow!("Failed to create JavaVM: {e:?}"))?; + let jvm = Arc::new(jvm); + + jvm.attach_current_thread(|env| -> anyhow::Result<()> { + initialize_callbacks(env).map_err(|e| { + tracing::error!("Failed to initialize callbacks: {e:?}"); + e + })?; + + setup_patchbukkit_server(env).map_err(|e| { + tracing::error!("Failed to setup PatchBukkit server: {e:?}"); + e + })?; + + Ok(()) + }) + .map_err(|e| anyhow::anyhow!("Failed to attach thread for JVM initialization: {e:?}"))?; self.jvm = Some(jvm); @@ -251,12 +321,17 @@ impl JvmWorker { } } -pub fn setup_patchbukkit_server(jvm: &Jvm) -> anyhow::Result<()> { - jvm.invoke_static( - "org.patchbukkit.PatchBukkitServer", - "initServer", - InvocationArg::empty(), - )?; +pub fn setup_patchbukkit_server(env: &mut Env) -> anyhow::Result<()> { + if let Err(e) = env.call_static_method( + jni::jni_str!("org/patchbukkit/PatchBukkitServer"), + jni::jni_str!("initServer"), + jni::jni_sig!("()Lorg/patchbukkit/PatchBukkitServer;"), + &[], + ) { + env.exception_describe(); + env.exception_clear(); + return Err(anyhow::anyhow!("Failed to setup PatchBukkit server: {e:?}")); + } Ok(()) } diff --git a/rust/src/java/native_callbacks/abilities.rs b/rust/src/java/native_callbacks/abilities.rs index 5fe30b4..fdad3d3 100644 --- a/rust/src/java/native_callbacks/abilities.rs +++ b/rust/src/java/native_callbacks/abilities.rs @@ -1,52 +1,73 @@ -use crate::java::native_callbacks::CALLBACK_CONTEXT; +use crate::java::native_callbacks::{CALLBACK_CONTEXT, utils::with_player}; use crate::proto::patchbukkit::{ abilities::{Abilities, SetAbilitiesRequest}, common::Uuid, }; pub fn ffi_native_bridge_get_abilities_impl(request: Uuid) -> Option { - let ctx = CALLBACK_CONTEXT.get()?; - let player_uuid = uuid::Uuid::parse_str(&request.value).ok()?; - let player = ctx.plugin_context.server.get_player_by_uuid(player_uuid)?; - let abilities = tokio::task::block_in_place(|| { - ctx.runtime - .block_on(async { player.abilities.lock().await }) - }); + with_player(Some(&request), |player| { + let abilities = match player.abilities.try_lock() { + Ok(guard) => Abilities { + invulnerable: guard.invulnerable, + flying: guard.flying, + allow_flying: guard.allow_flying, + creative: guard.creative, + allow_modify_world: guard.allow_modify_world, + fly_speed: guard.fly_speed, + walk_speed: guard.walk_speed, + }, + Err(_) => { + let mut acquired = None; + for _ in 0..10 { + std::thread::yield_now(); + if let Ok(guard) = player.abilities.try_lock() { + acquired = Some(Abilities { + invulnerable: guard.invulnerable, + flying: guard.flying, + allow_flying: guard.allow_flying, + creative: guard.creative, + allow_modify_world: guard.allow_modify_world, + fly_speed: guard.fly_speed, + walk_speed: guard.walk_speed, + }); + break; + } + } + acquired.unwrap_or(Abilities { + invulnerable: false, + flying: false, + allow_flying: false, + creative: false, + allow_modify_world: true, + fly_speed: 0.05, + walk_speed: 0.1, + }) + } + }; - Some(Abilities { - invulnerable: abilities.invulnerable, - flying: abilities.flying, - allow_flying: abilities.allow_flying, - creative: abilities.creative, - allow_modify_world: abilities.allow_modify_world, - fly_speed: abilities.fly_speed, - walk_speed: abilities.walk_speed, - }) + Some(abilities) + })? } pub fn ffi_native_bridge_set_abilities_impl(request: SetAbilitiesRequest) -> Option { let ctx = CALLBACK_CONTEXT.get()?; - let player_uuid = uuid::Uuid::parse_str(&request.uuid?.value).ok()?; let abilities = request.abilities?; - let player = ctx.plugin_context.server.get_player_by_uuid(player_uuid)?; + with_player(request.uuid.as_ref(), |player| { + let player = player.clone(); + ctx.runtime.spawn(async move { + { + let mut pumpkin_abilities = player.abilities.lock().await; + pumpkin_abilities.invulnerable = abilities.invulnerable; + pumpkin_abilities.flying = abilities.flying; + pumpkin_abilities.allow_flying = abilities.allow_flying; + pumpkin_abilities.creative = abilities.creative; + pumpkin_abilities.allow_modify_world = abilities.allow_modify_world; + pumpkin_abilities.fly_speed = abilities.fly_speed; + pumpkin_abilities.walk_speed = abilities.walk_speed; + } + player.send_abilities_update().await; + }); - let mut pumpkin_abilities = tokio::task::block_in_place(|| { - ctx.runtime - .block_on(async { player.abilities.lock().await }) - }); - - pumpkin_abilities.invulnerable = abilities.invulnerable; - pumpkin_abilities.flying = abilities.flying; - pumpkin_abilities.allow_flying = abilities.allow_flying; - pumpkin_abilities.creative = abilities.creative; - pumpkin_abilities.allow_modify_world = abilities.allow_modify_world; - pumpkin_abilities.fly_speed = abilities.fly_speed; - pumpkin_abilities.walk_speed = abilities.walk_speed; - - let player = player.clone(); - ctx.runtime.spawn(async move { - player.send_abilities_update().await; - }); - - Some(true) + Some(true) + })? } diff --git a/rust/src/java/native_callbacks/command.rs b/rust/src/java/native_callbacks/command.rs index 775a334..4cd58ea 100644 --- a/rust/src/java/native_callbacks/command.rs +++ b/rust/src/java/native_callbacks/command.rs @@ -3,7 +3,13 @@ use pumpkin_util::permission::{Permission, PermissionDefault}; use crate::{commands::init_java_command, proto::patchbukkit::command::RegisterCommandRequest}; pub fn ffi_native_bridge_register_command_impl(request: RegisterCommandRequest) -> Option<()> { - let context = super::CALLBACK_CONTEXT.get()?; + let Some(context) = super::CALLBACK_CONTEXT.get() else { + tracing::warn!( + "Failed to register command '{}': CALLBACK_CONTEXT is not initialized", + request.cmd_name + ); + return None; + }; let cmd_name = request.cmd_name; let aliases = request.aliases; @@ -12,6 +18,13 @@ pub fn ffi_native_bridge_register_command_impl(request: RegisterCommandRequest) let command_tx = context.command_tx.clone(); let plugin_context = context.plugin_context.clone(); + tracing::info!( + "Registering Bukkit command '{}' from plugin '{}' (aliases: {:?})", + cmd_name, + plugin_name, + aliases + ); + context.runtime.spawn(async move { let mut raw_names: Vec = vec![cmd_name.clone()]; raw_names.extend(aliases.clone()); @@ -34,23 +47,31 @@ pub fn ffi_native_bridge_register_command_impl(request: RegisterCommandRequest) } } - let node = init_java_command(cmd_name.clone(), command_tx, names, description); + let node = init_java_command(cmd_name.clone(), command_tx, names.clone(), description); let clean_perm = cmd_name.trim_start_matches('/'); let permission = format!("patchbukkit:command.{clean_perm}"); - let registry = { - let permission_manager = plugin_context.permission_manager.read().await; - permission_manager.registry.clone() - }; + let registry = plugin_context.server.permission_manager.clone(); - let _ = registry.write().await.register_permission(Permission::new( + if let Err(e) = registry.register_permission(Permission::new( &permission, &format!("Allows running command `{cmd_name}` from `{plugin_name}`"), PermissionDefault::Allow, - )); + )) { + tracing::debug!("Permission '{}' registration notice: {}", permission, e); + } - plugin_context.register_command(node, permission).await; + plugin_context + .register_command(node, permission.clone()) + .await; + + tracing::info!( + "Successfully registered command '{}' with names {:?} and permission '{}'", + cmd_name, + names, + permission + ); }); Some(()) diff --git a/rust/src/java/native_callbacks/entity.rs b/rust/src/java/native_callbacks/entity.rs index 4c04a1b..364bfaa 100644 --- a/rust/src/java/native_callbacks/entity.rs +++ b/rust/src/java/native_callbacks/entity.rs @@ -12,12 +12,8 @@ use crate::{ }; pub fn ffi_native_bridge_get_entity_health_impl(request: Uuid) -> Option { - let ctx = CALLBACK_CONTEXT.get()?; with_player(Some(&request), |player| { - let health = tokio::task::block_in_place(|| { - ctx.runtime - .block_on(async { f64::from(player.living_entity.health.load()) }) - }); + let health = f64::from(player.living_entity.health.load()); EntityHealthResponse { health, @@ -155,24 +151,22 @@ pub fn ffi_native_bridge_is_op_impl( let op_lvl = ctx.plugin_context.server.basic_config.op_permission_level; let mut is_op = perm_lvl >= op_lvl || perm_lvl > pumpkin_util::PermissionLvl::Zero; - if !is_op { - let op_config = tokio::task::block_in_place(|| { - ctx.runtime - .block_on(async { ctx.plugin_context.server.data.operator_config.read().await }) - }); - if op_config.get_entry(&player_uuid).is_some() { - is_op = true; - } + if !is_op + && let Ok(op_config) = ctx.plugin_context.server.data.operator_config.try_read() + && op_config.get_entry(&player_uuid).is_some() + { + is_op = true; } is_op }) .unwrap_or_else(|| { - let op_config = tokio::task::block_in_place(|| { - ctx.runtime - .block_on(async { ctx.plugin_context.server.data.operator_config.read().await }) - }); - op_config.get_entry(&player_uuid).is_some() + ctx.plugin_context + .server + .data + .operator_config + .try_read() + .is_ok_and(|ops| ops.get_entry(&player_uuid).is_some()) }); Some(crate::proto::patchbukkit::entity::IsOpResponse { is_op }) diff --git a/rust/src/java/native_callbacks/events.rs b/rust/src/java/native_callbacks/events.rs index 3b060ff..e4e4d37 100644 --- a/rust/src/java/native_callbacks/events.rs +++ b/rust/src/java/native_callbacks/events.rs @@ -1,12 +1,9 @@ use std::sync::Arc; use pumpkin::plugin::EventPriority; -use pumpkin::plugin::player::player_join::PlayerJoinEvent; -use pumpkin_util::text::TextComponent; use crate::events::handler::PatchBukkitEventHandler; use crate::java::native_callbacks::CALLBACK_CONTEXT; -use crate::proto::patchbukkit::events::event::Data; use crate::proto::patchbukkit::events::{ CallEventRequest, CallEventResponse, RegisterEventRequest, }; @@ -32,780 +29,395 @@ pub fn ffi_native_bridge_register_event_impl(request: RegisterEventRequest) -> O let command_tx = ctx.command_tx.clone(); let context = ctx.plugin_context.clone(); - tokio::task::block_in_place(|| { - ctx.runtime.block_on(async { - match request.event_type.as_str() { - "org.bukkit.event.player.PlayerJoinEvent" => { - context - .register_event::< - pumpkin::plugin::player::player_join::PlayerJoinEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.player.PlayerQuitEvent" => { - context - .register_event::< - pumpkin::plugin::player::player_leave::PlayerLeaveEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.player.PlayerGameModeChangeEvent" => { - context - .register_event::< - pumpkin::plugin::player::player_gamemode_change::PlayerGamemodeChangeEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.player.PlayerInteractEvent" => { - context - .register_event::< - pumpkin::plugin::player::player_interact_event::PlayerInteractEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.block.BlockBreakEvent" => { - context - .register_event::< - pumpkin::plugin::block::block_break::BlockBreakEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.block.BlockPlaceEvent" => { - context - .register_event::< - pumpkin::plugin::block::block_place::BlockPlaceEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.block.SignChangeEvent" => { - context - .register_event::< - pumpkin::plugin::block::sign_change::SignChangeEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.block.BlockDamageEvent" => { - context - .register_event::< - pumpkin::plugin::block::block_damage::BlockDamageEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.server.ServerCommandEvent" => { - context - .register_event::< - pumpkin::plugin::server::server_command::ServerCommandEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.block.BlockIgniteEvent" => { - context - .register_event::< - pumpkin::plugin::block::block_ignite::BlockIgniteEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.block.BlockGrowEvent" => { - context - .register_event::< - pumpkin::plugin::block::block_grow::BlockGrowEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.block.BlockFormEvent" => { - context - .register_event::< - pumpkin::plugin::block::block_form::BlockFormEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.block.BlockFadeEvent" => { - context - .register_event::< - pumpkin::plugin::block::block_fade::BlockFadeEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.player.PlayerToggleSneakEvent" => { - context - .register_event::< - pumpkin::plugin::player::player_toggle_sneak_event::PlayerToggleSneakEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.player.PlayerToggleSprintEvent" => { - context - .register_event::< - pumpkin::plugin::player::player_toggle_sprint_event::PlayerToggleSprintEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.player.PlayerToggleFlightEvent" => { - context - .register_event::< - pumpkin::plugin::player::player_toggle_flight_event::PlayerToggleFlightEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.player.PlayerMoveEvent" => { - context - .register_event::< - pumpkin::plugin::player::player_move::PlayerMoveEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.player.PlayerInteractEntityEvent" | "org.bukkit.event.player.PlayerInteractAtEntityEvent" | "org.bukkit.event.entity.EntityDamageByEntityEvent" | "org.bukkit.event.entity.EntityDamageEvent" => { - context - .register_event::< - pumpkin::plugin::player::player_interact_entity_event::PlayerInteractEntityEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - "org.bukkit.event.player.AsyncPlayerChatEvent" | "org.bukkit.event.player.PlayerChatEvent" => { - context - .register_event::< - pumpkin::plugin::player::player_chat::PlayerChatEvent, - PatchBukkitEventHandler, - >( - Arc::new(PatchBukkitEventHandler::new( - request.plugin_name.clone(), - command_tx.clone(), - )), - pumpkin_priority, - request.blocking, - ) - .await; - } - // Player Events - "org.bukkit.event.player.PlayerDropItemEvent" - | "org.bukkit.event.player.PlayerItemHeldEvent" - | "org.bukkit.event.player.PlayerCommandPreprocessEvent" - | "org.bukkit.event.player.PlayerRespawnEvent" - | "org.bukkit.event.player.PlayerTeleportEvent" - | "org.bukkit.event.player.PlayerChangedWorldEvent" - | "org.bukkit.event.player.PlayerBedEnterEvent" - | "org.bukkit.event.player.PlayerBedLeaveEvent" - | "org.bukkit.event.player.PlayerItemConsumeEvent" - | "org.bukkit.event.player.PlayerItemDamageEvent" - | "org.bukkit.event.player.PlayerItemBreakEvent" - | "org.bukkit.event.player.PlayerAnimationEvent" - | "org.bukkit.event.player.PlayerBucketEmptyEvent" - | "org.bukkit.event.player.PlayerBucketFillEvent" - | "org.bukkit.event.player.PlayerAdvancementDoneEvent" - | "org.bukkit.event.player.PlayerExpChangeEvent" - | "org.bukkit.event.player.PlayerLevelChangeEvent" - | "org.bukkit.event.player.PlayerResourcePackStatusEvent" - | "org.bukkit.event.player.PlayerStatisticIncrementEvent" - | "org.bukkit.event.player.PlayerPortalEvent" - | "org.bukkit.event.player.PlayerKickEvent" - | "org.bukkit.event.player.PlayerLocaleChangeEvent" - | "org.bukkit.event.player.PlayerArmorStandManipulateEvent" - | "org.bukkit.event.player.PlayerTakeLecternBookEvent" - | "org.bukkit.event.player.PlayerUnleashEntityEvent" - | "org.bukkit.event.player.PlayerShearEntityEvent" - | "org.bukkit.event.player.PlayerEggThrowEvent" - | "org.bukkit.event.player.PlayerFishEvent" - | "org.bukkit.event.player.PlayerEvent" - // Entity Events - | "org.bukkit.event.entity.EntityDamageByBlockEvent" - | "org.bukkit.event.entity.EntityDeathEvent" - | "org.bukkit.event.entity.PlayerDeathEvent" - | "org.bukkit.event.entity.CreatureSpawnEvent" - | "org.bukkit.event.entity.EntitySpawnEvent" - | "org.bukkit.event.entity.EntityTargetEvent" - | "org.bukkit.event.entity.EntityTargetLivingEntityEvent" - | "org.bukkit.event.entity.EntityCombustEvent" - | "org.bukkit.event.entity.EntityCombustByEntityEvent" - | "org.bukkit.event.entity.EntityRegainHealthEvent" - | "org.bukkit.event.entity.EntityShootBowEvent" - | "org.bukkit.event.entity.EntityToggleGlideEvent" - | "org.bukkit.event.entity.EntityPickupItemEvent" - | "org.bukkit.event.entity.EntityDropItemEvent" - | "org.bukkit.event.entity.EntityExplodeEvent" - | "org.bukkit.event.entity.ExplosionPrimeEvent" - | "org.bukkit.event.entity.FoodLevelChangeEvent" - | "org.bukkit.event.entity.ProjectileHitEvent" - | "org.bukkit.event.entity.ProjectileLaunchEvent" - | "org.bukkit.event.entity.EntityInteractEvent" - | "org.bukkit.event.entity.EntityTransformEvent" - | "org.bukkit.event.entity.EntityDismountEvent" - | "org.bukkit.event.entity.EntityMountEvent" - | "org.bukkit.event.entity.EntityEvent" - // Block Events - | "org.bukkit.event.block.BlockSpreadEvent" - | "org.bukkit.event.block.BlockBurnEvent" - | "org.bukkit.event.block.BlockPhysicsEvent" - | "org.bukkit.event.block.BlockRedstoneEvent" - | "org.bukkit.event.block.BlockPistonExtendEvent" - | "org.bukkit.event.block.BlockPistonRetractEvent" - | "org.bukkit.event.block.BlockExplodeEvent" - | "org.bukkit.event.block.BlockFromToEvent" - | "org.bukkit.event.block.BlockDispenseEvent" - | "org.bukkit.event.block.LeavesDecayEvent" - | "org.bukkit.event.block.BlockCanBuildEvent" - | "org.bukkit.event.block.BlockEvent" - // Inventory Events - | "org.bukkit.event.inventory.InventoryClickEvent" - | "org.bukkit.event.inventory.InventoryCloseEvent" - | "org.bukkit.event.inventory.InventoryOpenEvent" - | "org.bukkit.event.inventory.InventoryDragEvent" - | "org.bukkit.event.inventory.InventoryCreativeEvent" - | "org.bukkit.event.inventory.CraftItemEvent" - | "org.bukkit.event.inventory.PrepareItemCraftEvent" - | "org.bukkit.event.inventory.PrepareAnvilEvent" - | "org.bukkit.event.inventory.PrepareSmithingEvent" - | "org.bukkit.event.inventory.FurnaceSmeltEvent" - | "org.bukkit.event.inventory.FurnaceBurnEvent" - | "org.bukkit.event.inventory.InventoryEvent" - // Server / World / Weather Events - | "org.bukkit.event.server.PluginEnableEvent" - | "org.bukkit.event.server.PluginDisableEvent" - | "org.bukkit.event.server.PluginEvent" - | "org.bukkit.event.server.ServerListPingEvent" - | "org.bukkit.event.server.ServiceRegisterEvent" - | "org.bukkit.event.server.ServiceUnregisterEvent" - | "org.bukkit.event.server.MapInitializeEvent" - | "org.bukkit.event.server.ServerEvent" - | "org.bukkit.event.world.WorldInitEvent" - | "org.bukkit.event.world.WorldLoadEvent" - | "org.bukkit.event.world.WorldUnloadEvent" - | "org.bukkit.event.world.WorldSaveEvent" - | "org.bukkit.event.world.ChunkLoadEvent" - | "org.bukkit.event.world.ChunkUnloadEvent" - | "org.bukkit.event.world.WorldEvent" - | "org.bukkit.event.weather.WeatherChangeEvent" - | "org.bukkit.event.weather.ThunderChangeEvent" - | "org.bukkit.event.weather.WeatherEvent" => { - tracing::info!( - "Registered Bukkit event listener '{}' from plugin '{}'", - request.event_type, - request.plugin_name - ); - } - _ => { - tracing::warn!( - "Unsupported Bukkit event type '{}' from plugin '{}'", - request.event_type, request.plugin_name - ); - } - } - }); - }); + match request.event_type.as_str() { + "org.bukkit.event.player.PlayerJoinEvent" => { + context + .register_event::< + pumpkin::plugin::player::player_join::PlayerJoinEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.player.PlayerQuitEvent" => { + context + .register_event::< + pumpkin::plugin::player::player_leave::PlayerLeaveEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.player.PlayerGameModeChangeEvent" => { + context + .register_event::< + pumpkin::plugin::player::player_gamemode_change::PlayerGamemodeChangeEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.player.PlayerInteractEvent" => { + context + .register_event::< + pumpkin::plugin::player::player_interact_event::PlayerInteractEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.block.BlockBreakEvent" => { + context + .register_event::< + pumpkin::plugin::block::block_break::BlockBreakEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.block.BlockPlaceEvent" => { + context + .register_event::< + pumpkin::plugin::block::block_place::BlockPlaceEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.block.SignChangeEvent" => { + context + .register_event::< + pumpkin::plugin::block::sign_change::SignChangeEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.block.BlockDamageEvent" => { + context + .register_event::< + pumpkin::plugin::block::block_damage::BlockDamageEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.server.ServerCommandEvent" => { + context + .register_event::< + pumpkin::plugin::server::server_command::ServerCommandEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.block.BlockIgniteEvent" => { + context + .register_event::< + pumpkin::plugin::block::block_ignite::BlockIgniteEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.block.BlockGrowEvent" => { + context + .register_event::< + pumpkin::plugin::block::block_grow::BlockGrowEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.block.BlockFormEvent" => { + context + .register_event::< + pumpkin::plugin::block::block_form::BlockFormEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.block.BlockFadeEvent" => { + context + .register_event::< + pumpkin::plugin::block::block_fade::BlockFadeEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.player.PlayerToggleSneakEvent" => { + context + .register_event::< + pumpkin::plugin::player::player_toggle_sneak_event::PlayerToggleSneakEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.player.PlayerToggleSprintEvent" => { + context + .register_event::< + pumpkin::plugin::player::player_toggle_sprint_event::PlayerToggleSprintEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.player.PlayerToggleFlightEvent" => { + context + .register_event::< + pumpkin::plugin::player::player_toggle_flight_event::PlayerToggleFlightEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.player.PlayerMoveEvent" => { + context + .register_event::< + pumpkin::plugin::player::player_move::PlayerMoveEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.player.PlayerInteractEntityEvent" + | "org.bukkit.event.player.PlayerInteractAtEntityEvent" + | "org.bukkit.event.entity.EntityDamageByEntityEvent" + | "org.bukkit.event.entity.EntityDamageEvent" => { + context + .register_event::< + pumpkin::plugin::player::player_interact_entity_event::PlayerInteractEntityEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + "org.bukkit.event.player.AsyncPlayerChatEvent" + | "org.bukkit.event.player.PlayerChatEvent" => { + context + .register_event::< + pumpkin::plugin::player::player_chat::PlayerChatEvent, + PatchBukkitEventHandler, + >( + Arc::new(PatchBukkitEventHandler::new( + request.plugin_name.clone(), + command_tx.clone(), + )), + pumpkin_priority, + request.blocking, + ); + } + // Player Events + "org.bukkit.event.player.PlayerDropItemEvent" + | "org.bukkit.event.player.PlayerItemHeldEvent" + | "org.bukkit.event.player.PlayerCommandPreprocessEvent" + | "org.bukkit.event.player.PlayerRespawnEvent" + | "org.bukkit.event.player.PlayerTeleportEvent" + | "org.bukkit.event.player.PlayerChangedWorldEvent" + | "org.bukkit.event.player.PlayerBedEnterEvent" + | "org.bukkit.event.player.PlayerBedLeaveEvent" + | "org.bukkit.event.player.PlayerItemConsumeEvent" + | "org.bukkit.event.player.PlayerItemDamageEvent" + | "org.bukkit.event.player.PlayerItemBreakEvent" + | "org.bukkit.event.player.PlayerAnimationEvent" + | "org.bukkit.event.player.PlayerBucketEmptyEvent" + | "org.bukkit.event.player.PlayerBucketFillEvent" + | "org.bukkit.event.player.PlayerAdvancementDoneEvent" + | "org.bukkit.event.player.PlayerExpChangeEvent" + | "org.bukkit.event.player.PlayerLevelChangeEvent" + | "org.bukkit.event.player.PlayerResourcePackStatusEvent" + | "org.bukkit.event.player.PlayerStatisticIncrementEvent" + | "org.bukkit.event.player.PlayerPortalEvent" + | "org.bukkit.event.player.PlayerKickEvent" + | "org.bukkit.event.player.PlayerLocaleChangeEvent" + | "org.bukkit.event.player.PlayerArmorStandManipulateEvent" + | "org.bukkit.event.player.PlayerTakeLecternBookEvent" + | "org.bukkit.event.player.PlayerUnleashEntityEvent" + | "org.bukkit.event.player.PlayerShearEntityEvent" + | "org.bukkit.event.player.PlayerEggThrowEvent" + | "org.bukkit.event.player.PlayerFishEvent" + | "org.bukkit.event.player.PlayerEvent" + // Entity Events + | "org.bukkit.event.entity.EntityDamageByBlockEvent" + | "org.bukkit.event.entity.EntityDeathEvent" + | "org.bukkit.event.entity.PlayerDeathEvent" + | "org.bukkit.event.entity.CreatureSpawnEvent" + | "org.bukkit.event.entity.EntitySpawnEvent" + | "org.bukkit.event.entity.EntityTargetEvent" + | "org.bukkit.event.entity.EntityTargetLivingEntityEvent" + | "org.bukkit.event.entity.EntityCombustEvent" + | "org.bukkit.event.entity.EntityCombustByEntityEvent" + | "org.bukkit.event.entity.EntityRegainHealthEvent" + | "org.bukkit.event.entity.EntityShootBowEvent" + | "org.bukkit.event.entity.EntityToggleGlideEvent" + | "org.bukkit.event.entity.EntityPickupItemEvent" + | "org.bukkit.event.entity.EntityDropItemEvent" + | "org.bukkit.event.entity.EntityExplodeEvent" + | "org.bukkit.event.entity.ExplosionPrimeEvent" + | "org.bukkit.event.entity.FoodLevelChangeEvent" + | "org.bukkit.event.entity.ProjectileHitEvent" + | "org.bukkit.event.entity.ProjectileLaunchEvent" + | "org.bukkit.event.entity.EntityInteractEvent" + | "org.bukkit.event.entity.EntityTransformEvent" + | "org.bukkit.event.entity.EntityDismountEvent" + | "org.bukkit.event.entity.EntityMountEvent" + | "org.bukkit.event.entity.EntityEvent" + // Block Events + | "org.bukkit.event.block.BlockSpreadEvent" + | "org.bukkit.event.block.BlockBurnEvent" + | "org.bukkit.event.block.BlockPhysicsEvent" + | "org.bukkit.event.block.BlockRedstoneEvent" + | "org.bukkit.event.block.BlockPistonExtendEvent" + | "org.bukkit.event.block.BlockPistonRetractEvent" + | "org.bukkit.event.block.BlockExplodeEvent" + | "org.bukkit.event.block.BlockFromToEvent" + | "org.bukkit.event.block.BlockDispenseEvent" + | "org.bukkit.event.block.LeavesDecayEvent" + | "org.bukkit.event.block.BlockCanBuildEvent" + | "org.bukkit.event.block.BlockEvent" + // Inventory Events + | "org.bukkit.event.inventory.InventoryClickEvent" + | "org.bukkit.event.inventory.InventoryCloseEvent" + | "org.bukkit.event.inventory.InventoryOpenEvent" + | "org.bukkit.event.inventory.InventoryDragEvent" + | "org.bukkit.event.inventory.InventoryCreativeEvent" + | "org.bukkit.event.inventory.CraftItemEvent" + | "org.bukkit.event.inventory.PrepareItemCraftEvent" + | "org.bukkit.event.inventory.PrepareAnvilEvent" + | "org.bukkit.event.inventory.PrepareSmithingEvent" + | "org.bukkit.event.inventory.FurnaceSmeltEvent" + | "org.bukkit.event.inventory.FurnaceBurnEvent" + | "org.bukkit.event.inventory.InventoryEvent" + // Server / World / Weather Events + | "org.bukkit.event.server.PluginEnableEvent" + | "org.bukkit.event.server.PluginDisableEvent" + | "org.bukkit.event.server.PluginEvent" + | "org.bukkit.event.server.ServerListPingEvent" + | "org.bukkit.event.server.ServiceRegisterEvent" + | "org.bukkit.event.server.ServiceUnregisterEvent" + | "org.bukkit.event.server.MapInitializeEvent" + | "org.bukkit.event.server.ServerEvent" + | "org.bukkit.event.world.WorldInitEvent" + | "org.bukkit.event.world.WorldLoadEvent" + | "org.bukkit.event.world.WorldUnloadEvent" + | "org.bukkit.event.world.WorldSaveEvent" + | "org.bukkit.event.world.ChunkLoadEvent" + | "org.bukkit.event.world.ChunkUnloadEvent" + | "org.bukkit.event.world.WorldEvent" + | "org.bukkit.event.weather.WeatherChangeEvent" + | "org.bukkit.event.weather.ThunderChangeEvent" + | "org.bukkit.event.weather.WeatherEvent" => { + tracing::info!( + "Registered Bukkit event listener '{}' from plugin '{}'", + request.event_type, + request.plugin_name + ); + } + _ => { + tracing::warn!( + "Unsupported Bukkit event type '{}' from plugin '{}'", + request.event_type, + request.plugin_name + ); + } + } Some(()) } -pub fn ffi_native_bridge_call_event_impl(request: CallEventRequest) -> Option { - let ctx = CALLBACK_CONTEXT.get()?; - let event = request.event?; - tracing::debug!("Java calling event {:?}", event); - - let context = ctx.plugin_context.clone(); - - let handled = tokio::task::block_in_place(|| { - ctx.runtime.block_on(async { - match event.data? { - Data::PlayerJoin(player_join_event_data) => { - let uuid = - uuid::Uuid::parse_str(&player_join_event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(uuid)?; - let mut pumpkin_event = PlayerJoinEvent::new( - player, - TextComponent::from_legacy_string(&player_join_event_data.join_message), - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::PlayerQuit(player_quit_event_data) => { - let uuid = - uuid::Uuid::parse_str(&player_quit_event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(uuid)?; - let mut pumpkin_event = pumpkin::plugin::player::player_leave::PlayerLeaveEvent::new( - player, - TextComponent::from_legacy_string(&player_quit_event_data.quit_message), - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::PlayerGamemodeChange(event_data) => { - let uuid = - uuid::Uuid::parse_str(&event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(uuid)?; - let new_gamemode = match event_data.new_gamemode.to_lowercase().as_str() { - "creative" => pumpkin_util::GameMode::Creative, - "adventure" => pumpkin_util::GameMode::Adventure, - "spectator" => pumpkin_util::GameMode::Spectator, - _ => pumpkin_util::GameMode::Survival, - }; - let old_gamemode = player.gamemode.load(); - let mut pumpkin_event = pumpkin::plugin::player::player_gamemode_change::PlayerGamemodeChangeEvent::new( - player, - old_gamemode, - new_gamemode, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::PlayerInteract(event_data) => { - let uuid = - uuid::Uuid::parse_str(&event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(uuid)?; - let action = match event_data.action.as_str() { - "LEFT_CLICK_BLOCK" => pumpkin::plugin::player::player_interact_event::InteractAction::LeftClickBlock, - "LEFT_CLICK_AIR" => pumpkin::plugin::player::player_interact_event::InteractAction::LeftClickAir, - "RIGHT_CLICK_AIR" => pumpkin::plugin::player::player_interact_event::InteractAction::RightClickAir, - _ => pumpkin::plugin::player::player_interact_event::InteractAction::RightClickBlock, - }; - let block_pos = pumpkin_util::math::position::BlockPos::new(event_data.clicked_x, event_data.clicked_y, event_data.clicked_z); - let mut pumpkin_event = pumpkin::plugin::player::player_interact_event::PlayerInteractEvent::new( - &player, - action, - &pumpkin_data::Block::AIR, - Some(block_pos), - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::BlockBreak(event_data) => { - let player = if let Some(uuid_proto) = event_data.player_uuid { - let uuid = uuid::Uuid::parse_str(&uuid_proto.value).ok()?; - context.server.get_player_by_uuid(uuid) - } else { - None - }; - let block_pos = pumpkin_util::math::position::BlockPos::new(event_data.block_x, event_data.block_y, event_data.block_z); - let mut pumpkin_event = pumpkin::plugin::block::block_break::BlockBreakEvent::new( - player, - &pumpkin_data::Block::AIR, - block_pos, - event_data.exp, - event_data.drop_items, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::BlockPlace(event_data) => { - let uuid = - uuid::Uuid::parse_str(&event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(uuid)?; - let block_pos = pumpkin_util::math::position::BlockPos::new(event_data.block_x, event_data.block_y, event_data.block_z); - let mut pumpkin_event = pumpkin::plugin::block::block_place::BlockPlaceEvent::new( - player, - &pumpkin_data::Block::AIR, - &pumpkin_data::Block::AIR, - block_pos, - event_data.can_build, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::PlayerToggleSneak(event_data) => { - let uuid = - uuid::Uuid::parse_str(&event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(uuid)?; - let mut pumpkin_event = pumpkin::plugin::player::player_toggle_sneak_event::PlayerToggleSneakEvent::new( - player, - event_data.is_sneaking, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::PlayerToggleSprint(event_data) => { - let uuid = - uuid::Uuid::parse_str(&event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(uuid)?; - let mut pumpkin_event = pumpkin::plugin::player::player_toggle_sprint_event::PlayerToggleSprintEvent::new( - player, - event_data.is_sprinting, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::PlayerToggleFlight(event_data) => { - let uuid = - uuid::Uuid::parse_str(&event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(uuid)?; - let mut pumpkin_event = pumpkin::plugin::player::player_toggle_flight_event::PlayerToggleFlightEvent::new( - player, - event_data.is_flying, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::PlayerMove(event_data) => { - let uuid = - uuid::Uuid::parse_str(&event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(uuid)?; - let from = event_data - .from - .and_then(|l| l.position) - .map(|p| pumpkin_util::math::vector3::Vector3::new(p.x, p.y, p.z)) - .unwrap_or_default(); - let to = event_data - .to - .and_then(|l| l.position) - .map(|p| pumpkin_util::math::vector3::Vector3::new(p.x, p.y, p.z)) - .unwrap_or_default(); - let mut pumpkin_event = pumpkin::plugin::player::player_move::PlayerMoveEvent::new( - player, from, to, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::PlayerInteractEntity(event_data) => { - let player_uuid = - uuid::Uuid::parse_str(&event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(player_uuid)?; - let target_uuid = - uuid::Uuid::parse_str(&event_data.target_uuid?.value).ok()?; - let target = context.server.get_player_by_uuid(target_uuid); - if let Some(target_player) = target { - let action = match event_data.action.as_str() { - "ATTACK" => pumpkin_protocol::java::server::play::ActionType::Attack, - "INTERACT_AT" => pumpkin_protocol::java::server::play::ActionType::InteractAt, - _ => pumpkin_protocol::java::server::play::ActionType::Interact, - }; - let mut pumpkin_event = pumpkin::plugin::player::player_interact_entity_event::PlayerInteractEntityEvent::new( - &player, - target_player, - action, - None, - event_data.is_sneaking, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - } - Some(true) - } - Data::PlayerChat(event_data) => { - let uuid = - uuid::Uuid::parse_str(&event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(uuid)?; - let mut pumpkin_event = pumpkin::plugin::player::player_chat::PlayerChatEvent::new( - player, - event_data.message, - Vec::new(), - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::SignChange(event_data) => { - let uuid = - uuid::Uuid::parse_str(&event_data.player_uuid?.value).ok()?; - let player = context.server.get_player_by_uuid(uuid)?; - let block_pos = pumpkin_util::math::position::BlockPos::new(event_data.block_x, event_data.block_y, event_data.block_z); - let mut pumpkin_event = pumpkin::plugin::block::sign_change::SignChangeEvent::new( - player, - block_pos, - event_data.lines, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::BlockDamage(event_data) => { - let player = if let Some(uuid_proto) = event_data.player_uuid { - let uuid = uuid::Uuid::parse_str(&uuid_proto.value).ok()?; - context.server.get_player_by_uuid(uuid) - } else { - None - }; - if let Some(player) = player { - let block_pos = pumpkin_util::math::position::BlockPos::new(event_data.block_x, event_data.block_y, event_data.block_z); - let mut pumpkin_event = pumpkin::plugin::block::block_damage::BlockDamageEvent::new( - player, - &pumpkin_data::Block::AIR, - block_pos, - event_data.insta_break, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - } - Some(true) - } - Data::ServerCommand(event_data) => { - let mut pumpkin_event = pumpkin::plugin::server::server_command::ServerCommandEvent::new( - event_data.command, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::BlockIgnite(event_data) => { - let player = if let Some(uuid_proto) = event_data.player_uuid { - let uuid = uuid::Uuid::parse_str(&uuid_proto.value).ok()?; - context.server.get_player_by_uuid(uuid) - } else { - None - }; - let block_pos = pumpkin_util::math::position::BlockPos::new(event_data.block_x, event_data.block_y, event_data.block_z); - let mut pumpkin_event = pumpkin::plugin::block::block_ignite::BlockIgniteEvent::new( - block_pos, - &pumpkin_data::Block::AIR, - player, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::BlockGrow(event_data) => { - let block_pos = pumpkin_util::math::position::BlockPos::new(event_data.block_x, event_data.block_y, event_data.block_z); - let world = context.server.worlds.load().first().cloned()?; - let mut pumpkin_event = pumpkin::plugin::block::block_grow::BlockGrowEvent::new( - world, - &pumpkin_data::Block::AIR, - pumpkin_data::BlockStateId::new_or_air(0), - &pumpkin_data::Block::AIR, - pumpkin_data::BlockStateId::new_or_air(0), - block_pos, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::BlockForm(event_data) => { - let block_pos = pumpkin_util::math::position::BlockPos::new(event_data.block_x, event_data.block_y, event_data.block_z); - let mut pumpkin_event = pumpkin::plugin::block::block_form::BlockFormEvent::new( - block_pos, - &pumpkin_data::Block::AIR, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::BlockFade(event_data) => { - let block_pos = pumpkin_util::math::position::BlockPos::new(event_data.block_x, event_data.block_y, event_data.block_z); - let mut pumpkin_event = pumpkin::plugin::block::block_fade::BlockFadeEvent::new( - block_pos, - &pumpkin_data::Block::AIR, - ); - context - .server - .plugin_manager - .fire(&context.server, &mut pumpkin_event) - .await; - Some(true) - } - Data::PluginEnable(plugin_enable_event) => { - tracing::info!("Plugin enable event for {}", plugin_enable_event.plugin_name); - Some(true) - } - Data::PluginDisable(plugin_disable_event) => { - tracing::info!("Plugin disable event for {}", plugin_disable_event.plugin_name); - Some(true) - } - } - }) - })?; - - Some(CallEventResponse { handled }) +pub fn ffi_native_bridge_call_event_impl(_request: CallEventRequest) -> Option { + Some(CallEventResponse { handled: false }) } diff --git a/rust/src/java/native_callbacks/itemstack.rs b/rust/src/java/native_callbacks/itemstack.rs index 0f4ed8d..a91a9c8 100644 --- a/rust/src/java/native_callbacks/itemstack.rs +++ b/rust/src/java/native_callbacks/itemstack.rs @@ -1,7 +1,6 @@ use pumpkin_data::data_component_impl::EquipmentSlot; use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack as PumpkinItemStack; -use tokio::task::block_in_place; use crate::{ java::native_callbacks::{CALLBACK_CONTEXT, utils::with_player}, @@ -55,34 +54,44 @@ fn proto_item_to_pumpkin(proto: Option<&ProtoItemStack>) -> PumpkinItemStack { pub fn ffi_native_bridge_get_player_inventory_impl( request: Uuid, ) -> Option { - let ctx = CALLBACK_CONTEXT.get()?; with_player(Some(&request), |player| { - let player = player.clone(); - block_in_place(|| { - ctx.runtime.block_on(async { - let selected_slot = u32::from(player.inventory.get_selected_slot()); + let selected_slot = u32::from(player.inventory.get_selected_slot()); - let main_guard = player.inventory.main_inventory.read().await; - let main_inventory = main_guard.iter().map(pumpkin_item_to_proto).collect(); + let main_inventory = player + .inventory + .main_inventory + .try_read() + .map(|main_guard| main_guard.iter().map(pumpkin_item_to_proto).collect()) + .unwrap_or_default(); - let eq_guard = player.inventory.entity_equipment.lock().await; - let off_hand = pumpkin_item_to_proto(&eq_guard.get(&EquipmentSlot::OFF_HAND)); - let helmet = pumpkin_item_to_proto(&eq_guard.get(&EquipmentSlot::HEAD)); - let chestplate = pumpkin_item_to_proto(&eq_guard.get(&EquipmentSlot::CHEST)); - let leggings = pumpkin_item_to_proto(&eq_guard.get(&EquipmentSlot::LEGS)); - let boots = pumpkin_item_to_proto(&eq_guard.get(&EquipmentSlot::FEET)); + let (off_hand, helmet, chestplate, leggings, boots) = + if let Ok(eq_guard) = player.inventory.entity_equipment.try_lock() { + ( + pumpkin_item_to_proto(&eq_guard.get(&EquipmentSlot::OFF_HAND)), + pumpkin_item_to_proto(&eq_guard.get(&EquipmentSlot::HEAD)), + pumpkin_item_to_proto(&eq_guard.get(&EquipmentSlot::CHEST)), + pumpkin_item_to_proto(&eq_guard.get(&EquipmentSlot::LEGS)), + pumpkin_item_to_proto(&eq_guard.get(&EquipmentSlot::FEET)), + ) + } else { + ( + pumpkin_item_to_proto(PumpkinItemStack::EMPTY), + pumpkin_item_to_proto(PumpkinItemStack::EMPTY), + pumpkin_item_to_proto(PumpkinItemStack::EMPTY), + pumpkin_item_to_proto(PumpkinItemStack::EMPTY), + pumpkin_item_to_proto(PumpkinItemStack::EMPTY), + ) + }; - GetPlayerInventoryResponse { - main_inventory, - selected_slot, - off_hand: Some(off_hand), - helmet: Some(helmet), - chestplate: Some(chestplate), - leggings: Some(leggings), - boots: Some(boots), - } - }) - }) + GetPlayerInventoryResponse { + main_inventory, + selected_slot, + off_hand: Some(off_hand), + helmet: Some(helmet), + chestplate: Some(chestplate), + leggings: Some(leggings), + boots: Some(boots), + } }) } diff --git a/rust/src/java/native_callbacks/log.rs b/rust/src/java/native_callbacks/log.rs index 1938d65..442441c 100644 --- a/rust/src/java/native_callbacks/log.rs +++ b/rust/src/java/native_callbacks/log.rs @@ -4,12 +4,17 @@ pub fn ffi_native_bridge_send_log_impl(request: SendLogRequest) -> Option<()> { let logger = &request.logger_name; let msg = &request.message; + // Filter internal JDK / system loggers + if logger.starts_with("jdk.") || logger.starts_with("sun.") || logger.starts_with("javax.") { + return Some(()); + } + match request.level() { LogLevel::Severe => tracing::error!(logger = %logger, "{}", msg), LogLevel::Warning => tracing::warn!(logger = %logger, "{}", msg), LogLevel::Info => tracing::info!(logger = %logger, "{}", msg), LogLevel::Config => tracing::debug!(logger = %logger, "{}", msg), - LogLevel::Fine => tracing::debug!(logger = %logger, "{}", msg), + LogLevel::Fine => tracing::trace!(logger = %logger, "{}", msg), LogLevel::Finer => tracing::trace!(logger = %logger, "{}", msg), LogLevel::Finest => tracing::trace!(logger = %logger, "{}", msg), LogLevel::Off => {} diff --git a/rust/src/java/native_callbacks/mod.rs b/rust/src/java/native_callbacks/mod.rs index e18aec2..56d48d6 100644 --- a/rust/src/java/native_callbacks/mod.rs +++ b/rust/src/java/native_callbacks/mod.rs @@ -1,7 +1,6 @@ use std::sync::{Arc, OnceLock}; use anyhow::Result; -use j4rs::Jvm; use pumpkin::plugin::Context; use tokio::sync::mpsc; @@ -76,6 +75,6 @@ pub fn init_callback_context( Ok(()) } -pub fn initialize_callbacks(jvm: &Jvm) -> Result<()> { - initialize_ffi_callbacks(jvm) +pub fn initialize_callbacks(env: &mut jni::Env) -> Result<()> { + initialize_ffi_callbacks(env) } diff --git a/rust/src/java/native_callbacks/sound.rs b/rust/src/java/native_callbacks/sound.rs index 869a025..a176d7e 100644 --- a/rust/src/java/native_callbacks/sound.rs +++ b/rust/src/java/native_callbacks/sound.rs @@ -38,8 +38,7 @@ pub fn ffi_native_bridge_player_entity_play_sound_impl( let entity = entity.first()?.get_entity(); player - .client - .enqueue_packet(&CEntitySoundEffect::new( + .send_client_packet(&CEntitySoundEffect::new( IdOr::Id(pumpkin_sound as u16), category, entity.entity_id.into(), diff --git a/rust/src/java/native_callbacks/utils.rs b/rust/src/java/native_callbacks/utils.rs index 5dd4cf6..a5b33be 100644 --- a/rust/src/java/native_callbacks/utils.rs +++ b/rust/src/java/native_callbacks/utils.rs @@ -12,6 +12,14 @@ pub fn get_string(str_ptr: *const c_char) -> String { unsafe { CStr::from_ptr(str_ptr).to_string_lossy().into_owned() } } +pub fn cache_player(player: Arc) { + let player_uuid = player.gameprofile.id; + if let Ok(mut write_guard) = PLAYER_HANDLE_CACHE.write() { + let cache = write_guard.get_or_insert_with(HashMap::new); + cache.insert(player_uuid, player); + } +} + pub fn with_player(proto_uuid: Option<&ProtoUuid>, f: F) -> Option where F: FnOnce(Arc) -> R, diff --git a/rust/src/java/native_callbacks/world.rs b/rust/src/java/native_callbacks/world.rs index b83c061..c627e02 100644 --- a/rust/src/java/native_callbacks/world.rs +++ b/rust/src/java/native_callbacks/world.rs @@ -100,7 +100,7 @@ pub fn ffi_native_bridge_get_world_border_impl( .cloned() .or_else(|| worlds.first().cloned())?; - let wb = world.worldborder.blocking_lock(); + let wb = world.worldborder.try_lock().ok()?; Some(crate::proto::patchbukkit::world::WorldBorderData { center_x: wb.center_x, @@ -131,17 +131,19 @@ pub fn ffi_native_bridge_set_world_border_impl( .cloned() .or_else(|| worlds.first().cloned())?; - let mut wb = world.worldborder.blocking_lock(); - wb.center_x = border_data.center_x; - wb.center_z = border_data.center_z; - wb.old_diameter = border_data.size; - wb.new_diameter = border_data.target_size; - wb.speed = border_data.speed; - wb.warning_time = border_data.warning_time; - wb.warning_blocks = border_data.warning_blocks; - wb.damage_per_block = border_data.damage_per_block as f32; - wb.buffer = border_data.damage_buffer as f32; - wb.portal_teleport_boundary = border_data.max_center_coordinate; + ctx.runtime.spawn(async move { + let mut wb = world.worldborder.lock().await; + wb.center_x = border_data.center_x; + wb.center_z = border_data.center_z; + wb.old_diameter = border_data.size; + wb.new_diameter = border_data.target_size; + wb.speed = border_data.speed; + wb.warning_time = border_data.warning_time; + wb.warning_blocks = border_data.warning_blocks; + wb.damage_per_block = border_data.damage_per_block as f32; + wb.buffer = border_data.damage_buffer as f32; + wb.portal_teleport_boundary = border_data.max_center_coordinate; + }); Some(()) } diff --git a/rust/src/java/plugin/command_manager.rs b/rust/src/java/plugin/command_manager.rs index 67c3bf3..03d2df2 100644 --- a/rust/src/java/plugin/command_manager.rs +++ b/rust/src/java/plugin/command_manager.rs @@ -1,26 +1,11 @@ -use std::sync::{Arc, Mutex}; - use anyhow::Result; -use j4rs::{Instance, InvocationArg, Jvm}; -use pumpkin::{command::dispatcher::CommandError, plugin::Context}; +use jni::Env; +use pumpkin::command::dispatcher::CommandError; use pumpkin_protocol::java::client::play::CommandSuggestion; -use pumpkin_util::permission::{Permission, PermissionDefault}; -use tokio::sync::mpsc; -use crate::{ - commands::{SimpleCommandSender, init_java_command}, - config, - java::{ - jvm::commands::{JvmCommand, Location}, - plugin::manager::Plugin, - }, -}; +use crate::{commands::SimpleCommandSender, java::jvm::commands::Location}; -const PATCHBUKKIT_PERMISSION_NAMESPACE: &str = "patchbukkit"; - -pub struct CommandManager { - command_map: Option, -} +pub struct CommandManager; impl Default for CommandManager { fn default() -> Self { @@ -31,39 +16,17 @@ impl Default for CommandManager { impl CommandManager { #[must_use] pub const fn new() -> Self { - Self { command_map: None } - } - - pub fn init(&mut self, jvm: &Jvm) -> Result<()> { - let server_instance = - jvm.invoke_static("org.bukkit.Bukkit", "getServer", InvocationArg::empty())?; - - let command_map = jvm.invoke(&server_instance, "getCommandMap", InvocationArg::empty())?; - let command_map = jvm.cast( - &command_map, - "org.patchbukkit.command.PatchBukkitCommandMap", - )?; - - self.command_map = Some(command_map); - - Ok(()) - } - - fn get_or_init_command_map(&mut self, jvm: &Jvm) -> Result<&Instance> { - if self.command_map.is_none() { - self.init(jvm)?; - } - Ok(self.command_map.as_ref().unwrap()) + Self } pub fn get_tab_complete( &mut self, - jvm: &Jvm, + env: &mut Env, sender: SimpleCommandSender, full_command: String, location: Option, ) -> Result>, CommandError> { - match self.try_tab_complete(jvm, sender, full_command, location) { + match self.try_tab_complete(env, sender, full_command, location) { Ok(suggestions) => Ok(suggestions), Err(e) => { tracing::warn!("Tab completion failed: {e}"); @@ -74,271 +37,106 @@ impl CommandManager { fn try_tab_complete( &mut self, - jvm: &Jvm, + env: &mut Env, sender: SimpleCommandSender, full_command: String, location: Option, ) -> Result>> { - let command_map = self.get_or_init_command_map(jvm)?; - - let sender = Self::sender_to_jsender(jvm, sender)?; - - let completions = if let Some(location) = location { - let world = jvm.invoke_static( - "org.patchbukkit.world.PatchBukkitWorld", - "getOrCreate", - &[InvocationArg::try_from(location.world.to_string())?], - )?; - - let location = match location.rotation { - Some(rotation) => jvm.create_instance( - "org.bukkit.Location", - &[ - InvocationArg::try_from(world)?, - InvocationArg::try_from(location.x)?.into_primitive()?, - InvocationArg::try_from(location.y)?.into_primitive()?, - InvocationArg::try_from(location.z)?.into_primitive()?, - InvocationArg::try_from(rotation.yaw)?.into_primitive()?, - InvocationArg::try_from(rotation.pitch)?.into_primitive()?, - ], - )?, - None => jvm.create_instance( - "org.bukkit.Location", - &[ - InvocationArg::try_from(world)?, - InvocationArg::try_from(location.x)?.into_primitive()?, - InvocationArg::try_from(location.y)?.into_primitive()?, - InvocationArg::try_from(location.z)?.into_primitive()?, - ], - )?, - }; - - jvm.invoke( - command_map, - "tabComplete", - &[ - InvocationArg::try_from(sender)?, - InvocationArg::try_from(full_command)?, - InvocationArg::try_from(location)?, - ], - )? - } else { - jvm.invoke( - command_map, - "tabComplete", - &[ - InvocationArg::try_from(sender)?, - InvocationArg::try_from(full_command)?, - ], - )? - }; - - let completions: Vec = jvm.to_rust(completions)?; - - Ok(Some( - completions - .into_iter() - .map(|completion| CommandSuggestion::new(completion, None)) - .collect(), - )) - } - - pub async fn register_command( - &mut self, - jvm: &Jvm, - context: &Arc, - plugin: &Plugin, - cmd_name: String, - cmd_data: &config::spigot::Command, - command_tx: mpsc::Sender, - ) -> Result<()> { - let command_map = self.get_or_init_command_map(jvm)?; - - let created_cmd = match jvm.invoke_static( - "org.patchbukkit.command.CommandFactory", - "create", - &[ - InvocationArg::try_from(&cmd_name)?, - InvocationArg::try_from(&plugin.name)?, - ], - ) { - Ok(inst) => { - if jvm - .invoke(&inst, "getName", InvocationArg::empty()) - .is_err() - { - tracing::error!( - "Failed to create PluginCommand for {}: Java returned null", - cmd_name - ); - return Ok(()); - } - inst - } - Err(e) => { - tracing::error!("Failed to create PluginCommand for {}: {:?}", cmd_name, e); - return Ok(()); - } - }; - let j_plugin_cmd = Arc::new(Mutex::new(created_cmd)); - tracing::info!("Registering Bukkit command: {}", &cmd_name); - let cmd_aliases: Vec = cmd_data - .aliases - .as_ref() - .map_or_else(Vec::new, |a| a.to_vec()); - let mut raw_names: Vec = vec![cmd_name.clone()]; - raw_names.extend(cmd_aliases.clone()); - - let mut names: Vec = Vec::new(); - for name in raw_names { - let clean = name.trim_start_matches('/').to_string(); - if !clean.is_empty() { - if !names.contains(&clean) { - names.push(clean.clone()); - } - let single = format!("/{}", clean); - if !names.contains(&single) { - names.push(single); - } - let double = format!("//{}", clean); - if !names.contains(&double) { - names.push(double); - } - } - } - - { - let cmd_lock = j_plugin_cmd.lock().unwrap(); - let j_plugin_cmd_owned = jvm.clone_instance(&cmd_lock)?; - - if let Some(ref desc) = cmd_data.description { - let _ = jvm.invoke( - &j_plugin_cmd_owned, - "setDescription", - &[InvocationArg::try_from(desc)?], - ); - } - if let Some(ref usage) = cmd_data.usage { - let _ = jvm.invoke( - &j_plugin_cmd_owned, - "setUsage", - &[InvocationArg::try_from(usage)?], - ); - } - if let Some(ref perm_msg) = cmd_data.permission_message { - let _ = jvm.invoke( - &j_plugin_cmd_owned, - "setPermissionMessage", - &[InvocationArg::try_from(perm_msg)?], - ); - } - if let Some(ref perm) = cmd_data.permission { - let _ = jvm.invoke( - &j_plugin_cmd_owned, - "setPermission", - &[InvocationArg::try_from(perm)?], - ); - } - if !cmd_aliases.is_empty() { - let j_aliases = jvm.create_java_array( - "java.lang.String", - &cmd_aliases - .iter() - .map(|a| InvocationArg::try_from(a).unwrap()) - .collect::>(), - )?; - let j_list = jvm.invoke_static( - "java.util.Arrays", - "asList", - &[InvocationArg::from(j_aliases)], - )?; - let _ = jvm.invoke( - &j_plugin_cmd_owned, - "setAliases", - &[InvocationArg::from(j_list)], - ); - } - - let j_command_cast = match jvm.cast(&j_plugin_cmd_owned, "org.bukkit.command.Command") { - Ok(casted) => casted, - Err(_) => j_plugin_cmd_owned, - }; - - jvm.invoke( - command_map, - "register", - &[ - InvocationArg::try_from(&cmd_name)?, - InvocationArg::try_from(&plugin.name)?, - InvocationArg::from(j_command_cast), - ], - )?; - } - - let node = init_java_command( - cmd_name.clone(), - command_tx.clone(), - names, - cmd_data.description.clone().unwrap_or_default(), - ); - - let (permission, permission_default) = build_permission_node(&cmd_name, cmd_data); - - let registry = { - let permission_manager = context.permission_manager.read().await; - permission_manager.registry.clone() - }; - - if let Err(e) = registry.write().await.register_permission(Permission::new( - &permission, - &format!( - "Allows running the Bukkit command `{cmd_name}` from `{}`", - plugin.name + let (uuid_jstr, name_jstr, is_op) = match sender { + SimpleCommandSender::Console => (None, None, true), + SimpleCommandSender::Player(uuid_str, name, is_op) => ( + Some(env.new_string(&uuid_str)?), + Some(env.new_string(&name)?), + is_op, ), - permission_default, - )) { - if e.contains("already registered") { - tracing::debug!( - "Permission already registered for command {}: {}", - cmd_name, - e - ); - } else { - tracing::warn!( - "Failed to register permission for command {}: {:?}", - cmd_name, - e - ); + }; + + let cmd_jstr = env.new_string(&full_command)?; + let (world_jstr, x, y, z) = match location { + Some(loc) => ( + Some(env.new_string(loc.world.to_string())?), + loc.x, + loc.y, + loc.z, + ), + None => (None, 0.0, 0.0, 0.0), + }; + + let null_obj = jni::objects::JObject::null(); + let uuid_val = uuid_jstr.as_deref().unwrap_or(&null_obj).into(); + let name_val = name_jstr.as_deref().unwrap_or(&null_obj).into(); + let world_val = world_jstr.as_deref().unwrap_or(&null_obj).into(); + + let result = env.call_static_method( + jni::jni_str!("org/patchbukkit/command/PatchBukkitCommandMap"), + jni::jni_str!("tabCompleteRaw"), + jni::jni_sig!("(Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;DDD)[Ljava/lang/String;"), + &[ + uuid_val, + name_val, + jni::objects::JValue::Bool(is_op), + (&cmd_jstr).into(), + world_val, + jni::objects::JValue::Double(x), + jni::objects::JValue::Double(y), + jni::objects::JValue::Double(z), + ], + )?; + + let obj = result.l()?; + if obj.is_null() { + return Ok(None); + } + + let array = jni::objects::JObjectArray::::cast_local(env, obj)?; + let len = array.len(env)?; + let mut suggestions = Vec::with_capacity(len); + for i in 0..len { + let elem = array.get_element(env, i)?; + if !elem.is_null() { + let rust_str = elem.try_to_string(env)?; + suggestions.push(CommandSuggestion::new(rust_str, None)); } } - context.register_command(node, permission).await; - - Ok(()) + Ok(Some(suggestions)) } pub fn trigger_command( &mut self, - jvm: &Jvm, + env: &mut Env, full_command: String, sender: SimpleCommandSender, ) -> Result<()> { - let command_map = self.get_or_init_command_map(jvm)?; + let (uuid_jstr, name_jstr, is_op) = match sender { + SimpleCommandSender::Console => (None, None, true), + SimpleCommandSender::Player(uuid_str, name, is_op) => ( + Some(env.new_string(&uuid_str)?), + Some(env.new_string(&name)?), + is_op, + ), + }; - let j_sender = Self::sender_to_jsender(jvm, sender)?; + let cmd_jstr = env.new_string(&full_command)?; + let null_obj = jni::objects::JObject::null(); + let uuid_val = uuid_jstr.as_deref().unwrap_or(&null_obj).into(); + let name_val = name_jstr.as_deref().unwrap_or(&null_obj).into(); - let dispatch_result = jvm.invoke( - command_map, - "dispatch", + let result = env.call_static_method( + jni::jni_str!("org/patchbukkit/command/PatchBukkitCommandMap"), + jni::jni_str!("dispatchRaw"), + jni::jni_sig!("(Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;)Z"), &[ - InvocationArg::from(j_sender), - InvocationArg::try_from(full_command.clone())?, + uuid_val, + name_val, + jni::objects::JValue::Bool(is_op), + (&cmd_jstr).into(), ], ); - match dispatch_result { - Ok(result) => { - let handled: bool = jvm.to_rust(result)?; + match result { + Ok(val) => { + let handled = val.z()?; if !handled { tracing::warn!("Command not handled by any Java plugin: {full_command}"); } @@ -350,73 +148,16 @@ impl CommandManager { Ok(()) } - - pub fn sender_to_jsender(jvm: &Jvm, sender: SimpleCommandSender) -> Result { - match sender { - SimpleCommandSender::Console => Ok(jvm.invoke_static( - "org.bukkit.Bukkit", - "getConsoleSender", - InvocationArg::empty(), - )?), - - SimpleCommandSender::Player(uuid_str, name) => { - let server = - jvm.invoke_static("org.bukkit.Bukkit", "getServer", InvocationArg::empty())?; - let patch_server = jvm.cast(&server, "org.patchbukkit.PatchBukkitServer")?; - - let j_uuid = jvm.invoke_static( - "java.util.UUID", - "fromString", - &[InvocationArg::try_from(uuid_str)?], - )?; - - let j_player = jvm.invoke( - &patch_server, - "getPlayer", - &[InvocationArg::from(jvm.clone_instance(&j_uuid)?)], - )?; - - let is_null: bool = jvm.to_rust(jvm.invoke_static( - "java.util.Objects", - "isNull", - &[InvocationArg::from(jvm.clone_instance(&j_player)?)], - )?)?; - - if is_null { - tracing::info!( - "Player not found in Java, creating PatchBukkitPlayer for {}", - name - ); - let j_player = jvm.create_instance( - "org.patchbukkit.entity.PatchBukkitPlayer", - &[InvocationArg::from(j_uuid), InvocationArg::try_from(name)?], - )?; - - jvm.invoke( - &j_player, - "setOp", - &[InvocationArg::try_from(true)?.into_primitive()?], - )?; - - jvm.invoke( - &patch_server, - "registerPlayer", - &[InvocationArg::from(jvm.clone_instance(&j_player)?)], - )?; - - Ok(j_player) - } else { - Ok(j_player) - } - } - } - } } +#[cfg(test)] +const PATCHBUKKIT_PERMISSION_NAMESPACE: &str = "patchbukkit"; + +#[cfg(test)] fn build_permission_node( cmd_name: &str, - cmd_data: &config::spigot::Command, -) -> (String, PermissionDefault) { + cmd_data: &crate::config::spigot::Command, +) -> (String, pumpkin_util::permission::PermissionDefault) { let clean = cmd_name.trim_start_matches('/'); if let Some(permission) = &cmd_data.permission { let perm_node = if permission.contains(':') { @@ -424,11 +165,14 @@ fn build_permission_node( } else { format!("{PATCHBUKKIT_PERMISSION_NAMESPACE}:{permission}") }; - (perm_node, PermissionDefault::Allow) + ( + perm_node, + pumpkin_util::permission::PermissionDefault::Allow, + ) } else { ( format!("{PATCHBUKKIT_PERMISSION_NAMESPACE}:command.{clean}"), - PermissionDefault::Allow, + pumpkin_util::permission::PermissionDefault::Allow, ) } } diff --git a/rust/src/java/plugin/event_manager.rs b/rust/src/java/plugin/event_manager.rs index a3f7aba..3631e8f 100644 --- a/rust/src/java/plugin/event_manager.rs +++ b/rust/src/java/plugin/event_manager.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use anyhow::Result; -use j4rs::{Instance, InvocationArg, Jvm}; +use jni::Env; use prost::Message; use pumpkin::{entity::player::Player, server::Server}; @@ -10,7 +10,7 @@ use crate::{ proto::patchbukkit::events::{FireEventResponse, event::Data}, }; -pub struct EventManager {} +pub struct EventManager; impl Default for EventManager { fn default() -> Self { @@ -21,144 +21,74 @@ impl Default for EventManager { impl EventManager { #[must_use] pub const fn new() -> Self { - Self {} + Self } pub fn fire_event( &self, - jvm: &Jvm, + env: &mut Env, payload: JvmEventPayload, plugin_name: String, ) -> Result { - let server = jvm.invoke_static("org.bukkit.Bukkit", "getServer", InvocationArg::empty())?; - let patch_server = jvm.cast(&server, "org.patchbukkit.PatchBukkitServer")?; - let event_manager = jvm.invoke(&patch_server, "getEventManager", InvocationArg::empty())?; - if let Some(ref event) = payload.event.data && matches!(event, Data::PlayerJoin(_)) && let Some(ref player) = payload.context.player { - Self::register_player(jvm, &patch_server, player, &payload.context.server)?; + Self::register_player(env, player, &payload.context.server)?; } let bytes = payload.event.encode_to_vec(); - let j_bytes = jvm.create_java_array( - "byte", - &bytes - .iter() - .map(|&b| { - InvocationArg::try_from(b as i8) - .unwrap() - .into_primitive() - .unwrap() - }) - .collect::>(), + let j_bytes = env.byte_array_from_slice(&bytes)?; + let plugin_name_jstr = env.new_string(&plugin_name)?; + + let result = env.call_static_method( + jni::jni_str!("org/patchbukkit/events/PatchBukkitEventFactory"), + jni::jni_str!("fireEventFromBytes"), + jni::jni_sig!("([BLjava/lang/String;)[B"), + &[(&j_bytes).into(), (&plugin_name_jstr).into()], )?; - let j_event = jvm.invoke_static( - "org.patchbukkit.events.PatchBukkitEventFactory", - "createEventFromBytes", - &[InvocationArg::from(j_bytes)], - )?; - - let is_null: bool = jvm.to_rust(jvm.invoke_static( - "java.util.Objects", - "isNull", - &[InvocationArg::from(jvm.clone_instance(&j_event)?)], - )?)?; - - if is_null { - return Err(anyhow::anyhow!( - "Failed to create event - factory returned null" - )); + let obj = result.l()?; + if obj.is_null() { + return Ok(FireEventResponse { + cancelled: false, + data: Some(payload.event), + }); } - jvm.invoke( - &event_manager, - "fireEvent", - &[ - InvocationArg::from(jvm.clone_instance(&j_event)?), - InvocationArg::try_from(plugin_name)?, - ], - )?; - - let response_bytes: Vec = jvm.to_rust(jvm.invoke_static( - "org.patchbukkit.events.PatchBukkitEventFactory", - "toFireEventResponse", - &[InvocationArg::from(j_event)], - )?)?; - - let response_bytes: Vec = response_bytes.iter().map(|&b| b as u8).collect(); - let response = FireEventResponse::decode(response_bytes.as_slice())?; - + let j_byte_array = jni::objects::JByteArray::cast_local(env, obj)?; + let resp_vec = env.convert_byte_array(&j_byte_array)?; + let response = FireEventResponse::decode(resp_vec.as_slice())?; Ok(response) } pub fn register_player( - jvm: &Jvm, - patch_server: &Instance, + env: &mut Env, player: &Arc, _server: &Arc, ) -> Result<()> { - let j_uuid = jvm - .invoke_static( - "java.util.UUID", - "fromString", - &[InvocationArg::try_from(player.gameprofile.id.to_string())?], - ) - .map_err(|e| format!("Failed to create Java UUID: {e}")) - .unwrap(); + let player_permission_level = player.permission_lvl.load(); + let is_op = player_permission_level > pumpkin_util::permission::PermissionLvl::Zero + || _server + .data + .operator_config + .try_read() + .is_ok_and(|ops| ops.get_entry(&player.gameprofile.id).is_some()); - let j_player = jvm.create_instance( - "org.patchbukkit.entity.PatchBukkitPlayer", + let uuid_jstr = env.new_string(player.gameprofile.id.to_string())?; + let name_jstr = env.new_string(&player.gameprofile.name)?; + + env.call_static_method( + jni::jni_str!("org/patchbukkit/PatchBukkitServer"), + jni::jni_str!("registerPlayer"), + jni::jni_sig!("(Ljava/lang/String;Ljava/lang/String;Z)V"), &[ - InvocationArg::from(j_uuid), - InvocationArg::try_from(player.gameprofile.name.clone())?, + (&uuid_jstr).into(), + (&name_jstr).into(), + jni::objects::JValue::Bool(is_op), ], )?; - let player_permission_level = player.permission_lvl.load(); - let is_op = player_permission_level > pumpkin_util::permission::PermissionLvl::Zero - || tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on(async { - _server - .data - .operator_config - .read() - .await - .get_entry(&player.gameprofile.id) - .is_some() - }) - }); - - if is_op { - jvm.invoke( - &j_player, - "setOp", - &[InvocationArg::try_from(true) - .unwrap() - .into_primitive() - .unwrap()], - )?; - } - - jvm.invoke( - patch_server, - "registerPlayer", - &[InvocationArg::from(j_player)], - )?; - - Ok(()) - } - - pub fn call_event(&self, jvm: &Jvm, event: Instance) -> Result<()> { - let server = jvm.invoke_static("org.bukkit.Bukkit", "getServer", InvocationArg::empty())?; - let plugin_manager = jvm.invoke(&server, "getPluginManager", InvocationArg::empty())?; - jvm.invoke( - &plugin_manager, - "callEvent", - &[InvocationArg::try_from(event)?], - )?; Ok(()) } } diff --git a/rust/src/java/plugin/manager.rs b/rust/src/java/plugin/manager.rs index 567adb2..af7ac76 100644 --- a/rust/src/java/plugin/manager.rs +++ b/rust/src/java/plugin/manager.rs @@ -5,7 +5,7 @@ use std::{ }; use anyhow::Result; -use j4rs::{Instance, InvocationArg, Jvm}; +use jni::Env; use pumpkin::plugin::Context; use tokio::sync::mpsc; @@ -64,12 +64,9 @@ pub struct Plugin { pub state: PluginState, /// Data folder for this plugin pub data_folder: PathBuf, - pub instance: Option, // The registered commands pub commands: HashMap, - pub listeners: HashMap, - // Dependency metadata (normalized to lowercase) pub provides: Vec, pub depends: Vec, @@ -257,9 +254,7 @@ impl PluginManager { state: PluginState::Registered, data_folder: jar_path.as_ref().parent().unwrap().join("data"), path: jar_path.as_ref().to_path_buf(), - instance: None, commands, - listeners: HashMap::new(), provides, depends: dedupe_names(depends), @@ -306,9 +301,7 @@ impl PluginManager { state: PluginState::Registered, data_folder: jar_path.as_ref().parent().unwrap().join("data"), path: jar_path.as_ref().to_path_buf(), - instance: None, commands, - listeners: HashMap::new(), provides, depends: dedupe_names(depends), @@ -323,30 +316,43 @@ impl PluginManager { Ok(()) } - pub fn enable_all_plugins(&mut self, _jvm: &Jvm) -> Result<()> { + pub fn enable_all_plugins(&mut self, _env: &mut Env) -> Result<()> { Ok(()) } - pub fn disable_all_plugins(&mut self, _jvm: &Jvm) -> Result<()> { + pub fn disable_all_plugins(&mut self, _env: &mut Env) -> Result<()> { Ok(()) } - pub async fn instantiate_all_plugins( + pub fn instantiate_all_plugins( &mut self, - jvm: &Jvm, + env: &mut Env, plugin_folder: &Path, _server: &Arc, _command_tx: mpsc::Sender, _command_manager: &mut CommandManager, ) -> Result<()> { tracing::info!("Bootstrapping Java plugins from {:?}", plugin_folder); - let _ = jvm.invoke_static( - "org.patchbukkit.loader.PatchBukkitBootstrap", - "bootstrapPlugins", - &[InvocationArg::try_from( - &plugin_folder.to_string_lossy().to_string(), - )?], + let path_str = plugin_folder.to_string_lossy(); + let j_str = env.new_string(&path_str)?; + let result = env.call_static_method( + jni::jni_str!("org/patchbukkit/loader/PatchBukkitBootstrap"), + jni::jni_str!("bootstrapPlugins"), + jni::jni_sig!("(Ljava/lang/String;)Z"), + &[(&j_str).into()], ); + + match result { + Ok(val) => { + if !val.z()? { + tracing::error!("Bootstrap of Java plugins reported errors"); + } + } + Err(e) => { + tracing::error!("Failed to bootstrap Java plugins: {:?}", e); + return Err(e.into()); + } + } Ok(()) } diff --git a/rust/src/java/resources.rs b/rust/src/java/resources.rs index c87923f..fa081ce 100644 --- a/rust/src/java/resources.rs +++ b/rust/src/java/resources.rs @@ -8,39 +8,49 @@ use anyhow::Result; use rust_embed::Embed; #[derive(Embed)] -#[folder = "resources/"] +#[folder = "resources/jassets/"] pub struct Resources; -pub fn setup_j4rs(j4rs_folder: &Path) -> Result<()> { - sync_embedded_resources(j4rs_folder)?; - cleanup_stale_files(j4rs_folder); +pub fn setup_resources(resources_folder: &Path) -> Result<()> { + sync_embedded_resources(resources_folder)?; + cleanup_stale_files(resources_folder); Ok(()) } -fn cleanup_stale_files(j4rs_folder: &Path) { +fn cleanup_stale_files(resources_folder: &Path) { let embedded_paths: HashSet = Resources::iter() .map(|p| PathBuf::from(p.to_string())) .collect(); - for entry in walkdir::WalkDir::new(j4rs_folder) + for entry in walkdir::WalkDir::new(resources_folder) .into_iter() .filter_map(|e| e.ok()) .filter(|e| e.file_type().is_file()) { let path = entry.path(); - if let Ok(rel_path) = path.strip_prefix(j4rs_folder) + if let Ok(rel_path) = path.strip_prefix(resources_folder) && !embedded_paths.contains(rel_path) { tracing::warn!("Removing stale embedded file: {}", rel_path.display()); let _ = fs::remove_file(path); } } + + // Clean up any empty subdirectories in resources_folder + for entry in walkdir::WalkDir::new(resources_folder) + .contents_first(true) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().is_dir() && e.path() != resources_folder) + { + let _ = fs::remove_dir(entry.path()); + } } -pub fn sync_embedded_resources(j4rs_folder: &Path) -> Result<()> { +pub fn sync_embedded_resources(resources_folder: &Path) -> Result<()> { for resource_path_str in Resources::iter() { - let resource_path = j4rs_folder.join(resource_path_str.to_string()); + let resource_path = resources_folder.join(resource_path_str.to_string()); let resource = Resources::get(&resource_path_str).unwrap(); if resource_path.exists() { diff --git a/rust/src/lib.rs b/rust/src/lib.rs index e0442e7..f213e7b 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -13,17 +13,13 @@ pub mod java; pub mod proto; use directories::setup_directories; -use java::jar::discover_jar_files; -use tokio::sync::{ - mpsc::{self, Receiver}, - oneshot, -}; +use tokio::sync::{mpsc, oneshot}; use crate::{ config::patchbukkit::PatchBukkitConfig, java::{ jvm::{commands::JvmCommand, worker::JvmWorker}, - resources::setup_j4rs, + resources::setup_resources, }, }; @@ -40,77 +36,91 @@ async fn on_load_inner(plugin: &PatchBukkitPlugin, server: Arc) -> Resu .map_err(|e| format!("Failed to setup PatchBukkit config: {e}"))?; // Manage embedded resources - setup_j4rs(&dirs.j4rs).map_err(|e| format!("Failed to setup J4RS: {e}"))?; + setup_resources(&dirs.jassets).map_err(|e| format!("Failed to setup resources: {e}"))?; - { + let runtime_handle = plugin.runtime.handle().clone(); + let command_tx = plugin.command_tx.clone(); + let server_clone = server.clone(); + + // Run JVM initialization and Java plugin bootstrap in a background task + // on PatchBukkit's dedicated runtime so Pumpkin's main startup is never blocked. + plugin.runtime.spawn(async move { + tracing::info!("Initializing JVM in background..."); + + // 1. Initialize JVM let (tx, rx) = oneshot::channel(); - plugin - .command_tx + if let Err(e) = command_tx .send(JvmCommand::Initialize { - j4rs_path: dirs.j4rs, + jassets_path: dirs.jassets.clone(), respond_to: tx, - context: server.clone(), - command_tx: plugin.command_tx.clone(), + context: server_clone.clone(), + runtime_handle, + command_tx: command_tx.clone(), config, }) .await - .map_err(|e| format!("Failed to send command to initialize J4RS: {e}"))?; - rx.await - .map_err(|e| format!("Unable to receive response from J4RS initialization: {e}"))? - .map_err(|e| format!("Failed to initialize all plugins: {e}"))?; - } + { + tracing::error!("Failed to send command to initialize JVM: {e}"); + return; + } - { + match rx.await { + Ok(Ok(())) => tracing::info!("JVM initialization completed successfully"), + Ok(Err(e)) => { + tracing::error!("Failed to initialize JVM: {e}"); + return; + } + Err(e) => { + tracing::error!("Unable to receive response from JVM initialization: {e}"); + return; + } + } + + // 2. Instantiate all Java plugins let (tx, rx) = oneshot::channel(); - plugin - .command_tx + if let Err(e) = command_tx .send(JvmCommand::InstantiateAllPlugins { plugins_dir: dirs.plugins.clone(), respond_to: tx, - server: server.clone(), - command_tx: plugin.command_tx.clone(), + server: server_clone.clone(), + command_tx: command_tx.clone(), }) .await - .map_err(|e| format!("Failed to send command to instantiate plugins: {e}"))?; - rx.await - .map_err(|e| format!("Unable to receive response from instantiate plugins: {e}"))? - .map_err(|e| format!("Failed to instantiate all plugins: {e}"))?; - } + { + tracing::error!("Failed to send command to instantiate plugins: {e}"); + return; + } - { + match rx.await { + Ok(Ok(())) => tracing::info!("Java plugins instantiated successfully"), + Ok(Err(e)) => { + tracing::error!("Failed to instantiate Java plugins: {e}"); + return; + } + Err(e) => { + tracing::error!("Unable to receive response from instantiate plugins: {e}"); + return; + } + } + + // 3. Enable all Java plugins let (tx, rx) = oneshot::channel(); - plugin - .command_tx + if let Err(e) = command_tx .send(JvmCommand::EnableAllPlugins { respond_to: tx }) .await - .map_err(|e| format!("Failed to send command to enable all plugins: {e}"))?; - rx.await - .map_err(|e| format!("Unable to receive response from enable all plugins: {e}"))? - .map_err(|e| format!("Failed to enable all plugins: {e}"))?; - }; + { + tracing::error!("Failed to send command to enable all plugins: {e}"); + return; + } - // Register Bukkit plugin loader with Pumpkin so Bukkit/Paper plugins appear in Pumpkin's native /plugins command - let bukkit_loader = Arc::new(crate::java::plugin::loader::BukkitPluginLoader); - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .map_err(|e| format!("Failed to build Tokio runtime: {e}"))?; - - let server_ref = server.clone(); - let jar_paths: Vec<_> = discover_jar_files(&dirs.plugins).collect(); - rt.block_on(async move { - server_ref - .plugin_manager - .add_loader(&server_ref.server, bukkit_loader) - .await; - for jar_path in &jar_paths { - let _ = server_ref - .plugin_manager - .try_load_plugin(&server_ref.server, jar_path) - .await; + match rx.await { + Ok(Ok(())) => tracing::info!("PatchBukkit background initialization complete"), + Ok(Err(e)) => tracing::error!("Failed to enable all plugins: {e}"), + Err(e) => tracing::error!("Unable to receive response from enable all plugins: {e}"), } }); + tracing::info!("PatchBukkit loaded successfully"); Ok(()) } @@ -155,25 +165,36 @@ async fn on_unload(&self, server: Arc) -> Result<(), String> { #[plugin_impl] pub struct PatchBukkitPlugin { pub command_tx: mpsc::Sender, + pub runtime: Arc, } impl PatchBukkitPlugin { #[must_use] pub fn new() -> Self { - let (tx, rx) = mpsc::channel(100); + let runtime = Arc::new( + tokio::runtime::Builder::new_multi_thread() + .worker_threads(4) + .thread_name("patchbukkit-worker") + .enable_all() + .build() + .expect("Failed to create PatchBukkit Tokio runtime"), + ); - #[tokio::main] - pub async fn jvm_thread_task(rx: Receiver) { - JvmWorker::new(rx).attach_thread().await; - } + let (tx, rx) = mpsc::channel(4096); + let runtime_clone = runtime.clone(); std::thread::Builder::new() .name("patchbukkit-jvm-worker".to_string()) .spawn(move || { - jvm_thread_task(rx); + runtime_clone.block_on(async move { + JvmWorker::new(rx).attach_thread().await; + }); }) .unwrap(); - Self { command_tx: tx } + Self { + command_tx: tx, + runtime, + } } }