Skip to content

Commit b8c90d1

Browse files
Upsteam updates
- SurfaceRule optimizations are pending. - Fixes #40
1 parent 512a4f7 commit b8c90d1

9 files changed

Lines changed: 129 additions & 85 deletions

File tree

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ org.gradle.jvmargs=-Xmx2G
44

55
# Mod properties
66
mod_id=modernfix
7-
version=5.27.7-build.1
7+
version=5.27.17-build.1
88

99
# Minecraft/Fabric
1010
minecraft_version=26.1.2

src/main/java/org/embeddedt/modernfix/ModernFix.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.embeddedt.modernfix.util.ClassInfoManager;
1616
import org.spongepowered.asm.mixin.MixinEnvironment;
1717

18+
import net.minecraft.client.Minecraft;
1819
import java.lang.management.ManagementFactory;
1920

2021
// The value here should match an entry in the META-INF/mods.toml file
@@ -51,6 +52,8 @@ public static void runAuditIfRequested() {
5152
if (auditAndExit || Boolean.getBoolean("modernfix.auditMixinsAtStart")) {
5253
MixinEnvironment.getCurrentEnvironment().audit();
5354
if (auditAndExit) {
55+
// Prevents Crash Assistant from treating mixin audit as a crash
56+
Minecraft.getInstance().stop();
5457
System.exit(0);
5558
}
5659
}

src/main/java/org/embeddedt/modernfix/common/mixin/perf/attribute_supplier_dedup/AttributeSupplierMixin.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package org.embeddedt.modernfix.common.mixin.perf.attribute_supplier_dedup;
22

3-
import net.minecraft.core.Holder;
3+
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
44
import net.minecraft.world.entity.ai.attributes.Attribute;
55
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
66
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
@@ -19,15 +19,15 @@ public class AttributeSupplierMixin {
1919
@Shadow
2020
@Final
2121
@Mutable
22-
private Map<Holder<Attribute>, AttributeInstance> instances;
22+
private Map<Attribute, AttributeInstance> instances;
2323

2424
/**
2525
* @author embeddedt
26-
* @reason Java 9's Map.of() implementation is significantly more compact than ImmutableMap, and we do not
26+
* @reason more compact than ImmutableMap due to less wrapper objects, and we do not
2727
* care about insertion order in this context
2828
*/
2929
@Inject(method = "<init>", at = @At("RETURN"))
30-
private void useCompactJavaMap(Map<Holder<Attribute>, AttributeInstance> instances, CallbackInfo ci) {
31-
this.instances = Map.copyOf(this.instances);
30+
private void useCompactJavaMap(Map<Attribute, AttributeInstance> instances, CallbackInfo ci) {
31+
this.instances = new Object2ObjectOpenHashMap<>(this.instances);
3232
}
3333
}

src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_languages/ClientLanguageMixin.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,28 @@ public class ClientLanguageMixin {
2828
* @reason collect the list of all known language resources
2929
*/
3030
@WrapOperation(method = "loadFrom", at = @At(value = "INVOKE",
31-
target = "Lnet/minecraft/client/resources/language/ClientLanguage;appendFrom(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V"))
31+
target = "Lnet/minecraft/client/resources/language/ClientLanguage;appendFrom(Ljava/lang/String;Ljava/util/List;Ljava/util/Map;Ljava/util/Map;)V"))
3232
private static void collectResources(String languageName, List<Resource> resources,
33-
Map<String, String> destinationMap, Operation<Void> original,
33+
Map<String, String> destinationMap,
34+
Map<String, net.minecraft.network.chat.Component> componentMap,
35+
Operation<Void> original,
3436
@Share("usedResources") LocalRef<List<Resource>> usedResources) {
3537
List<Resource> collected = usedResources.get();
3638
if (collected == null) {
3739
collected = new ArrayList<>();
3840
usedResources.set(collected);
3941
}
4042
collected.addAll(resources);
41-
original.call(languageName, resources, destinationMap);
43+
original.call(languageName, resources, destinationMap, componentMap);
4244
}
4345

4446
/**
4547
* @author embeddedt
4648
* @reason figure out which keys are dynamically loaded and which are injected by mixins
4749
*/
48-
@ModifyArg(method = "loadFrom", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/language/ClientLanguage;<init>(Ljava/util/Map;Z)V"), index = 0)
50+
@ModifyArg(method = "loadFrom", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/language/ClientLanguage;<init>(Ljava/util/Map;ZLjava/util/Map;)V"), index = 0)
4951
private static Map<String, String> modifyLanguageMap(Map<String, String> storage, @Share("usedResources") LocalRef<List<Resource>> usedResources) {
5052
List<Resource> collected = Objects.requireNonNullElse(usedResources.get(), List.of());
5153
return DynamicLanguageMap.forVanillaData(storage, collected);
5254
}
53-
}
55+
}

src/main/java/org/embeddedt/modernfix/common/mixin/perf/dynamic_resources/MixinModelManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
2323

2424
import java.util.Map;
25+
import java.util.Objects;
2526
import java.util.concurrent.CompletableFuture;
2627
import java.util.concurrent.CompletionStage;
2728
import java.util.function.Function;
@@ -67,6 +68,7 @@ private static Object2IntMap<BlockState> buildModelGroups(BlockColors blockColor
6768
@Overwrite
6869
private static Map<BlockState, BlockStateModel> createBlockStateToModelDispatch(Map<BlockState, BlockStateModel> blockStateModels, BlockStateModel missingModel) {
6970
BlockStateModelMap.resetCache();
71+
Objects.requireNonNull(missingModel);
7072
return new BlockStateModelMap(blockStateModels, missingModel);
7173
}
7274
}

src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ private ModernFixEarlyConfig(File file) {
235235
disableIfModPresent("mixin.bugfix.paper_chunk_patches", "c2me");
236236
disableIfModPresent("mixin.bugfix.preserve_early_window_pos", "better_loading_screen");
237237
disableIfModPresent("mixin.perf.dynamic_dfu", "litematica");
238-
disableIfModPresent("mixin.perf.cache_strongholds", "littletiles", "c2me");
238+
disableIfModPresent("mixin.perf.cache_strongholds", "littletiles", "c2me", "flashback");
239239
// content overlap
240240
disableIfModPresent("mixin.perf.deduplicate_wall_shapes", "dashloader");
241241
disableIfModPresent("mixin.perf.nbt_memory_usage", "c2me");

src/main/java/org/embeddedt/modernfix/dynresources/BlockStateModelMap.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import java.util.Collection;
1111
import java.util.Map;
12+
import java.util.Objects;
1213
import java.util.Set;
1314

1415
/**
@@ -18,6 +19,10 @@
1819
public record BlockStateModelMap(Map<BlockState, BlockStateModel> modelMap,
1920
BlockStateModel fallbackModel) implements Map<BlockState, BlockStateModel> {
2021

22+
public BlockStateModelMap {
23+
Objects.requireNonNull(fallbackModel);
24+
}
25+
2126
@Override
2227
public int size() {
2328
return Block.BLOCK_STATE_REGISTRY.size();
@@ -55,6 +60,12 @@ public BlockStateModel get(Object o) {
5560
}
5661
}
5762

63+
@Override
64+
public BlockStateModel getOrDefault(Object key, BlockStateModel defaultValue) {
65+
var value = get(key);
66+
return value != fallbackModel ? value : defaultValue;
67+
}
68+
5869
@Override
5970
public @Nullable BlockStateModel put(BlockState blockState, BlockStateModel blockStateModel) {
6071
var oldModel = modelMap.put(blockState, blockStateModel);
@@ -102,4 +113,4 @@ public static void resetCache() {
102113
((IModelHoldingBlockState) state).mfix$setModel(null);
103114
}
104115
}
105-
}
116+
}

src/main/java/org/embeddedt/modernfix/dynresources/DynamicModelSystem.java

Lines changed: 83 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,24 @@
44
import com.google.common.cache.CacheLoader;
55
import com.google.common.cache.LoadingCache;
66
import com.google.common.collect.Maps;
7-
import com.google.common.collect.Sets;
87
import it.unimi.dsi.fastutil.objects.AbstractObject2IntMap;
98
import it.unimi.dsi.fastutil.objects.Object2IntMap;
109
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
1110
import it.unimi.dsi.fastutil.objects.ObjectSet;
1211
import it.unimi.dsi.fastutil.objects.ObjectSets;
13-
import it.unimi.dsi.fastutil.objects.ReferenceSets;
1412
import net.fabricmc.fabric.impl.client.model.loading.UnbakedModelDeserializerRegistry;
1513
import net.minecraft.client.color.block.BlockColors;
1614
import net.minecraft.client.renderer.item.ClientItem;
17-
import net.minecraft.client.renderer.block.dispatch.BlockStateModel;
18-
import net.minecraft.client.resources.model.cuboid.ItemModelGenerator;
1915
import net.minecraft.client.resources.model.BlockStateModelLoader;
2016
import net.minecraft.client.resources.model.ClientItemInfoLoader;
21-
import net.minecraft.client.resources.model.cuboid.MissingCuboidModel;
2217
import net.minecraft.client.resources.model.ModelDiscovery;
2318
import net.minecraft.client.resources.model.ModelManager;
2419
import net.minecraft.client.resources.model.ResolvedModel;
2520
import net.minecraft.client.resources.model.UnbakedModel;
26-
import net.minecraft.resources.FileToIdConverter;
21+
import net.minecraft.client.resources.model.cuboid.ItemModelGenerator;
22+
import net.minecraft.client.resources.model.cuboid.MissingCuboidModel;
2723
import net.minecraft.core.registries.BuiltInRegistries;
24+
import net.minecraft.resources.FileToIdConverter;
2825
import net.minecraft.resources.Identifier;
2926
import net.minecraft.server.packs.resources.Resource;
3027
import net.minecraft.world.level.block.Block;
@@ -40,6 +37,8 @@
4037
import java.util.Iterator;
4138
import java.util.List;
4239
import java.util.Map;
40+
import java.util.Objects;
41+
import java.util.Optional;
4342
import java.util.Set;
4443
import java.util.function.BiFunction;
4544
import java.util.function.Function;
@@ -51,46 +50,77 @@ public class DynamicModelSystem {
5150
private static final FileToIdConverter ITEM_LISTER = FileToIdConverter.json("items");
5251

5352
public static final boolean DEBUG_DYNAMIC_MODEL_LOADING = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
54-
55-
public static Map<Identifier, UnbakedModel> createDynamicUnbakedModelMap(Map<Identifier, Resource> resourceMap) {
56-
LoadingCache<Identifier, UnbakedModel> unbakedModelCache = CacheBuilder.newBuilder().softValues().maximumSize(1000).build(new CacheLoader<>() {
53+
54+
private interface ResultLoader<RESOURCE, RESULT> {
55+
RESULT load(Identifier file, @Nullable RESOURCE resource) throws Exception;
56+
}
57+
58+
private static <RESOURCE, RESULT> Map<Identifier, RESULT> createCachedResourceBackedMap(Map<Identifier, RESOURCE> resourceMap,
59+
FileToIdConverter converter,
60+
String debugName,
61+
ResultLoader<RESOURCE, RESULT> loader) {
62+
LoadingCache<Identifier, Optional<RESULT>> resultCache = CacheBuilder.newBuilder().softValues().maximumSize(1000).build(new CacheLoader<>() {
5763
@Override
58-
public UnbakedModel load(Identifier key) throws Exception {
59-
var resource = resourceMap.get(MODEL_LISTER.idToFile(key));
64+
public Optional<RESULT> load(Identifier id) throws Exception {
65+
var file = converter.idToFile(id);
66+
var resource = resourceMap.get(file);
6067
if (resource == null) {
61-
throw new IllegalArgumentException("Model " + key + " does not exist in map");
68+
return Optional.empty();
6269
}
6370
if (DEBUG_DYNAMIC_MODEL_LOADING) {
64-
ModernFix.LOGGER.info("Loading unbaked model {}", key);
65-
}
66-
try (Reader reader = resource.openAsReader()) {
67-
return UnbakedModelDeserializerRegistry.deserialize(reader);
71+
ModernFix.LOGGER.info("Loading {} {}", debugName, id);
6872
}
73+
return Optional.of(loader.load(file, resource));
74+
}
75+
});
76+
Set<Identifier> idSet = resourceMap.keySet().stream().map(converter::fileToId).collect(Collectors.toUnmodifiableSet());
77+
return new DynamicRegistryMap<>(idSet, key -> {
78+
if (key == null) {
79+
return null;
80+
}
81+
try {
82+
return resultCache.getUnchecked(key).orElse(null);
83+
} catch (RuntimeException e) {
84+
ModernFix.LOGGER.error("Error loading {} {} from cache", debugName, key);
85+
throw e;
86+
}
87+
});
88+
}
89+
90+
public static Map<Identifier, UnbakedModel> createDynamicUnbakedModelMap(Map<Identifier, Resource> resourceMap) {
91+
return createCachedResourceBackedMap(resourceMap, MODEL_LISTER, "unbaked model", (id, resource) -> {
92+
Objects.requireNonNull(resource, "unbaked model not present");
93+
try (Reader reader = resource.openAsReader()) {
94+
return UnbakedModelDeserializerRegistry.deserialize(reader);
6995
}
7096
});
71-
Set<Identifier> unbakedIdSet = resourceMap.keySet().stream().map(MODEL_LISTER::fileToId).collect(Collectors.toUnmodifiableSet());
72-
return Maps.asMap(unbakedIdSet, key -> key != null ? unbakedModelCache.getUnchecked(key) : null);
7397
}
7498

7599
public interface SingleBlockStateEntryLoader {
76100
BlockStateModelLoader.LoadedModels loadEntry(Identifier identifier, List<Resource> blockstateResources);
77101
}
78102

79-
private static Set<BlockState> getAllBlockStates() {
80-
return ((IdMapperAccessor<BlockState>) Block.BLOCK_STATE_REGISTRY).getReferenceMap().keySet();
81-
}
82-
public static BlockStateModelLoader.LoadedModels createDynamicBlockStateLoadedModels(Map<Identifier, List<Resource>> resourceMap, SingleBlockStateEntryLoader entryLoader) {
83-
LoadingCache<Identifier, BlockStateModelLoader.LoadedModels> definitionCache = CacheBuilder.newBuilder().softValues().maximumSize(1000).build(new CacheLoader<>() {
103+
public static Set<BlockState> getAllBlockStates() {
104+
var blockStateSet = ((IdMapperAccessor<BlockState>) Block.BLOCK_STATE_REGISTRY).getReferenceMap().keySet();
105+
return new AbstractSet<>() {
84106
@Override
85-
public BlockStateModelLoader.LoadedModels load(Identifier key) throws Exception {
86-
if (DEBUG_DYNAMIC_MODEL_LOADING) {
87-
ModernFix.LOGGER.info("Loading blockstate definition for {}", key);
88-
}
89-
var file = BLOCKSTATE_LISTER.idToFile(key);
90-
var resources = resourceMap.getOrDefault(file, List.of());
91-
return entryLoader.loadEntry(file, resources);
107+
public Iterator<BlockState> iterator() {
108+
// We explicitly override iterator() and handle it differently so that mods iterating the maps
109+
// are likely to work with the same block many times in a row, which hits our caches better
110+
return BuiltInRegistries.BLOCK.stream().flatMap(b -> b.getStateDefinition().getPossibleStates().stream()).iterator();
92111
}
93-
});
112+
@Override
113+
public boolean contains(Object o) {
114+
return blockStateSet.contains(o);
115+
}
116+
@Override
117+
public int size() {
118+
return blockStateSet.size();
119+
}
120+
};
121+
}
122+
public static BlockStateModelLoader.LoadedModels createDynamicBlockStateLoadedModels(Map<Identifier, List<Resource>> resourceMap, SingleBlockStateEntryLoader entryLoader) {
123+
var blockStateDefinitions = createCachedResourceBackedMap(resourceMap, BLOCKSTATE_LISTER, "blockstate definition", entryLoader::loadEntry);
94124
var staticDefinitions = BlockStateDefinitionsAccessor.getStaticDefinitions();
95125
var staticIdentifiers = staticDefinitions.entrySet()
96126
.stream()
@@ -102,8 +132,12 @@ public BlockStateModelLoader.LoadedModels load(Identifier key) throws Exception
102132
if (identifier == null) {
103133
identifier = BuiltInRegistries.BLOCK.getKey(state.getBlock());
104134
}
105-
var loadedModels = definitionCache.getUnchecked(identifier);
106-
return loadedModels.models().get(state);
135+
var loadedModels = blockStateDefinitions.get(identifier);
136+
if (loadedModels != null) {
137+
return loadedModels.models().get(state);
138+
} else {
139+
return null;
140+
}
107141
}));
108142
}
109143

@@ -112,34 +146,8 @@ public interface SingleClientItemEntryLoader {
112146
}
113147

114148
public static ClientItemInfoLoader.LoadedClientInfos createDynamicClientInfos(Map<Identifier, Resource> resourceMap, SingleClientItemEntryLoader entryLoader) {
115-
Set<Identifier> itemIdSet = resourceMap.keySet().stream().map(ITEM_LISTER::fileToId).collect(Collectors.toUnmodifiableSet());
116-
LoadingCache<Identifier, Object> clientItemCache = CacheBuilder.newBuilder().softValues().maximumSize(1000).build(new CacheLoader<>() {
117-
@Override
118-
public Object load(Identifier key) {
119-
Identifier fileId = ITEM_LISTER.idToFile(key);
120-
Resource resource = resourceMap.get(fileId);
121-
if (resource == null) {
122-
return NULL_SENTINEL;
123-
}
124-
if (DEBUG_DYNAMIC_MODEL_LOADING) {
125-
ModernFix.LOGGER.info("Loading client item info {}", key);
126-
}
127-
try {
128-
ClientItem result = entryLoader.loadEntry(fileId, resource);
129-
return result != null ? result : NULL_SENTINEL;
130-
} catch (RuntimeException e) {
131-
ModernFix.LOGGER.warn("Failed to build dynamic client item info for {}", key, e);
132-
return NULL_SENTINEL;
133-
}
134-
}
135-
});
136-
return new ClientItemInfoLoader.LoadedClientInfos(Maps.asMap(itemIdSet, key -> {
137-
if (key == null) {
138-
return null;
139-
}
140-
Object value = clientItemCache.getUnchecked(key);
141-
return value == NULL_SENTINEL ? null : (ClientItem) value;
142-
}));
149+
var clientItems = createCachedResourceBackedMap(resourceMap, ITEM_LISTER, "client item info", entryLoader::loadEntry);
150+
return new ClientItemInfoLoader.LoadedClientInfos(clientItems);
143151
}
144152

145153
public record DynamicResolver(Map<Identifier, UnbakedModel> inputModels,
@@ -205,28 +213,32 @@ public int getInt(Object key) {
205213
}
206214
}
207215

208-
private static final Object NULL_SENTINEL = new Object();
216+
private static final Object NULL_BAKED = new Object();
209217

210218
public static <K, U, V> Map<K, V> createDynamicBakedRegistry(Map<K, U> input, BiFunction<K, U, V> baker) {
211219
// TODO: support persistence of overrides
212220
LoadingCache<K, Object> bakedCache = CacheBuilder.newBuilder().softValues().maximumSize(1000).build(new CacheLoader<>() {
213221
@Override
214222
public Object load(K key) throws Exception {
215223
var unbaked = input.get(key);
216-
if (unbaked != null) {
217-
if (DEBUG_DYNAMIC_MODEL_LOADING) {
218-
ModernFix.LOGGER.info("Baking {}", key);
219-
}
220-
return baker.apply(key, unbaked);
221-
} else {
222-
return NULL_SENTINEL;
224+
if (unbaked == null) {
225+
return NULL_BAKED;
226+
}
227+
if (DEBUG_DYNAMIC_MODEL_LOADING) {
228+
ModernFix.LOGGER.info("Baking {}", key);
229+
}
230+
var bakerResult = baker.apply(key, unbaked);
231+
if (bakerResult == null) {
232+
ModernFix.LOGGER.warn("Baker has returned null for {}", key);
233+
return NULL_BAKED;
223234
}
235+
return bakerResult;
224236
}
225237
});
226238
return new DynamicRegistryMap<>(input.keySet(), k -> {
227239
if (k != null) {
228240
Object value = bakedCache.getUnchecked(k);
229-
if (value == NULL_SENTINEL) {
241+
if (value == NULL_BAKED) {
230242
value = null;
231243
}
232244
return (V) value;
@@ -235,4 +247,4 @@ public Object load(K key) throws Exception {
235247
}
236248
});
237249
}
238-
}
250+
}

0 commit comments

Comments
 (0)