ModernFix/src/main/java/org/embeddedt/modernfix/dynamicresources/DynamicModelCache.java
2025-12-26 19:04:59 -05:00

80 lines
2.2 KiB
Java

package org.embeddedt.modernfix.dynamicresources;
import it.unimi.dsi.fastutil.Function;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap;
import net.minecraft.client.resources.model.BakedModel;
import java.util.concurrent.locks.StampedLock;
/**
* The Mojang Triple-based baked cache system is too slow to be hitting on every model retrieval, so
* we need a fast, concurrency-safe wrapper on top.
*/
public class DynamicModelCache<K> {
private final Reference2ReferenceLinkedOpenHashMap<K, BakedModel> cache = new Reference2ReferenceLinkedOpenHashMap<>();
private final StampedLock lock = new StampedLock();
private final Function<K, BakedModel> modelRetriever;
private final boolean allowNulls;
public DynamicModelCache(Function<K, BakedModel> modelRetriever, boolean allowNulls) {
this.modelRetriever = modelRetriever;
this.allowNulls = allowNulls;
}
public void clear() {
long stamp = lock.writeLock();
try {
cache.clear();
} finally {
lock.unlock(stamp);
}
}
private boolean needToPopulate(K state) {
long stamp = lock.readLock();
try {
return !cache.containsKey(state);
} finally {
lock.unlock(stamp);
}
}
private BakedModel getModelFromCache(K state) {
long stamp = lock.readLock();
try {
return cache.get(state);
} finally {
lock.unlock(stamp);
}
}
private BakedModel cacheModel(K state) {
BakedModel model = modelRetriever.apply(state);
// Lock and modify our local, faster cache
long stamp = lock.writeLock();
try {
cache.putAndMoveToFirst(state, model);
// TODO: choose less arbitrary number
if(cache.size() >= 1000) {
cache.removeLast();
}
} finally {
lock.unlock(stamp);
}
return model;
}
public BakedModel get(K key) {
BakedModel model = getModelFromCache(key);
if(model == null && (!allowNulls || needToPopulate(key))) {
model = cacheModel(key);
}
return model;
}
}