Skip to content

Commit

Permalink
Update structure file cleaning
Browse files Browse the repository at this point in the history
- Use ASM instead of event handler
- Add config option to control which files to clear
- Add different cleaning modes
  • Loading branch information
Meldexun committed Nov 5, 2023
1 parent e3f1244 commit 05ebca6
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 158 deletions.
6 changes: 6 additions & 0 deletions src/main/java/com/charles445/rltweaker/asm/RLTweakerASM.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.charles445.rltweaker.asm.patch.PatchBountifulBaubles;
import com.charles445.rltweaker.asm.patch.PatchBroadcastSounds;
import com.charles445.rltweaker.asm.patch.PatchChunkTicks;
import com.charles445.rltweaker.asm.patch.PatchCleanStructureFiles;
import com.charles445.rltweaker.asm.patch.PatchConcurrentParticles;
import com.charles445.rltweaker.asm.patch.PatchDoorPathfinding;
import com.charles445.rltweaker.asm.patch.PatchEnchant;
Expand Down Expand Up @@ -454,6 +455,11 @@ private void createPatches()
new PatchFasterBlockCollision();
}

if(ASMConfig.getBoolean("general.patches.patchCleanupStructureWorldgenFiles", false))
{
new PatchCleanStructureFiles();
}

//new PatchForgeNetwork();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.charles445.rltweaker.asm.patch;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;

import com.charles445.rltweaker.asm.util.ASMUtil;
import com.charles445.rltweaker.asm.util.TransformUtil;

public class PatchCleanStructureFiles extends PatchManager {

public PatchCleanStructureFiles() {
super("Patch Clean Structure Files");

this.add(new Patch(this, "net.minecraft.world.WorldServer", ClassWriter.COMPUTE_FRAMES) {
@Override
public void patch(ClassNode clazzNode) {
MethodNode m_saveLevel = this.findMethod(clazzNode, "saveLevel");

TransformUtil.insertBeforeFirst(m_saveLevel, ASMUtil.listOf(
new VarInsnNode(Opcodes.ALOAD, 0),
new MethodInsnNode(Opcodes.INVOKESTATIC, "com/charles445/rltweaker/hook/HookMinecraft", "preSaveWorld", "(Lnet/minecraft/world/WorldServer;)V", false)));
}
});
}

}
22 changes: 14 additions & 8 deletions src/main/java/com/charles445/rltweaker/config/ConfigMinecraft.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.charles445.rltweaker.config;

import com.charles445.rltweaker.config.annotation.RLConfig;
import com.charles445.rltweaker.hook.StructureCleanupMode;

import net.minecraftforge.common.config.Config;
import net.minecraftforge.common.config.Config.RangeDouble;
Expand Down Expand Up @@ -63,14 +64,19 @@ public class ConfigMinecraft
@RLConfig.RLCraftTwoEightTwo("true")
@RLConfig.RLCraftTwoNine("true")
public boolean lessCollisions = true;

@Config.Comment("Cleans up Mineshaft .dat files regularly to lower RAM usage. May break mods that need to locate Mineshafts.")
@Config.Name("Cleanup Mineshaft Worldgen Files")
@RLConfig.ImprovementsOnly("true")
@RLConfig.RLCraftTwoEightTwo("true")
@RLConfig.RLCraftTwoNine("true")
public boolean cleanupMineshaftWorldgenFiles = true;


@Config.Comment("Cleans up structure .dat files regularly to lower RAM usage. May break mods that use this data. (Stronghold, Village, Mineshaft, Temple, Monument, Mansion, Fortress, EndCity)")
@Config.Name("Cleanup Structure Worldgen Files Structures")
public String[] cleanupStructureWorldgenFilesStructures = { "Village", "Mineshaft" };

@Config.Comment("")
@Config.Name("Cleanup Structure Worldgen Files Mode")
public StructureCleanupMode cleanupStructureWorldgenFilesMode = StructureCleanupMode.GENERATED;

@Config.Comment("")
@Config.Name("Cleanup Structure Worldgen Files Size Limit")
public int cleanupStructureWorldgenFilesSizeLimit = 1 << 14;

@Config.Comment("Replace thrown witch potions with configured potions")
@Config.Name("Witch Potion Replacements")
@RLConfig.ImprovementsOnly("false")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,8 @@ public class PatchConfig
@RLConfig.RLCraftTwoEightTwo("true")
@RLConfig.RLCraftTwoNine("true")
public boolean patchFasterBlockCollision = true;

@Config.RequiresMcRestart
@Config.Comment("Allows cleaning up structure .dat files.")
public boolean patchCleanupStructureWorldgenFiles = true;
}
148 changes: 0 additions & 148 deletions src/main/java/com/charles445/rltweaker/handler/MinecraftHandler.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.charles445.rltweaker.handler;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -31,16 +29,13 @@
import net.minecraft.init.SoundEvents;
import net.minecraft.inventory.Container;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.PotionType;
import net.minecraft.potion.PotionUtils;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.management.PlayerList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraft.world.gen.structure.MapGenStructureData;
import net.minecraft.world.storage.loot.LootPool;
import net.minecraft.world.storage.loot.LootTable;
import net.minecraft.world.storage.loot.LootTableList;
Expand All @@ -54,7 +49,6 @@
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
import net.minecraftforge.event.entity.living.LivingKnockBackEvent;
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedOutEvent;
Expand Down Expand Up @@ -122,148 +116,6 @@ public void onEntityJoinWorldEvent(EntityJoinWorldEvent event)
}
}

