Skip to content

Commit

Permalink
🐛 Fix FML shakehands is not working
Browse files Browse the repository at this point in the history
  • Loading branch information
unhappychoice committed May 24, 2021
1 parent 89057d3 commit 4855f37
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 107 deletions.
17 changes: 15 additions & 2 deletions src/main/java/com/replaymod/recording/packet/PacketListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
//$$ import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
//#endif

//#if FABRIC<1
//$$ import net.minecraft.network.login.server.SCustomPayloadLoginPacket;
//#endif

//#if MC>=10904
//#else
//$$ import java.util.List;
Expand Down Expand Up @@ -104,8 +108,8 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
private boolean loginPhase = true;
//#else
//#if MC>=11600
//$$ private ProtocolType connectionState = ProtocolType.PLAY;
//$$ private boolean loginPhase = false;
//$$ private ProtocolType connectionState = ProtocolType.LOGIN;
//$$ private boolean loginPhase = true;
//#else
//$$ private EnumConnectionState connectionState = EnumConnectionState.PLAY;
//$$ private boolean loginPhase = false;
Expand Down Expand Up @@ -365,6 +369,15 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
//$$ }
//#endif

//#if FABRIC<1
//$$ if (packet instanceof SCustomPayloadLoginPacket) {
//$$ PacketBuffer buffer = new PacketBuffer(Unpooled.buffer());
//$$ packet.writePacketData(buffer);
//$$ packet = new SCustomPayloadLoginPacket();
//$$ packet.readPacketData(buffer);
//$$ }
//#endif

