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);
}
@Override
public void write(CompoundTag compound, boolean clientPacket) {
protected void writeCommon(CompoundTag compound) {
compound.putFloat("OnAbove", onWhenAbove);
compound.putFloat("OffBelow", offWhenBelow);
compound.putBoolean("Inverted", inverted);
}
@Override
public void write(CompoundTag compound, boolean clientPacket) {
writeCommon(compound);
compound.putFloat("Current", currentLevel);
compound.putBoolean("Powered", redstoneState);
compound.putBoolean("Inverted", inverted);
compound.putBoolean("PoweredAfterDelay", poweredAfterDelay);
super.write(compound, clientPacket);
}
@Override
public void writeSafe(CompoundTag compound) {
writeCommon(compound);
super.writeSafe(compound);
}
public float getStockLevel() {
return currentLevel;
}

View file

@ -31,7 +31,10 @@ public enum ContraptionMovementSetting {
public static ContraptionMovementSetting get(Block block) {
if (block instanceof IMovementSettingProvider provider)
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) {

View file

@ -1,36 +1,50 @@
package com.simibubi.create.foundation.utility;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.Nonnull;
import net.minecraft.world.level.LevelAccessor;
import net.minecraftforge.common.util.NonNullFunction;
public class WorldAttached<T> {
static List<Map<LevelAccessor, ?>> allMaps = new ArrayList<>();
Map<LevelAccessor, T> attached;
private final NonNullFunction<LevelAccessor, T> factory;
// weak references to prevent leaking hashmaps when a WorldAttached is GC'd during runtime
static List<WeakReference<Map<LevelAccessor, ?>>> allMaps = new ArrayList<>();
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;
attached = new HashMap<>();
allMaps.add(attached);
allMaps.add(new WeakReference<>(attached));
}
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
public T get(LevelAccessor world) {
T t = attached.get(world);
if (t != null)
return t;
if (t != null) return t;
T entry = factory.apply(world);
put(world, entry);
return entry;
@ -40,4 +54,47 @@ public class WorldAttached<T> {
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();
}
}