Fix Neoforge

This commit is contained in:
s809
2024-04-12 22:38:21 +05:00
parent e8e56b66a3
commit 220ac89ad8
20 changed files with 188 additions and 183 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
</option>
<option name="taskNames">
<list>
<option value="neoforged:runClient" />
<option value="neoforge:runClient" />
</list>
</option>
<option name="vmOptions" />
+1 -1
View File
@@ -10,7 +10,7 @@
</option>
<option name="taskNames">
<list>
<option value="neoforged:runServer" />
<option value="neoforge:runServer" />
</list>
</option>
<option name="vmOptions" />
@@ -325,12 +325,6 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
return this.mc.gameDirectory;
}
@Override
public List<IServerPlayerWrapper> getPlayerList()
{
return null;
}
@Override
public void executeOnRenderThread(Runnable runnable) { this.mc.execute(runnable); }
@@ -30,12 +30,4 @@ public class MinecraftDedicatedServerWrapper implements IMinecraftSharedWrapper
return this.dedicatedServer.getServerDirectory();
}
@Override
public List<IServerPlayerWrapper> getPlayerList()
{
return this.dedicatedServer.getPlayerList().getPlayers().stream()
.map(serverPlayer -> (IServerPlayerWrapper) ServerPlayerWrapper.getWrapper(serverPlayer))
.collect(Collectors.toList());
}
}
@@ -14,6 +14,7 @@ import java.util.function.Consumer;
public abstract class AbstractPluginPacketSender implements IPluginPacketSender
{
public static final ResourceLocation PLUGIN_CHANNEL_RESOURCE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.PLUGIN_CHANNEL_PATH);
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
@Override
@@ -29,7 +29,6 @@ import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.block.cache.ServerBlockDetailMap;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
@@ -54,10 +54,7 @@ import net.minecraft.client.gui.screens.TitleScreen;
import java.nio.FloatBuffer;
#endif
import java.util.HashSet;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.network.FriendlyByteBuf;
@@ -7,7 +7,6 @@ import com.seibel.distanthorizons.common.wrappers.network.AbstractPluginPacketSe
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
@@ -0,0 +1,8 @@
package com.seibel.distanthorizons.fabric.mixins.mods.examplemixinintomod;
public class ExampleMixin
{
// This isnt a real mixin, but if a mixin into a mod were to be created it would be like this
// The folder that this mixin is in should be the mod id so that our mixin plugin applies it only when that mod is detected
// Other than that, everything else should be the same as a normal mixin
}
@@ -1,73 +0,0 @@
package com.seibel.distanthorizons.fabric.mixins.mods.imm_ptl_core;
// TODO: Fix this eventually
/*
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Vector3f;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IImmersivePortalsAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
import com.seibel.distanthorizons.fabric.wrappers.modAccessor.ImmersivePortalsAccessor;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import qouteall.imm_ptl.core.render.MyGameRenderer;
import qouteall.imm_ptl.core.render.context_management.WorldRenderInfo;
import java.util.function.Consumer;
@Mixin(MyGameRenderer.class)
public class MixinImmersivePortalsGameRenderer {
@Shadow public static Minecraft client;
// @Unique
// static ImmersivePortalsAccessor accessor = null;
// TODO: Find a way to inject just before line 190 in the immersive portals code
// This current place that we are injecting into is not correct
@Inject(remap = false,
method = "switchAndRenderTheWorld",
at = @At(value = "INVOKE", target = "Ljava/util/function/Consumer;accept(Ljava/lang/Object;)V", shift = At.Shift.AFTER))
private static void injectDHLoDRendering(ClientLevel newWorld, Vec3 thisTickCameraPos, Vec3 lastTickCameraPos, Consumer<Runnable> invokeWrapper, int renderDistance, boolean doRenderHand, CallbackInfo ci) {
// TODO: Is there a better way to do this?
if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("sodium"))
return;
// TODO: Move this out of the function to not run it every frame
ImmersivePortalsAccessor accessor = null;
if (accessor == null) {
accessor = (ImmersivePortalsAccessor) ModAccessorInjector.INSTANCE.get(IImmersivePortalsAccessor.class);
}
// System.out.println(McObjectConverter.Convert(RenderSystem.getProjectionMatrix()).toString());
Mat4f modelViewMatrix = McObjectConverter.Convert(RenderSystem.getModelViewMatrix());
Mat4f projectionMatrix = McObjectConverter.Convert(client.gameRenderer.getProjectionMatrix(client.options.fov));
Vector3f vanillaLookVector = client.gameRenderer.getMainCamera().getLookVector();
// client.getCameraEntity()
// System.out.println(client.gameRenderer.getMainCamera().getLookVector());
ClientApi.INSTANCE.renderLods(
ClientLevelWrapper.getWrapper(client.level),
modelViewMatrix,
projectionMatrix,
accessor.partialTicks
);
}
}
*/
@@ -1,15 +0,0 @@
package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
// TODO: Fix Immersive Portals compat
//import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IImmersivePortalsAccessor;
//
//public class ImmersivePortalsAccessor implements IImmersivePortalsAccessor
//{
// @Override
// public String getModName()
// {
// return "ImmersivePortals-Fabric";
// }
//
// public float partialTicks;
//}
@@ -6,9 +6,7 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.resources.ResourceKey;
@@ -2,8 +2,10 @@ package com.seibel.distanthorizons.forge.mixins.client;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -11,10 +13,16 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientPacketListener.class)
public class MixinClientPacketListener
{
@Shadow
private ClientLevel level;
// TODO update fabric version as well
@Inject(method = "handleLogin", at = @At("RETURN"))
void onHandleLoginEnd(CallbackInfo ci) { ClientApi.INSTANCE.onClientOnlyConnected(); }
void onHandleLoginEnd(CallbackInfo ci)
{
ClientApi.INSTANCE.onClientOnlyConnected();
ClientApi.INSTANCE.clientLevelLoadEvent(ClientLevelWrapper.getWrapper(this.level));
}
#if MC_VER < MC_1_19_4
@Inject(method = "cleanup", at = @At("HEAD"))
+2 -2
View File
@@ -53,14 +53,14 @@ loom {
client()
setConfigName("NeoForge Client")
ideConfigGenerated(false)
runDir("../run")
runDir("../run/client")
// vmArgs("-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg)
}
server {
server()
setConfigName("NeoForge Server")
ideConfigGenerated(false)
runDir("../run")
runDir("../run/server")
}
}
}
@@ -32,8 +32,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
//import io.netty.buffer.ByteBuf;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.client.multiplayer.ClientLevel;
@@ -45,8 +43,6 @@ import net.neoforged.neoforge.event.level.LevelEvent;
import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent;
import net.minecraft.world.level.chunk.ChunkAccess;
//import net.neoforged.network.NetworkRegistry;
//import net.neoforged.network.simple.SimpleChannel;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
@@ -81,7 +77,6 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
public void registerEvents()
{
NeoForge.EVENT_BUS.register(this);
setupNetworkingListeners();
}
@@ -218,65 +213,6 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
//============//
// networking //
//============//
public static void setupNetworkingListeners()
{
// multiversePluginChannel = NetworkRegistry.newSimpleChannel(
// new ResourceLocation(ModInfo.NETWORKING_RESOURCE_NAMESPACE, ModInfo.MULTIVERSE_PLUGIN_NAMESPACE),
// // network protocol version
// () -> ModInfo.MULTIVERSE_PLUGIN_PROTOCOL_VERSION +"",
// // client accepted versions
// ForgeClientProxy::isReceivedProtocolVersionAcceptable,
// // server accepted versions
// ForgeClientProxy::isReceivedProtocolVersionAcceptable
// );
//
// multiversePluginChannel.registerMessage(0/*should be incremented for each simple channel we listen to*/, ByteBuf.class,
// // encoder
// (pack, friendlyByteBuf) -> { },
// // decoder
// (friendlyByteBuf) -> friendlyByteBuf.asByteBuf(),
// // message consumer
// (nettyByteBuf, contextRef) ->
// {
// ClientApi.INSTANCE.serverMessageReceived(nettyByteBuf);
// contextRef.get().setPacketHandled(true);
// }
// );
}
public static boolean isReceivedProtocolVersionAcceptable(String versionString)
{
if (versionString.toLowerCase().contains("allowvanilla"))
{
// allow using networking on vanilla servers
return true;
}
else if (versionString.toLowerCase().contains("absent"))
{
// allow using networking even if DH isn't installed on the server
return true;
}
else
{
// DH is installed on the server, check if the version is valid to use
try
{
int version = Integer.parseInt(versionString);
return ModInfo.PLUGIN_PROTOCOL_VERSION == version;
}
catch (NumberFormatException ignored)
{
return false;
}
}
}
//===========//
// rendering //
//===========//
@@ -22,7 +22,10 @@ package com.seibel.distanthorizons.neoforge;
import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.distanthorizons.coreapi.ModInfo;
@@ -39,6 +42,7 @@ import net.neoforged.neoforge.client.ConfigScreenHandler;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.RegisterCommandsEvent;
import net.neoforged.neoforge.event.server.ServerStartingEvent;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlerEvent;
import java.util.function.Consumer;
@@ -53,15 +57,41 @@ public class NeoforgeMain extends AbstractModInitializer
{
public NeoforgeMain(IEventBus eventBus)
{
eventBus.addListener((FMLClientSetupEvent e) -> this.onInitializeClient());
eventBus.addListener((FMLDedicatedServerSetupEvent e) -> this.onInitializeServer());
eventBus.addListener((FMLClientSetupEvent e) -> {
this.onInitializeClient();
eventBus.addListener(this::registerNetworkingClient);
});
eventBus.addListener((FMLDedicatedServerSetupEvent e) -> {
this.onInitializeServer();
eventBus.addListener(this::registerNetworkingServer);
});
}
//============//
// networking //
//============//
public void registerNetworkingClient(RegisterPayloadHandlerEvent event)
{
NeoforgePluginPacketSender.setPacketHandler(event, ClientApi.INSTANCE::pluginMessageReceived);
}
public void registerNetworkingServer(RegisterPayloadHandlerEvent event)
{
NeoforgePluginPacketSender.setPacketHandler(event, ServerApi.INSTANCE::pluginMessageReceived);
}
@Override
protected IEventProxy createServerProxy(boolean isDedicated) { return new NeoforgeServerProxy(isDedicated); }
@Override
protected void createInitialBindings() { SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE); }
protected void createInitialBindings()
{
SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE);
SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new NeoforgePluginPacketSender());
}
@Override
protected IEventProxy createClientProxy() { return new NeoforgeClientProxy(); }
@@ -0,0 +1,88 @@
package com.seibel.distanthorizons.neoforge;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.common.wrappers.network.AbstractPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.neoforged.neoforge.network.PacketDistributor;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlerEvent;
import net.neoforged.neoforge.network.handling.PlayPayloadContext;
import net.neoforged.neoforge.network.registration.IPayloadRegistrar;
import org.jetbrains.annotations.NotNull;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class NeoforgePluginPacketSender extends AbstractPluginPacketSender
{
private static BiConsumer<IServerPlayerWrapper, ByteBuf> packetConsumer;
public static void setPacketHandler(RegisterPayloadHandlerEvent event, Consumer<ByteBuf> consumer)
{
setPacketHandler(event, (player, buffer) -> consumer.accept(buffer));
}
public static void setPacketHandler(RegisterPayloadHandlerEvent event, BiConsumer<IServerPlayerWrapper, ByteBuf> consumer)
{
packetConsumer = consumer;
IPayloadRegistrar registrar = event.registrar(ModInfo.RESOURCE_NAMESPACE);
registrar.play(PacketWrapperPayload.ID, PacketWrapperPayload::createFrom, handler -> handler
.client(NeoforgePluginPacketSender::handlePacket)
.server(NeoforgePluginPacketSender::handlePacket))
.optional();
}
public static void handlePacket(PacketWrapperPayload data, PlayPayloadContext context)
{
ServerPlayerWrapper serverPlayer = context.player()
.map(player -> player instanceof ServerPlayer ? (ServerPlayer) player : null)
.map(ServerPlayerWrapper::getWrapper)
.orElse(null);
packetConsumer.accept(serverPlayer, data.buffer);
}
@Override
protected void sendPluginPacketClient(FriendlyByteBuf buffer)
{
PacketDistributor.SERVER.noArg().send(new PacketWrapperPayload(buffer));
}
@Override
protected void sendPluginPacketServer(ServerPlayer serverPlayer, FriendlyByteBuf buffer)
{
PacketDistributor.PLAYER.with(serverPlayer).send(new PacketWrapperPayload(buffer));
}
public record PacketWrapperPayload(
ByteBuf buffer
) implements CustomPacketPayload
{
public static final ResourceLocation ID = AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE;
public static PacketWrapperPayload createFrom(FriendlyByteBuf buffer)
{
return new PacketWrapperPayload(buffer.readBytes(buffer.readableBytes()));
}
@Override
@NotNull
public ResourceLocation id()
{
return ID;
}
@Override
public void write(FriendlyByteBuf out)
{
this.buffer.resetReaderIndex();
out.writeBytes(this.buffer);
}
}
}
@@ -3,16 +3,21 @@ package com.seibel.distanthorizons.neoforge;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.util.ProxyUtil;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.TickEvent;
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
import net.neoforged.neoforge.event.level.ChunkEvent;
import net.neoforged.neoforge.event.level.LevelEvent;
import net.neoforged.bus.api.SubscribeEvent;
@@ -119,6 +124,26 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
this.serverApi.serverChunkSaveEvent(chunk, levelWrapper);
}
@SubscribeEvent
public void playerLoggedInEvent(PlayerEvent.PlayerLoggedInEvent event)
{
this.serverApi.serverPlayerJoinEvent(getServerPlayerWrapper(event));
}
@SubscribeEvent
public void playerLoggedOutEvent(PlayerEvent.PlayerLoggedOutEvent event)
{
this.serverApi.serverPlayerDisconnectEvent(getServerPlayerWrapper(event));
}
@SubscribeEvent
public void playerChangedDimensionEvent(PlayerEvent.PlayerChangedDimensionEvent event)
{
this.serverApi.serverPlayerLevelChangeEvent(
getServerPlayerWrapper(event),
getServerLevelWrapper(event.getFrom(), event),
getServerLevelWrapper(event.getTo(), event)
);
}
//================//
@@ -126,6 +151,15 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
//================//
private static ServerLevelWrapper getServerLevelWrapper(ServerLevel level) { return ServerLevelWrapper.getWrapper(level); }
private static ServerLevelWrapper getServerLevelWrapper(ResourceKey<Level> resourceKey, PlayerEvent event)
{
//noinspection DataFlowIssue (possible NPE after getServer())
return getServerLevelWrapper(event.getEntity().getServer().getLevel(resourceKey));
}
private static ServerPlayerWrapper getServerPlayerWrapper(PlayerEvent event)
{
return ServerPlayerWrapper.getWrapper((ServerPlayer) event.getEntity());
}
}
@@ -1,8 +1,11 @@
package com.seibel.distanthorizons.neoforge.mixins.client;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -10,10 +13,16 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientPacketListener.class)
public class MixinClientPacketListener
{
@Shadow
private ClientLevel level;
// TODO update fabric version as well
@Inject(method = "handleLogin", at = @At("RETURN"))
void onHandleLoginEnd(CallbackInfo ci) { ClientApi.INSTANCE.onClientOnlyConnected(); }
void onHandleLoginEnd(CallbackInfo ci)
{
ClientApi.INSTANCE.onClientOnlyConnected();
ClientApi.INSTANCE.clientLevelLoadEvent(ClientLevelWrapper.getWrapper(this.level));
}
#if MC_VER < MC_1_19_4
@Inject(method = "cleanup", at = @At("HEAD"))