Fix Forge client-side rendering
This commit is contained in:
+1
-1
Submodule coreSubProjects updated: af5bb351e8...895a0db542
-6
@@ -24,12 +24,6 @@ public class MixinClientPacketListener
|
||||
|
||||
|
||||
|
||||
/** THIS EXPLANATION IS WRITTEN BY FABRIC.
|
||||
* An explanation why we unload entities during onGameJoin: (On in our remapping name case, handleLogin(TODO: CHECK))
|
||||
* Proxies such as Waterfall may send another Game Join packet if entity meta rewrite is disabled, so we will cover ourselves.
|
||||
* Velocity by default will send a Game Join packet when the player changes servers, which will create a new client world.
|
||||
* Also anyone can send another GameJoinPacket at any time, so we need to watch out.
|
||||
*/
|
||||
@Inject(method = "handleLogin", at = @At("HEAD"))
|
||||
void onHandleLoginStart(CallbackInfo ci)
|
||||
{
|
||||
|
||||
@@ -21,14 +21,21 @@ package com.seibel.distanthorizons.forge;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
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.coreapi.ModInfo;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
#if PRE_MC_1_19_2
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.event.world.ChunkEvent;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
#else
|
||||
@@ -36,6 +43,9 @@ import net.minecraftforge.event.level.ChunkEvent;
|
||||
import net.minecraftforge.event.level.LevelEvent;
|
||||
#endif
|
||||
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
import net.minecraftforge.network.NetworkRegistry;
|
||||
import net.minecraftforge.network.simple.SimpleChannel;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
@@ -51,18 +61,28 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
* and is the starting point for most of the mod.
|
||||
*
|
||||
* @author James_Seibel
|
||||
* @version 11-12-2021
|
||||
* @version 2023-7-27
|
||||
*/
|
||||
public class ForgeClientProxy
|
||||
{
|
||||
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
private static SimpleChannel SIMPLE_CHANNEL;
|
||||
|
||||
|
||||
#if PRE_MC_1_19_2
|
||||
private static LevelAccessor GetLevel(WorldEvent e) { return e.getWorld(); }
|
||||
#else
|
||||
private static LevelAccessor GetLevel(LevelEvent e) { return e.getLevel(); }
|
||||
#endif
|
||||
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// tick events //
|
||||
//=============//
|
||||
|
||||
@SubscribeEvent
|
||||
public void clientTickEvent(TickEvent.ClientTickEvent event)
|
||||
{
|
||||
@@ -71,26 +91,79 @@ public class ForgeClientProxy
|
||||
ClientApi.INSTANCE.clientTickEvent();
|
||||
}
|
||||
}
|
||||
|
||||
// ClientLevelLoadEvent - Done in MixinClientPacketListener
|
||||
// @SubscribeEvent
|
||||
// public void clientLevelLoadEvent(WorldEvent.Load event)
|
||||
// {
|
||||
// if (event.getWorld() instanceof ClientLevel)
|
||||
// {
|
||||
// ClientApi.INSTANCE.clientLevelLoadEvent(ClientLevelWrapper.getWrapper((ClientLevel) event.getWorld()));
|
||||
// }
|
||||
// }
|
||||
// ClientLevelUnloadEvent - Done in MixinClientPacketListener
|
||||
// @SubscribeEvent
|
||||
// public void clientLevelUnloadEvent(WorldEvent.Unload event)
|
||||
// {
|
||||
// if (event.getWorld() instanceof ClientLevel)
|
||||
// {
|
||||
// ClientApi.INSTANCE.clientLevelUnloadEvent(ClientLevelWrapper.getWrapper((ClientLevel) event.getWorld()));
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// world events //
|
||||
//==============//
|
||||
|
||||
@SubscribeEvent
|
||||
public void clientLevelLoadEvent(WorldEvent.Load event)
|
||||
{
|
||||
LOGGER.info("level load");
|
||||
|
||||
if (event != null && event.getWorld() != null && event.getWorld() instanceof ClientLevel)
|
||||
{
|
||||
ClientLevel clientLevel = (ClientLevel) event.getWorld();
|
||||
IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel);
|
||||
// TODO this causes a crash due to level being set to null somewhere
|
||||
ClientApi.INSTANCE.clientLevelLoadEvent(clientLevelWrapper);
|
||||
}
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void clientLevelUnloadEvent(WorldEvent.Unload event)
|
||||
{
|
||||
LOGGER.info("level unload");
|
||||
|
||||
if (event != null && event.getWorld() != null && event.getWorld() instanceof ClientLevel)
|
||||
{
|
||||
ClientLevel clientLevel = (ClientLevel) event.getWorld();
|
||||
IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel);
|
||||
ClientApi.INSTANCE.clientLevelUnloadEvent(clientLevelWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// chunk events //
|
||||
//==============//
|
||||
|
||||
@SubscribeEvent
|
||||
public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event)
|
||||
{
|
||||
LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
|
||||
|
||||
LevelAccessor level = event.getWorld();
|
||||
ChunkAccess chunk = level.getChunk(event.getPos());
|
||||
this.onClientBlockChangeEvent(level, chunk);
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event)
|
||||
{
|
||||
LOGGER.trace("break or block attack at blockPos: " + event.getPos());
|
||||
|
||||
LevelAccessor level = event.getWorld();
|
||||
ChunkAccess chunk = level.getChunk(event.getPos());
|
||||
this.onClientBlockChangeEvent(level, chunk);
|
||||
}
|
||||
private void onClientBlockChangeEvent(LevelAccessor level, ChunkAccess chunk)
|
||||
{
|
||||
// TODO rate limit this event per blockPos to prevent spam
|
||||
|
||||
// if we have access to the server, use the chunk save event instead
|
||||
if (MC.clientConnectedToDedicatedServer())
|
||||
{
|
||||
if (chunk != null)
|
||||
{
|
||||
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
|
||||
ClientApi.INSTANCE.clientChunkLoadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public void clientChunkLoadEvent(ChunkEvent.Load event)
|
||||
{
|
||||
@@ -102,7 +175,7 @@ public class ForgeClientProxy
|
||||
}
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void clientChunkSaveEvent(ChunkEvent.Unload event)
|
||||
public void clientChunkUnloadEvent(ChunkEvent.Unload event)
|
||||
{
|
||||
if (GetLevel(event) instanceof ClientLevel)
|
||||
{
|
||||
@@ -111,82 +184,78 @@ public class ForgeClientProxy
|
||||
ClientApi.INSTANCE.clientChunkSaveEvent(chunk, ClientLevelWrapper.getWrapper((ClientLevel) GetLevel(event)));
|
||||
}
|
||||
}
|
||||
|
||||
// RendererStartupEvent - Done in MixinGameRenderer
|
||||
// RendererShutdownEvent - Done in MixinGameRenderer
|
||||
// ClientRenderLevelTerrainEvent - Done in MixinGameRenderer
|
||||
|
||||
// Register KeyBindings
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// key bindings //
|
||||
//==============//
|
||||
|
||||
@SubscribeEvent
|
||||
public void registerKeyBindings(#if PRE_MC_1_19_2 InputEvent.KeyInputEvent #else InputEvent.Key #endif event)
|
||||
{
|
||||
if (Minecraft.getInstance().player == null) return;
|
||||
if (event.getAction() != GLFW.GLFW_PRESS) return;
|
||||
if (Minecraft.getInstance().player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (event.getAction() != GLFW.GLFW_PRESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClientApi.INSTANCE.keyPressedEvent(event.getKey());
|
||||
}
|
||||
|
||||
// @SubscribeEvent
|
||||
// public void serverTickEvent(TickEvent.ServerTickEvent event)
|
||||
// {
|
||||
// if (event.phase != TickEvent.Phase.START) return;
|
||||
// eventApi.serverTickEvent();
|
||||
// }
|
||||
////
|
||||
//
|
||||
// @SubscribeEvent
|
||||
// public void chunkLoadEvent(ChunkEvent.Load event)
|
||||
// {
|
||||
// clientApi.clientChunkLoadEvent(new ChunkWrapper(event.getChunk(), event.getWorld()), LevelWrapper.getWorldWrapper(event.getWorld()));
|
||||
// }
|
||||
//
|
||||
// @SubscribeEvent
|
||||
// public void worldSaveEvent(WorldEvent.Save event)
|
||||
// {
|
||||
// eventApi.worldSaveEvent();
|
||||
// }
|
||||
//
|
||||
// /** This is also called when a new dimension loads */
|
||||
// @SubscribeEvent
|
||||
// public void worldLoadEvent(WorldEvent.Load event)
|
||||
// {
|
||||
// if (Minecraft.getInstance().screen instanceof TitleScreen) return;
|
||||
// if (event.getWorld() != null) {
|
||||
// eventApi.worldLoadEvent(LevelWrapper.getWorldWrapper(event.getWorld()));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @SubscribeEvent
|
||||
// public void worldUnloadEvent(WorldEvent.Unload event)
|
||||
// {
|
||||
// eventApi.worldUnloadEvent(LevelWrapper.getWorldWrapper(event.getWorld()));
|
||||
// }
|
||||
//
|
||||
// @SubscribeEvent
|
||||
// public void blockChangeEvent(BlockEvent event)
|
||||
// {
|
||||
// // we only care about certain block events
|
||||
// if (event.getClass() == BlockEvent.BreakEvent.class ||
|
||||
// event.getClass() == BlockEvent.EntityPlaceEvent.class ||
|
||||
// event.getClass() == BlockEvent.EntityMultiPlaceEvent.class ||
|
||||
// event.getClass() == BlockEvent.FluidPlaceBlockEvent.class ||
|
||||
// event.getClass() == BlockEvent.PortalSpawnEvent.class)
|
||||
// {
|
||||
// IChunkWrapper chunk = new ChunkWrapper(event.getWorld().getChunk(event.getPos()), event.getWorld());
|
||||
// DimensionTypeWrapper dimType = DimensionTypeWrapper.getDimensionTypeWrapper(event.getWorld().dimensionType());
|
||||
//
|
||||
// // recreate the LOD where the blocks were changed
|
||||
// eventApi.blockChangeEvent(chunk, dimType);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @SubscribeEvent
|
||||
// public void onKeyInput(InputEvent.KeyInputEvent event)
|
||||
// {
|
||||
// if (Minecraft.getInstance().player == null) return;
|
||||
// if (event.getAction() != GLFW.GLFW_PRESS) return;
|
||||
// clientApi.keyPressedEvent(event.getKey());
|
||||
// }
|
||||
//
|
||||
|
||||
|
||||
|
||||
//============//
|
||||
// networking //
|
||||
//============//
|
||||
|
||||
/** @param event this is just to ensure the event is called at the right time, if it is called outside the {@link FMLClientSetupEvent} event, the binding may fail */
|
||||
public static void setupNetworkingListeners(FMLClientSetupEvent event)
|
||||
{
|
||||
|
||||
SIMPLE_CHANNEL = NetworkRegistry.newSimpleChannel(
|
||||
new ResourceLocation("distant_horizons", "world_control"), // TODO move to common location
|
||||
() -> ModInfo.PROTOCOL_VERSION+"",
|
||||
// client accepted versions
|
||||
ForgeClientProxy::isReceivedProtocolVersionAcceptable,
|
||||
// server accepted versions
|
||||
ForgeClientProxy::isReceivedProtocolVersionAcceptable
|
||||
);
|
||||
|
||||
SIMPLE_CHANNEL.registerMessage(0, ByteBuf.class,
|
||||
// encoder
|
||||
(pack, buf) -> { },
|
||||
// decoder
|
||||
(buf) -> buf.asByteBuf(),
|
||||
// message consumer
|
||||
(nettyByteBuf, contextSupplier) ->
|
||||
{
|
||||
ClientApi.INSTANCE.serverMessageReceived(nettyByteBuf);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean isReceivedProtocolVersionAcceptable(String versionString)
|
||||
{
|
||||
try
|
||||
{
|
||||
// may be necessary in order to connect to vanilla servers?
|
||||
if (versionString.toLowerCase().contains("allowvanilla"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int version = Integer.parseInt(versionString);
|
||||
return ModInfo.PROTOCOL_VERSION == version;
|
||||
}
|
||||
catch (NumberFormatException ignored)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -131,8 +131,9 @@ public class ForgeMain implements LodForgeMethodCaller
|
||||
ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class,
|
||||
() -> new ConfigScreenHandler.ConfigScreenFactory((client, parent) -> GetConfigScreen.getScreen(parent)));
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
ForgeClientProxy.setupNetworkingListeners(event);
|
||||
|
||||
LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
|
||||
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
|
||||
|
||||
+3
-34
@@ -1,11 +1,8 @@
|
||||
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;
|
||||
@@ -13,17 +10,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
@Mixin(ClientPacketListener.class)
|
||||
public class MixinClientPacketListener
|
||||
{
|
||||
@Shadow
|
||||
private ClientLevel level;
|
||||
|
||||
|
||||
|
||||
/** THIS EXPLANATION IS WRITTEN BY FABRIC.
|
||||
* An explanation why we unload entities during onGameJoin: (On in our remapping name case, handleLogin(TODO: CHECK))
|
||||
* Proxies such as Waterfall may send another Game Join packet if entity meta rewrite is disabled, so we will cover ourselves.
|
||||
* Velocity by default will send a Game Join packet when the player changes servers, which will create a new client world.
|
||||
* Also anyone can send another GameJoinPacket at any time, so we need to watch out.
|
||||
*/
|
||||
// TODO update fabric version as well
|
||||
|
||||
@Inject(method = "handleLogin", at = @At("HEAD"))
|
||||
void onHandleLoginStart(CallbackInfo ci)
|
||||
{
|
||||
@@ -32,24 +20,5 @@ public class MixinClientPacketListener
|
||||
}
|
||||
@Inject(method = "handleLogin", at = @At("RETURN"))
|
||||
void onHandleLoginEnd(CallbackInfo ci) { ClientApi.INSTANCE.onClientOnlyConnected(); }
|
||||
|
||||
@Inject(method = "handleRespawn", at = @At("HEAD"))
|
||||
void onHandleRespawnStart(CallbackInfo ci) { ClientApi.INSTANCE.clientLevelUnloadEvent(ClientLevelWrapper.getWrapper(level)); }
|
||||
@Inject(method = "handleRespawn", at = @At("RETURN"))
|
||||
void onHandleRespawnEnd(CallbackInfo ci) { ClientApi.INSTANCE.clientLevelLoadEvent(ClientLevelWrapper.getWrapper(level)); }
|
||||
|
||||
#if PRE_MC_1_19_4
|
||||
@Inject(method = "cleanup", at = @At("HEAD"))
|
||||
#else
|
||||
@Inject(method = "close", at = @At("HEAD"))
|
||||
#endif
|
||||
void onCleanupStart(CallbackInfo ci)
|
||||
{
|
||||
// TODO which unload method should be used? do we need both?
|
||||
if (level != null)
|
||||
{
|
||||
ClientApi.INSTANCE.clientLevelUnloadEvent(ClientLevelWrapper.getWrapper(level));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ import java.nio.FloatBuffer;
|
||||
* before Minecraft starts rendering blocks.
|
||||
* If this wasn't done, and we used Forge's
|
||||
* render last event, the LODs would render on top
|
||||
* of the normal terrain.
|
||||
* of the normal terrain. <br><br>
|
||||
*
|
||||
* This is also the mixin for rendering the clouds
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user