Skip to content

Commit

Permalink
Release 1.1.25
Browse files Browse the repository at this point in the history
EventManagerHook is now a Listener and is not extended from EventManager
More BlockEntityVersions (fixes #157)
Gradle updated
  • Loading branch information
hevav committed Jul 14, 2024
1 parent fab2948 commit c0b748a
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 136 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Test server: [``ely.su``](https://hotmc.ru/minecraft-server-203216)
<dependency>
<groupId>net.elytrium.limboapi</groupId>
<artifactId>api</artifactId>
<version>1.1.24</version>
<version>1.1.25</version>
<scope>provided</scope>
</dependency>
</dependencies>
Expand All @@ -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")
}
```

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.24
1.1.25
Original file line number Diff line number Diff line change
Expand Up @@ -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<ProtocolVersion, BlockEntityVersion> MC_VERSION_TO_ITEM_VERSIONS = new EnumMap<>(ProtocolVersion.class);

Expand Down Expand Up @@ -48,6 +56,14 @@ public Set<ProtocolVersion> 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;
};
}
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
12 changes: 9 additions & 3 deletions plugin/src/main/java/net/elytrium/limboapi/LimboAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.")
Expand All @@ -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));

Expand Down Expand Up @@ -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);
Expand Down
3 changes: 0 additions & 3 deletions plugin/src/main/java/net/elytrium/limboapi/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> 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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<GameProfile> 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 <E> CompletableFuture<E> fire(E event) {
CompletableFuture<E> 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 <E> CompletableFuture<E> proxyHook(E event) {
if (event instanceof GameProfileRequestEvent) {
GameProfile originalProfile = ((GameProfileRequestEvent) event).getGameProfile();
if (this.proceededProfiles.remove(originalProfile)) {
return null;
} else {
CompletableFuture<E> fireFuture = new CompletableFuture<>();
CompletableFuture<E> 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<GameProfileRequestEvent> fireFuture = new CompletableFuture<>();
CompletableFuture<GameProfileRequestEvent> 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<E> 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<KickedFromServerEvent, Boolean> callback = this.plugin.getKickCallback(kicked.getPlayer());
if (callback == null || !callback.apply(kicked)) {
hookFuture.complete(event);
CompletableFuture<GameProfileRequestEvent> future = (CompletableFuture<GameProfileRequestEvent>) 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<KickedFromServerEvent> hookFuture = new CompletableFuture<>();
try {
Function<KickedFromServerEvent, Boolean> 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) {
Expand All @@ -158,7 +130,7 @@ public void proceedProfile(GameProfile profile) {

@SuppressWarnings("rawtypes")
public void reloadHandlers() throws IllegalAccessException {
ListMultimap<Class<?>, ?> handlersMap = (ListMultimap<Class<?>, ?>) HANDLERS_BY_TYPE_FIELD.get(this);
ListMultimap<Class<?>, ?> handlersMap = (ListMultimap<Class<?>, ?>) HANDLERS_BY_TYPE_FIELD.get(this.eventManager);
List disabledHandlers = handlersMap.get(GameProfileRequestEvent.class);
List preEvents = new ArrayList<>();
List newHandlers = new ArrayList<>(disabledHandlers);
Expand Down Expand Up @@ -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.
Expand All @@ -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();
}
}
Loading

0 comments on commit c0b748a

Please sign in to comment.