@SubscribeEvent
public void onWorldUnload(WorldEvent.Save event)
{
//Cleans up after the world gets saved, not before
//So a dimension must be loaded when saved for this to work

//This means cleanup will run erroneously when unloading a world, and on server start

World world = event.getWorld();

if(world.isRemote)
return;

//TODO other worldgen
//Uf you enter an area for the first time, leave, world saves twice, come back, the area will be broken
//This is very uncommon, but it's still a possibility
//They seem to regenerate when the game is restarted, which is very peculiar, but could also be incredibly helpful
//It may be possible to run these cleanups on server stopping, or something similar
//
//Nobody uses Mineshafts, though, so that cleaner gets to stay for now


if(ModConfig.server.minecraft.cleanupMineshaftWorldgenFiles)
{
//TODO fix checkIsLoaded to handle invalid structures
cleanMapGenStructureData(world, "Mineshaft", false, true);
//cleanMapGenStructureData(world, "Village", false, true);
//cleanMapGenStructureData(world, "Fortress", false, true);
}
}

private void cleanMapGenStructureData(World world, String dataName, boolean checkIsUngenerated, boolean checkIsLoaded)
{
long nanoA = System.nanoTime();
MapGenStructureData structureData = (MapGenStructureData)world.getPerWorldStorage().getOrLoadData(MapGenStructureData.class, dataName);
if(structureData == null)
return;

NBTTagCompound structureDataCompound = structureData.getTagCompound();

List<String> toRemove = new ArrayList<>();

long cleanupCount = 0L;

for (String s : structureDataCompound.getKeySet())
{
toRemove.add(s);

//Load check routine removed
/*
NBTBase structureNestedBase = structureDataCompound.getTag(s);
if (structureNestedBase.getId() == 10)
{
NBTTagCompound structureNestedCompound = (NBTTagCompound)structureNestedBase;
if (structureNestedCompound.hasKey("BB"))
{
boolean isLoaded = false;
boolean shouldRemove = false;
StructureBoundingBox sbb = new StructureBoundingBox(structureNestedCompound.getIntArray("BB"));
List<ChunkPos> containedChunks = containedChunks(sbb.minX,sbb.minZ, sbb.maxX, sbb.maxZ);
if(checkIsLoaded)
{
for(ChunkPos cPos : containedChunks)
{
if(((ChunkProviderServer)world.getChunkProvider()).chunkExists(cPos.x, cPos.z))
{
isLoaded = true;
break;
}
}
if(isLoaded)
{
//Skip this removal as it's currently loaded
containedChunks.clear();
continue;
}
}
if(checkIsUngenerated)
{
for(ChunkPos cPos : containedChunks)
{
if(!world.isChunkGeneratedAt(cPos.x, cPos.z))
{
shouldRemove = true;
break;
}
}
}
else
{
shouldRemove = true;
}
containedChunks.clear();
if(shouldRemove)
toRemove.add(s);
}
}
*/
}

for(String remove : toRemove)
{
structureDataCompound.removeTag(remove);
}

if(toRemove.size() > 0)
structureData.markDirty();

long nanoB = System.nanoTime();

//RLTweaker.logger.debug(""+dataName+" cleanup took nanoseconds: "+(nanoB - nanoA));
//RLTweaker.logger.debug(""+dataName+" cleanup cleaned entries: "+toRemove.size());

toRemove.clear();
}

private List<ChunkPos> containedChunks(int xIn1, int zIn1, int xIn2, int zIn2)
{
int xStart = xIn1 >> 4;
int zStart = zIn1 >> 4;
int xEnd = xIn2 >> 4;
int zEnd = zIn2 >> 4;

List<ChunkPos> chunks = new ArrayList<ChunkPos>();

for (int i = xStart; i <= xEnd; ++i)
{
for (int j = zStart; j <= zEnd; ++j)
{
chunks.add(new ChunkPos(i,j));
}
}

return chunks;
}

//Capabilities

@SubscribeEvent(priority = EventPriority.LOW)
Expand Down
19 changes: 17 additions & 2 deletions src/main/java/com/charles445/rltweaker/hook/HookMinecraft.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.charles445.rltweaker.hook;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.stream.Stream;

import javax.annotation.Nullable;

Expand All @@ -23,9 +25,7 @@
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.ai.attributes.IAttributeInstance;
import net.minecraft.entity.item.EntityItemFrame;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.inventory.ContainerRepair;
import net.minecraft.inventory.IInventory;
Expand All @@ -39,7 +39,10 @@
import net.minecraft.world.ChunkCache;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.gen.structure.MapGenStructureData;
import net.minecraft.world.storage.MapStorage;
import net.minecraftforge.items.IItemHandler;

public class HookMinecraft
Expand Down Expand Up @@ -346,4 +349,16 @@ public static Chunk cacheGetChunkFromChunkCoords(World world, int chunkX, int ch
return world.getChunkFromChunkCoords(chunkX, chunkZ);
}
}

public static void preSaveWorld(WorldServer world) {
MapStorage mapStorage = world.getPerWorldStorage();

Stream.of(ModConfig.server.minecraft.cleanupStructureWorldgenFilesStructures)
.map(structureName -> mapStorage.getOrLoadData(MapGenStructureData.class, structureName))
.map(MapGenStructureData.class::cast)
.filter(Objects::nonNull)
.filter(structureData -> ModConfig.server.minecraft.cleanupStructureWorldgenFilesMode.clean(world, structureData))
.forEach(MapGenStructureData::markDirty);
}

}
Loading

0 comments on commit 05ebca6

Please sign in to comment.