//#if MC>=10800
if (packet instanceof CustomPayloadS2CPacket) {
// Forge may read from this ByteBuf and/or release it during handling
Expand Down
131 changes: 68 additions & 63 deletions src/main/java/com/replaymod/replay/FullReplaySender.java
Original file line number Diff line number Diff line change
Expand Up @@ -390,78 +390,83 @@ public void channelRead(ChannelHandlerContext ctx, Object msg)

if (msg instanceof byte[]) {
try {
Packet p = deserializePacket((byte[]) msg);
Packet deserialized = deserializePacket((byte[]) msg);

if (p != null) {
p = processPacket(p);
if (p != null) {
super.channelRead(ctx, p);
}
if (deserialized == null) {
System.out.println("Failed to deserialize a packet to send. loginPhase: " + loginPhase);
return;
}

// If we do not give minecraft time to tick, there will be dead entity artifacts left in the world
// Therefore we have to remove all loaded, dead entities manually if we are in sync mode.
// We do this after every SpawnX packet and after the destroy entities packet.
if (!asyncMode && mc.world != null) {
if (p instanceof PlayerSpawnS2CPacket
|| p instanceof EntitySpawnS2CPacket
|| p instanceof MobSpawnS2CPacket
//#if MC<11600
//$$ || p instanceof EntitySpawnGlobalS2CPacket
//#endif
|| p instanceof PaintingSpawnS2CPacket
|| p instanceof ExperienceOrbSpawnS2CPacket
|| p instanceof EntitiesDestroyS2CPacket) {
ClientWorld world = mc.world;
//#if MC>=11400
// Note: Not sure if it's still required but there's this really handy method anyway
world.finishRemovingEntities();
//#else
//$$ Iterator<Entity> iter = world.loadedEntityList.iterator();
//$$ while (iter.hasNext()) {
//$$ Entity entity = iter.next();
//$$ if (entity.isDead) {
//$$ int chunkX = entity.chunkCoordX;
//$$ int chunkY = entity.chunkCoordZ;
//$$
//#if MC>=11400
//$$ if (entity.addedToChunk && world.getChunkProvider().provideChunk(chunkX, chunkY, false, false) != null) {
//#else
//#if MC>=10904
//$$ if (entity.addedToChunk && world.getChunkProvider().getLoadedChunk(chunkX, chunkY) != null) {
//#else
//$$ if (entity.addedToChunk && world.getChunkProvider().chunkExists(chunkX, chunkY)) {
//#endif
//#endif
//$$ world.getChunkFromChunkCoords(chunkX, chunkY).removeEntity(entity);
//$$ }
//$$
//$$ iter.remove();
//$$ world.onEntityRemoved(entity);
//$$ }
//$$
//$$ }
Packet processed = processPacket(deserialized);

if (processed != null) {
// System.out.println("Processing a packet. Class: " + processed.getClass().toString());
super.channelRead(ctx, processed);
}

// If we do not give minecraft time to tick, there will be dead entity artifacts left in the world
// Therefore we have to remove all loaded, dead entities manually if we are in sync mode.
// We do this after every SpawnX packet and after the destroy entities packet.
if (!asyncMode && mc.world != null) {
if (processed instanceof PlayerSpawnS2CPacket
|| processed instanceof EntitySpawnS2CPacket
|| processed instanceof MobSpawnS2CPacket
//#if MC<11600
//$$ || processed instanceof EntitySpawnGlobalS2CPacket
//#endif
}
|| processed instanceof PaintingSpawnS2CPacket
|| processed instanceof ExperienceOrbSpawnS2CPacket
|| processed instanceof EntitiesDestroyS2CPacket) {
ClientWorld world = mc.world;
//#if MC>=11400
// Note: Not sure if it's still required but there's this really handy method anyway
world.finishRemovingEntities();
//#else
//$$ Iterator<Entity> iter = world.loadedEntityList.iterator();
//$$ while (iter.hasNext()) {
//$$ Entity entity = iter.next();
//$$ if (entity.isDead) {
//$$ int chunkX = entity.chunkCoordX;
//$$ int chunkY = entity.chunkCoordZ;
//$$
//#if MC>=11400
//$$ if (entity.addedToChunk && world.getChunkProvider().provideChunk(chunkX, chunkY, false, false) != null) {
//#else
//#if MC>=10904
//$$ if (entity.addedToChunk && world.getChunkProvider().getLoadedChunk(chunkX, chunkY) != null) {
//#else
//$$ if (entity.addedToChunk && world.getChunkProvider().chunkExists(chunkX, chunkY)) {
//#endif
//#endif
//$$ world.getChunkFromChunkCoords(chunkX, chunkY).removeEntity(entity);
//$$ }
//$$
//$$ iter.remove();
//$$ world.onEntityRemoved(entity);
//$$ }
//$$
//$$ }
//#endif
}
}

//#if MC>=11400
if (p instanceof ChunkDataS2CPacket) {
Runnable doLightUpdates = () -> {
if (mc.world != null) {
LightingProvider provider = mc.world.getChunkManager().getLightingProvider();
while (provider.hasUpdates()) {
provider.doLightUpdates(Integer.MAX_VALUE, true, true);
}
//#if MC>=11400
if (processed instanceof ChunkDataS2CPacket) {
Runnable doLightUpdates = () -> {
if (mc.world != null) {
LightingProvider provider = mc.world.getChunkManager().getLightingProvider();
while (provider.hasUpdates()) {
provider.doLightUpdates(Integer.MAX_VALUE, true, true);
}
};
if (mc.isOnThread()) {
doLightUpdates.run();
} else {
mc.send(doLightUpdates);
}
};
if (mc.isOnThread()) {
doLightUpdates.run();
} else {
mc.send(doLightUpdates);
}
//#endif
}
//#endif
} catch (Exception e) {
// We'd rather not have a failure parsing one packet screw up the whole replay process
e.printStackTrace();
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/replaymod/replay/ReplayHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
import java.io.IOException;
import java.util.*;

//#if FABRIC<1
//$$ import io.netty.util.AttributeKey;
//#endif

//#if MC>=11600
import net.minecraft.client.util.math.MatrixStack;
//#endif
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/mixins.replaymod.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"MixinDownloadingPackFinder",
"MixinEntityRenderer",
"MixinFMLHandshakeHandler",
"MixinFMLPlayMessages",
"MixinClientPlayNetHandler",
"MixinGameRenderer",
"MixinGlStateManager",
Expand Down Expand Up @@ -79,6 +78,7 @@
"Mixin_Stereoscopic_HandRenderPass",
"Mixin_WindowsWorkaroundForTinyEXRNatives",
"NetworkManagerAccessor",
"NetworkRegistryAccessor",
"ParticleAccessor",
"SPacketSpawnMobAccessor",
"SPacketSpawnPlayerAccessor",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

@Mixin(ClientPlayNetHandler.class)
public abstract class MixinClientPlayNetHandler {

@Shadow @Final
private NetworkManager netManager;

Expand All @@ -30,7 +29,8 @@ public void replayMod_ignoreHandshakeConnectionClose(STagsListPacket packetIn, C
System.out.println("Injected ClientPlayNetHandler.handleTags");
// PacketThreadUtil.checkThreadAndEnqueue(packetIn, this, this.client);
ITagCollectionSupplier itagcollectionsupplier = packetIn.getTags();
boolean vanillaConnection = net.minecraftforge.fml.network.NetworkHooks.isVanillaConnection(netManager);
// boolean vanillaConnection = net.minecraftforge.fml.network.NetworkHooks.isVanillaConnection(netManager);
boolean vanillaConnection = false;
net.minecraftforge.common.ForgeTagHandler.resetCachedTagCollections(true, vanillaConnection);
itagcollectionsupplier = ITagCollectionSupplier.reinjectOptionalTags(itagcollectionsupplier);
this.networkTagManager = itagcollectionsupplier;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,41 @@
package com.replaymod.mixin;

import io.netty.channel.ChannelPipeline;
import net.minecraft.network.NetworkManager;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.fml.network.FMLHandshakeHandler;
import net.minecraftforge.fml.network.NetworkDirection;
import net.minecraftforge.fml.network.NetworkRegistry;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.MixinEnvironment;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.util.List;

@Mixin(FMLHandshakeHandler.class)
public abstract class MixinFMLHandshakeHandler {
@Shadow
private List<NetworkRegistry.LoginPayload> messageList;

@Shadow @Final
private NetworkDirection direction;

@Inject(method = "<init>(Lnet/minecraft/network/NetworkManager;Lnet/minecraftforge/fml/network/NetworkDirection;)V", at = @At("TAIL"))
public void replayModRecording_setupForLocalRecording(NetworkManager networkManager, NetworkDirection side, CallbackInfo ci) {
if (!networkManager.isLocalChannel()) {
return;
}

System.out.println("Force FML handshaking and set LoginPayloads");
this.messageList = NetworkRegistryAccessor.invokeGatherLoginPayloads(this.direction, false);
}

@Redirect(method = "handleRegistryLoading", at=@At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkManager;closeChannel(Lnet/minecraft/util/text/ITextComponent;)V"))
public void replayMod_ignoreHandshakeConnectionClose(NetworkManager networkManager, ITextComponent message) {}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.replaymod.mixin;

import com.replaymod.core.versions.MCVer;
import com.replaymod.recording.ReplayModRecording;
import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.network.login.ClientLoginNetHandler;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.login.server.SCustomPayloadLoginPacket;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
Expand All @@ -21,8 +24,19 @@ public abstract class MixinNetHandlerLoginClient {
* We cannot use the {@link FMLNetworkEvent.ClientConnectedToServerEvent}
* as it only fires after the forge handshake.
*/
@Inject(method = "handleLoginSuccess", at=@At("HEAD"))
public void replayModRecording_initiateRecording(CallbackInfo cb) {
@Inject(method = "handleCustomPayloadLogin", at=@At("HEAD"))
public void replayModRecording_initiateRecording(SCustomPayloadLoginPacket packetIn, CallbackInfo cb) {
initiateRecording(packetIn);
}

private void initiateRecording(SCustomPayloadLoginPacket packet) {
RecordingEventHandler.RecordingEventSender eventSender = (RecordingEventHandler.RecordingEventSender) MCVer.getMinecraft().worldRenderer;
if (eventSender.getRecordingEventHandler() != null) {
return; // already recording
}
ReplayModRecording.instance.initiateRecording(this.networkManager);
if (eventSender.getRecordingEventHandler() != null) {
eventSender.getRecordingEventHandler().onPacket(packet);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.replaymod.mixin;

import net.minecraftforge.fml.network.NetworkDirection;
import net.minecraftforge.fml.network.NetworkRegistry;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;

import java.util.List;

@Mixin(NetworkRegistry.class)
public interface NetworkRegistryAccessor {
@Invoker("gatherLoginPayloads")
static List<NetworkRegistry.LoginPayload> invokeGatherLoginPayloads(NetworkDirection direction, boolean isLocal) {
throw new AssertionError();
}
}

0 comments on commit 4855f37

Please sign in to comment.