diff --git a/README.md b/README.md index 7b071a3d..559505ac 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Test server: [``ely.su``](https://hotmc.ru/minecraft-server-203216) net.elytrium.limboapi api - 1.1.24 + 1.1.25 provided @@ -70,7 +70,7 @@ Test server: [``ely.su``](https://hotmc.ru/minecraft-server-203216) } dependencies { - compileOnly("net.elytrium.limboapi:api:1.1.24") + compileOnly("net.elytrium.limboapi:api:1.1.25") } ``` diff --git a/VERSION b/VERSION index 3fe3e58a..0bfff3dc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.24 +1.1.25 diff --git a/api/src/main/java/net/elytrium/limboapi/api/chunk/BlockEntityVersion.java b/api/src/main/java/net/elytrium/limboapi/api/chunk/BlockEntityVersion.java index 27c6351f..986c6797 100644 --- a/api/src/main/java/net/elytrium/limboapi/api/chunk/BlockEntityVersion.java +++ b/api/src/main/java/net/elytrium/limboapi/api/chunk/BlockEntityVersion.java @@ -15,7 +15,15 @@ public enum BlockEntityVersion { LEGACY(EnumSet.range(ProtocolVersion.MINECRAFT_1_7_2, ProtocolVersion.MINECRAFT_1_18_2)), - MINECRAFT_1_19(EnumSet.range(ProtocolVersion.MINECRAFT_1_19, ProtocolVersion.MAXIMUM_VERSION)); + MINECRAFT_1_19(EnumSet.of(ProtocolVersion.MINECRAFT_1_19)), + MINECRAFT_1_19_1(EnumSet.of(ProtocolVersion.MINECRAFT_1_19_1)), + MINECRAFT_1_19_3(EnumSet.of(ProtocolVersion.MINECRAFT_1_19_3)), + MINECRAFT_1_19_4(EnumSet.of(ProtocolVersion.MINECRAFT_1_19_4)), + MINECRAFT_1_20(EnumSet.of(ProtocolVersion.MINECRAFT_1_20)), + MINECRAFT_1_20_2(EnumSet.of(ProtocolVersion.MINECRAFT_1_20_2)), + MINECRAFT_1_20_3(EnumSet.of(ProtocolVersion.MINECRAFT_1_20_3)), + MINECRAFT_1_20_5(EnumSet.of(ProtocolVersion.MINECRAFT_1_20_5)), + MINECRAFT_1_21(EnumSet.of(ProtocolVersion.MINECRAFT_1_21)); private static final EnumMap MC_VERSION_TO_ITEM_VERSIONS = new EnumMap<>(ProtocolVersion.class); @@ -48,6 +56,14 @@ public Set getVersions() { public static BlockEntityVersion parse(String from) { return switch (from) { case "1.19" -> MINECRAFT_1_19; + case "1.19.1" -> MINECRAFT_1_19_1; + case "1.19.3" -> MINECRAFT_1_19_3; + case "1.19.4" -> MINECRAFT_1_19_4; + case "1.20" -> MINECRAFT_1_20; + case "1.20.2" -> MINECRAFT_1_20_2; + case "1.20.3" -> MINECRAFT_1_20_3; + case "1.20.5" -> MINECRAFT_1_20_5; + case "1.21" -> MINECRAFT_1_21; default -> LEGACY; }; } diff --git a/build.gradle b/build.gradle index a2d3909d..0487d2d2 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ allprojects() { apply(plugin: "org.cadixdev.licenser") setGroup("net.elytrium.limboapi") - setVersion("1.1.24") + setVersion("1.1.25") compileJava() { sourceCompatibility = JavaVersion.VERSION_17 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a5952066..48c0a02c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/plugin/build.gradle b/plugin/build.gradle index 8ed2e775..28a3c7d6 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -9,7 +9,7 @@ buildscript() { plugins() { id("java") - id("com.github.johnrengelman.shadow").version("7.1.2") + id("io.github.goooler.shadow").version("8.1.8") } compileJava() { diff --git a/plugin/src/main/java/net/elytrium/limboapi/LimboAPI.java b/plugin/src/main/java/net/elytrium/limboapi/LimboAPI.java index 713ced5b..ecfddd20 100644 --- a/plugin/src/main/java/net/elytrium/limboapi/LimboAPI.java +++ b/plugin/src/main/java/net/elytrium/limboapi/LimboAPI.java @@ -155,6 +155,7 @@ public class LimboAPI implements LimboFactory { private ProtocolVersion maxVersion; private LoginListener loginListener; private boolean compressionEnabled; + private EventManagerHook eventManagerHook; @Inject public LimboAPI(Logger logger, ProxyServer server, Metrics.Factory metricsFactory, @DataDirectory Path dataDirectory) { @@ -186,9 +187,8 @@ public LimboAPI(Logger logger, ProxyServer server, Metrics.Factory metricsFactor SimpleBlockEntity.init(); SimpleItem.init(); SimpleTagManager.init(); - LOGGER.info("Hooking into EventManager, PlayerList/UpsertPlayerInfo and StateRegistry..."); + LOGGER.info("Hooking into PlayerList/UpsertPlayerInfo and StateRegistry..."); try { - EventManagerHook.init(this); LegacyPlayerListItemHook.init(this, LimboProtocol.PLAY_CLIENTBOUND_REGISTRY); UpsertPlayerInfoHook.init(this, LimboProtocol.PLAY_CLIENTBOUND_REGISTRY); RemovePlayerInfoHook.init(this, LimboProtocol.PLAY_CLIENTBOUND_REGISTRY); @@ -268,7 +268,7 @@ public void onProxyInitialization(ProxyInitializeEvent event) { @Subscribe(order = PostOrder.LAST) public void postProxyInitialization(ProxyInitializeEvent event) throws IllegalAccessException { - ((EventManagerHook) this.server.getEventManager()).reloadHandlers(); + this.eventManagerHook.reloadHandlers(); } @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH", justification = "LEGACY_AMPERSAND can't be null in velocity.") @@ -286,9 +286,11 @@ public void reload() { this.reloadVersion(); this.packets.createPackets(); this.loginListener = new LoginListener(this, this.server); + this.eventManagerHook = new EventManagerHook(this, this.server.getEventManager()); VelocityEventManager eventManager = this.server.getEventManager(); eventManager.unregisterListeners(this); eventManager.register(this, this.loginListener); + eventManager.register(this, this.eventManagerHook); eventManager.register(this, new DisconnectListener(this)); eventManager.register(this, new ReloadListener(this)); @@ -632,6 +634,10 @@ public ProtocolVersion getPrepareMaxVersion() { return this.maxVersion; } + public EventManagerHook getEventManagerHook() { + return this.eventManagerHook; + } + @Override public WorldFile openWorldFile(BuiltInWorldFileType apiType, Path file) throws IOException { return WorldFileTypeRegistry.fromApiType(apiType, file); diff --git a/plugin/src/main/java/net/elytrium/limboapi/Settings.java b/plugin/src/main/java/net/elytrium/limboapi/Settings.java index 2c3eb493..f5ac0c10 100644 --- a/plugin/src/main/java/net/elytrium/limboapi/Settings.java +++ b/plugin/src/main/java/net/elytrium/limboapi/Settings.java @@ -87,9 +87,6 @@ public static class MAIN { @Comment("Helpful if you want some plugins proceed before LimboAPI. For example, it is needed to Floodgate to replace UUID.") public List PRE_LIMBO_PROFILE_REQUEST_PLUGINS = List.of("floodgate", "geyser"); - @Comment("Regenerates listeners that need to proceed before LimboAPI on each EventManager#register call.") - public boolean AUTO_REGENERATE_LISTENERS = false; - @Comment("Should reduced debug info be enabled (reduced information in F3) if there is no preference for Limbo") public boolean REDUCED_DEBUG_INFO = false; diff --git a/plugin/src/main/java/net/elytrium/limboapi/injection/event/EventManagerHook.java b/plugin/src/main/java/net/elytrium/limboapi/injection/event/EventManagerHook.java index 9e64b517..79136253 100644 --- a/plugin/src/main/java/net/elytrium/limboapi/injection/event/EventManagerHook.java +++ b/plugin/src/main/java/net/elytrium/limboapi/injection/event/EventManagerHook.java @@ -18,15 +18,13 @@ package net.elytrium.limboapi.injection.event; import com.google.common.collect.ListMultimap; -import com.velocitypowered.api.event.EventManager; +import com.velocitypowered.api.event.EventTask; +import com.velocitypowered.api.event.PostOrder; +import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.player.GameProfileRequestEvent; import com.velocitypowered.api.event.player.KickedFromServerEvent; import com.velocitypowered.api.plugin.PluginContainer; -import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.util.GameProfile; -import com.velocitypowered.proxy.VelocityServer; -import com.velocitypowered.proxy.command.VelocityCommandManager; -import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.event.VelocityEventManager; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -46,110 +44,84 @@ import net.elytrium.limboapi.Settings; @SuppressWarnings("unchecked") -public class EventManagerHook extends VelocityEventManager { +public class EventManagerHook { private static final Field HANDLERS_BY_TYPE_FIELD; - private static final Field HANDLERS_CACHE_FIELD; - private static final Field HANDLER_ADAPTERS_FIELD; - private static final Field EVENT_TYPE_TRACKER_FIELD; - private static final Field VELOCITY_SERVER_EVENT_MANAGER_FIELD; private static final Class HANDLER_REGISTRATION_CLASS; - private static final Field UNTARGETED_METHOD_HANDLERS_FIELD; private static final MethodHandle PLUGIN_FIELD; - private static final Field VELOCITY_COMMAND_MANAGER_EVENT_MANAGER_FIELD; private static final MethodHandle FIRE_METHOD; + private static final MethodHandle FUTURE_FIELD; private final Set proceededProfiles = new HashSet<>(); private final LimboAPI plugin; + private final VelocityEventManager eventManager; private Object handlerRegistrations; private boolean hasHandlerRegistration; - private EventManagerHook(PluginManager pluginManager, LimboAPI plugin) { - super(pluginManager); - + public EventManagerHook(LimboAPI plugin, VelocityEventManager eventManager) { this.plugin = plugin; + this.eventManager = eventManager; } - @Override - public void register(Object plugin, Object listener) { - super.register(plugin, listener); - - if (Settings.IMP.MAIN != null && Settings.IMP.MAIN.AUTO_REGENERATE_LISTENERS) { - try { - this.reloadHandlers(); - } catch (IllegalAccessException e) { - throw new ReflectionException(e); - } - } - } - - @Override - public void fireAndForget(Object event) { - Object toReply = this.proxyHook(event); - if (toReply == null) { - super.fireAndForget(event); - } - } - - @Override - public CompletableFuture fire(E event) { - CompletableFuture toReply = this.proxyHook(event); - if (toReply == null) { - return super.fire(event); + @Subscribe(order = PostOrder.FIRST) + public EventTask onGameProfileRequest(GameProfileRequestEvent event) { + GameProfile originalProfile = event.getGameProfile(); + if (this.proceededProfiles.remove(originalProfile)) { + return null; } else { - return toReply; - } - } - - private CompletableFuture proxyHook(E event) { - if (event instanceof GameProfileRequestEvent) { - GameProfile originalProfile = ((GameProfileRequestEvent) event).getGameProfile(); - if (this.proceededProfiles.remove(originalProfile)) { - return null; - } else { - CompletableFuture fireFuture = new CompletableFuture<>(); - CompletableFuture hookFuture = new CompletableFuture<>(); - fireFuture.thenAccept(modifiedEvent -> { - try { - GameProfileRequestEvent requestEvent = (GameProfileRequestEvent) modifiedEvent; - this.plugin.getLoginListener().hookLoginSession(requestEvent); - hookFuture.complete(modifiedEvent); - } catch (Throwable e) { - throw new ReflectionException(e); - } - }); + CompletableFuture fireFuture = new CompletableFuture<>(); + CompletableFuture hookFuture = new CompletableFuture<>(); + fireFuture.thenAccept(modifiedEvent -> { + try { + this.plugin.getLoginListener().hookLoginSession(modifiedEvent); + hookFuture.complete(modifiedEvent); + } catch (Throwable e) { + throw new ReflectionException(e); + } + }); - if (this.hasHandlerRegistration) { - try { - FIRE_METHOD.invoke(this, fireFuture, event, 0, false, this.handlerRegistrations); - } catch (Throwable e) { - fireFuture.complete(event); - throw new ReflectionException(e); - } - } else { + if (this.hasHandlerRegistration) { + try { + FIRE_METHOD.invoke(this.eventManager, fireFuture, event, 0, false, this.handlerRegistrations); + } catch (Throwable e) { fireFuture.complete(event); + throw new ReflectionException(e); } - - return hookFuture; + } else { + fireFuture.complete(event); } - } else if (event instanceof KickedFromServerEvent kicked) { - CompletableFuture hookFuture = new CompletableFuture<>(); - super.fire(kicked).thenRunAsync(() -> { + + // ignoring other subscribers by directly completing the future + return EventTask.withContinuation(continuation -> hookFuture.whenComplete((result, cause) -> { try { - Function callback = this.plugin.getKickCallback(kicked.getPlayer()); - if (callback == null || !callback.apply(kicked)) { - hookFuture.complete(event); + CompletableFuture future = (CompletableFuture) FUTURE_FIELD.invokeExact(continuation); + if (future != null) { + future.complete(result); } - } catch (Throwable throwable) { - LimboAPI.getLogger().error("Failed to handle KickCallback, ignoring its result", throwable); - hookFuture.complete(event); + } catch (Throwable e) { + throw new ReflectionException(e); } - }, ((ConnectedPlayer) kicked.getPlayer()).getConnection().eventLoop()); - return hookFuture; - } else { - return null; + })); + } + } + + @Subscribe(order = PostOrder.LAST) + public EventTask onKickedFromServer(KickedFromServerEvent event) { + CompletableFuture hookFuture = new CompletableFuture<>(); + try { + Function callback = this.plugin.getKickCallback(event.getPlayer()); + if (callback == null || !callback.apply(event)) { + hookFuture.complete(event); + } + } catch (Throwable throwable) { + LimboAPI.getLogger().error("Failed to handle KickCallback, ignoring its result", throwable); + hookFuture.complete(event); } + + // if kick callback is null and no exception occurred, hookFuture won't be ever finished, and + // the event chain would be broken, that is what we need. + return EventTask.resumeWhenComplete(hookFuture); } public void proceedProfile(GameProfile profile) { @@ -158,7 +130,7 @@ public void proceedProfile(GameProfile profile) { @SuppressWarnings("rawtypes") public void reloadHandlers() throws IllegalAccessException { - ListMultimap, ?> handlersMap = (ListMultimap, ?>) HANDLERS_BY_TYPE_FIELD.get(this); + ListMultimap, ?> handlersMap = (ListMultimap, ?>) HANDLERS_BY_TYPE_FIELD.get(this.eventManager); List disabledHandlers = handlersMap.get(GameProfileRequestEvent.class); List preEvents = new ArrayList<>(); List newHandlers = new ArrayList<>(disabledHandlers); @@ -198,27 +170,13 @@ public void reloadHandlers() throws IllegalAccessException { HANDLERS_BY_TYPE_FIELD = VelocityEventManager.class.getDeclaredField("handlersByType"); HANDLERS_BY_TYPE_FIELD.setAccessible(true); - HANDLERS_CACHE_FIELD = VelocityEventManager.class.getDeclaredField("handlersCache"); - HANDLERS_CACHE_FIELD.setAccessible(true); - - HANDLER_ADAPTERS_FIELD = VelocityEventManager.class.getDeclaredField("handlerAdapters"); - HANDLER_ADAPTERS_FIELD.setAccessible(true); - - VELOCITY_SERVER_EVENT_MANAGER_FIELD = VelocityServer.class.getDeclaredField("eventManager"); - VELOCITY_SERVER_EVENT_MANAGER_FIELD.setAccessible(true); - - UNTARGETED_METHOD_HANDLERS_FIELD = VelocityEventManager.class.getDeclaredField("untargetedMethodHandlers"); - UNTARGETED_METHOD_HANDLERS_FIELD.setAccessible(true); - - EVENT_TYPE_TRACKER_FIELD = VelocityEventManager.class.getDeclaredField("eventTypeTracker"); - EVENT_TYPE_TRACKER_FIELD.setAccessible(true); - HANDLER_REGISTRATION_CLASS = Class.forName("com.velocitypowered.proxy.event.VelocityEventManager$HandlerRegistration"); PLUGIN_FIELD = MethodHandles.privateLookupIn(HANDLER_REGISTRATION_CLASS, MethodHandles.lookup()) .findGetter(HANDLER_REGISTRATION_CLASS, "plugin", PluginContainer.class); - VELOCITY_COMMAND_MANAGER_EVENT_MANAGER_FIELD = VelocityCommandManager.class.getDeclaredField("eventManager"); - VELOCITY_COMMAND_MANAGER_EVENT_MANAGER_FIELD.setAccessible(true); + Class continuationTaskClass = Class.forName("com.velocitypowered.proxy.event.VelocityEventManager$ContinuationTask"); + FUTURE_FIELD = MethodHandles.privateLookupIn(continuationTaskClass, MethodHandles.lookup()) + .findGetter(continuationTaskClass, "future", CompletableFuture.class); // The desired 5-argument fire method is private, and its 5th argument is the array of the private class, // so we can't pass it into the Class#getDeclaredMethod(Class...) method. @@ -240,20 +198,4 @@ public void reloadHandlers() throws IllegalAccessException { throw new ReflectionException(e); } } - - public static void init(LimboAPI plugin) throws ReflectiveOperationException, InterruptedException { - VelocityServer server = plugin.getServer(); - EventManager newEventManager = new EventManagerHook(server.getPluginManager(), plugin); - VelocityEventManager oldEventManager = server.getEventManager(); - HANDLERS_BY_TYPE_FIELD.set(newEventManager, HANDLERS_BY_TYPE_FIELD.get(oldEventManager)); - HANDLERS_CACHE_FIELD.set(newEventManager, HANDLERS_CACHE_FIELD.get(oldEventManager)); - UNTARGETED_METHOD_HANDLERS_FIELD.set(newEventManager, UNTARGETED_METHOD_HANDLERS_FIELD.get(oldEventManager)); - HANDLER_ADAPTERS_FIELD.set(newEventManager, HANDLER_ADAPTERS_FIELD.get(oldEventManager)); - EVENT_TYPE_TRACKER_FIELD.set(newEventManager, EVENT_TYPE_TRACKER_FIELD.get(oldEventManager)); - - VELOCITY_SERVER_EVENT_MANAGER_FIELD.set(server, newEventManager); - VELOCITY_COMMAND_MANAGER_EVENT_MANAGER_FIELD.set(server.getCommandManager(), newEventManager); - - oldEventManager.shutdown(); - } } diff --git a/plugin/src/main/java/net/elytrium/limboapi/injection/login/LoginTasksQueue.java b/plugin/src/main/java/net/elytrium/limboapi/injection/login/LoginTasksQueue.java index b0aca061..5423e0f3 100644 --- a/plugin/src/main/java/net/elytrium/limboapi/injection/login/LoginTasksQueue.java +++ b/plugin/src/main/java/net/elytrium/limboapi/injection/login/LoginTasksQueue.java @@ -34,6 +34,7 @@ package net.elytrium.limboapi.injection.login; +import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.LoginEvent; import com.velocitypowered.api.event.connection.PostLoginEvent; @@ -72,7 +73,6 @@ import java.util.concurrent.CompletableFuture; import net.elytrium.commons.utils.reflection.ReflectionException; import net.elytrium.limboapi.LimboAPI; -import net.elytrium.limboapi.injection.event.EventManagerHook; import net.elytrium.limboapi.injection.login.confirmation.LoginConfirmHandler; import net.elytrium.limboapi.server.LimboSessionHandlerImpl; import net.kyori.adventure.text.Component; @@ -120,15 +120,14 @@ public void next() { } } - @SuppressWarnings("deprecation") private void finish() { this.plugin.removeLoginQueue(this.player); - EventManagerHook eventManager = (EventManagerHook) this.server.getEventManager(); + EventManager eventManager = this.server.getEventManager(); MinecraftConnection connection = this.player.getConnection(); Logger logger = LimboAPI.getLogger(); - eventManager.proceedProfile(this.player.getGameProfile()); + this.plugin.getEventManagerHook().proceedProfile(this.player.getGameProfile()); eventManager.fire(new GameProfileRequestEvent(this.inbound, this.player.getGameProfile(), this.player.isOnlineMode())).thenAcceptAsync( gameProfile -> { try {