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 {