Crash and leak fix

- Fix crash when invoking ContraptionMovementSetting#get
- Fix memory leak in WorldAttached by copying Flywheel's updated version
- Fix stockpile switches not preserving settings after being printed
This commit is contained in:
PepperCode1 2022-08-03 22:36:14 -07:00
parent 9c8df2ff27
commit 9fbb71e4e9
3 changed files with 83 additions and 13 deletions

View file

@ -59,17 +59,27 @@ public class StockpileSwitchTileEntity extends SmartTileEntity {
super.read(compound, clientPacket); super.read(compound, clientPacket);
} }
@Override protected void writeCommon(CompoundTag compound) {
public void write(CompoundTag compound, boolean clientPacket) {
compound.putFloat("OnAbove", onWhenAbove); compound.putFloat("OnAbove", onWhenAbove);
compound.putFloat("OffBelow", offWhenBelow); compound.putFloat("OffBelow", offWhenBelow);
compound.putBoolean("Inverted", inverted);
}
@Override
public void write(CompoundTag compound, boolean clientPacket) {
writeCommon(compound);
compound.putFloat("Current", currentLevel); compound.putFloat("Current", currentLevel);
compound.putBoolean("Powered", redstoneState); compound.putBoolean("Powered", redstoneState);
compound.putBoolean("Inverted", inverted);
compound.putBoolean("PoweredAfterDelay", poweredAfterDelay); compound.putBoolean("PoweredAfterDelay", poweredAfterDelay);
super.write(compound, clientPacket); super.write(compound, clientPacket);
} }
@Override
public void writeSafe(CompoundTag compound) {
writeCommon(compound);
super.writeSafe(compound);
}
public float getStockLevel() { public float getStockLevel() {
return currentLevel; return currentLevel;
} }

View file

@ -31,7 +31,10 @@ public enum ContraptionMovementSetting {
public static ContraptionMovementSetting get(Block block) { public static ContraptionMovementSetting get(Block block) {
if (block instanceof IMovementSettingProvider provider) if (block instanceof IMovementSettingProvider provider)
return provider.getContraptionMovementSetting(); return provider.getContraptionMovementSetting();
return SETTING_SUPPLIERS.get(block).get(); Supplier<ContraptionMovementSetting> supplier = SETTING_SUPPLIERS.get(block);
if (supplier == null)
return null;
return supplier.get();
} }
public static boolean allAre(Collection<StructureTemplate.StructureBlockInfo> blocks, ContraptionMovementSetting are) { public static boolean allAre(Collection<StructureTemplate.StructureBlockInfo> blocks, ContraptionMovementSetting are) {

View file

@ -1,36 +1,50 @@
package com.simibubi.create.foundation.utility; package com.simibubi.create.foundation.utility;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraftforge.common.util.NonNullFunction;
public class WorldAttached<T> { public class WorldAttached<T> {
static List<Map<LevelAccessor, ?>> allMaps = new ArrayList<>(); // weak references to prevent leaking hashmaps when a WorldAttached is GC'd during runtime
Map<LevelAccessor, T> attached; static List<WeakReference<Map<LevelAccessor, ?>>> allMaps = new ArrayList<>();
private final NonNullFunction<LevelAccessor, T> factory; private final Map<LevelAccessor, T> attached;
private final Function<LevelAccessor, T> factory;
public WorldAttached(NonNullFunction<LevelAccessor, T> factory) { public WorldAttached(Function<LevelAccessor, T> factory) {
this.factory = factory; this.factory = factory;
attached = new HashMap<>(); attached = new HashMap<>();
allMaps.add(attached); allMaps.add(new WeakReference<>(attached));
} }
public static void invalidateWorld(LevelAccessor world) { public static void invalidateWorld(LevelAccessor world) {
allMaps.forEach(m -> m.remove(world)); var i = allMaps.iterator();
while (i.hasNext()) {
Map<LevelAccessor, ?> map = i.next()
.get();
if (map == null) {
// If the map has been GC'd, remove the weak reference
i.remove();
} else {
// Prevent leaks
map.remove(world);
}
}
} }
@Nonnull @Nonnull
public T get(LevelAccessor world) { public T get(LevelAccessor world) {
T t = attached.get(world); T t = attached.get(world);
if (t != null) if (t != null) return t;
return t;
T entry = factory.apply(world); T entry = factory.apply(world);
put(world, entry); put(world, entry);
return entry; return entry;
@ -40,4 +54,47 @@ public class WorldAttached<T> {
attached.put(world, entry); attached.put(world, entry);
} }
/**
* Replaces the entry with a new one from the factory and returns the new entry.
*/
@Nonnull
public T replace(LevelAccessor world) {
attached.remove(world);
return get(world);
}
/**
* Replaces the entry with a new one from the factory and returns the new entry.
*/
@Nonnull
public T replace(LevelAccessor world, Consumer<T> finalizer) {
T remove = attached.remove(world);
if (remove != null)
finalizer.accept(remove);
return get(world);
}
/**
* Deletes all entries after calling a function on them.
*
* @param finalizer Do something with all of the world-value pairs
*/
public void empty(BiConsumer<LevelAccessor, T> finalizer) {
attached.forEach(finalizer);
attached.clear();
}
/**
* Deletes all entries after calling a function on them.
*
* @param finalizer Do something with all of the values
*/
public void empty(Consumer<T> finalizer) {
attached.values()
.forEach(finalizer);
attached.clear();
}
} }