Skip to content

Commit

Permalink
3.0.5 - optimized BWM's blasting oil implementation heavily
Browse files Browse the repository at this point in the history
- Made transformation map a multimap, may transform t he same class multiple times
- Moved hideous code to proxy
  • Loading branch information
Rongmario committed Sep 28, 2021
1 parent ab8e431 commit f1509a0
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 79 deletions.
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ apply plugin: 'net.minecraftforge.gradle.forge'
apply plugin: 'org.spongepowered.mixin'
//Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.

version = "3.0.4"
version = "3.0.5"
group = "zone.rong.loliasm" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = "loliasm"

Expand Down Expand Up @@ -99,6 +99,8 @@ dependencies {
deobfCompile 'slimeknights.mantle:Mantle:1.12-1.3.3.55+'
deobfCompile 'slimeknights:TConstruct:1.12.2-2.13.0.183+'

deobfCompile "betterwithmods:BetterWithMods:1.12-2.3.20-1030"

deobfCompile "codechicken:CodeChickenLib:1.12.2-3.1.7.+:universal"
deobfCompile "com.azanor.baubles:Baubles:1.12-1.5.2"
deobfCompile "blusunrize:ImmersiveEngineering:0.12-92-+"
Expand Down
92 changes: 27 additions & 65 deletions src/main/java/zone/rong/loliasm/LoliASM.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.google.common.cache.CacheBuilder;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.launchwrapper.LaunchClassLoader;
import net.minecraft.util.HttpUtil;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.event.*;
Expand All @@ -16,7 +15,6 @@
import zone.rong.loliasm.core.LoliLoadingPlugin;
import zone.rong.loliasm.proxy.CommonProxy;

import java.lang.reflect.Field;
import java.util.*;

@Mod(modid = "loliasm", name = "LoliASM", version = LoliLoadingPlugin.VERSION, dependencies = "required-after:mixinbooter;after:jei")
Expand All @@ -28,7 +26,32 @@ public class LoliASM {

public static List<RegistrySimpleExtender> simpleRegistryInstances = new ArrayList<>();

public LoliASM() {
public static void cleanupLaunchClassLoader() {
try {
LoliLogger.instance.info("Cleaning up LaunchClassLoader");
if (LoliConfig.instance.noClassCache) {
LoliReflector.resolveFieldSetter(LaunchClassLoader.class, "cachedClasses").invoke(Launch.classLoader, DummyMap.of());
} else if (LoliConfig.instance.weakClassCache) {
Map<String, Class<?>> oldClassCache = (Map<String, Class<?>>) LoliReflector.resolveFieldGetter(LaunchClassLoader.class, "cachedClasses").invoke(Launch.classLoader);
Cache<String, Class<?>> newClassCache = CacheBuilder.newBuilder().concurrencyLevel(2).weakValues().build();
newClassCache.putAll(oldClassCache);
LoliReflector.resolveFieldSetter(LaunchClassLoader.class, "cachedClasses").invoke(Launch.classLoader, newClassCache.asMap());
}
if (LoliConfig.instance.noResourceCache) {
LoliReflector.resolveFieldSetter(LaunchClassLoader.class, "resourceCache").invoke(Launch.classLoader, new ResourceCache());
LoliReflector.resolveFieldSetter(LaunchClassLoader.class, "negativeResourceCache").invokeExact(Launch.classLoader, DummyMap.asSet());
} else if (LoliConfig.instance.weakResourceCache) {
Map<String, byte[]> oldResourceCache = (Map<String, byte[]>) LoliReflector.resolveFieldGetter(LaunchClassLoader.class, "resourceCache").invoke(Launch.classLoader);
Cache<String, byte[]> newResourceCache = CacheBuilder.newBuilder().concurrencyLevel(2).weakValues().build();
newResourceCache.putAll(oldResourceCache);
LoliReflector.resolveFieldSetter(LaunchClassLoader.class, "resourceCache").invoke(Launch.classLoader, newResourceCache.asMap());
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}

static {
if (LoliConfig.instance.cleanupLaunchClassLoaderEarly) {
cleanupLaunchClassLoader();
}
Expand Down Expand Up @@ -56,73 +79,12 @@ public void init(FMLInitializationEvent event) {

@Mod.EventHandler
public void postInit(FMLPostInitializationEvent event) {
if (LoliConfig.instance.skipCraftTweakerRecalculatingSearchTrees) {
LoliReflector.getClass("crafttweaker.mc1120.CraftTweaker").ifPresent(c -> {
try {
Field alreadyChangedThePlayer = c.getDeclaredField("alreadyChangedThePlayer");
alreadyChangedThePlayer.setAccessible(true);
alreadyChangedThePlayer.setBoolean(null, true);
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
});
}
proxy.postInit(event);
}

@Mod.EventHandler
public void loadComplete(FMLLoadCompleteEvent event) {
proxy.loadComplete(event);
LoliLogger.instance.info("Trimming simple registries");
HttpUtil.DOWNLOADER_EXECUTOR.execute(() -> {
simpleRegistryInstances.forEach(RegistrySimpleExtender::trim);
simpleRegistryInstances = null;
});
if (LoliConfig.instance.cleanupLaunchClassLoaderEarly || LoliConfig.instance.cleanCachesOnGameLoad) {
invalidateLaunchClassLoaderCaches();
} else if (LoliConfig.instance.cleanupLaunchClassLoaderLate) {
cleanupLaunchClassLoader();
}
}

private void cleanupLaunchClassLoader() {
try {
LoliLogger.instance.info("Cleaning up LaunchClassLoader");
if (LoliConfig.instance.noClassCache) {
LoliReflector.resolveFieldSetter(LaunchClassLoader.class, "cachedClasses").invoke(Launch.classLoader, DummyMap.of());
} else if (LoliConfig.instance.weakClassCache) {
Map<String, Class<?>> oldClassCache = (Map<String, Class<?>>) LoliReflector.resolveFieldGetter(LaunchClassLoader.class, "cachedClasses").invoke(Launch.classLoader);
Cache<String, Class<?>> newClassCache = CacheBuilder.newBuilder().concurrencyLevel(2).weakValues().build();
newClassCache.putAll(oldClassCache);
LoliReflector.resolveFieldSetter(LaunchClassLoader.class, "cachedClasses").invoke(Launch.classLoader, newClassCache.asMap());
}
if (LoliConfig.instance.noResourceCache) {
LoliReflector.resolveFieldSetter(LaunchClassLoader.class, "resourceCache").invoke(Launch.classLoader, new ResourceCache());
LoliReflector.resolveFieldSetter(LaunchClassLoader.class, "negativeResourceCache").invokeExact(Launch.classLoader, DummyMap.asSet());
} else if (LoliConfig.instance.weakResourceCache) {
Map<String, byte[]> oldResourceCache = (Map<String, byte[]>) LoliReflector.resolveFieldGetter(LaunchClassLoader.class, "resourceCache").invoke(Launch.classLoader);
Cache<String, byte[]> newResourceCache = CacheBuilder.newBuilder().concurrencyLevel(2).weakValues().build();
newResourceCache.putAll(oldResourceCache);
LoliReflector.resolveFieldSetter(LaunchClassLoader.class, "resourceCache").invoke(Launch.classLoader, newResourceCache.asMap());
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}

private void invalidateLaunchClassLoaderCaches() {
try {
LoliLogger.instance.info("Invalidating and Cleaning LaunchClassLoader caches");
if (!LoliConfig.instance.noClassCache) {
((Map<String, Class<?>>) LoliReflector.resolveFieldGetter(LaunchClassLoader.class, "cachedClasses").invoke(Launch.classLoader)).clear();
}
if (!LoliConfig.instance.noResourceCache) {
((Map<String, byte[]>) LoliReflector.resolveFieldGetter(LaunchClassLoader.class, "resourceCache").invoke(Launch.classLoader)).clear();
((Set<String>) LoliReflector.resolveFieldGetter(LaunchClassLoader.class, "negativeResourceCache").invoke(Launch.classLoader)).clear();
}
((Set<String>) LoliReflector.resolveFieldGetter(LaunchClassLoader.class, "invalidClasses").invoke(Launch.classLoader)).clear();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package zone.rong.loliasm.common.modfixes.betterwithmods;

import betterwithmods.common.BWMItems;
import betterwithmods.common.items.ItemMaterial;
import betterwithmods.module.gameplay.Gameplay;
import it.unimi.dsi.fastutil.objects.Reference2DoubleMap;
import it.unimi.dsi.fastutil.objects.Reference2DoubleOpenHashMap;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.DamageSource;
import net.minecraftforge.event.entity.living.LivingHurtEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;

public class BWMBlastingOilOptimization {

private static Reference2DoubleMap<EntityItem> highestPoint;

@SubscribeEvent
public static void onPlayerTakeDamage(LivingHurtEvent e) {
if (e.getSource() == null || e.getSource().damageType == null || Gameplay.blacklistDamageSources.contains(e.getSource().damageType)) {
return;
}
EntityLivingBase living = e.getEntityLiving();
if (living.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null)) {
IItemHandler inventory = living.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null);
if (inventory != null) {
int count = 0;
for (int i = 0; i < inventory.getSlots(); i++) {
ItemStack stack = inventory.getStackInSlot(i);
if (stack.getItem() == BWMItems.MATERIAL && stack.getMetadata() == ItemMaterial.EnumMaterial.BLASTING_OIL.getMetadata()) {
count += stack.getCount();
inventory.extractItem(i, stack.getCount(), false);
}
}
if (count > 0) {
living.attackEntityFrom(new DamageSource("blasting_oil"), Float.MAX_VALUE);
living.getEntityWorld().createExplosion(null, living.posX, living.posY + living.height / 16, living.posZ, (float) (Math.sqrt((double) count / 5) / 2.5 + 1), true);
}
}
}
}

public static boolean inject$ItemMaterial$onEntityItemUpdate(EntityItem entity) {
ItemStack stack = entity.getItem();
if (stack.getMetadata() != ItemMaterial.EnumMaterial.BLASTING_OIL.getMetadata()) {
return false;
}
if (highestPoint == null) {
highestPoint = new Reference2DoubleOpenHashMap<>();
}
if (entity.isBurning() || (entity.onGround && Math.abs(entity.posY - highestPoint.getOrDefault(entity, entity.posY)) > 2.0)) {
int count = stack.getCount();
if (count > 0) {
highestPoint.removeDouble(entity);
entity.getEntityWorld().createExplosion(entity, entity.posX, entity.posY + entity.height / 16, entity.posZ, (float) (Math.sqrt((double) count / 5) / 2.5 + 1), true);
entity.setDead();
return true;
}
}
if (entity.motionY > 0 || !highestPoint.containsKey(entity)) {
highestPoint.put(entity, entity.posY);
}
return false;
}

}
7 changes: 4 additions & 3 deletions src/main/java/zone/rong/loliasm/config/LoliConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public boolean shouldSkipClass(Class<?> clazz) {
public boolean releaseSpriteFramesCache;
public boolean optimizeSomeRendering, stripUnnecessaryLocalsInRenderHelper;
public boolean quickerEnableUniversalBucketCheck, stripInstancedRandomFromSoundEventAccessor;
public boolean fixBlockIEBaseArrayIndexOutOfBoundsException, cleanupChickenASMClassHierarchyManager, optimizeAmuletRelatedFunctions, labelCanonicalization, skipCraftTweakerRecalculatingSearchTrees;
public boolean fixBlockIEBaseArrayIndexOutOfBoundsException, cleanupChickenASMClassHierarchyManager, optimizeAmuletRelatedFunctions, labelCanonicalization, skipCraftTweakerRecalculatingSearchTrees, bwmBlastingOilOptimization;
public boolean fixAmuletHolderCapability;

private void initialize() {
Expand Down Expand Up @@ -120,8 +120,9 @@ public void load() {
fixBlockIEBaseArrayIndexOutOfBoundsException = getBoolean("fixBlockIEBaseArrayIndexOutOfBoundsException", "modfixes", "When Immersive Engineering is installed, sometimes it or it's addons can induce an ArrayIndexOutOfBoundsException in BlockIEBase#getPushReaction. This option will be ignored when IE isn't installed", true);
cleanupChickenASMClassHierarchyManager = getBoolean("cleanupChickenASMClassHierarchyManager", "modfixes", "EXPERIMENTAL: When ChickenASM (Library of CodeChickenLib and co.) is installed, ClassHierarchyManager can cache a lot of Strings and seem to be unused in any transformation purposes. This clears ClassHierarchyManager of those redundant strings. This option will be ignored when ChickenASM isn't installed", true);
optimizeAmuletRelatedFunctions = getBoolean("optimizeAmuletRelatedFunctions", "modfixes", "Optimizes Astral Sorcery's Resplendent Prism related functions. This option will be ignored when Astral Sorcery isn't installed", true);
labelCanonicalization = getBoolean("labelCanonicalization", "modfixes", "When Just Enough Items is installed, it deduplicates strings in the generated generalized suffix trees' edge labels", true);
skipCraftTweakerRecalculatingSearchTrees = getBoolean("skipCraftTweakerRecalculatingSearchTrees", "modfixes", "When CraftTweaker is installed, large modpacks tend to stall in the last stage of loading, when CraftTweaker inexplicably recalculates search trees", true);
labelCanonicalization = getBoolean("labelCanonicalization", "modfixes", "When Just Enough Items is installed, it deduplicates strings in the generated generalized suffix trees' edge labels. This option will be ignored when Just Enough Items isn't installed", true);
skipCraftTweakerRecalculatingSearchTrees = getBoolean("skipCraftTweakerRecalculatingSearchTrees", "modfixes", "When CraftTweaker is installed, large modpacks tend to stall in the last stage of loading, when CraftTweaker inexplicably recalculates search trees. This option will be ignored when CraftTweaker isn't installed", true);
bwmBlastingOilOptimization = getBoolean("bwmBlastingOilOptimization", "modfixes", "When Better with Mods is installed, optimize Blasting Oil related events. The original implementation harms server performance at any given moment. This option will be ignored when Better with Mods isn't installed", true);

fixAmuletHolderCapability = getBoolean("fixAmuletHolderCapability", "capability", "Fixes Astral Sorcery applying AmuletHolderCapability to large amount of ItemStacks when it isn't needed. This option will be ignored when Astral Sorcery isn't installed", true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
@IFMLLoadingPlugin.MCVersion(ForgeVersion.mcVersion)
public class LoliLoadingPlugin implements IFMLLoadingPlugin {

public static final String VERSION = "3.0.4";
public static final String VERSION = "3.0.5";

public static final boolean isDeobf = FMLLaunchHandler.isDeobfuscatedEnvironment();
public static final boolean isOptifineInstalled = LoliReflector.doesClassExist("optifine.OptiFineForgeTweaker");
Expand Down
50 changes: 42 additions & 8 deletions src/main/java/zone/rong/loliasm/core/LoliTransformer.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package zone.rong.loliasm.core;

import betterwithmods.module.ModuleLoader;
import betterwithmods.module.gameplay.Gameplay;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.launchwrapper.IClassTransformer;
import net.minecraft.launchwrapper.Launch;
import net.minecraftforge.fml.common.Loader;
import org.apache.commons.lang3.ArrayUtils;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.*;
Expand All @@ -11,21 +16,18 @@
import zone.rong.loliasm.LoliLogger;
import zone.rong.loliasm.patches.*;

import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.function.Function;

import static org.objectweb.asm.Opcodes.*;

public class LoliTransformer implements IClassTransformer {

Map<String, Function<byte[], byte[]>> transformations;
Multimap<String, Function<byte[], byte[]>> transformations;

public LoliTransformer() {
LoliLogger.instance.info("The lolis are now preparing to bytecode manipulate your game.");
transformations = new Object2ObjectOpenHashMap<>();
transformations = MultimapBuilder.hashKeys(30).arrayListValues(1).build();
if (LoliLoadingPlugin.isClient) {
// addTransformation("codechicken.lib.model.loader.blockstate.CCBlockStateLoader", bytes -> stripSubscribeEventAnnotation(bytes, "onModelBake", "onTextureStitchPre"));
if (LoliLoadingPlugin.squashBakedQuads) {
Expand Down Expand Up @@ -90,6 +92,10 @@ public LoliTransformer() {
if (LoliConfig.instance.labelCanonicalization) {
addTransformation("mezz.jei.suffixtree.Edge", this::deduplicateEdgeLabels);
}
if (LoliConfig.instance.bwmBlastingOilOptimization) {
addTransformation("betterwithmods.event.BlastingOilEvent", bytes -> stripSubscribeEventAnnotation(bytes, "onPlayerTakeDamage", "onHitGround"));
addTransformation("betterwithmods.common.items.ItemMaterial", this::injectBlastingOilEntityItemUpdate);
}
addTransformation("net.minecraft.nbt.NBTTagCompound", bytes -> nbtTagCompound$replaceDefaultHashMap(bytes, LoliConfig.instance.optimizeNBTTagCompoundBackingMap, LoliConfig.instance.nbtBackingMapStringCanonicalization));
}

Expand All @@ -100,10 +106,15 @@ public void addTransformation(String key, Function<byte[], byte[]> value) {

@Override
public byte[] transform(String name, String transformedName, byte[] bytes) {
Function<byte[], byte[]> getBytes = transformations.get(transformedName);
Collection<Function<byte[], byte[]>> getBytes = transformations.get(transformedName);
if (getBytes != null) {
return getBytes.apply(bytes);
byte[] transformedByteArray = bytes;
for (Function<byte[], byte[]> func : getBytes) {
transformedByteArray = func.apply(transformedByteArray);
}
return transformedByteArray;
}
// transformations.removeAll(transformedName);
return bytes;
}

Expand Down Expand Up @@ -711,4 +722,27 @@ private byte[] canonicalizeSpriteNames(byte[] bytes) {
return writer.toByteArray();
}

private byte[] injectBlastingOilEntityItemUpdate(byte[] bytes) {
LoliLogger.instance.info(ModuleLoader.config);
if (!Gameplay.disableBlastingOilEvents) {
ClassReader reader = new ClassReader(bytes);
ClassNode node = new ClassNode();
reader.accept(node, 0);

ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
node.accept(writer);
MethodVisitor methodVisitor = writer.visitMethod(ACC_PUBLIC, "onEntityItemUpdate", "(Lnet/minecraft/entity/item/EntityItem;)Z", null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitMethodInsn(INVOKESTATIC, "zone/rong/loliasm/common/modfixes/betterwithmods/BWMBlastingOilOptimization", "inject$ItemMaterial$onEntityItemUpdate", "(Lnet/minecraft/entity/item/EntityItem;)Z", false);
methodVisitor.visitInsn(IRETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
writer.visitEnd();

return writer.toByteArray();
}
return bytes;
}

}
Loading

0 comments on commit f1509a0

Please sign in to comment.