Skip to content

Commit

Permalink
Merge pull request #496 from jchung01/memory-leaks
Browse files Browse the repository at this point in the history
Fix OpenComputers/CodeChickenLib network leaks & EnderStorage frequency tracking
  • Loading branch information
ACGaming authored Jun 21, 2024
2 parents 1df421b + 0c746d3 commit 875242a
Show file tree
Hide file tree
Showing 14 changed files with 314 additions and 1 deletion.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ All changes are toggleable via config files.
* **Duplication Fixes:** Fixes various duplication exploits
* **Chocolate Quest Repoured**
* **Legacy Golden Feather:** Restores the golden feather behavior from the original Better Dungeons mod
* **CodeChicken Lib**
* **Packet Leak Fix:** Fixes network ByteBuf leaks from PacketCustom
* **CoFH Core**
* **Vorpal Enchantment Damage:** Sets the damage multiplier of the Vorpal enchantment
* **Compact Machines**
Expand All @@ -304,6 +306,8 @@ All changes are toggleable via config files.
* **Extinguishing Dodges:** Chance per dodge to extinguish the player when burning
* **Feathers Helper API Fix:** Fixes server-sided crashes when the Feathers Helper API is utilized
* **Sprinting Integration:** Configurable consumption of feathers when the player is sprinting
* **Ender Storage**
* **Fix Frequency Tracking:** Fixes storage frequencies being tracked multiple times
* **Epic Siege Mod**
* **Disable Digger AI Debug:** Disables leftover debug logging inside the digger AI of the beta builds
* **Extra Utilities 2**
Expand Down Expand Up @@ -347,6 +351,8 @@ All changes are toggleable via config files.
* **Radiation Environment Map:** Changes the data table of the radiation environment handler to improve tick time
* **OpenBlocks**
* **Last Stand Trigger Fix:** Fixes the Last Stand enchantment triggering too early on pre-mitigation damage (before enchants, potions, etc), instead of on post-mitigation damage.
* **OpenComputers**
* **Packet Leak Fix:** Fixes network ByteBuf leaks from PacketHandler
* **ProjectRed**
* **Duplication Fixes:** Fixes various duplication exploits
* **Quark**
Expand Down
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ dependencies {
compileOnly rfg.deobf('curse.maven:ceramics-250617:3158763')
compileOnly rfg.deobf('curse.maven:chameleon-230497:2450900')
compileOnly rfg.deobf('curse.maven:chickens-241941:2537643')
compileOnly rfg.deobf('curse.maven:codechickenlib-242818:2779848')
compileOnly rfg.deobf('curse.maven:collective-342584:3533131')
compileOnly rfg.deobf('curse.maven:cqrepoured-303422:3953103')
compileOnly rfg.deobf('curse.maven:elementary-staffs-346007:2995593')
Expand All @@ -151,6 +152,7 @@ dependencies {
compileOnly rfg.deobf('curse.maven:modtweaker-220954:3840577')
compileOnly rfg.deobf('curse.maven:nuclearcraft-226254:3784145')
compileOnly rfg.deobf('curse.maven:openblocks-228816:2699056')
compileOnly rfg.deobf('curse.maven:opencomputers-223008:5274236')
compileOnly rfg.deobf('curse.maven:reborn-core-237903:3330308')
compileOnly rfg.deobf('curse.maven:reskillable-286382:2815686')
compileOnly rfg.deobf('curse.maven:requious-frakto-336748:3218640')
Expand All @@ -177,12 +179,12 @@ dependencies {
compileOnly 'curse.maven:arcanearchives-311357:3057332'
compileOnly 'curse.maven:bewitchment-285439:3044569'
compileOnly 'curse.maven:chisel-235279:2915375'
compileOnly 'curse.maven:codechickenlib-242818:2779848'
compileOnly 'curse.maven:cofhworld-271384:2920434'
compileOnly 'curse.maven:compactmachines-224218:2707509'
compileOnly 'curse.maven:effortlessbuilding-302113:2847346'
compileOnly 'curse.maven:endercore-231868:2972849'
compileOnly 'curse.maven:enderio-64578:2989201'
compileOnly 'curse.maven:enderstorage-245174:2755787'
compileOnly 'curse.maven:extrautilities-225561:2678374'
compileOnly 'curse.maven:forgemultipartcbe-258426:2755790' // aka "CB Multipart"
compileOnly 'curse.maven:guideapi-228832:2645992'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public class UTConfigMods
@Config.Name("CB Multipart/Forge Multipart CBE")
public static final CBMultipartCategory CB_MULTIPART = new CBMultipartCategory();

@Config.LangKey("cfg.universaltweaks.modintegration.ccl")
@Config.Name("CodeChicken Lib")
public static final CodeChickenLibCategory CCL = new CodeChickenLibCategory();

@Config.LangKey("cfg.universaltweaks.modintegration.chisel")
@Config.Name("Chisel")
public static final ChiselCategory CHISEL = new ChiselCategory();
Expand Down Expand Up @@ -79,6 +83,10 @@ public class UTConfigMods
@Config.Name("Elenai Dodge 2")
public static final ElenaiDodge2Category ELENAI_DODGE_2 = new ElenaiDodge2Category();

@Config.LangKey("cfg.universaltweaks.modintegration.enderstorage")
@Config.Name("Ender Storage")
public static final EnderStorageCategory ENDER_STORAGE = new EnderStorageCategory();

@Config.LangKey("cfg.universaltweaks.modintegration.esm")
@Config.Name("Epic Siege Mod")
public static final EpicSiegeModCategory EPIC_SIEGE_MOD = new EpicSiegeModCategory();
Expand Down Expand Up @@ -147,6 +155,10 @@ public class UTConfigMods
@Config.Name("OpenBlocks")
public static final OpenBlocksCategory OPEN_BLOCKS = new OpenBlocksCategory();

@Config.LangKey("cfg.universaltweaks.modintegration.opencomputers")
@Config.Name("OpenComputers")
public static final OpenComputersCategory OPEN_COMPUTERS = new OpenComputersCategory();

@Config.LangKey("cfg.universaltweaks.modintegration.projectred")
@Config.Name("ProjectRed")
public static final ProjectRedCategory PROJECTRED = new ProjectRedCategory();
Expand Down Expand Up @@ -354,6 +366,14 @@ public static class ChocolateQuestCategory
public boolean utCQRGoldenFeatherToggle = true;
}

public static class CodeChickenLibCategory
{
@Config.RequiresMcRestart
@Config.Name("Packet Leak Fix")
@Config.Comment("Fixes network ByteBuf leaks from PacketCustom")
public boolean utPacketLeakFixToggle = true;
}

public static class CoFHCoreCategory
{
@Config.Name("Vorpal Enchantment Damage")
Expand Down Expand Up @@ -411,6 +431,14 @@ public static class ElenaiDodge2Category
public int utED2SprintingFeatherRequirement = 6;
}

public static class EnderStorageCategory
{
@Config.RequiresMcRestart
@Config.Name("Fix Frequency Tracking")
@Config.Comment("Fixes storage frequencies being tracked multiple times")
public boolean utFrequencyTrackFixToggle = true;
}

public static class EpicSiegeModCategory
{
@Config.RequiresMcRestart
Expand Down Expand Up @@ -647,6 +675,14 @@ public static class OpenBlocksCategory
public boolean utLastStandFixToggle = true;
}

public static class OpenComputersCategory
{
@Config.RequiresMcRestart
@Config.Name("Packet Leak Fix")
@Config.Comment("Fixes network ByteBuf leaks from PacketHandler")
public boolean utPacketLeakFixToggle = true;
}

public static class ProjectRedCategory
{
@Config.RequiresMcRestart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ public class UTMixinLoader implements ILateMixinLoader
put("mixins.mods.cbmultipart.json", () -> loaded("forgemultipartcbe") && UTConfigMods.CB_MULTIPART.utMemoryLeakFixToggle);
put("mixins.mods.ceramics.json", () -> loaded("ceramics"));
put("mixins.mods.chisel.tcomplement.dupes.json", () -> loaded("chisel") && loaded("tcomplement") && UTConfigMods.CHISEL.utDuplicationFixesToggle);
put("mixins.mods.codechickenlib.json", () -> loaded("codechickenlib") && UTConfigMods.CCL.utPacketLeakFixToggle);
put("mixins.mods.cofhcore.json", () -> loaded("cofhcore"));
put("mixins.mods.collective.json", () -> loaded("collective"));
put("mixins.mods.cqrepoured.json", () -> loaded("cqrepoured"));
put("mixins.mods.effortlessbuilding.json", () -> loaded("effortlessbuilding"));
put("mixins.mods.elementarystaffs.json", () -> loaded("element"));
put("mixins.mods.elenaidodge2.json", () -> loaded("elenaidodge2"));
put("mixins.mods.enderstorage.json", () -> loaded("enderstorage") && UTConfigMods.ENDER_STORAGE.utFrequencyTrackFixToggle);
put("mixins.mods.epicsiegemod.json", () -> loaded("epicsiegemod"));
put("mixins.mods.erebus.cabbage.json", () -> loaded("erebus") && UTConfigMods.EREBUS.utCabbageDrop);
put("mixins.mods.erebus.json", () -> loaded("erebus"));
Expand All @@ -80,6 +82,7 @@ public class UTMixinLoader implements ILateMixinLoader
put("mixins.mods.netherrocks.json", () -> loaded("netherrocks"));
put("mixins.mods.nuclearcraft.json", () -> loaded("nuclearcraft"));
put("mixins.mods.openblocks.json", () -> loaded("openblocks") && UTConfigMods.OPEN_BLOCKS.utLastStandFixToggle);
put("mixins.mods.opencomputers.json", () -> loaded("opencomputers") && UTConfigMods.OPEN_COMPUTERS.utPacketLeakFixToggle);
put("mixins.mods.quark.dupes.json", () -> loaded("quark") && UTConfigMods.QUARK.utDuplicationFixesToggle);
put("mixins.mods.requiousfrakto.json", () -> loaded("requious") && UTConfigMods.REQUIOUS_FRAKTO.utParticleFixesToggle);
put("mixins.mods.reskillable.json", () -> loaded("reskillable"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package mod.acgaming.universaltweaks.mods.codechickenlib.mixin;

import codechicken.lib.packet.PacketCustom;
import net.minecraft.network.INetHandler;
import org.apache.commons.lang3.Validate;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

// Courtesy of jchung01
@Mixin(value = PacketCustom.ClientInboundHandler.class, remap = false)
public class UTPacketCustomClientMixin
{
/**
* This releases the COPIED ByteBuf that was created and assigned to PacketCustom.buf in the ctor.
* @reason Release wrapped ByteBuf after whatever mod has handled the packet.
*/
@Inject(method = "handle", at = @At(value = "INVOKE", target = "Lcodechicken/lib/packet/ICustomPacketHandler$IClientPacketHandler;handlePacket(Lcodechicken/lib/packet/PacketCustom;Lnet/minecraft/client/Minecraft;Lnet/minecraft/network/play/INetHandlerPlayClient;)V", shift = At.Shift.AFTER))
private void utReleaseClientBuf(INetHandler netHandler, String channel, PacketCustom packet, CallbackInfo ci)
{
Validate.isTrue(packet.refCnt() == 1);
packet.release();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package mod.acgaming.universaltweaks.mods.codechickenlib.mixin;

import codechicken.lib.packet.PacketCustom;
import io.netty.channel.ChannelHandlerContext;
import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

// Courtesy of jchung01
@Mixin(value = PacketCustom.CustomInboundHandler.class, remap = false)
public class UTPacketCustomReleaseMixin
{
/**
* This releases the ORIGINAL FMLProxyPacket payload that was passed into and copied by PacketCustom's constructor.
* <p>
* For S->C packets, some are reused and sent to multiple clients, so those packets are retained in {@link UTPacketCustomRetainMixin}.
* @reason Release the message's payload after everything has been handled.
*/
@Inject(method = "channelRead0(Lio/netty/channel/ChannelHandlerContext;Lnet/minecraftforge/fml/common/network/internal/FMLProxyPacket;)V", at = @At(value = "TAIL"))
private void utReleasePayload(ChannelHandlerContext ctx, FMLProxyPacket msg, CallbackInfo ci)
{
msg.payload().release();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package mod.acgaming.universaltweaks.mods.codechickenlib.mixin;

import java.util.List;
import java.util.function.Predicate;
import codechicken.lib.packet.PacketCustom;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.INetHandler;
import net.minecraft.network.Packet;
import net.minecraft.server.management.PlayerChunkMapEntry;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
import net.minecraftforge.fml.common.network.internal.FMLProxyPacket;
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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

/**
* This mixin retains PacketCustom payloads, increasing the ref count by the number of players
* that are to receive the packet. This is necessary as the payload is shared between all receivers
* and each receiver releases the payload in {@link UTPacketCustomReleaseMixin}.
* <p>
* CCL doesn't fully utilize Forge's built-in networking, so this mixin adds the missing behavior that
* {@link net.minecraftforge.fml.common.network.FMLOutboundHandler.OutboundTarget#selectNetworks} and
* {@link net.minecraftforge.fml.common.network.FMLOutboundHandler#write}
* would normally do to clean up packets.
* @author jchung01
*/
@Mixin(value = PacketCustom.class, remap = false)
public abstract class UTPacketCustomRetainMixin
{
@Shadow
public abstract boolean release();

@Inject(method = "sendToClients(Lnet/minecraft/network/Packet;)V", at = @At(value = "HEAD"))
private static void utRetainForAllClients(Packet<INetHandler> packet, CallbackInfo ci)
{
ut$retainForPlayers(packet, player -> true);
}

@Inject(method = "sendToAllAround", at = @At(value = "HEAD"))
private static void utRetainForAllAround(Packet<INetHandler> packet, double x, double y, double z, double range, int dim, CallbackInfo ci)
{
ut$retainForPlayers(packet, player -> {
if (player.dimension != dim) return false;
double dx = player.posX - x;
double dy = player.posY - y;
double dz = player.posZ - z;
return dx * dx + dy * dy + dz * dz < range * range;
});
}

@Inject(method = "sendToDimension(Lnet/minecraft/network/Packet;I)V", at = @At(value = "HEAD"))
private static void utRetainForAllInDimension(Packet<INetHandler> packet, int dim, CallbackInfo ci)
{
ut$retainForPlayers(packet, player -> player.dimension == dim);
}

@Inject(method = "sendToChunk(Lnet/minecraft/network/Packet;Lnet/minecraft/world/World;II)V", at = @At(value = "HEAD"))
private static void utRetainForAllInChunk(Packet<INetHandler> packet, World world, int chunkX, int chunkZ, CallbackInfo ci)
{
PlayerChunkMapEntry playersInChunk = ((WorldServer) world).getPlayerChunkMap().getEntry(chunkX, chunkZ);
if (playersInChunk != null) ut$retainForPlayers(packet, playersInChunk::containsPlayer);
}

@Inject(method = "sendToOps(Lnet/minecraft/network/Packet;)V", at = @At(value = "HEAD"))
private static void utRetainForOps(Packet<INetHandler> packet, CallbackInfo ci)
{
ut$retainForPlayers(packet, player -> FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerList().canSendCommands(player.getGameProfile()));
}

/**
* Release this buf after copying it, just to be safe.
*/
@Inject(method = "toPacket", at = @At(value = "RETURN"))
private void utReleaseOriginal(CallbackInfoReturnable<FMLProxyPacket> cir)
{
this.release();
}

@Unique
private static void ut$retainForPlayers(Packet<INetHandler> packet, Predicate<EntityPlayerMP> condition)
{
if (packet instanceof FMLProxyPacket)
{
List<EntityPlayerMP> playerList = FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerList().getPlayers();
// Check for null dispatchers like Forge's FMLOutboundHandler.OutboundTarget#selectNetworks does.
Predicate<EntityPlayerMP> hasNetworkDispatcher = player -> player.connection.netManager.channel().attr(NetworkDispatcher.FML_DISPATCHER).get() != null;
int retainCount = (int) (playerList.stream().filter(condition.and(hasNetworkDispatcher)).count() - 1);
if (retainCount > 0) ((FMLProxyPacket) packet).payload().retain(retainCount);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package mod.acgaming.universaltweaks.mods.codechickenlib.mixin;

import codechicken.lib.packet.PacketCustom;
import net.minecraft.network.INetHandler;
import org.apache.commons.lang3.Validate;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

// Courtesy of jchung01
@Mixin(value = PacketCustom.ServerInboundHandler.class, remap = false)
public class UTPacketCustomServerMixin
{
/**
* This releases the COPIED ByteBuf that was created and assigned to PacketCustom.buf in the ctor.
* @reason Release wrapped ByteBuf after whatever mod has handled the packet.
*/
@Inject(method = "handle", at = @At(value = "INVOKE", target = "Lcodechicken/lib/packet/ICustomPacketHandler$IServerPacketHandler;handlePacket(Lcodechicken/lib/packet/PacketCustom;Lnet/minecraft/entity/player/EntityPlayerMP;Lnet/minecraft/network/play/INetHandlerPlayServer;)V", shift = At.Shift.AFTER))
private void utReleaseServerBuf(INetHandler netHandler, String channel, PacketCustom packet, CallbackInfo ci)
{
Validate.isTrue(packet.refCnt() == 1);
packet.release();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package mod.acgaming.universaltweaks.mods.enderstorage.mixin;

import java.util.Objects;
import codechicken.enderstorage.api.Frequency;
import codechicken.lib.colour.EnumColour;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;

// Courtesy of jchung01
@Mixin(value = Frequency.class, remap = false)
public class UTFrequencyMixin
{
@Shadow
public EnumColour left;
@Shadow
public EnumColour middle;
@Shadow
public EnumColour right;
@Shadow
public String owner;

/**
* Add equals() override to fulfill Object equality contract.
*/
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Frequency that = (Frequency) o;
return left == that.left && middle == that.middle && right == that.right && Objects.equals(owner, that.owner);
}

/**
* @author jchung01
* @reason Use a more proper hashCode method.
*/
@Override
@Overwrite
public int hashCode()
{
return Objects.hash(left, middle, right, owner);
}
}
Loading

0 comments on commit 875242a

Please sign in to comment.