Compare commits
298 Commits
1.20
...
5.19.1+1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
820169667a | ||
|
|
512a7e237c | ||
|
|
7db5d6a1da | ||
|
|
631ad0528b | ||
|
|
1daea1f5e3 | ||
|
|
b2eb14b766 | ||
|
|
70fba2e0af | ||
|
|
30e91f2056 | ||
|
|
beccbef151 | ||
|
|
c3b17b2927 | ||
|
|
783627f4c5 | ||
|
|
d7bfeedc62 | ||
|
|
212f139bf1 | ||
|
|
85740f83af | ||
|
|
d46d24542f | ||
|
|
700ccc25b7 | ||
|
|
1b6075562c | ||
|
|
2697a8f358 | ||
|
|
f056fe4d0c | ||
|
|
ef07197345 | ||
|
|
180606eea1 | ||
|
|
a6e0785252 | ||
|
|
4673293039 | ||
|
|
f2379dc9e3 | ||
|
|
d4cc7664f6 | ||
|
|
1dce8d4ccf | ||
|
|
6fd3dde1a2 | ||
|
|
f586522dfe | ||
|
|
168ab8effa | ||
|
|
6e9dfaf0c6 | ||
|
|
9d584d13d2 | ||
|
|
a0f391e258 | ||
|
|
8c51ccc022 | ||
|
|
390404d1d5 | ||
|
|
57af3c6491 | ||
|
|
99da801c3f | ||
|
|
c292f0ddce | ||
|
|
aa2a3817ba | ||
|
|
4473a4ffb4 | ||
|
|
b4398565a6 | ||
|
|
9cfccb58fa | ||
|
|
a9f4ce72f1 | ||
|
|
bc3af0450e | ||
|
|
d0598055c0 | ||
|
|
ab8810b7fe | ||
|
|
2ef7d5fc85 | ||
|
|
d8b207ff10 | ||
|
|
4e8427545f | ||
|
|
f85d31d24d | ||
|
|
6a7b6abb23 | ||
|
|
a443972d28 | ||
|
|
b6865eff6c | ||
|
|
cc653249e4 | ||
|
|
e75810db5b | ||
|
|
a4359e1ad5 | ||
|
|
95a5a1b7b1 | ||
|
|
d846a077ac | ||
|
|
2f98fadc9e | ||
|
|
de70fbafad | ||
|
|
a66a1dab9d | ||
|
|
4d251e61ca | ||
|
|
57d5f08b1d | ||
|
|
f13910a6ed | ||
|
|
bebf3ccec2 | ||
|
|
a5b1cbdc13 | ||
|
|
89592a2c42 | ||
|
|
3f688148d0 | ||
|
|
c8f9b1cd84 | ||
|
|
7ee968f719 | ||
|
|
2cce200e4d | ||
|
|
2e9a6f27e0 | ||
|
|
730932b018 | ||
|
|
c6338f9736 | ||
|
|
0277d80ce7 | ||
|
|
b1c0b0f813 | ||
|
|
0476cdc35a | ||
|
|
23bbf7b092 | ||
|
|
0061e1de8e | ||
|
|
159f27a8bf | ||
|
|
c5dd43f2a1 | ||
|
|
da065350d0 | ||
|
|
9bf4faa4a5 | ||
|
|
7bd88b4f04 | ||
|
|
3bedde4ebe | ||
|
|
2e1bed7cfb | ||
|
|
73f706164f | ||
|
|
5b5cbf9380 | ||
|
|
50556ab005 | ||
|
|
c3d78c3d09 | ||
|
|
ad07fb3f0c | ||
|
|
a9efc62867 | ||
|
|
4b741da54d | ||
|
|
60c2b02e37 | ||
|
|
042a7caa9e | ||
|
|
02e4cb6cf1 | ||
|
|
b2e3ae82eb | ||
|
|
180f4eaf8d | ||
|
|
95cd576c68 | ||
|
|
0ea4284846 | ||
|
|
8fc9c0b3c1 | ||
|
|
60fa5d4afe | ||
|
|
95a8e4fe21 | ||
|
|
add7dd4609 | ||
|
|
827550e8af | ||
|
|
9d7ef772a0 | ||
|
|
a47a61a923 | ||
|
|
5e20e25c4d | ||
|
|
e6b28de740 | ||
|
|
61c2116946 | ||
|
|
72845d8952 | ||
|
|
70eaabe756 | ||
|
|
e22f5caec7 | ||
|
|
8a872fed27 | ||
|
|
0a2299ee33 | ||
|
|
826e5a4c20 | ||
|
|
a62c0635a5 | ||
|
|
4584457a79 | ||
|
|
8f663a0b07 | ||
|
|
b65a63896b | ||
|
|
fdc98b2600 | ||
|
|
8806dffaea | ||
|
|
761703b4ab | ||
|
|
967d39997f | ||
|
|
48b492b906 | ||
|
|
96db279d58 | ||
|
|
8d2d3d8e15 | ||
|
|
b82942fe75 | ||
|
|
e6ca5f633c | ||
|
|
611dc4a266 | ||
|
|
eacab89181 | ||
|
|
139b967ce6 | ||
|
|
d52725012a | ||
|
|
23e6091747 | ||
|
|
3f7af1c2e0 | ||
|
|
f38a06ecc1 | ||
|
|
c8485eaef8 | ||
|
|
fc86d0eee0 | ||
|
|
6143d8a783 | ||
|
|
90cf39e9d8 | ||
|
|
739b1663cd | ||
|
|
4dfba0cab4 | ||
|
|
46fc1e8539 | ||
|
|
2699fe448e | ||
|
|
5c72a527ad | ||
|
|
25f13a9701 | ||
|
|
16ef9253e6 | ||
|
|
f25ecf337d | ||
|
|
675ce1cd43 | ||
|
|
c4855e0b70 | ||
|
|
6c206c47e9 | ||
|
|
daa84565a6 | ||
|
|
8658784906 | ||
|
|
d3758eb4f6 | ||
|
|
e9bfd965cd | ||
|
|
c2a2fd876b | ||
|
|
649c25d0d2 | ||
|
|
ad4bfc79ab | ||
|
|
a4bb17d2af | ||
|
|
b55ed00748 | ||
|
|
806fb7dcfe | ||
|
|
b5becf6ba3 | ||
|
|
c65fdbccc0 | ||
|
|
dcee8b4169 | ||
|
|
d6fc939f41 | ||
|
|
a012b60ae3 | ||
|
|
97a2c0e0f5 | ||
|
|
34bc295b3c | ||
|
|
e08531c228 | ||
|
|
456bca47b6 | ||
|
|
b25bb14d3d | ||
|
|
d4fcc80db0 | ||
|
|
64ea7eab19 | ||
|
|
432b137edb | ||
|
|
991ec339b2 | ||
|
|
1bf98ced86 | ||
|
|
7646bfa153 | ||
|
|
7be97e61e8 | ||
|
|
b23502c32b | ||
|
|
d03571cb05 | ||
|
|
e1c91f13ac | ||
|
|
324fb3af97 | ||
|
|
585fdb9e99 | ||
|
|
fa9d39bd31 | ||
|
|
501db5b84a | ||
|
|
8c46b4629d | ||
|
|
9dcc87b227 | ||
|
|
0eb70468a1 | ||
|
|
ba2b740075 | ||
|
|
063289faac | ||
|
|
d3ed56a1c1 | ||
|
|
45a7f1a63e | ||
|
|
04aac43db0 | ||
|
|
0ea5139315 | ||
|
|
06bb9f9545 | ||
|
|
adf169c3fa | ||
|
|
8d5e66218e | ||
|
|
45b734dead | ||
|
|
2ec1000ae8 | ||
|
|
1f66c7becd | ||
|
|
ccc21d76d8 | ||
|
|
4fec9f53c3 | ||
|
|
b8cd5adbb1 | ||
|
|
bf69e58169 | ||
|
|
6592ac5cac | ||
|
|
1f447b689f | ||
|
|
8df6fab0e7 | ||
|
|
328507cea3 | ||
|
|
c6b38f340a | ||
|
|
69a9aa76da | ||
|
|
87ee1017ae | ||
|
|
fcf21283d8 | ||
|
|
bfdd1f913d | ||
|
|
3187c80d48 | ||
|
|
f594ec6c5b | ||
|
|
efa46c3842 | ||
|
|
473597a915 | ||
|
|
84061b197f | ||
|
|
dc86d3e137 | ||
|
|
4d111cb381 | ||
|
|
29d1f88539 | ||
|
|
0ff032bd1a | ||
|
|
e34e9fcf8b | ||
|
|
9861926f1d | ||
|
|
acb6809459 | ||
|
|
fd975388ca | ||
|
|
13ee6b3523 | ||
|
|
af8e23f41a | ||
|
|
19ba30280c | ||
|
|
33609d234c | ||
|
|
6e3134161a | ||
|
|
27142d2c42 | ||
|
|
c672948d8c | ||
|
|
0a418bc55d | ||
|
|
d8263c55f8 | ||
|
|
a212a3fc7e | ||
|
|
7af609b56f | ||
|
|
45c11ea7e0 | ||
|
|
894173cf1d | ||
|
|
fc52570951 | ||
|
|
207521f778 | ||
|
|
9efe912090 | ||
|
|
15d16ffa9a | ||
|
|
95f2dc95d3 | ||
|
|
86c6e90436 | ||
|
|
915de8187e | ||
|
|
181be3cf80 | ||
|
|
79751dac21 | ||
|
|
184dec0739 | ||
|
|
1339fc7af6 | ||
|
|
624a4483eb | ||
|
|
3b7d2f1e2e | ||
|
|
91a084f860 | ||
|
|
546a362d9c | ||
|
|
87b984bfe9 | ||
|
|
7a2b71381c | ||
|
|
850fdcbf1b | ||
|
|
3add97202e | ||
|
|
5d481334a8 | ||
|
|
6d096e8ae0 | ||
|
|
c09b530a8c | ||
|
|
fbacc85f86 | ||
|
|
0e9142e68f | ||
|
|
f6f7badde8 | ||
|
|
2c5b664c68 | ||
|
|
2a099d8537 | ||
|
|
472ed42f22 | ||
|
|
9830d4d6f7 | ||
|
|
348a2bcf3d | ||
|
|
43da9ffcdc | ||
|
|
7f80f38862 | ||
|
|
231c3f41e5 | ||
|
|
cc54b6d67d | ||
|
|
e8fa483917 | ||
|
|
dc3cb998b0 | ||
|
|
972121fa50 | ||
|
|
93554d1854 | ||
|
|
a00efe62c7 | ||
|
|
1267a25db1 | ||
|
|
0149a113a8 | ||
|
|
97d4f6bbc2 | ||
|
|
dfb0c52fa3 | ||
|
|
02fa2be42b | ||
|
|
45c29216a8 | ||
|
|
5e8e7649e5 | ||
|
|
0a2cda0814 | ||
|
|
5952fa2178 | ||
|
|
9bf0017aeb | ||
|
|
cb286c0bca | ||
|
|
b56a65c192 | ||
|
|
8d1058cc3f | ||
|
|
eb15718023 | ||
|
|
861474d635 | ||
|
|
f3bda91ebf | ||
|
|
84c72f8da8 | ||
|
|
3716912a53 | ||
|
|
42688757a7 | ||
|
|
23b473f85d | ||
|
|
7fcaf716d8 |
|
|
@ -166,6 +166,7 @@ public class ClientMixinValidator {
|
||||||
|
|
||||||
clzsses = wrappedClzss.stream()
|
clzsses = wrappedClzss.stream()
|
||||||
.map(AnnotationValue::getValue)
|
.map(AnnotationValue::getValue)
|
||||||
|
.filter(o -> o instanceof TypeMirror)
|
||||||
.map(TypeMirror.class::cast)
|
.map(TypeMirror.class::cast)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ public record MixinConfig(
|
||||||
InjectorOptions injectors, OverwriteOptions overwrites
|
InjectorOptions injectors, OverwriteOptions overwrites
|
||||||
) {
|
) {
|
||||||
public MixinConfig(String packageName, List<String> commonMixins, List<String> clientMixins) {
|
public MixinConfig(String packageName, List<String> commonMixins, List<String> clientMixins) {
|
||||||
this(true, "0.8", packageName, "org.embeddedt.modernfix.core.ModernFixMixinPlugin", "JAVA_17",
|
this(true, "0.8", packageName, "org.embeddedt.modernfix.core.ModernFixMixinPlugin", "JAVA_21",
|
||||||
commonMixins, clientMixins, InjectorOptions.DEFAULT, OverwriteOptions.DEFAULT);
|
commonMixins, clientMixins, InjectorOptions.DEFAULT, OverwriteOptions.DEFAULT);
|
||||||
}
|
}
|
||||||
public record InjectorOptions(int defaultRequire) {
|
public record InjectorOptions(int defaultRequire) {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ def versionString = "${baseVersion}${preMarker}+mc${minecraft_version}${commitHa
|
||||||
version = versionString
|
version = versionString
|
||||||
archivesBaseName = rootProject.archives_base_name + '-' + project.name
|
archivesBaseName = rootProject.archives_base_name + '-' + project.name
|
||||||
|
|
||||||
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_21
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
exclusiveContent {
|
exclusiveContent {
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ curseforge {
|
||||||
changelog = file("${rootDir}/CHANGELOG.md")
|
changelog = file("${rootDir}/CHANGELOG.md")
|
||||||
changelogType = "markdown"
|
changelogType = "markdown"
|
||||||
releaseType = isBeta ? "beta" : "release"
|
releaseType = isBeta ? "beta" : "release"
|
||||||
addGameVersion project.name.capitalize()
|
addGameVersion (project.name.equals("neoforge") ? "NeoForge" : project.name.capitalize())
|
||||||
gameVersionStrings.addAll(supported_minecraft_versions.tokenize(","))
|
gameVersionStrings.addAll(supported_minecraft_versions.tokenize(","))
|
||||||
mainArtifact remapJar
|
mainArtifact remapJar
|
||||||
}
|
}
|
||||||
|
|
@ -66,4 +66,4 @@ tasks.modrinth.dependsOn(rootProject.generateChangelog)
|
||||||
tasks.register('publishToModSites') {
|
tasks.register('publishToModSites') {
|
||||||
publishToModSites.dependsOn(tasks.modrinth)
|
publishToModSites.dependsOn(tasks.modrinth)
|
||||||
publishToModSites.dependsOn(tasks.curseforge)
|
publishToModSites.dependsOn(tasks.curseforge)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ architectury {
|
||||||
common(rootProject.enabled_platforms.split(","))
|
common(rootProject.enabled_platforms.split(","))
|
||||||
}
|
}
|
||||||
|
|
||||||
ext.jei_minecraft_version = "1.20.1" /* temporary, till 1.20 releases */
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
|
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
|
||||||
// Do NOT use other classes from fabric loader
|
// Do NOT use other classes from fabric loader
|
||||||
|
|
@ -20,18 +18,8 @@ dependencies {
|
||||||
modApi("dev.latvian.mods:rhino:${rhino_version}") {
|
modApi("dev.latvian.mods:rhino:${rhino_version}") {
|
||||||
transitive = false
|
transitive = false
|
||||||
}
|
}
|
||||||
modApi("me.shedaniel:RoughlyEnoughItems-api:${rei_version}") {
|
|
||||||
transitive = false
|
|
||||||
}
|
|
||||||
modCompileOnly("me.shedaniel:RoughlyEnoughItems-fabric:${rei_version}") {
|
|
||||||
transitive = false
|
|
||||||
}
|
|
||||||
|
|
||||||
modCompileOnly "curse.maven:spark-361579:${rootProject.spark_version}"
|
modCompileOnly "curse.maven:spark-361579:${rootProject.spark_version}"
|
||||||
// compile against the JEI API but do not include it at runtime
|
|
||||||
modCompileOnly("mezz.jei:jei-${jei_minecraft_version}-common:${jei_version}")
|
|
||||||
modCompileOnly("mezz.jei:jei-${jei_minecraft_version}-gui:${jei_version}")
|
|
||||||
modCompileOnly("mezz.jei:jei-${jei_minecraft_version}-lib:${jei_version}")
|
|
||||||
// Remove the next line if you don't want to depend on the API
|
// Remove the next line if you don't want to depend on the API
|
||||||
// modApi "me.shedaniel:architectury:${rootProject.architectury_version}"
|
// modApi "me.shedaniel:architectury:${rootProject.architectury_version}"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,19 @@
|
||||||
package org.embeddedt.modernfix;
|
package org.embeddedt.modernfix;
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||||
import net.minecraft.network.syncher.SynchedEntityData;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.util.MemoryReserve;
|
import net.minecraft.util.MemoryReserve;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
|
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
|
||||||
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
||||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||||
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
|
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||||
import org.embeddedt.modernfix.searchtree.JEIBackedSearchTree;
|
|
||||||
import org.embeddedt.modernfix.searchtree.REIBackedSearchTree;
|
|
||||||
import org.embeddedt.modernfix.searchtree.SearchTreeProviderRegistry;
|
|
||||||
import org.embeddedt.modernfix.util.ClassInfoManager;
|
import org.embeddedt.modernfix.util.ClassInfoManager;
|
||||||
import org.embeddedt.modernfix.world.IntegratedWatchdog;
|
import org.embeddedt.modernfix.world.IntegratedWatchdog;
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.lang.reflect.Field;
|
import java.util.List;
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
public class ModernFixClient {
|
public class ModernFixClient {
|
||||||
|
|
@ -47,8 +39,6 @@ public class ModernFixClient {
|
||||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.branding.F3Screen")) {
|
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.branding.F3Screen")) {
|
||||||
brandingString = ModernFix.NAME + " " + ModernFixPlatformHooks.INSTANCE.getVersionString();
|
brandingString = ModernFix.NAME + " " + ModernFixPlatformHooks.INSTANCE.getVersionString();
|
||||||
}
|
}
|
||||||
SearchTreeProviderRegistry.register(JEIBackedSearchTree.PROVIDER);
|
|
||||||
SearchTreeProviderRegistry.register(REIBackedSearchTree.PROVIDER);
|
|
||||||
for(String className : ModernFixPlatformHooks.INSTANCE.getCustomModOptions().get(IntegrationConstants.CLIENT_INTEGRATION_CLASS)) {
|
for(String className : ModernFixPlatformHooks.INSTANCE.getCustomModOptions().get(IntegrationConstants.CLIENT_INTEGRATION_CLASS)) {
|
||||||
try {
|
try {
|
||||||
CLIENT_INTEGRATIONS.add((ModernFixClientIntegration)Class.forName(className).getDeclaredConstructor().newInstance());
|
CLIENT_INTEGRATIONS.add((ModernFixClientIntegration)Class.forName(className).getDeclaredConstructor().newInstance());
|
||||||
|
|
@ -123,85 +113,6 @@ public class ModernFixClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Horrendous hack to allow tracking every synced entity data manager.
|
|
||||||
*
|
|
||||||
* This is to ensure we can perform ID fixup on already constructed managers.
|
|
||||||
*/
|
|
||||||
public static final Set<SynchedEntityData> allEntityDatas = Collections.newSetFromMap(new WeakHashMap<>());
|
|
||||||
|
|
||||||
private static final Field entriesArrayField;
|
|
||||||
static {
|
|
||||||
Field field;
|
|
||||||
try {
|
|
||||||
field = SynchedEntityData.class.getDeclaredField("entriesArray");
|
|
||||||
field.setAccessible(true);
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
field = null;
|
|
||||||
}
|
|
||||||
entriesArrayField = field;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extremely hacky method to detect and correct mismatched entity data parameter IDs on the client and server.
|
|
||||||
*
|
|
||||||
* The technique is far from ideal, but it should detect reliably and also not break already constructed entities.
|
|
||||||
*/
|
|
||||||
public static void handleEntityIDSync(EntityIDSyncPacket packet) {
|
|
||||||
Map<Class<? extends Entity>, List<Pair<String, Integer>>> info = packet.getFieldInfo();
|
|
||||||
boolean fixNeeded = false;
|
|
||||||
for(Map.Entry<Class<? extends Entity>, List<Pair<String, Integer>>> entry : info.entrySet()) {
|
|
||||||
Class<? extends Entity> eClass = entry.getKey();
|
|
||||||
for(Pair<String, Integer> field : entry.getValue()) {
|
|
||||||
String fieldName = field.getFirst();
|
|
||||||
int newId = field.getSecond();
|
|
||||||
try {
|
|
||||||
Field f = eClass.getDeclaredField(fieldName);
|
|
||||||
f.setAccessible(true);
|
|
||||||
EntityDataAccessor<?> accessor = (EntityDataAccessor<?>)f.get(null);
|
|
||||||
if(compareAndSwitchIds(eClass, fieldName, accessor, newId))
|
|
||||||
fixNeeded = true;
|
|
||||||
} catch(NoSuchFieldException e) {
|
|
||||||
ModernFix.LOGGER.warn("Couldn't find field on {}: {}", eClass, fieldName);
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException("Unexpected exception", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Now the ID mappings on synced entity data instances are probably all wrong. Fix that. */
|
|
||||||
List<SynchedEntityData> dataEntries;
|
|
||||||
synchronized (allEntityDatas) {
|
|
||||||
if(fixNeeded) {
|
|
||||||
dataEntries = new ArrayList<>(allEntityDatas);
|
|
||||||
for(SynchedEntityData manager : dataEntries) {
|
|
||||||
Int2ObjectOpenHashMap<SynchedEntityData.DataItem<?>> fixedMap = new Int2ObjectOpenHashMap<>();
|
|
||||||
List<SynchedEntityData.DataItem<?>> items = new ArrayList<>(manager.itemsById.values());
|
|
||||||
for(SynchedEntityData.DataItem<?> item : items) {
|
|
||||||
fixedMap.put(item.getAccessor().id, item);
|
|
||||||
}
|
|
||||||
manager.lock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
manager.itemsById.replaceAll((id, parameter) -> fixedMap.get((int)id));
|
|
||||||
if(entriesArrayField != null) {
|
|
||||||
try {
|
|
||||||
SynchedEntityData.DataItem<?>[] dataArray = new SynchedEntityData.DataItem[items.size()];
|
|
||||||
for(int i = 0; i < dataArray.length; i++) {
|
|
||||||
dataArray[i] = fixedMap.get(i);
|
|
||||||
}
|
|
||||||
entriesArrayField.set(manager, dataArray);
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
ModernFix.LOGGER.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
manager.lock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
allEntityDatas.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onServerStarted(MinecraftServer server) {
|
public void onServerStarted(MinecraftServer server) {
|
||||||
if(!ModernFixMixinPlugin.instance.isOptionEnabled("feature.integrated_server_watchdog.IntegratedWatchdog"))
|
if(!ModernFixMixinPlugin.instance.isOptionEnabled("feature.integrated_server_watchdog.IntegratedWatchdog"))
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
package org.embeddedt.modernfix.api.entrypoint;
|
package org.embeddedt.modernfix.api.entrypoint;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
import net.minecraft.client.resources.model.*;
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
|
import net.minecraft.client.resources.model.ModelState;
|
||||||
import java.util.function.Function;
|
import net.minecraft.client.resources.model.UnbakedModel;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement this interface in a mod class and add it to "modernfix:integration_v1" in your mod metadata file
|
* Implement this interface in a mod class and add it to "modernfix:integration_v1" in your mod metadata file
|
||||||
|
|
@ -21,49 +20,10 @@ public interface ModernFixClientIntegration {
|
||||||
default void onDynamicResourcesStatusChange(boolean enabled) {
|
default void onDynamicResourcesStatusChange(boolean enabled) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to allow mods to observe the loading of an unbaked model and either make changes to it or wrap it with their
|
|
||||||
* own instance.
|
|
||||||
* @param location the ResourceLocation of the model (this may be a ModelResourceLocation)
|
|
||||||
* @param originalModel the original model
|
|
||||||
* @param bakery the model bakery - do not touch internal fields as they probably don't behave the way you expect
|
|
||||||
* with dynamic resources on
|
|
||||||
* @return the model which should actually be loaded for this resource location
|
|
||||||
*/
|
|
||||||
default UnbakedModel onUnbakedModelLoad(ResourceLocation location, UnbakedModel originalModel, ModelBakery bakery) {
|
|
||||||
return originalModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to allow mods to observe the use of an unbaked model at bake time and either make changes to it or wrap it with their
|
|
||||||
* own instance.
|
|
||||||
* @param location the ResourceLocation of the model (this may be a ModelResourceLocation)
|
|
||||||
* @param originalModel the original model
|
|
||||||
* @param bakery the model bakery - do not touch internal fields as they probably don't behave the way you expect
|
|
||||||
* with dynamic resources on
|
|
||||||
* @return the model which should actually be loaded for this resource location
|
|
||||||
*/
|
|
||||||
default UnbakedModel onUnbakedModelPreBake(ResourceLocation location, UnbakedModel originalModel, ModelBakery bakery) {
|
|
||||||
return originalModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to allow mods to observe the loading of a baked model and either make changes to it or wrap it with their
|
|
||||||
* own instance.
|
|
||||||
* @param location the ResourceLocation of the model (this may be a ModelResourceLocation)
|
|
||||||
* @param originalModel the original model
|
|
||||||
* @param bakery the model bakery - do not touch internal fields as they probably don't behave the way you expect
|
|
||||||
* with dynamic resources on
|
|
||||||
* @return the model which should actually be loaded for this resource location
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
default BakedModel onBakedModelLoad(ResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery) {
|
|
||||||
return originalModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to allow mods to observe the loading of a baked model and either make changes to it or wrap it with their
|
* Called to allow mods to observe the loading of a baked model and either make changes to it or wrap it with their
|
||||||
* own instance.
|
* own instance.
|
||||||
|
*
|
||||||
* @param location the ResourceLocation of the model (this may be a ModelResourceLocation)
|
* @param location the ResourceLocation of the model (this may be a ModelResourceLocation)
|
||||||
* @param originalModel the original model
|
* @param originalModel the original model
|
||||||
* @param bakery the model bakery - do not touch internal fields as they probably don't behave the way you expect
|
* @param bakery the model bakery - do not touch internal fields as they probably don't behave the way you expect
|
||||||
|
|
@ -71,7 +31,7 @@ public interface ModernFixClientIntegration {
|
||||||
* @param textureGetter function to retrieve textures for this model
|
* @param textureGetter function to retrieve textures for this model
|
||||||
* @return the model which should actually be loaded for this resource location
|
* @return the model which should actually be loaded for this resource location
|
||||||
*/
|
*/
|
||||||
default BakedModel onBakedModelLoad(ResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery, Function<Material, TextureAtlasSprite> textureGetter) {
|
default BakedModel onBakedModelLoad(ModelResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery, ModelBakery.TextureGetter textureGetter) {
|
||||||
return onBakedModelLoad(location, baseModel, originalModel, state, bakery);
|
return originalModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,8 @@ import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.block.state.StateDefinition;
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
|
||||||
import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers;
|
import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers;
|
||||||
import org.embeddedt.modernfix.util.DynamicMap;
|
import org.embeddedt.modernfix.util.DynamicMap;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
@ -25,7 +23,7 @@ public final class ModelHelpers {
|
||||||
* @return a list of all blockstates related to the model
|
* @return a list of all blockstates related to the model
|
||||||
*/
|
*/
|
||||||
public static ImmutableList<BlockState> getBlockStateForLocation(ModelResourceLocation location) {
|
public static ImmutableList<BlockState> getBlockStateForLocation(ModelResourceLocation location) {
|
||||||
Optional<Block> blockOpt = BuiltInRegistries.BLOCK.getOptional(new ResourceLocation(location.getNamespace(), location.getPath()));
|
Optional<Block> blockOpt = BuiltInRegistries.BLOCK.getOptional(location.id());
|
||||||
if(blockOpt.isPresent())
|
if(blockOpt.isPresent())
|
||||||
return ModelBakeryHelpers.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), location);
|
return ModelBakeryHelpers.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), location);
|
||||||
else
|
else
|
||||||
|
|
@ -58,6 +56,8 @@ public final class ModelHelpers {
|
||||||
* @return an appropriate ModelBaker
|
* @return an appropriate ModelBaker
|
||||||
*/
|
*/
|
||||||
public static ModelBaker adaptBakery(ModelBakery bakery) {
|
public static ModelBaker adaptBakery(ModelBakery bakery) {
|
||||||
|
throw new UnsupportedOperationException("TODO");
|
||||||
|
/*
|
||||||
return new ModelBaker() {
|
return new ModelBaker() {
|
||||||
@Override
|
@Override
|
||||||
public UnbakedModel getModel(ResourceLocation resourceLocation) {
|
public UnbakedModel getModel(ResourceLocation resourceLocation) {
|
||||||
|
|
@ -70,5 +70,7 @@ public final class ModelHelpers {
|
||||||
return ((IExtendedModelBakery)bakery).bakeDefault(resourceLocation, modelState);
|
return ((IExtendedModelBakery)bakery).bakeDefault(resourceLocation, modelState);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.material.FluidState;
|
import net.minecraft.world.level.material.FluidState;
|
||||||
import net.minecraft.world.level.material.Fluids;
|
import net.minecraft.world.level.material.Fluids;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
@ -32,7 +32,7 @@ public class SafeBlockGetter implements BlockGetter {
|
||||||
if(!(access instanceof ChunkAccess))
|
if(!(access instanceof ChunkAccess))
|
||||||
return null;
|
return null;
|
||||||
ChunkAccess chunk = (ChunkAccess)access;
|
ChunkAccess chunk = (ChunkAccess)access;
|
||||||
if(!chunk.getStatus().isOrAfter(ChunkStatus.FULL))
|
if(!chunk.getPersistedStatus().isOrAfter(ChunkStatus.FULL))
|
||||||
return null;
|
return null;
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,58 +2,9 @@ package org.embeddedt.modernfix.command;
|
||||||
|
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.packs.resources.Resource;
|
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.structure.CachingStructureManager;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import static net.minecraft.commands.Commands.literal;
|
|
||||||
|
|
||||||
public class ModernFixCommands {
|
public class ModernFixCommands {
|
||||||
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||||
dispatcher.register(literal("modernfix")
|
|
||||||
.then(literal("upgradeStructures")
|
|
||||||
.requires(source -> source.hasPermission(3))
|
|
||||||
.executes(context -> {
|
|
||||||
ServerLevel level = context.getSource().getLevel();
|
|
||||||
if(level == null) {
|
|
||||||
context.getSource().sendFailure(Component.literal("Couldn't find server level"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceManager manager = level.getServer().resources.resourceManager();
|
|
||||||
Map<ResourceLocation, Resource> structures = manager.listResources("structures", p -> p.getPath().endsWith(".nbt"));
|
|
||||||
int upgradedNum = 0;
|
|
||||||
Pattern pathPattern = Pattern.compile("^structures/(.*)\\.nbt$");
|
|
||||||
for(Map.Entry<ResourceLocation, Resource> entry : structures.entrySet()) {
|
|
||||||
upgradedNum++;
|
|
||||||
ResourceLocation found = entry.getKey();
|
|
||||||
Matcher matcher = pathPattern.matcher(found.getPath());
|
|
||||||
if(!matcher.matches())
|
|
||||||
continue;
|
|
||||||
ResourceLocation structureLocation = new ResourceLocation(found.getNamespace(), matcher.group(1));
|
|
||||||
try(InputStream resource = entry.getValue().open()) {
|
|
||||||
CachingStructureManager.readStructureTag(structureLocation, level.getServer().getFixerUpper(), resource);
|
|
||||||
Component msg = Component.literal("checked " + structureLocation + " (" + upgradedNum + "/" + structures.size() + ")");
|
|
||||||
context.getSource().sendSuccess(() -> msg, false);
|
|
||||||
} catch(Throwable e) {
|
|
||||||
ModernFix.LOGGER.error("Couldn't upgrade structure " + found, e);
|
|
||||||
context.getSource().sendFailure(Component.literal("error reading " + structureLocation + " (" + upgradedNum + "/" + structures.size() + ")"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.getSource().sendSuccess(() -> Component.literal("All structures upgraded"), false);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.bugfix.buffer_builder_leak;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.render.UnsafeBufferHelper;
|
|
||||||
import org.spongepowered.asm.mixin.Dynamic;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
@Mixin(value = BufferBuilder.class, priority = 1500)
|
|
||||||
@ClientOnlyMixin
|
|
||||||
public class BufferBuilderMixin {
|
|
||||||
@Shadow private ByteBuffer buffer;
|
|
||||||
|
|
||||||
private static boolean leakReported = false;
|
|
||||||
|
|
||||||
private boolean mfix$shouldFree = true;
|
|
||||||
|
|
||||||
@Dynamic
|
|
||||||
@Inject(method = "flywheel$injectForRender", at = @At("RETURN"), remap = false, require = 0)
|
|
||||||
private void preventFree(CallbackInfo ci) {
|
|
||||||
mfix$shouldFree = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure UnsafeBufferHelper is classloaded early, to avoid Forge's event transformer showing an error in the log.
|
|
||||||
*/
|
|
||||||
@Inject(method = "<clinit>", at = @At(value = "RETURN"))
|
|
||||||
private static void initUnsafeBufferHelper(CallbackInfo ci) {
|
|
||||||
UnsafeBufferHelper.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
try {
|
|
||||||
ByteBuffer buf = buffer;
|
|
||||||
// can be null if a mod already tried to free the buffer
|
|
||||||
if(buf != null && mfix$shouldFree) {
|
|
||||||
if(!leakReported) {
|
|
||||||
leakReported = true;
|
|
||||||
ModernFix.LOGGER.warn("One or more BufferBuilders have been leaked, ModernFix will attempt to correct this.");
|
|
||||||
}
|
|
||||||
UnsafeBufferHelper.free(buf);
|
|
||||||
buffer = null;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
super.finalize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.bugfix.paper_chunk_patches;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Either;
|
|
||||||
import net.minecraft.server.level.*;
|
|
||||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
|
||||||
import net.minecraft.util.thread.BlockableEventLoop;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
|
|
||||||
@Mixin(ChunkMap.class)
|
|
||||||
public abstract class ChunkMapMixin {
|
|
||||||
@Shadow @Final private BlockableEventLoop<Runnable> mainThreadExecutor;
|
|
||||||
|
|
||||||
@Shadow @Final private ChunkMap.DistanceManager distanceManager;
|
|
||||||
|
|
||||||
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> protoChunkToFullChunk(ChunkHolder arg);
|
|
||||||
|
|
||||||
@Shadow @Final private ServerLevel level;
|
|
||||||
@Shadow @Final private ThreadedLevelLightEngine lightEngine;
|
|
||||||
@Shadow @Final private ChunkProgressListener progressListener;
|
|
||||||
|
|
||||||
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> scheduleChunkGeneration(ChunkHolder chunkHolder, ChunkStatus chunkStatus);
|
|
||||||
|
|
||||||
@Shadow @Final private StructureTemplateManager structureTemplateManager;
|
|
||||||
|
|
||||||
/* https://github.com/PaperMC/Paper/blob/ver/1.17.1/patches/server/0752-Fix-chunks-refusing-to-unload-at-low-TPS.patch */
|
|
||||||
@ModifyArg(method = "prepareAccessibleChunk", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenApplyAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 1)
|
|
||||||
private Executor useMainThreadExecutor(Executor executor) {
|
|
||||||
return this.mainThreadExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason revert 1.17 chunk system changes, significantly reduces time and RAM needed to load chunks
|
|
||||||
*/
|
|
||||||
@Inject(method = "schedule", at = @At("HEAD"), cancellable = true)
|
|
||||||
private void useLegacySchedulingLogic(ChunkHolder holder, ChunkStatus requiredStatus, CallbackInfoReturnable<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> cir) {
|
|
||||||
if(requiredStatus != ChunkStatus.EMPTY && !requiredStatus.hasLoadDependencies()) {
|
|
||||||
ChunkPos chunkpos = holder.getPos();
|
|
||||||
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = holder.getOrScheduleFuture(requiredStatus.getParent(), (ChunkMap)(Object)this);
|
|
||||||
cir.setReturnValue(future.thenComposeAsync((either) -> {
|
|
||||||
Optional<ChunkAccess> optional = either.left();
|
|
||||||
|
|
||||||
if (requiredStatus == ChunkStatus.LIGHT) {
|
|
||||||
this.distanceManager.addTicket(TicketType.LIGHT, chunkpos, 33 + ChunkStatus.getDistance(ChunkStatus.LIGHT), chunkpos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// from original method
|
|
||||||
if (optional.isPresent() && optional.get().getStatus().isOrAfter(requiredStatus)) {
|
|
||||||
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = requiredStatus.load(this.level, this.structureTemplateManager, this.lightEngine, (arg2) -> {
|
|
||||||
return this.protoChunkToFullChunk(holder);
|
|
||||||
}, (ChunkAccess)optional.get());
|
|
||||||
this.progressListener.onStatusChange(chunkpos, requiredStatus);
|
|
||||||
return completablefuture;
|
|
||||||
} else {
|
|
||||||
return this.scheduleChunkGeneration(holder, requiredStatus);
|
|
||||||
}
|
|
||||||
}, this.mainThreadExecutor).thenComposeAsync(CompletableFuture::completedFuture, this.mainThreadExecutor));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -24,7 +24,7 @@ public class MinecraftMixin {
|
||||||
/**
|
/**
|
||||||
* To mitigate the effect of leaked client worlds, clear most of the data structures that waste memory.
|
* To mitigate the effect of leaked client worlds, clear most of the data structures that waste memory.
|
||||||
*/
|
*/
|
||||||
@Inject(method = "clearLevel(Lnet/minecraft/client/gui/screens/Screen;)V", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/Minecraft;level:Lnet/minecraft/client/multiplayer/ClientLevel;"))
|
@Inject(method = "disconnect(Lnet/minecraft/client/gui/screens/Screen;Z)V", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/Minecraft;level:Lnet/minecraft/client/multiplayer/ClientLevel;"))
|
||||||
private void clearLevelDataForLeaks(CallbackInfo ci) {
|
private void clearLevelDataForLeaks(CallbackInfo ci) {
|
||||||
if(this.level != null) {
|
if(this.level != null) {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.core;
|
|
||||||
|
|
||||||
import net.minecraft.network.syncher.SynchedEntityData;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import org.embeddedt.modernfix.ModernFixClient;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
@Mixin(SynchedEntityData.class)
|
|
||||||
@ClientOnlyMixin
|
|
||||||
public class SynchedEntityDataMixin {
|
|
||||||
/**
|
|
||||||
* Store this in our set of all entity data objects.
|
|
||||||
*
|
|
||||||
* Not an ideal solution, but it should guarantee compatibility with mods.
|
|
||||||
*/
|
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
|
||||||
private void storeInSet(Entity arg, CallbackInfo ci) {
|
|
||||||
synchronized (ModernFixClient.allEntityDatas) {
|
|
||||||
ModernFixClient.allEntityDatas.add((SynchedEntityData)(Object)this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.feature.cause_lag_by_disabling_threads;
|
package org.embeddedt.modernfix.common.mixin.feature.cause_lag_by_disabling_threads;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
|
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
@ -11,7 +11,7 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Mixin(ChunkRenderDispatcher.class)
|
@Mixin(SectionRenderDispatcher.class)
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
public class ChunkRenderDispatcherMixin {
|
public class ChunkRenderDispatcherMixin {
|
||||||
private static final Executor MFIX_CHUNK_BUILD_EXECUTOR = new ThreadPoolExecutor(1, computeNumThreads(), 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
|
private static final Executor MFIX_CHUNK_BUILD_EXECUTOR = new ThreadPoolExecutor(1, computeNumThreads(), 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
public class CrashReportMixin {
|
public class CrashReportMixin {
|
||||||
@Shadow @Final private Throwable exception;
|
@Shadow @Final private Throwable exception;
|
||||||
|
|
||||||
@Inject(method = "addCategory(Ljava/lang/String;I)Lnet/minecraft/CrashReportCategory;", at = @At(value = "INVOKE", target = "Ljava/io/PrintStream;println(Ljava/lang/String;)V"))
|
@Inject(method = "addCategory(Ljava/lang/String;I)Lnet/minecraft/CrashReportCategory;", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false))
|
||||||
private void dumpStacktrace(String s, int i, CallbackInfoReturnable<CrashReportCategory> cir) {
|
private void dumpStacktrace(String s, int i, CallbackInfoReturnable<CrashReportCategory> cir) {
|
||||||
new Exception("ModernFix crash stacktrace").printStackTrace();
|
new Exception("ModernFix crash stacktrace").printStackTrace();
|
||||||
if(this.exception != null)
|
if(this.exception != null)
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.feature.stalled_chunk_load_detection;
|
package org.embeddedt.modernfix.common.mixin.feature.stalled_chunk_load_detection;
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Either;
|
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.core.Holder;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
import net.minecraft.server.level.ChunkResult;
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
import net.minecraft.server.level.ServerChunkCache;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.biome.Biomes;
|
import net.minecraft.world.level.biome.Biomes;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||||
import net.minecraft.world.level.chunk.EmptyLevelChunk;
|
import net.minecraft.world.level.chunk.EmptyLevelChunk;
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
|
@ -26,7 +25,7 @@ public abstract class ServerChunkCacheMixin {
|
||||||
@Shadow @Final private Thread mainThread;
|
@Shadow @Final private Thread mainThread;
|
||||||
@Shadow @Final public ServerLevel level;
|
@Shadow @Final public ServerLevel level;
|
||||||
|
|
||||||
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkFutureMainThread(int k, int l, ChunkStatus arg, boolean bl);
|
@Shadow protected abstract CompletableFuture<ChunkResult<ChunkAccess>> getChunkFutureMainThread(int k, int l, ChunkStatus arg, boolean bl);
|
||||||
|
|
||||||
@Shadow @Final private ServerChunkCache.MainThreadExecutor mainThreadProcessor;
|
@Shadow @Final private ServerChunkCache.MainThreadExecutor mainThreadProcessor;
|
||||||
private final boolean debugDeadServerAccess = Boolean.getBoolean("modernfix.debugBadChunkloading");
|
private final boolean debugDeadServerAccess = Boolean.getBoolean("modernfix.debugBadChunkloading");
|
||||||
|
|
@ -41,24 +40,24 @@ public abstract class ServerChunkCacheMixin {
|
||||||
Holder<Biome> plains = this.level.registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.PLAINS);
|
Holder<Biome> plains = this.level.registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(Biomes.PLAINS);
|
||||||
cir.setReturnValue(new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ), plains));
|
cir.setReturnValue(new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ), plains));
|
||||||
} else if(Thread.currentThread() != this.mainThread) {
|
} else if(Thread.currentThread() != this.mainThread) {
|
||||||
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = CompletableFuture.supplyAsync(() -> this.getChunkFutureMainThread(chunkX, chunkZ, requiredStatus, false), this.mainThreadProcessor).join();
|
CompletableFuture<ChunkResult<ChunkAccess>> future = CompletableFuture.supplyAsync(() -> this.getChunkFutureMainThread(chunkX, chunkZ, requiredStatus, false), this.mainThreadProcessor).join();
|
||||||
if(!future.isDone()) {
|
if(!future.isDone()) {
|
||||||
// Wait at least 500 milliseconds before printing anything
|
// Wait at least 500 milliseconds before printing anything
|
||||||
Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> resultingChunk = null;
|
ChunkResult<ChunkAccess> resultingChunk = null;
|
||||||
try {
|
try {
|
||||||
resultingChunk = future.get(500, TimeUnit.MILLISECONDS);
|
resultingChunk = future.get(500, TimeUnit.MILLISECONDS);
|
||||||
} catch(InterruptedException | ExecutionException | TimeoutException ignored) {
|
} catch(InterruptedException | ExecutionException | TimeoutException ignored) {
|
||||||
}
|
}
|
||||||
if(resultingChunk != null && resultingChunk.left().isPresent()) {
|
if(resultingChunk != null && resultingChunk.isSuccess()) {
|
||||||
cir.setReturnValue(resultingChunk.left().get());
|
cir.setReturnValue(resultingChunk.orElse(null));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(debugDeadServerAccess)
|
if(debugDeadServerAccess)
|
||||||
ModernFix.LOGGER.warn("Async loading of a chunk was requested, this might not be desirable", new Exception());
|
ModernFix.LOGGER.warn("Async loading of a chunk was requested, this might not be desirable", new Exception());
|
||||||
try {
|
try {
|
||||||
resultingChunk = future.get(10, TimeUnit.SECONDS);
|
resultingChunk = future.get(10, TimeUnit.SECONDS);
|
||||||
if(resultingChunk.left().isPresent()) {
|
if(resultingChunk.isSuccess()) {
|
||||||
cir.setReturnValue(resultingChunk.left().get());
|
cir.setReturnValue(resultingChunk.orElse(null));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch(InterruptedException | ExecutionException | TimeoutException e) {
|
} catch(InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
|
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.blast_search_trees;
|
|
||||||
|
|
||||||
import net.minecraft.client.KeyMapping;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.searchtree.SearchRegistry;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
|
||||||
import org.embeddedt.modernfix.searchtree.RecipeBookSearchTree;
|
|
||||||
import org.embeddedt.modernfix.searchtree.SearchTreeProviderRegistry;
|
|
||||||
import org.lwjgl.glfw.GLFW;
|
|
||||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Mixin(Minecraft.class)
|
|
||||||
@ClientOnlyMixin
|
|
||||||
public abstract class MinecraftMixin {
|
|
||||||
@Shadow @Final private SearchRegistry searchRegistry;
|
|
||||||
|
|
||||||
@Shadow public abstract <T> void populateSearchTree(SearchRegistry.Key<T> key, List<T> list);
|
|
||||||
|
|
||||||
@Inject(method = "createSearchTrees", at = @At("HEAD"), cancellable = true)
|
|
||||||
private void replaceSearchTrees(CallbackInfo ci) {
|
|
||||||
SearchTreeProviderRegistry.Provider provider = SearchTreeProviderRegistry.getSearchTreeProvider();
|
|
||||||
if(provider == null)
|
|
||||||
return;
|
|
||||||
ModernFix.LOGGER.info("Replacing search trees with '{}' provider", provider.getName());
|
|
||||||
SearchRegistry.TreeBuilderSupplier<ItemStack> nameSupplier = list -> provider.getSearchTree(false);
|
|
||||||
SearchRegistry.TreeBuilderSupplier<ItemStack> tagSupplier = list -> provider.getSearchTree(true);
|
|
||||||
this.searchRegistry.register(SearchRegistry.CREATIVE_NAMES, nameSupplier);
|
|
||||||
this.searchRegistry.register(SearchRegistry.CREATIVE_TAGS, tagSupplier);
|
|
||||||
this.searchRegistry.register(SearchRegistry.RECIPE_COLLECTIONS, list -> new RecipeBookSearchTree(provider.getSearchTree(false), list));
|
|
||||||
ModernFixPlatformHooks.INSTANCE.registerCreativeSearchTrees(this.searchRegistry, nameSupplier, tagSupplier, this::populateSearchTree);
|
|
||||||
// grab components for all key mappings in order to prevent them from being loaded off-thread later
|
|
||||||
// this populates the LazyLoadedValues
|
|
||||||
// we also need to suppress GLFW errors to prevent crashes if a key is missing
|
|
||||||
GLFWErrorCallback oldCb = GLFW.glfwSetErrorCallback(null);
|
|
||||||
for(KeyMapping mapping : KeyMapping.ALL.values()) {
|
|
||||||
mapping.getTranslatedKeyMessage();
|
|
||||||
}
|
|
||||||
GLFW.glfwSetErrorCallback(oldCb);
|
|
||||||
ci.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,7 +3,6 @@ package org.embeddedt.modernfix.common.mixin.perf.cache_profile_texture_url;
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||||
import net.minecraft.client.resources.SkinManager;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
@ -13,7 +12,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Mixin(SkinManager.class)
|
@Mixin(targets = {"net/minecraft/client/resources/SkinManager$TextureCache" })
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
public class SkinManagerMixin {
|
public class SkinManagerMixin {
|
||||||
@Unique
|
@Unique
|
||||||
|
|
@ -22,7 +21,7 @@ public class SkinManagerMixin {
|
||||||
.concurrencyLevel(1)
|
.concurrencyLevel(1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@Redirect(method = "registerTexture(Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;Lcom/mojang/authlib/minecraft/MinecraftProfileTexture$Type;Lnet/minecraft/client/resources/SkinManager$SkinTextureCallback;)Lnet/minecraft/resources/ResourceLocation;",
|
@Redirect(method = { "getOrLoad", "registerTexture" },
|
||||||
at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;getHash()Ljava/lang/String;", remap = false))
|
at = @At(value = "INVOKE", target = "Lcom/mojang/authlib/minecraft/MinecraftProfileTexture;getHash()Ljava/lang/String;", remap = false))
|
||||||
private String useCachedHash(MinecraftProfileTexture texture) {
|
private String useCachedHash(MinecraftProfileTexture texture) {
|
||||||
// avoid lambda allocation for common case
|
// avoid lambda allocation for common case
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ public abstract class ServerLevelMixin extends Level implements IServerLevel {
|
||||||
*/
|
*/
|
||||||
@Inject(method = "<init>", at = @At("TAIL"))
|
@Inject(method = "<init>", at = @At("TAIL"))
|
||||||
private void ensureGeneration(CallbackInfo ci) {
|
private void ensureGeneration(CallbackInfo ci) {
|
||||||
mfix$strongholdCache = this.getDataStorage().computeIfAbsent(StrongholdLocationCache::load,
|
mfix$strongholdCache = this.getDataStorage().computeIfAbsent(
|
||||||
StrongholdLocationCache::new,
|
StrongholdLocationCache.factory((ServerLevel)(Object)this),
|
||||||
StrongholdLocationCache.getFileId(this.dimensionTypeRegistration()));
|
StrongholdLocationCache.getFileId(this.dimensionTypeRegistration()));
|
||||||
this.chunkSource.getGeneratorState().ensureStructuresGenerated();
|
this.chunkSource.getGeneratorState().ensureStructuresGenerated();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.cache_upgraded_structures;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.DataFixer;
|
|
||||||
import net.minecraft.core.HolderGetter;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.structure.CachingStructureManager;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Mixin(StructureTemplateManager.class)
|
|
||||||
public class StructureManagerMixin {
|
|
||||||
@Shadow @Final private DataFixer fixerUpper;
|
|
||||||
|
|
||||||
@Shadow private ResourceManager resourceManager;
|
|
||||||
|
|
||||||
@Shadow @Final private HolderGetter<Block> blockLookup;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason use our own manager to avoid needless DFU updates
|
|
||||||
*/
|
|
||||||
@Overwrite
|
|
||||||
private Optional<StructureTemplate> loadFromResource(ResourceLocation id) {
|
|
||||||
ResourceLocation arg = new ResourceLocation(id.getNamespace(), "structures/" + id.getPath() + ".nbt");
|
|
||||||
try(InputStream stream = this.resourceManager.open(arg)) {
|
|
||||||
return Optional.of(CachingStructureManager.readStructure(id, this.fixerUpper, stream, this.blockLookup));
|
|
||||||
} catch(FileNotFoundException e) {
|
|
||||||
return Optional.empty();
|
|
||||||
} catch(IOException e) {
|
|
||||||
ModernFix.LOGGER.error("Can't read structure", e);
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.chunk_meshing;
|
package org.embeddedt.modernfix.common.mixin.perf.chunk_meshing;
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.sugar.Local;
|
import net.minecraft.client.renderer.chunk.SectionCompiler;
|
||||||
import net.minecraft.client.renderer.chunk.RenderChunkRegion;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
import org.embeddedt.modernfix.util.blockpos.SectionBlockPosIterator;
|
import org.embeddedt.modernfix.util.blockpos.SectionBlockPosIterator;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
@Mixin(targets = { "net/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk$RebuildTask"}, priority = 2000)
|
@Mixin(value = SectionCompiler.class, priority = 2000)
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
public class RebuildTaskMixin {
|
public class RebuildTaskMixin {
|
||||||
/**
|
/**
|
||||||
|
|
@ -21,13 +19,4 @@ public class RebuildTaskMixin {
|
||||||
private Iterable<BlockPos> fastBetweenClosed(BlockPos firstPos, BlockPos secondPos) {
|
private Iterable<BlockPos> fastBetweenClosed(BlockPos firstPos, BlockPos secondPos) {
|
||||||
return () -> new SectionBlockPosIterator(firstPos);
|
return () -> new SectionBlockPosIterator(firstPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason RenderChunkRegion.getBlockState is expensive, avoid calling it multiple times for the same position
|
|
||||||
*/
|
|
||||||
@Redirect(method = "compile", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/chunk/RenderChunkRegion;getBlockState(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/block/state/BlockState;", ordinal = 1), require = 0)
|
|
||||||
private BlockState useExistingBlockState(RenderChunkRegion instance, BlockPos pos, @Local(ordinal = 0) BlockState state) {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
|
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
|
||||||
|
|
||||||
import com.mojang.serialization.Lifecycle;
|
|
||||||
import net.minecraft.core.MappedRegistry;
|
import net.minecraft.core.MappedRegistry;
|
||||||
|
import net.minecraft.core.RegistrationInfo;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
|
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
|
||||||
import org.embeddedt.modernfix.registry.LifecycleMap;
|
import org.embeddedt.modernfix.registry.LifecycleMap;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
|
@ -20,10 +21,10 @@ public abstract class MappedRegistryMixin<T> {
|
||||||
@Shadow
|
@Shadow
|
||||||
@Final
|
@Final
|
||||||
@Mutable
|
@Mutable
|
||||||
private Map<T, Lifecycle> lifecycles;
|
private Map<ResourceKey<T>, RegistrationInfo> registrationInfos;
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
private void replaceStorage(CallbackInfo ci) {
|
private void replaceStorage(CallbackInfo ci) {
|
||||||
this.lifecycles = new LifecycleMap<>();
|
this.registrationInfos = new LifecycleMap<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import java.util.concurrent.ExecutorService;
|
||||||
@Mixin(Minecraft.class)
|
@Mixin(Minecraft.class)
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
public class MinecraftMixin {
|
public class MinecraftMixin {
|
||||||
@Redirect(method = { "<init>", "reloadResourcePacks(Z)Ljava/util/concurrent/CompletableFuture;" }, at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;backgroundExecutor()Ljava/util/concurrent/ExecutorService;", ordinal = 0))
|
@Redirect(method = { "<init>", "reloadResourcePacks(ZLnet/minecraft/client/Minecraft$GameLoadCookie;)Ljava/util/concurrent/CompletableFuture;" }, at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;backgroundExecutor()Ljava/util/concurrent/ExecutorService;", ordinal = 0))
|
||||||
private ExecutorService getResourceReloadExecutor() {
|
private ExecutorService getResourceReloadExecutor() {
|
||||||
return ModernFix.resourceReloadExecutor();
|
return ModernFix.resourceReloadExecutor();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@Mixin(MinecraftServer.class)
|
@Mixin(MinecraftServer.class)
|
||||||
public class MinecraftServerMixin {
|
public class MinecraftServerMixin {
|
||||||
@ModifyArg(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ReloadableServerResources;loadResources(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/core/RegistryAccess$Frozen;Lnet/minecraft/world/flag/FeatureFlagSet;Lnet/minecraft/commands/Commands$CommandSelection;ILjava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 5)
|
@ModifyArg(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ReloadableServerResources;loadResources(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/core/LayeredRegistryAccess;Lnet/minecraft/world/flag/FeatureFlagSet;Lnet/minecraft/commands/Commands$CommandSelection;ILjava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 5)
|
||||||
private Executor getReloadExecutor(Executor asyncExecutor) {
|
private Executor getReloadExecutor(Executor asyncExecutor) {
|
||||||
return ModernFix.resourceReloadExecutor();
|
return ModernFix.resourceReloadExecutor();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,15 @@ public class MixinResourceLocation {
|
||||||
@Mutable
|
@Mutable
|
||||||
@Shadow
|
@Shadow
|
||||||
@Final
|
@Final
|
||||||
protected String namespace;
|
private String namespace;
|
||||||
|
|
||||||
@Mutable
|
@Mutable
|
||||||
@Shadow
|
@Shadow
|
||||||
@Final
|
@Final
|
||||||
protected String path;
|
private String path;
|
||||||
|
|
||||||
@Inject(method = "<init>([Ljava/lang/String;)V", at = @At("RETURN"))
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
private void reinit(String[] id, CallbackInfo ci) {
|
private void reinit(String string, String string2, CallbackInfo ci) {
|
||||||
this.namespace = IdentifierCaches.NAMESPACES.deduplicate(this.namespace);
|
this.namespace = IdentifierCaches.NAMESPACES.deduplicate(this.namespace);
|
||||||
this.path = IdentifierCaches.PATH.deduplicate(this.path);
|
this.path = IdentifierCaches.PATH.deduplicate(this.path);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
@Mixin(WallBlock.class)
|
@Mixin(WallBlock.class)
|
||||||
public abstract class WallBlockMixin extends Block {
|
public abstract class WallBlockMixin extends Block {
|
||||||
private static Map<ImmutableList<Float>, Pair<Map<ImmutableMap<Property<?>, Comparable<?>>, VoxelShape>, StateDefinition<Block, BlockState>>> CACHE_BY_SHAPE_VALS = new HashMap<>();
|
private static Map<ImmutableList<Float>, Pair<Map<Map<Property<?>, Comparable<?>>, VoxelShape>, StateDefinition<Block, BlockState>>> CACHE_BY_SHAPE_VALS = new HashMap<>();
|
||||||
|
|
||||||
public WallBlockMixin(Properties properties) {
|
public WallBlockMixin(Properties properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
|
|
@ -34,7 +34,7 @@ public abstract class WallBlockMixin extends Block {
|
||||||
@Inject(method = "makeShapes", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "makeShapes", at = @At("HEAD"), cancellable = true)
|
||||||
private synchronized void useCachedShapeMap(float f1, float f2, float f3, float f4, float f5, float f6, CallbackInfoReturnable<Map<BlockState, VoxelShape>> cir) {
|
private synchronized void useCachedShapeMap(float f1, float f2, float f3, float f4, float f5, float f6, CallbackInfoReturnable<Map<BlockState, VoxelShape>> cir) {
|
||||||
ImmutableList<Float> key = ImmutableList.of(f1, f2, f3, f4, f5, f6);
|
ImmutableList<Float> key = ImmutableList.of(f1, f2, f3, f4, f5, f6);
|
||||||
Pair<Map<ImmutableMap<Property<?>, Comparable<?>>, VoxelShape>, StateDefinition<Block, BlockState>> cache = CACHE_BY_SHAPE_VALS.get(key);
|
Pair<Map<Map<Property<?>, Comparable<?>>, VoxelShape>, StateDefinition<Block, BlockState>> cache = CACHE_BY_SHAPE_VALS.get(key);
|
||||||
// require the properties to be identical
|
// require the properties to be identical
|
||||||
if(cache == null || !cache.getSecond().getProperties().equals(this.stateDefinition.getProperties()))
|
if(cache == null || !cache.getSecond().getProperties().equals(this.stateDefinition.getProperties()))
|
||||||
return;
|
return;
|
||||||
|
|
@ -55,7 +55,7 @@ public abstract class WallBlockMixin extends Block {
|
||||||
return;
|
return;
|
||||||
ImmutableList<Float> key = ImmutableList.of(f1, f2, f3, f4, f5, f6);
|
ImmutableList<Float> key = ImmutableList.of(f1, f2, f3, f4, f5, f6);
|
||||||
if(!CACHE_BY_SHAPE_VALS.containsKey(key)) {
|
if(!CACHE_BY_SHAPE_VALS.containsKey(key)) {
|
||||||
Map<ImmutableMap<Property<?>, Comparable<?>>, VoxelShape> cacheByProperties = new HashMap<>();
|
Map<Map<Property<?>, Comparable<?>>, VoxelShape> cacheByProperties = new HashMap<>();
|
||||||
Map<BlockState, VoxelShape> shapeMap = cir.getReturnValue();
|
Map<BlockState, VoxelShape> shapeMap = cir.getReturnValue();
|
||||||
for(Map.Entry<BlockState, VoxelShape> entry : shapeMap.entrySet()) {
|
for(Map.Entry<BlockState, VoxelShape> entry : shapeMap.entrySet()) {
|
||||||
cacheByProperties.put(entry.getKey().getValues(), entry.getValue());
|
cacheByProperties.put(entry.getKey().getValues(), entry.getValue());
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.DSL;
|
|
||||||
import com.mojang.datafixers.types.Type;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prevent fetchChoiceType calls from loading DFU early. Vanilla doesn't need the return values here.
|
|
||||||
*/
|
|
||||||
@Mixin(BlockEntityType.class)
|
|
||||||
public class BlockEntityTypeMixin {
|
|
||||||
@Redirect(method = "register", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;fetchChoiceType(Lcom/mojang/datafixers/DSL$TypeReference;Ljava/lang/String;)Lcom/mojang/datafixers/types/Type;"))
|
|
||||||
private static Type<?> skipSchemaCheck(DSL.TypeReference ref, String s) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.DSL;
|
|
||||||
import com.mojang.datafixers.DataFixer;
|
|
||||||
import net.minecraft.util.datafix.DataFixers;
|
|
||||||
import org.embeddedt.modernfix.dfu.LazyDataFixer;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Mixin(DataFixers.class)
|
|
||||||
public abstract class DataFixersMixin {
|
|
||||||
@Shadow protected static DataFixer createFixerUpper(Set<DSL.TypeReference> set) {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LazyDataFixer lazyDataFixer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Avoid classloading the DFU logic until we actually need it.
|
|
||||||
*/
|
|
||||||
@Inject(method = "createFixerUpper", at = @At("HEAD"), cancellable = true)
|
|
||||||
private static void createLazyFixerUpper(Set<DSL.TypeReference> set, CallbackInfoReturnable<DataFixer> cir) {
|
|
||||||
if(lazyDataFixer == null) {
|
|
||||||
lazyDataFixer = new LazyDataFixer(() -> createFixerUpper(set));
|
|
||||||
cir.setReturnValue(lazyDataFixer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.DSL;
|
|
||||||
import com.mojang.datafixers.types.Type;
|
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prevent fetchChoiceType calls from loading DFU early. Vanilla doesn't need the return values here.
|
|
||||||
*/
|
|
||||||
@Mixin(EntityType.Builder.class)
|
|
||||||
public class EntityTypeBuilderMixin {
|
|
||||||
@Redirect(method = "build", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;fetchChoiceType(Lcom/mojang/datafixers/DSL$TypeReference;Ljava/lang/String;)Lcom/mojang/datafixers/types/Type;"))
|
|
||||||
private Type<?> skipSchemaCheck(DSL.TypeReference ref, String s) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
|
||||||
|
|
||||||
import com.google.gson.JsonDeserializationContext;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import net.minecraft.client.renderer.block.model.BlockElementFace;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.dynamicresources.UVController;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
|
|
||||||
@Mixin(BlockElementFace.Deserializer.class)
|
|
||||||
@ClientOnlyMixin
|
|
||||||
public class BlockElementFaceDeserializerMixin {
|
|
||||||
|
|
||||||
@Redirect(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/renderer/block/model/BlockElementFace;",
|
|
||||||
at = @At(value = "INVOKE", target = "Lcom/google/gson/JsonDeserializationContext;deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;)Ljava/lang/Object;", ordinal = 0, remap = false))
|
|
||||||
private Object skipUvsForInitialLoad(JsonDeserializationContext context, JsonElement element, Type type) {
|
|
||||||
return UVController.useDummyUv.get() ? UVController.dummyUv : context.deserialize(element, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.color.block.BlockColors;
|
||||||
|
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||||
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
|
import net.minecraft.client.resources.model.UnbakedModel;
|
||||||
|
import net.minecraft.core.DefaultedRegistry;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.profiling.ProfilerFiller;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.embeddedt.modernfix.duck.IBlockStateModelLoader;
|
||||||
|
import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Mutable;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
@Mixin(BlockStateModelLoader.class)
|
||||||
|
@ClientOnlyMixin
|
||||||
|
public abstract class BlockStateModelLoaderMixin implements IBlockStateModelLoader {
|
||||||
|
@Shadow protected abstract void loadBlockStateDefinitions(ResourceLocation resourceLocation, StateDefinition<Block, BlockState> stateDefinition);
|
||||||
|
|
||||||
|
@Shadow @Mutable @Final private Object2IntMap<BlockState> modelGroups;
|
||||||
|
|
||||||
|
private ImmutableList<BlockState> filteredStates;
|
||||||
|
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
private void makeModelGroupsSynchronized(Map map, ProfilerFiller profilerFiller, UnbakedModel unbakedModel, BlockColors blockColors, BiConsumer biConsumer, CallbackInfo ci) {
|
||||||
|
this.modelGroups = Object2IntMaps.synchronize(this.modelGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadSpecificBlock(ModelResourceLocation location) {
|
||||||
|
var optionalBlock = BuiltInRegistries.BLOCK.getOptional(location.id());
|
||||||
|
if(optionalBlock.isPresent()) {
|
||||||
|
try {
|
||||||
|
// Only filter states if we are not in the loading overlay
|
||||||
|
filteredStates = Minecraft.getInstance().getOverlay() == null ? ModelBakeryHelpers.getBlockStatesForMRL(optionalBlock.get().getStateDefinition(), location) : null;
|
||||||
|
} catch(RuntimeException e) {
|
||||||
|
ModernFix.LOGGER.error("Exception filtering states on {}", location, e);
|
||||||
|
filteredStates = null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.loadBlockStateDefinitions(location.id(), optionalBlock.get().getStateDefinition());
|
||||||
|
} finally {
|
||||||
|
filteredStates = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "loadAllBlockStates", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/DefaultedRegistry;iterator()Ljava/util/Iterator;"))
|
||||||
|
private Iterator<?> skipIteratingBlocks(DefaultedRegistry instance) {
|
||||||
|
return Collections.emptyIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "loadBlockStateDefinitions", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;"))
|
||||||
|
private ImmutableList<BlockState> getFilteredStates(StateDefinition<Block, BlockState> instance) {
|
||||||
|
return this.filteredStates != null ? this.filteredStates : instance.getPossibleStates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ public abstract class ItemModelShaperMixin {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final ModelResourceLocation SENTINEL_VANILLA = new ModelResourceLocation(new ResourceLocation("modernfix", "sentinel"), "sentinel");
|
private static final ModelResourceLocation SENTINEL_VANILLA = new ModelResourceLocation(ResourceLocation.fromNamespaceAndPath("modernfix", "sentinel"), "sentinel");
|
||||||
|
|
||||||
private final DynamicModelCache<Item> mfix$itemModelCache = new DynamicModelCache<>(k -> this.mfix$getModelForItem((Item)k), true);
|
private final DynamicModelCache<Item> mfix$itemModelCache = new DynamicModelCache<>(k -> this.mfix$getModelForItem((Item)k), true);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
|
||||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
|
||||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
|
||||||
import net.minecraft.client.resources.model.ModelBaker;
|
|
||||||
import net.minecraft.client.resources.model.UnbakedModel;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelBaker;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
|
|
||||||
@Mixin(ItemOverrides.class)
|
|
||||||
@ClientOnlyMixin
|
|
||||||
public class ItemOverridesMixin {
|
|
||||||
@WrapOperation(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBaker;getModel(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/UnbakedModel;"))
|
|
||||||
private UnbakedModel preventThrowForMissing(ModelBaker instance, ResourceLocation resourceLocation, Operation<UnbakedModel> original) {
|
|
||||||
boolean prevState = ((IExtendedModelBaker)instance).throwOnMissingModel(false);
|
|
||||||
try {
|
|
||||||
return original.call(instance, resourceLocation);
|
|
||||||
} finally {
|
|
||||||
((IExtendedModelBaker)instance).throwOnMissingModel(prevState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.resources.model.ModelManager;
|
||||||
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.embeddedt.modernfix.duck.IExtendedModelManager;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
@Mixin(Minecraft.class)
|
||||||
|
@ClientOnlyMixin
|
||||||
|
public abstract class MinecraftMixin_ModelTicking {
|
||||||
|
@Shadow public abstract ModelManager getModelManager();
|
||||||
|
|
||||||
|
@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/tutorial/Tutorial;tick()V"))
|
||||||
|
private void tickModels(CallbackInfo ci) {
|
||||||
|
((IExtendedModelManager)this.getModelManager()).mfix$tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,233 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||||
|
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
|
import net.minecraft.client.color.block.BlockColors;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.client.resources.model.BlockModelRotation;
|
||||||
|
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||||
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
|
import net.minecraft.client.resources.model.UnbakedModel;
|
||||||
|
import net.minecraft.core.DefaultedRegistry;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.profiling.ProfilerFiller;
|
||||||
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
import org.embeddedt.modernfix.ModernFixClient;
|
||||||
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
||||||
|
import org.embeddedt.modernfix.duck.IBlockStateModelLoader;
|
||||||
|
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||||
|
import org.embeddedt.modernfix.util.DynamicOverridableMap;
|
||||||
|
import org.embeddedt.modernfix.util.LRUMap;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Mutable;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
@Mixin(ModelBakery.class)
|
||||||
|
@ClientOnlyMixin
|
||||||
|
public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
||||||
|
@Unique
|
||||||
|
private BlockStateModelLoader dynamicLoader;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private final ReentrantLock modelBakeryLock = new ReentrantLock();
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private ModelBakery.TextureGetter textureGetter;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private BakedModel bakedMissingModel;
|
||||||
|
|
||||||
|
@Shadow abstract UnbakedModel getModel(ResourceLocation resourceLocation);
|
||||||
|
|
||||||
|
@Shadow @Final private UnbakedModel missingModel;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private static final boolean DEBUG_MODEL_LOADS = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bake a model using the provided texture getter and location. The model is stored in {@link ModelBakeryMixin#bakedTopLevelModels}.
|
||||||
|
*/
|
||||||
|
@Shadow(aliases = "lambda$bakeModels$6") protected abstract void method_61072(ModelBakery.TextureGetter getter, ModelResourceLocation location, UnbakedModel model);
|
||||||
|
|
||||||
|
@Shadow @Mutable @Final private Map<ModelResourceLocation, BakedModel> bakedTopLevelModels;
|
||||||
|
@Shadow @Mutable @Final public Map<ModelResourceLocation, UnbakedModel> topLevelModels;
|
||||||
|
@Shadow @Mutable @Final private Map<ResourceLocation, UnbakedModel> unbakedCache;
|
||||||
|
@Shadow @Mutable @Final public Map<ModelBakery.BakedCacheKey, BakedModel> bakedCache;
|
||||||
|
|
||||||
|
@Shadow protected abstract void loadItemModelAndDependencies(ResourceLocation resourceLocation);
|
||||||
|
|
||||||
|
|
||||||
|
@Shadow @Final public static ModelResourceLocation MISSING_MODEL_VARIANT;
|
||||||
|
|
||||||
|
private final Map<ModelResourceLocation, BakedModel> mfix$emulatedBakedRegistry = new DynamicOverridableMap<>(this::loadBakedModelDynamic);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UnbakedModel mfix$loadUnbakedModelDynamic(ModelResourceLocation location) {
|
||||||
|
if(location.equals(MISSING_MODEL_VARIANT)) {
|
||||||
|
return missingModel;
|
||||||
|
}
|
||||||
|
modelBakeryLock.lock();
|
||||||
|
try {
|
||||||
|
UnbakedModel existing = this.topLevelModels.get(location);
|
||||||
|
if (existing != null) {
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
if(DEBUG_MODEL_LOADS) {
|
||||||
|
ModernFix.LOGGER.info("Loading model {}", location);
|
||||||
|
}
|
||||||
|
if(location.variant().equals("inventory")) {
|
||||||
|
this.loadItemModelAndDependencies(location.id());
|
||||||
|
} else {
|
||||||
|
((IBlockStateModelLoader)dynamicLoader).loadSpecificBlock(location);
|
||||||
|
}
|
||||||
|
return this.topLevelModels.getOrDefault(location, this.missingModel);
|
||||||
|
} finally {
|
||||||
|
modelBakeryLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UnbakedModel mfix$getMissingModel() {
|
||||||
|
return missingModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private BakedModel loadBakedModelDynamic(ModelResourceLocation location) {
|
||||||
|
if(location.equals(MISSING_MODEL_VARIANT)) {
|
||||||
|
return bakedMissingModel;
|
||||||
|
}
|
||||||
|
BakedModel model;
|
||||||
|
modelBakeryLock.lock();
|
||||||
|
try {
|
||||||
|
model = bakedTopLevelModels.get(location);
|
||||||
|
if(model == null) {
|
||||||
|
UnbakedModel prototype = mfix$loadUnbakedModelDynamic(location);
|
||||||
|
if(prototype == missingModel) {
|
||||||
|
model = bakedMissingModel;
|
||||||
|
} else {
|
||||||
|
prototype.resolveParents(this::getModel);
|
||||||
|
if(DEBUG_MODEL_LOADS) {
|
||||||
|
ModernFix.LOGGER.info("Baking model {}", location);
|
||||||
|
}
|
||||||
|
this.method_61072(this.textureGetter, location, prototype);
|
||||||
|
model = bakedTopLevelModels.remove(location);
|
||||||
|
if(model == null) {
|
||||||
|
ModernFix.LOGGER.error("Failed to load model " + location);
|
||||||
|
model = bakedMissingModel;
|
||||||
|
}
|
||||||
|
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
|
||||||
|
model = integration.onBakedModelLoad(location, prototype, model, BlockModelRotation.X0_Y0, (ModelBakery)(Object)this, this.textureGetter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
modelBakeryLock.unlock();
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModifyExpressionValue(method = "<init>", at = @At(value = "CONSTANT", args = "stringValue=missing_model"))
|
||||||
|
private String replaceBackingMaps(String original) {
|
||||||
|
this.unbakedCache = new LRUMap<>(this.unbakedCache);
|
||||||
|
this.bakedCache = new LRUMap<>(this.bakedCache);
|
||||||
|
this.topLevelModels = new LRUMap<>(this.topLevelModels);
|
||||||
|
this.bakedTopLevelModels = new LRUMap<>(this.bakedTopLevelModels);
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
@WrapOperation(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/BlockStateModelLoader;loadAllBlockStates()V"))
|
||||||
|
private void noInitialBlockStateLoad(BlockStateModelLoader instance, Operation<Void> original) {
|
||||||
|
dynamicLoader = instance;
|
||||||
|
original.call(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/DefaultedRegistry;keySet()Ljava/util/Set;"))
|
||||||
|
private Set<?> skipLoadingItems(DefaultedRegistry instance) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Inject(method = "bakeModels", at = @At("HEAD"))
|
||||||
|
private void storeTextureGetterAndBakeMissing(ModelBakery.TextureGetter textureGetter, CallbackInfo ci) {
|
||||||
|
this.textureGetter = textureGetter;
|
||||||
|
this.method_61072(textureGetter, MISSING_MODEL_VARIANT, Objects.requireNonNull(this.topLevelModels.get(MISSING_MODEL_VARIANT)));
|
||||||
|
this.bakedMissingModel = this.bakedTopLevelModels.get(MISSING_MODEL_VARIANT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean inInitialLoad = true;
|
||||||
|
|
||||||
|
@Inject(method = "bakeModels", at = @At("RETURN"))
|
||||||
|
private void onInitialBakeFinish(ModelBakery.TextureGetter textureGetter, CallbackInfo ci) {
|
||||||
|
var permanentMRLs = new ObjectOpenHashSet<>(this.bakedTopLevelModels.keySet());
|
||||||
|
((LRUMap<ModelResourceLocation, BakedModel>)this.bakedTopLevelModels).setPermanentEntries(permanentMRLs);
|
||||||
|
ModernFix.LOGGER.info("Dynamic model bakery initial baking finished, with {} permanent top level baked models", this.bakedTopLevelModels.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
private void onInitialLoadFinish(BlockColors blockColors, ProfilerFiller profilerFiller, Map map, Map map2, CallbackInfo ci) {
|
||||||
|
var permanentMRLs = new ObjectOpenHashSet<>(this.topLevelModels.keySet());
|
||||||
|
((LRUMap<ModelResourceLocation, UnbakedModel>)this.topLevelModels).setPermanentEntries(permanentMRLs);
|
||||||
|
ModernFix.LOGGER.info("Dynamic model bakery loading finished, with {} permanent top level models", this.topLevelModels.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private int tickCount;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private static final int MAXIMUM_CACHE_SIZE = 1000;
|
||||||
|
|
||||||
|
private void runCleanup() {
|
||||||
|
((LRUMap<?, ?>)this.unbakedCache).dropEntriesToMeetSize(MAXIMUM_CACHE_SIZE);
|
||||||
|
((LRUMap<?, ?>)this.bakedCache).dropEntriesToMeetSize(MAXIMUM_CACHE_SIZE);
|
||||||
|
((LRUMap<?, ?>)this.topLevelModels).dropEntriesToMeetSize(MAXIMUM_CACHE_SIZE);
|
||||||
|
((LRUMap<?, ?>)this.bakedTopLevelModels).dropEntriesToMeetSize(MAXIMUM_CACHE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mfix$finishLoading() {
|
||||||
|
inInitialLoad = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mfix$tick() {
|
||||||
|
if(inInitialLoad) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tickCount++;
|
||||||
|
if((tickCount % 200) == 0) {
|
||||||
|
if(modelBakeryLock.tryLock()) {
|
||||||
|
try {
|
||||||
|
runCleanup();
|
||||||
|
} finally {
|
||||||
|
modelBakeryLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author embeddedt
|
||||||
|
* @reason We provide a fake baked registry to the rest of Minecraft, that dynamically loads models.
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public Map<ModelResourceLocation, BakedModel> getBakedTopLevelModels() {
|
||||||
|
return this.mfix$emulatedBakedRegistry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,35 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||||
|
import net.minecraft.client.resources.model.AtlasSet;
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.client.resources.model.BlockStateModelLoader;
|
||||||
import net.minecraft.client.resources.model.ModelBakery;
|
import net.minecraft.client.resources.model.ModelBakery;
|
||||||
import net.minecraft.client.resources.model.ModelManager;
|
import net.minecraft.client.resources.model.ModelManager;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.packs.resources.ResourceManager;
|
import net.minecraft.server.packs.resources.ResourceManager;
|
||||||
import net.minecraft.util.GsonHelper;
|
import net.minecraft.util.GsonHelper;
|
||||||
|
import net.minecraft.util.profiling.ProfilerFiller;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.block.state.StateDefinition;
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
||||||
|
import org.embeddedt.modernfix.duck.IExtendedModelManager;
|
||||||
import org.embeddedt.modernfix.util.CacheUtil;
|
import org.embeddedt.modernfix.util.CacheUtil;
|
||||||
import org.embeddedt.modernfix.util.LambdaMap;
|
import org.embeddedt.modernfix.util.LambdaMap;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Coerce;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -34,9 +43,12 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Mixin(ModelManager.class)
|
@Mixin(ModelManager.class)
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
public class ModelManagerMixin {
|
public class ModelManagerMixin implements IExtendedModelManager {
|
||||||
@Shadow private Map<ResourceLocation, BakedModel> bakedRegistry;
|
@Shadow private Map<ResourceLocation, BakedModel> bakedRegistry;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private Runnable tickHandler = () -> {};
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
private void injectDummyBakedRegistry(CallbackInfo ci) {
|
private void injectDummyBakedRegistry(CallbackInfo ci) {
|
||||||
if(this.bakedRegistry == null) {
|
if(this.bakedRegistry == null) {
|
||||||
|
|
@ -51,8 +63,8 @@ public class ModelManagerMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockStates(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockStates(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
||||||
private CompletableFuture<Map<ResourceLocation, List<ModelBakery.LoadedJson>>> deferBlockStateLoad(ResourceManager manager, Executor executor) {
|
private CompletableFuture<Map<ResourceLocation, List<BlockStateModelLoader.LoadedJson>>> deferBlockStateLoad(ResourceManager manager, Executor executor) {
|
||||||
var cache = CacheUtil.<ResourceLocation, List<ModelBakery.LoadedJson>>simpleCacheForLambda(location -> loadSingleBlockState(manager, location), 100L);
|
var cache = CacheUtil.<ResourceLocation, List<BlockStateModelLoader.LoadedJson>>simpleCacheForLambda(location -> loadSingleBlockState(manager, location), 100L);
|
||||||
return CompletableFuture.completedFuture(new LambdaMap<>(location -> cache.getUnchecked(location)));
|
return CompletableFuture.completedFuture(new LambdaMap<>(location -> cache.getUnchecked(location)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,14 +84,29 @@ public class ModelManagerMixin {
|
||||||
}).orElse(null);
|
}).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ModelBakery.LoadedJson> loadSingleBlockState(ResourceManager manager, ResourceLocation location) {
|
private List<BlockStateModelLoader.LoadedJson> loadSingleBlockState(ResourceManager manager, ResourceLocation location) {
|
||||||
return manager.getResourceStack(location).stream().map(resource -> {
|
return manager.getResourceStack(location).stream().map(resource -> {
|
||||||
try (BufferedReader reader = resource.openAsReader()) {
|
try (BufferedReader reader = resource.openAsReader()) {
|
||||||
return new ModelBakery.LoadedJson(resource.sourcePackId(), GsonHelper.parse(reader));
|
return new BlockStateModelLoader.LoadedJson(resource.sourcePackId(), GsonHelper.parse(reader));
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
ModernFix.LOGGER.error("Couldn't load blockstate", e);
|
ModernFix.LOGGER.error("Couldn't load blockstate", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject(method = "loadModels", at = @At("RETURN"))
|
||||||
|
private void storeTicker(ProfilerFiller profilerFiller, Map<ResourceLocation, AtlasSet.StitchResult> map, ModelBakery modelBakery, CallbackInfoReturnable<?> cir) {
|
||||||
|
tickHandler = ((IExtendedModelBakery)modelBakery)::mfix$tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "apply", at = @At("RETURN"))
|
||||||
|
private void freezeBakery(@Coerce Object reloadState, ProfilerFiller profilerFiller, CallbackInfo ci, @Local(ordinal = 0) ModelBakery bakery) {
|
||||||
|
((IExtendedModelBakery)bakery).mfix$finishLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mfix$tick() {
|
||||||
|
tickHandler.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
@Mixin(GameRenderer.class)
|
@Mixin(GameRenderer.class)
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
public class GameRendererMixin {
|
public class GameRendererMixin {
|
||||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;renderLevel(FJLcom/mojang/blaze3d/vertex/PoseStack;)V", shift = At.Shift.BEFORE))
|
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;renderLevel(Lnet/minecraft/client/DeltaTracker;)V", shift = At.Shift.BEFORE))
|
||||||
private void markRenderingLevel(CallbackInfo ci) {
|
private void markRenderingLevel(CallbackInfo ci) {
|
||||||
RenderState.IS_RENDERING_LEVEL = true;
|
RenderState.IS_RENDERING_LEVEL = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;renderLevel(FJLcom/mojang/blaze3d/vertex/PoseStack;)V", shift = At.Shift.AFTER))
|
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;renderLevel(Lnet/minecraft/client/DeltaTracker;)V", shift = At.Shift.AFTER))
|
||||||
private void markNotRenderingLevel(CallbackInfo ci) {
|
private void markNotRenderingLevel(CallbackInfo ci) {
|
||||||
RenderState.IS_RENDERING_LEVEL = false;
|
RenderState.IS_RENDERING_LEVEL = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.faster_structure_location;
|
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.StructureCheck;
|
|
||||||
import org.embeddedt.modernfix.duck.IStructureCheck;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
|
|
||||||
@Mixin(ServerLevel.class)
|
|
||||||
public class ServerLevelMixin {
|
|
||||||
@Shadow @Final private ServerChunkCache chunkSource;
|
|
||||||
|
|
||||||
@ModifyExpressionValue(method = "<init>", at = @At(value = "NEW", target = "(Lnet/minecraft/world/level/chunk/storage/ChunkScanAccess;Lnet/minecraft/core/RegistryAccess;Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplateManager;Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/world/level/chunk/ChunkGenerator;Lnet/minecraft/world/level/levelgen/RandomState;Lnet/minecraft/world/level/LevelHeightAccessor;Lnet/minecraft/world/level/biome/BiomeSource;JLcom/mojang/datafixers/DataFixer;)Lnet/minecraft/world/level/levelgen/structure/StructureCheck;", ordinal = 0))
|
|
||||||
private StructureCheck attachGeneratorState(StructureCheck check) {
|
|
||||||
((IStructureCheck)check).mfix$setStructureState(this.chunkSource.getGeneratorState());
|
|
||||||
return check;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.faster_structure_location;
|
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.StructureCheck;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.StructureCheckResult;
|
|
||||||
import org.embeddedt.modernfix.duck.IStructureCheck;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
|
|
||||||
@Mixin(StructureCheck.class)
|
|
||||||
public class StructureCheckMixin implements IStructureCheck {
|
|
||||||
@Shadow @Final private Registry<Structure> structureConfigs;
|
|
||||||
|
|
||||||
private ChunkGeneratorStructureState mfix$structureState;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mfix$setStructureState(ChunkGeneratorStructureState state) {
|
|
||||||
mfix$structureState = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author embeddedt (inspired by 24w04a and Bytzo's comment on https://bugs.mojang.com/browse/MC-249136)
|
|
||||||
* @reason Avoid running the canCreateStructure method (which can be expensive) if the structure placement already
|
|
||||||
* forbids placing the structure in this chunk.
|
|
||||||
*/
|
|
||||||
@ModifyExpressionValue(method = "checkStart", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/StructureCheck;tryLoadFromStorage(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/world/level/levelgen/structure/Structure;ZJ)Lnet/minecraft/world/level/levelgen/structure/StructureCheckResult;"))
|
|
||||||
private StructureCheckResult mfix$checkForValidPosition(StructureCheckResult storageResult, ChunkPos chunkPos, Structure structure, boolean skipKnownStructures) {
|
|
||||||
if (storageResult != null) {
|
|
||||||
return storageResult;
|
|
||||||
} else if(mfix$structureState != null) {
|
|
||||||
// Check if any of the placements allow for this structure to be in this chunk
|
|
||||||
var structureHolder = this.structureConfigs.wrapAsHolder(structure);
|
|
||||||
for (var placement : mfix$structureState.getPlacementsForStructure(structureHolder)) {
|
|
||||||
if (placement.isStructureChunk(mfix$structureState, chunkPos.x, chunkPos.z)) {
|
|
||||||
// Allowed - return null so regular check runs
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Not allowed - early exit by returning a non-null value
|
|
||||||
return StructureCheckResult.START_NOT_PRESENT;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -17,7 +17,7 @@ public class BlockableEventLoopMixin {
|
||||||
* @reason yielding the thread is pretty pointless if we're about to park anyway
|
* @reason yielding the thread is pretty pointless if we're about to park anyway
|
||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
protected void waitForTasks() {
|
public void waitForTasks() {
|
||||||
LockSupport.parkNanos("waiting for tasks", MFIX$TICK_WAIT_TIME);
|
LockSupport.parkNanos("waiting for tasks", MFIX$TICK_WAIT_TIME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.mojang_registry_size;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectList;
|
|
||||||
import net.minecraft.core.MappedRegistry;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
@Mixin(MappedRegistry.class)
|
|
||||||
public class MappedRegistryMixin {
|
|
||||||
/**
|
|
||||||
* Avoid copying the ID list to a slightly larger one every time an entry is added to the registry.
|
|
||||||
* The original behavior causes O(n) time complexity for registration.
|
|
||||||
*/
|
|
||||||
@Redirect(
|
|
||||||
method = "registerMapping(ILnet/minecraft/resources/ResourceKey;Ljava/lang/Object;Lcom/mojang/serialization/Lifecycle;)Lnet/minecraft/core/Holder$Reference;",
|
|
||||||
at = @At(value = "INVOKE", target = "Lit/unimi/dsi/fastutil/objects/ObjectList;size(I)V", remap = false)
|
|
||||||
)
|
|
||||||
private void setSizeSmart(ObjectList<?> list, int size) {
|
|
||||||
if(list instanceof ObjectArrayList && size > list.size()) {
|
|
||||||
int requestedSize = size;
|
|
||||||
/* choose next power of two, or this value if it is a power of two */
|
|
||||||
int p2Size = Integer.highestOneBit(size);
|
|
||||||
if(p2Size != size)
|
|
||||||
size = p2Size << 1;
|
|
||||||
// grow backing array to power-of-two size, this will return instantly in most cases
|
|
||||||
((ObjectArrayList<?>)list).ensureCapacity(size);
|
|
||||||
// write null entries to fill size, to match the behavior of list.size(int)
|
|
||||||
while(list.size() < requestedSize)
|
|
||||||
list.add(null);
|
|
||||||
} else {
|
|
||||||
list.size(size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -10,8 +10,8 @@ import java.util.Map;
|
||||||
|
|
||||||
@Mixin(targets = "net/minecraft/nbt/CompoundTag$1")
|
@Mixin(targets = "net/minecraft/nbt/CompoundTag$1")
|
||||||
public class CompoundTag1Mixin {
|
public class CompoundTag1Mixin {
|
||||||
@ModifyVariable(method = "load(Ljava/io/DataInput;ILnet/minecraft/nbt/NbtAccounter;)Lnet/minecraft/nbt/CompoundTag;", at = @At(value = "INVOKE_ASSIGN", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false))
|
@ModifyVariable(method = "loadCompound", at = @At(value = "INVOKE_ASSIGN", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false))
|
||||||
private Map<String, Tag> modifyMap(Map<String, Tag> map) {
|
private static Map<String, Tag> modifyMap(Map<String, Tag> map) {
|
||||||
CanonizingStringMap<Tag> newMap = new CanonizingStringMap<>();
|
CanonizingStringMap<Tag> newMap = new CanonizingStringMap<>();
|
||||||
if(map != null)
|
if(map != null)
|
||||||
newMap.putAll(map);
|
newMap.putAll(map);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.material.FluidState;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||||
|
|
||||||
|
@Mixin(BlockBehaviour.class)
|
||||||
|
public interface BlockBehaviourInvoker {
|
||||||
|
@Invoker
|
||||||
|
FluidState invokeGetFluidState(BlockState blockState);
|
||||||
|
@Invoker
|
||||||
|
boolean invokeIsRandomlyTicking(BlockState blockState);
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds;
|
package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.mojang.serialization.MapCodec;
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
@ -17,9 +17,10 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Mixin(BlockBehaviour.BlockStateBase.class)
|
@Mixin(BlockBehaviour.BlockStateBase.class)
|
||||||
public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implements IBlockState {
|
public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implements IBlockState {
|
||||||
protected BlockStateBaseMixin(Block object, ImmutableMap<Property<?>, Comparable<?>> immutableMap, MapCodec<BlockState> mapCodec) {
|
protected BlockStateBaseMixin(Block object, Reference2ObjectArrayMap<Property<?>, Comparable<?>> immutableMap, MapCodec<BlockState> mapCodec) {
|
||||||
super(object, immutableMap, mapCodec);
|
super(object, immutableMap, mapCodec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +94,7 @@ public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState>
|
||||||
if(!buildingCache) {
|
if(!buildingCache) {
|
||||||
buildingCache = true;
|
buildingCache = true;
|
||||||
try {
|
try {
|
||||||
this.fluidState = this.owner.getFluidState(this.asState());
|
this.fluidState = ((BlockBehaviourInvoker)this.owner).invokeGetFluidState(this.asState());
|
||||||
} finally {
|
} finally {
|
||||||
buildingCache = false;
|
buildingCache = false;
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +113,7 @@ public abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState>
|
||||||
))
|
))
|
||||||
private boolean genCacheBeforeGettingTicking(BlockBehaviour.BlockStateBase base) {
|
private boolean genCacheBeforeGettingTicking(BlockBehaviour.BlockStateBase base) {
|
||||||
if(this.cacheInvalid)
|
if(this.cacheInvalid)
|
||||||
return this.owner.isRandomlyTicking(this.asState());
|
return ((BlockBehaviourInvoker)this.owner).invokeIsRandomlyTicking(this.asState());
|
||||||
return this.isRandomlyTicking;
|
return this.isRandomlyTicking;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
|
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.TicketType;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
|
|
||||||
@Mixin(Entity.class)
|
|
||||||
public class EntityMixin {
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason If the spawn chunks are not loaded, end portals linking to the overworld will teleport entities into
|
|
||||||
* the void at the spawn position, which is not ideal. To solve this, we create a PORTAL ticket if the expected
|
|
||||||
* overworld chunk is missing.
|
|
||||||
*/
|
|
||||||
@ModifyExpressionValue(method = "findDimensionEntryPoint", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;getSharedSpawnPos()Lnet/minecraft/core/BlockPos;"), require = 0)
|
|
||||||
private BlockPos mfix$triggerChunkloadAtSpawnPos(BlockPos spawnPos, ServerLevel destination) {
|
|
||||||
// Only apply this change if the overworld is the destination
|
|
||||||
if (destination.dimension() == ServerLevel.OVERWORLD) {
|
|
||||||
// No ticket is required if the chunk happens to already be loaded
|
|
||||||
if(!destination.hasChunk(spawnPos.getX() >> 4, spawnPos.getZ() >> 4)) {
|
|
||||||
// Create a portal ticket. While we could just load the chunk once, it would immediately unload on the
|
|
||||||
// next tick, causing churn. The ticket will keep it loaded for a few seconds which should give high
|
|
||||||
// performance for farms pumping things through portals frequently.
|
|
||||||
BlockPos key = spawnPos.immutable();
|
|
||||||
destination.getChunkSource().addRegionTicket(TicketType.PORTAL, new ChunkPos(key), 3, key);
|
|
||||||
// Wait for the chunk to be loaded, as adding the ticket is asynchronous
|
|
||||||
destination.getChunk(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return spawnPos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
|
|
||||||
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
|
||||||
import net.minecraft.server.level.TicketType;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
@Mixin(value = MinecraftServer.class, priority = 1100)
|
|
||||||
public class MinecraftServerMixin {
|
|
||||||
@Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;addRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V"))
|
|
||||||
private void addSpawnChunkTicket(ServerChunkCache cache, TicketType<?> type, ChunkPos pos, int distance, Object o) {
|
|
||||||
// load first chunk
|
|
||||||
cache.getChunk(pos.x, pos.z, ChunkStatus.FULL, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;getTickingGenerated()I"), require = 0)
|
|
||||||
private int getGenerated(ServerChunkCache cache) {
|
|
||||||
return 441;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
|
|
||||||
|
|
||||||
import net.minecraft.server.level.DistanceManager;
|
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
|
||||||
|
|
||||||
@Mixin(ServerChunkCache.class)
|
|
||||||
public interface ServerChunkCacheAccessor {
|
|
||||||
@Accessor("distanceManager")
|
|
||||||
DistanceManager getDistanceManager();
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
|
|
||||||
|
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.TicketType;
|
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
@Mixin(ServerLevel.class)
|
|
||||||
public class ServerLevelMixin {
|
|
||||||
@Redirect(method = "setDefaultSpawnPos", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;removeRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V"))
|
|
||||||
private void removeTicket(ServerChunkCache cache, TicketType<?> type, ChunkPos pos, int distance, Object o) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = "setDefaultSpawnPos", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;addRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V"))
|
|
||||||
private void addTicket(ServerChunkCache cache, TicketType<?> type, ChunkPos pos, int distance, Object o) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
|
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
|
||||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
|
||||||
import net.minecraft.util.SortedArraySet;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
|
|
||||||
@Mixin(SortedArraySet.class)
|
|
||||||
public class SortedArraySetMixin<T> {
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason Make add() not crash with a null key, since some mods (Carpet) assume there will always be a spawn ticket,
|
|
||||||
* and then assume the reference they have is non-null (it can be null with this option enabled).
|
|
||||||
*/
|
|
||||||
@WrapOperation(method = "add", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/SortedArraySet;findIndex(Ljava/lang/Object;)I"), require = 0)
|
|
||||||
private int checkStatus(SortedArraySet<T> instance, T object, Operation<Integer> original) {
|
|
||||||
if(object == null) {
|
|
||||||
ModernFix.LOGGER.error("Attempted to insert a null key into SortedArraySet, ignoring");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return original.call(instance, object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.ticking_chunk_alloc;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Either;
|
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import org.embeddedt.modernfix.util.EitherUtil;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
@Mixin(value = ChunkHolder.class, priority = 500)
|
|
||||||
public abstract class ChunkHolderMixin {
|
|
||||||
@Shadow public abstract CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getTickingChunkFuture();
|
|
||||||
|
|
||||||
@Shadow public abstract CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getFullChunkFuture();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason avoid Optional allocation
|
|
||||||
*/
|
|
||||||
@Overwrite
|
|
||||||
public LevelChunk getTickingChunk() {
|
|
||||||
CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completableFuture = this.getTickingChunkFuture();
|
|
||||||
Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = completableFuture.getNow(null);
|
|
||||||
return either == null ? null : EitherUtil.leftOrNull(either);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason avoid Optional allocation
|
|
||||||
*/
|
|
||||||
@Overwrite
|
|
||||||
public LevelChunk getFullChunk() {
|
|
||||||
CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completableFuture = this.getFullChunkFuture();
|
|
||||||
Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = completableFuture.getNow(null);
|
|
||||||
return either == null ? null : EitherUtil.leftOrNull(either);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -62,7 +62,7 @@ public class ModernFixEarlyConfig {
|
||||||
private static final String MIXIN_REQUIRES_MOD_DESC = Type.getDescriptor(RequiresMod.class);
|
private static final String MIXIN_REQUIRES_MOD_DESC = Type.getDescriptor(RequiresMod.class);
|
||||||
private static final String MIXIN_DEV_ONLY_DESC = Type.getDescriptor(IgnoreOutsideDev.class);
|
private static final String MIXIN_DEV_ONLY_DESC = Type.getDescriptor(IgnoreOutsideDev.class);
|
||||||
|
|
||||||
private static final Pattern PLATFORM_PREFIX = Pattern.compile("(forge|fabric|common)\\.");
|
private static final Pattern PLATFORM_PREFIX = Pattern.compile("(neoforge|fabric|common)\\.");
|
||||||
|
|
||||||
public static String sanitize(String mixinClassName) {
|
public static String sanitize(String mixinClassName) {
|
||||||
return PLATFORM_PREFIX.matcher(mixinClassName).replaceFirst("");
|
return PLATFORM_PREFIX.matcher(mixinClassName).replaceFirst("");
|
||||||
|
|
@ -78,7 +78,7 @@ public class ModernFixEarlyConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scanForAndBuildMixinOptions() {
|
private void scanForAndBuildMixinOptions() {
|
||||||
List<String> configFiles = ImmutableList.of("modernfix-common.mixins.json", "modernfix-fabric.mixins.json", "modernfix-forge.mixins.json");
|
List<String> configFiles = ImmutableList.of("modernfix-common.mixins.json", "modernfix-fabric.mixins.json", "modernfix-neoforge.mixins.json");
|
||||||
List<String> mixinPaths = new ArrayList<>();
|
List<String> mixinPaths = new ArrayList<>();
|
||||||
for(String configFile : configFiles) {
|
for(String configFile : configFiles) {
|
||||||
InputStream stream = ModernFixEarlyConfig.class.getClassLoader().getResourceAsStream(configFile);
|
InputStream stream = ModernFixEarlyConfig.class.getClassLoader().getResourceAsStream(configFile);
|
||||||
|
|
@ -163,7 +163,6 @@ public class ModernFixEarlyConfig {
|
||||||
.put("mixin.perf.dynamic_resources", false)
|
.put("mixin.perf.dynamic_resources", false)
|
||||||
.put("mixin.feature.direct_stack_trace", false)
|
.put("mixin.feature.direct_stack_trace", false)
|
||||||
.put("mixin.feature.stalled_chunk_load_detection", false)
|
.put("mixin.feature.stalled_chunk_load_detection", false)
|
||||||
.put("mixin.perf.blast_search_trees.force", false)
|
|
||||||
.put("mixin.bugfix.restore_old_dragon_movement", false)
|
.put("mixin.bugfix.restore_old_dragon_movement", false)
|
||||||
.put("mixin.perf.worldgen_allocation", false) // experimental
|
.put("mixin.perf.worldgen_allocation", false) // experimental
|
||||||
.put("mixin.feature.cause_lag_by_disabling_threads", false)
|
.put("mixin.feature.cause_lag_by_disabling_threads", false)
|
||||||
|
|
@ -181,9 +180,7 @@ public class ModernFixEarlyConfig {
|
||||||
.put("mixin.feature.warn_missing_perf_mods", true)
|
.put("mixin.feature.warn_missing_perf_mods", true)
|
||||||
.put("mixin.feature.spark_profile_launch", false)
|
.put("mixin.feature.spark_profile_launch", false)
|
||||||
.put("mixin.devenv", isDevEnv)
|
.put("mixin.devenv", isDevEnv)
|
||||||
.put("mixin.perf.remove_spawn_chunks", isDevEnv)
|
|
||||||
.putConditionally(() -> !isFabric, "mixin.bugfix.fix_config_crashes", true)
|
.putConditionally(() -> !isFabric, "mixin.bugfix.fix_config_crashes", true)
|
||||||
.putConditionally(() -> !isFabric, "mixin.bugfix.forge_at_inject_error", true)
|
|
||||||
.putConditionally(() -> !isFabric, "mixin.feature.registry_event_progress", false)
|
.putConditionally(() -> !isFabric, "mixin.feature.registry_event_progress", false)
|
||||||
.putConditionally(() -> isFabric, "mixin.perf.clear_fabric_mapping_tables", false)
|
.putConditionally(() -> isFabric, "mixin.perf.clear_fabric_mapping_tables", false)
|
||||||
.build();
|
.build();
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
package org.embeddedt.modernfix.dfu;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.DSL;
|
|
||||||
import com.mojang.datafixers.DataFixer;
|
|
||||||
import com.mojang.datafixers.schemas.Schema;
|
|
||||||
import com.mojang.serialization.Dynamic;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class LazyDataFixer implements DataFixer {
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger("ModernFix");
|
|
||||||
private DataFixer backingDataFixer;
|
|
||||||
private final Supplier<DataFixer> dfuSupplier;
|
|
||||||
|
|
||||||
public LazyDataFixer(Supplier<DataFixer> dfuSupplier) {
|
|
||||||
LOGGER.info("Bypassed Mojang DFU");
|
|
||||||
this.backingDataFixer = null;
|
|
||||||
this.dfuSupplier = dfuSupplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DataFixer getDataFixer() {
|
|
||||||
synchronized (this) {
|
|
||||||
if(backingDataFixer == null) {
|
|
||||||
LOGGER.info("Instantiating Mojang DFU");
|
|
||||||
DFUBlaster.blastMaps();
|
|
||||||
backingDataFixer = dfuSupplier.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return backingDataFixer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> Dynamic<T> update(DSL.TypeReference type, Dynamic<T> input, int version, int newVersion) {
|
|
||||||
if(version >= newVersion)
|
|
||||||
return input;
|
|
||||||
return getDataFixer().update(type, input, version, newVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Schema getSchema(int key) {
|
|
||||||
return getDataFixer().getSchema(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.embeddedt.modernfix.duck;
|
||||||
|
|
||||||
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
|
|
||||||
|
public interface IBlockStateModelLoader {
|
||||||
|
void loadSpecificBlock(ModelResourceLocation location);
|
||||||
|
}
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
package org.embeddedt.modernfix.duck;
|
|
||||||
|
|
||||||
public interface IExtendedModelBaker {
|
|
||||||
/**
|
|
||||||
* Causes the ModelBaker to throw when it finds a missing model instead of proceeding with the bake.
|
|
||||||
* @return the previous value of this flag
|
|
||||||
*/
|
|
||||||
boolean throwOnMissingModel(boolean flag);
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +1,11 @@
|
||||||
package org.embeddedt.modernfix.duck;
|
package org.embeddedt.modernfix.duck;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
import net.minecraft.client.resources.model.ModelState;
|
|
||||||
import net.minecraft.client.resources.model.UnbakedModel;
|
import net.minecraft.client.resources.model.UnbakedModel;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.block.state.StateDefinition;
|
|
||||||
|
|
||||||
public interface IExtendedModelBakery {
|
public interface IExtendedModelBakery {
|
||||||
ImmutableList<BlockState> getBlockStatesForMRL(StateDefinition<Block, BlockState> stateDefinition, ModelResourceLocation location);
|
void mfix$tick();
|
||||||
BakedModel bakeDefault(ResourceLocation modelLocation, ModelState state);
|
void mfix$finishLoading();
|
||||||
UnbakedModel mfix$getUnbakedMissingModel();
|
UnbakedModel mfix$loadUnbakedModelDynamic(ModelResourceLocation location);
|
||||||
|
UnbakedModel mfix$getMissingModel();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package org.embeddedt.modernfix.duck;
|
||||||
|
|
||||||
|
public interface IExtendedModelManager {
|
||||||
|
void mfix$tick();
|
||||||
|
}
|
||||||
|
|
@ -1,240 +0,0 @@
|
||||||
package org.embeddedt.modernfix.dynamicresources;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.mojang.math.Transformation;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
|
||||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
|
||||||
import net.minecraft.client.renderer.block.model.ItemTransforms;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
|
||||||
import net.minecraft.client.resources.model.BlockModelRotation;
|
|
||||||
import net.minecraft.client.resources.model.ModelBakery;
|
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.core.registries.BuiltInRegistries;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.util.RandomSource;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class DynamicBakedModelProvider implements Map<ResourceLocation, BakedModel> {
|
|
||||||
/**
|
|
||||||
* The list of blacklisted resource locations that are never baked as top-level models.
|
|
||||||
*
|
|
||||||
* This is a hack to get around the fact that we don't really know exactly what models were supposed to end up
|
|
||||||
* in the baked registry ahead of time.
|
|
||||||
*/
|
|
||||||
private static final ImmutableSet<ResourceLocation> BAKE_SKIPPED_TOPLEVEL = ImmutableSet.<ResourceLocation>builder()
|
|
||||||
.add(new ResourceLocation("custommachinery", "block/custom_machine_block"))
|
|
||||||
.build();
|
|
||||||
public static DynamicBakedModelProvider currentInstance = null;
|
|
||||||
private final ModelBakery bakery;
|
|
||||||
private final Map<ModelBakery.BakedCacheKey, BakedModel> bakedCache;
|
|
||||||
private final Map<ResourceLocation, BakedModel> permanentOverrides;
|
|
||||||
private BakedModel missingModel;
|
|
||||||
private static final BakedModel SENTINEL = new BakedModel() {
|
|
||||||
@Override
|
|
||||||
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction direction, RandomSource random) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean useAmbientOcclusion() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isGui3d() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean usesBlockLight() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCustomRenderer() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TextureAtlasSprite getParticleIcon() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ItemTransforms getTransforms() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ItemOverrides getOverrides() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public DynamicBakedModelProvider(ModelBakery bakery, Map<ModelBakery.BakedCacheKey, BakedModel> cache) {
|
|
||||||
this.bakery = bakery;
|
|
||||||
this.bakedCache = cache;
|
|
||||||
this.permanentOverrides = Collections.synchronizedMap(new Object2ObjectOpenHashMap<>());
|
|
||||||
if(currentInstance == null)
|
|
||||||
currentInstance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ModelBakery.BakedCacheKey vanillaKey(Object o) {
|
|
||||||
return new ModelBakery.BakedCacheKey((ResourceLocation)o, BlockModelRotation.X0_Y0.getRotation(), false);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return bakedCache.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return bakedCache.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsKey(Object o) {
|
|
||||||
return permanentOverrides.getOrDefault(o, SENTINEL) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsValue(Object o) {
|
|
||||||
return permanentOverrides.containsValue(o) || bakedCache.containsValue(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isVanillaTopLevelModel(ResourceLocation location) {
|
|
||||||
if(location instanceof ModelResourceLocation) {
|
|
||||||
try {
|
|
||||||
ModelResourceLocation mrl = (ModelResourceLocation)location;
|
|
||||||
ResourceLocation registryKey = new ResourceLocation(mrl.getNamespace(), mrl.getPath());
|
|
||||||
// check for standard inventory model
|
|
||||||
if(mrl.getVariant().equals("inventory") && BuiltInRegistries.ITEM.containsKey(registryKey))
|
|
||||||
return true;
|
|
||||||
Optional<Block> blockOpt = BuiltInRegistries.BLOCK.getOptional(registryKey);
|
|
||||||
if(blockOpt.isPresent()) {
|
|
||||||
return ModelBakeryHelpers.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), mrl).size() > 0;
|
|
||||||
}
|
|
||||||
} catch(RuntimeException ignored) {
|
|
||||||
// can occur if the MRL is not valid for that blockstate, ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(location.getNamespace().equals("minecraft") && location.getPath().equals("builtin/missing"))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BakedModel getMissingModel() {
|
|
||||||
BakedModel m = missingModel;
|
|
||||||
if(m == null) {
|
|
||||||
m = missingModel = ((IExtendedModelBakery)bakery).bakeDefault(ModelBakery.MISSING_MODEL_LOCATION, BlockModelRotation.X0_Y0);
|
|
||||||
}
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BakedModel get(Object o) {
|
|
||||||
BakedModel model = permanentOverrides.getOrDefault(o, SENTINEL);
|
|
||||||
if(model != SENTINEL)
|
|
||||||
return model;
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
if(BAKE_SKIPPED_TOPLEVEL.contains((ResourceLocation)o))
|
|
||||||
model = getMissingModel();
|
|
||||||
else
|
|
||||||
model = ((IExtendedModelBakery)bakery).bakeDefault((ResourceLocation)o, BlockModelRotation.X0_Y0);
|
|
||||||
} catch(RuntimeException e) {
|
|
||||||
ModernFix.LOGGER.error("Exception baking {}: {}", o, e);
|
|
||||||
model = getMissingModel();
|
|
||||||
}
|
|
||||||
if(model == getMissingModel()) {
|
|
||||||
// to correctly emulate the original map, we return null for missing models, unless they are top-level
|
|
||||||
model = isVanillaTopLevelModel((ResourceLocation)o) ? model : null;
|
|
||||||
permanentOverrides.put((ResourceLocation) o, model);
|
|
||||||
}
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BakedModel put(ResourceLocation resourceLocation, BakedModel bakedModel) {
|
|
||||||
BakedModel m = permanentOverrides.put(resourceLocation, bakedModel);
|
|
||||||
if(m != null)
|
|
||||||
return m;
|
|
||||||
else
|
|
||||||
return bakedCache.get(vanillaKey(resourceLocation));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BakedModel remove(Object o) {
|
|
||||||
BakedModel m = permanentOverrides.remove(o);
|
|
||||||
if(m != null)
|
|
||||||
return m;
|
|
||||||
return bakedCache.remove(vanillaKey(o));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void putAll(@NotNull Map<? extends ResourceLocation, ? extends BakedModel> map) {
|
|
||||||
permanentOverrides.putAll(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Set<ResourceLocation> keySet() {
|
|
||||||
return bakedCache.keySet().stream().map(ModelBakery.BakedCacheKey::id).collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Collection<BakedModel> values() {
|
|
||||||
return bakedCache.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Set<Entry<ResourceLocation, BakedModel>> entrySet() {
|
|
||||||
return bakedCache.entrySet().stream().map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getKey().id(), entry.getValue())).collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public BakedModel replace(ResourceLocation key, BakedModel value) {
|
|
||||||
BakedModel existingOverride = permanentOverrides.get(key);
|
|
||||||
// as long as no valid override was put in (null can mean unable to load model, so we treat as invalid), replace
|
|
||||||
// the model
|
|
||||||
if(existingOverride == null)
|
|
||||||
return this.put(key, value);
|
|
||||||
else
|
|
||||||
return existingOverride;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void replaceAll(BiFunction<? super ResourceLocation, ? super BakedModel, ? extends BakedModel> function) {
|
|
||||||
Set<ResourceLocation> overridenLocations = permanentOverrides.keySet();
|
|
||||||
permanentOverrides.replaceAll(function);
|
|
||||||
boolean uvLock = BlockModelRotation.X0_Y0.isUvLocked();
|
|
||||||
Transformation rotation = BlockModelRotation.X0_Y0.getRotation();
|
|
||||||
bakedCache.replaceAll((loc, oldModel) -> {
|
|
||||||
if(loc.transformation() != rotation || loc.isUvLocked() != uvLock || overridenLocations.contains(loc.id()))
|
|
||||||
return oldModel;
|
|
||||||
else
|
|
||||||
return function.apply(loc.id(), oldModel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,17 +2,13 @@ package org.embeddedt.modernfix.dynamicresources;
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.mojang.datafixers.util.Pair;
|
|
||||||
import net.minecraft.client.resources.model.*;
|
import net.minecraft.client.resources.model.*;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.block.state.StateDefinition;
|
import net.minecraft.world.level.block.state.StateDefinition;
|
||||||
import net.minecraft.world.level.block.state.properties.Property;
|
import net.minecraft.world.level.block.state.properties.Property;
|
||||||
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.BiFunction;
|
|
||||||
|
|
||||||
public class ModelBakeryHelpers {
|
public class ModelBakeryHelpers {
|
||||||
/**
|
/**
|
||||||
|
|
@ -108,13 +104,4 @@ public class ModelBakeryHelpers {
|
||||||
}
|
}
|
||||||
return ImmutableList.copyOf(finalList);
|
return ImmutableList.copyOf(finalList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ModernFixClientIntegration bakedModelWrapper(BiFunction<ResourceLocation, Pair<UnbakedModel, BakedModel>, BakedModel> consumer) {
|
|
||||||
return new ModernFixClientIntegration() {
|
|
||||||
@Override
|
|
||||||
public BakedModel onBakedModelLoad(ResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery) {
|
|
||||||
return consumer.apply(location, Pair.of(baseModel, originalModel));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
package org.embeddedt.modernfix.entity;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
|
||||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
|
||||||
import net.minecraft.network.syncher.SynchedEntityData;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
|
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class EntityDataIDSyncHandler {
|
|
||||||
private static Map<Class<? extends Entity>, List<Pair<String, Integer>>> fieldsToSyncMap;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static void onDatapackSyncEvent(ServerPlayer targetPlayer) {
|
|
||||||
if(targetPlayer != null) {
|
|
||||||
/* Compute the current set of serializer IDs in use and send them */
|
|
||||||
if(fieldsToSyncMap == null) {
|
|
||||||
fieldsToSyncMap = new HashMap<>();
|
|
||||||
Map<Class<? extends Entity>, Integer> entityPoolMap = SynchedEntityData.ENTITY_ID_POOL;
|
|
||||||
List<Field> fieldsToSync = new ArrayList<>();
|
|
||||||
for(Class<? extends Entity> eClass : entityPoolMap.keySet()) {
|
|
||||||
fieldsToSync.clear();
|
|
||||||
try {
|
|
||||||
Field[] classFields = eClass.getDeclaredFields();
|
|
||||||
for(Field field : classFields) {
|
|
||||||
if(!Modifier.isStatic(field.getModifiers()))
|
|
||||||
continue;
|
|
||||||
field.setAccessible(true);
|
|
||||||
Object o = field.get(null);
|
|
||||||
if(o != null && EntityDataAccessor.class.isAssignableFrom(o.getClass())) {
|
|
||||||
fieldsToSync.add(field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(Field field : fieldsToSync) {
|
|
||||||
int id = ((EntityDataAccessor<?>)field.get(null)).id;
|
|
||||||
fieldsToSyncMap.computeIfAbsent(eClass, k -> new ArrayList<>()).add(Pair.of(field.getName(), id));
|
|
||||||
}
|
|
||||||
} catch(Throwable e) {
|
|
||||||
ModernFix.LOGGER.error("Skipping entity ID sync for {}: {}", eClass.getName(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EntityIDSyncPacket packet = new EntityIDSyncPacket(fieldsToSyncMap);
|
|
||||||
ModernFix.LOGGER.debug("Sending ID correction packet to client with " + fieldsToSyncMap.size() + " classes");
|
|
||||||
ModernFixPlatformHooks.INSTANCE.sendPacket(targetPlayer, packet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
package org.embeddedt.modernfix.packet;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
|
||||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class EntityIDSyncPacket {
|
|
||||||
private Map<Class<? extends Entity>, List<Pair<String, Integer>>> map;
|
|
||||||
|
|
||||||
public EntityIDSyncPacket(Map<Class<? extends Entity>, List<Pair<String, Integer>>> map) {
|
|
||||||
this.map = map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<Class<? extends Entity>, List<Pair<String, Integer>>> getFieldInfo() {
|
|
||||||
return this.map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityIDSyncPacket() {
|
|
||||||
this.map = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void serialize(FriendlyByteBuf buf) {
|
|
||||||
buf.writeVarInt(map.keySet().size());
|
|
||||||
for(Map.Entry<Class<? extends Entity>, List<Pair<String, Integer>>> entry : map.entrySet()) {
|
|
||||||
buf.writeUtf(entry.getKey().getName());
|
|
||||||
buf.writeVarInt(entry.getValue().size());
|
|
||||||
for(Pair<String, Integer> field : entry.getValue()) {
|
|
||||||
buf.writeUtf(field.getFirst());
|
|
||||||
buf.writeVarInt(field.getSecond());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static EntityIDSyncPacket deserialize(FriendlyByteBuf buf) {
|
|
||||||
EntityIDSyncPacket self = new EntityIDSyncPacket();
|
|
||||||
int numEntityClasses = buf.readVarInt();
|
|
||||||
for(int i = 0; i < numEntityClasses; i++) {
|
|
||||||
String clzName = buf.readUtf();
|
|
||||||
try {
|
|
||||||
Class<?> clz;
|
|
||||||
try {
|
|
||||||
clz = Class.forName(clzName);
|
|
||||||
} catch(ClassNotFoundException e) {
|
|
||||||
ModernFix.LOGGER.warn("Entity class not found: {}", clzName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(!Entity.class.isAssignableFrom(clz)) {
|
|
||||||
ModernFix.LOGGER.error("Not an entity: " + clzName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int numFields = buf.readVarInt();
|
|
||||||
for(int j = 0; j < numFields; j++) {
|
|
||||||
String fieldName = buf.readUtf();
|
|
||||||
int id = buf.readVarInt();
|
|
||||||
Field f = clz.getDeclaredField(fieldName);
|
|
||||||
if(!Modifier.isStatic(f.getModifiers()))
|
|
||||||
continue;
|
|
||||||
f.setAccessible(true);
|
|
||||||
if(!EntityDataAccessor.class.isAssignableFrom(f.get(null).getClass())) {
|
|
||||||
ModernFix.LOGGER.error("Not a data accessor field: " + clz + "." + fieldName);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
self.map.computeIfAbsent((Class<? extends Entity>)clz, k -> new ArrayList<>()).add(Pair.of(fieldName, id));
|
|
||||||
}
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
ModernFix.LOGGER.error("Error deserializing packet", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,16 +2,13 @@ package org.embeddedt.modernfix.platform;
|
||||||
|
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
import net.minecraft.client.searchtree.SearchRegistry;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public interface ModernFixPlatformHooks {
|
public interface ModernFixPlatformHooks {
|
||||||
|
|
@ -39,7 +36,7 @@ public interface ModernFixPlatformHooks {
|
||||||
|
|
||||||
Path getGameDirectory();
|
Path getGameDirectory();
|
||||||
|
|
||||||
void sendPacket(ServerPlayer player, Object packet);
|
void sendPacket(ServerPlayer player, CustomPacketPayload packet);
|
||||||
|
|
||||||
Multimap<String, String> getCustomModOptions();
|
Multimap<String, String> getCustomModOptions();
|
||||||
|
|
||||||
|
|
@ -47,7 +44,5 @@ public interface ModernFixPlatformHooks {
|
||||||
|
|
||||||
void onLaunchComplete();
|
void onLaunchComplete();
|
||||||
|
|
||||||
void registerCreativeSearchTrees(SearchRegistry registry, SearchRegistry.TreeBuilderSupplier<ItemStack> nameSupplier, SearchRegistry.TreeBuilderSupplier<ItemStack> tagSupplier, BiConsumer<SearchRegistry.Key<ItemStack>, List<ItemStack>> populator);
|
|
||||||
|
|
||||||
String getPlatformName();
|
String getPlatformName();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import java.lang.reflect.Constructor;
|
||||||
|
|
||||||
class PlatformHookLoader {
|
class PlatformHookLoader {
|
||||||
static ModernFixPlatformHooks findInstance() {
|
static ModernFixPlatformHooks findInstance() {
|
||||||
String[] locations = new String[] { "forge", "fabric" };
|
String[] locations = new String[] { "neoforge", "fabric" };
|
||||||
for(String location : locations) {
|
for(String location : locations) {
|
||||||
try {
|
try {
|
||||||
Class<?> clz = Class.forName("org.embeddedt.modernfix.platform." + location + ".ModernFixPlatformHooksImpl");
|
Class<?> clz = Class.forName("org.embeddedt.modernfix.platform." + location + ".ModernFixPlatformHooksImpl");
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
package org.embeddedt.modernfix.registry;
|
package org.embeddedt.modernfix.registry;
|
||||||
|
|
||||||
import com.mojang.serialization.Lifecycle;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||||
|
import net.minecraft.core.RegistrationInfo;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
|
||||||
public class LifecycleMap<T> extends Reference2ReferenceOpenHashMap<T, Lifecycle> {
|
public class LifecycleMap<T> extends Reference2ReferenceOpenHashMap<ResourceKey<T>, RegistrationInfo> {
|
||||||
public LifecycleMap() {
|
public LifecycleMap() {
|
||||||
this.defaultReturnValue(Lifecycle.stable());
|
this.defaultReturnValue(RegistrationInfo.BUILT_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Lifecycle put(T t, Lifecycle lifecycle) {
|
public RegistrationInfo put(ResourceKey<T> t, RegistrationInfo lifecycle) {
|
||||||
if(lifecycle != defRetValue)
|
if(lifecycle != defRetValue)
|
||||||
return super.put(t, lifecycle);
|
return super.put(t, lifecycle);
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
package org.embeddedt.modernfix.render;
|
|
||||||
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
|
||||||
import sun.misc.Unsafe;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper that frees ByteBuffers allocated by BufferBuilders, and nulls out the address pointer
|
|
||||||
* to prevent double frees.
|
|
||||||
*
|
|
||||||
* @author Moulberry
|
|
||||||
*/
|
|
||||||
public class UnsafeBufferHelper {
|
|
||||||
private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false);
|
|
||||||
|
|
||||||
private static sun.misc.Unsafe UNSAFE = null;
|
|
||||||
private static long ADDRESS = -1;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
|
|
||||||
theUnsafe.setAccessible(true);
|
|
||||||
UNSAFE = (Unsafe)theUnsafe.get(null);
|
|
||||||
|
|
||||||
final Field addressField = MemoryUtil.class.getDeclaredField("ADDRESS");
|
|
||||||
addressField.setAccessible(true);
|
|
||||||
ADDRESS = addressField.getLong(null);
|
|
||||||
} catch(Throwable t) {
|
|
||||||
ModernFix.LOGGER.error("Could load unsafe/buffer address", t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void init() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void free(ByteBuffer buf) {
|
|
||||||
if(UNSAFE != null && ADDRESS >= 0) {
|
|
||||||
// set the address to 0 to prevent double free
|
|
||||||
long address = UNSAFE.getAndSetLong(buf, ADDRESS, 0);
|
|
||||||
if(address != 0) {
|
|
||||||
ALLOCATOR.free(address);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ALLOCATOR.free(MemoryUtil.memAddress0(buf));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -158,7 +158,7 @@ public class PackResourcesCacheEngine {
|
||||||
if(!fullTestPath.startsWith(testPath)) {
|
if(!fullTestPath.startsWith(testPath)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ResourceLocation foundResource = new ResourceLocation(resourceNamespace, fullPath);
|
ResourceLocation foundResource = ResourceLocation.fromNamespaceAndPath(resourceNamespace, fullPath);
|
||||||
if(!filter.test(foundResource))
|
if(!filter.test(foundResource))
|
||||||
continue;
|
continue;
|
||||||
resources.add(foundResource);
|
resources.add(foundResource);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package org.embeddedt.modernfix.resources;
|
package org.embeddedt.modernfix.resources;
|
||||||
|
|
||||||
|
import net.minecraft.ReportType;
|
||||||
import net.minecraft.ReportedException;
|
import net.minecraft.ReportedException;
|
||||||
import net.minecraft.server.Bootstrap;
|
import net.minecraft.server.Bootstrap;
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
|
@ -39,7 +40,7 @@ public class ReloadExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (throwable instanceof ReportedException) {
|
if (throwable instanceof ReportedException) {
|
||||||
Bootstrap.realStdoutPrintln(((ReportedException)throwable).getReport().getFriendlyReport());
|
Bootstrap.realStdoutPrintln(((ReportedException)throwable).getReport().getFriendlyReport(ReportType.CRASH));
|
||||||
System.exit(-1);
|
System.exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,10 @@ public class ModernFixConfigScreen extends Screen {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
this.renderBackground(guiGraphics);
|
super.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||||
this.optionList.render(guiGraphics, mouseX, mouseY, partialTicks);
|
this.optionList.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||||
guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, 8, 16777215);
|
guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, 8, 16777215);
|
||||||
this.doneButton.setMessage(madeChanges ? Component.translatable("modernfix.config.done_restart") : CommonComponents.GUI_DONE);
|
this.doneButton.setMessage(madeChanges ? Component.translatable("modernfix.config.done_restart") : CommonComponents.GUI_DONE);
|
||||||
super.render(guiGraphics, mouseX, mouseY, partialTicks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLastScrollAmount(double d) {
|
public void setLastScrollAmount(double d) {
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,8 @@ public class ModernFixOptionInfoScreen extends Screen {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
this.renderBackground(guiGraphics);
|
super.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||||
guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, 8, 16777215);
|
guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, 8, 16777215);
|
||||||
this.drawMultilineString(guiGraphics, this.minecraft.font, description, 10, 50);
|
this.drawMultilineString(guiGraphics, this.minecraft.font, description, 10, 50);
|
||||||
super.render(guiGraphics, mouseX, mouseY, partialTicks);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public OptionList(ModernFixConfigScreen arg, Minecraft arg2) {
|
public OptionList(ModernFixConfigScreen arg, Minecraft arg2) {
|
||||||
super(arg2,arg.width + 45, arg.height, 43, arg.height - 32, 20);
|
super(arg2,arg.width + 45, arg.height - 52, 20, 20);
|
||||||
|
|
||||||
this.mainScreen = arg;
|
this.mainScreen = arg;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
package org.embeddedt.modernfix.searchtree;
|
|
||||||
|
|
||||||
import net.minecraft.client.searchtree.RefreshableSearchTree;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dummy search tree that stores nothing and returns nothing on searches.
|
|
||||||
*/
|
|
||||||
public class DummySearchTree<T> implements RefreshableSearchTree<T> {
|
|
||||||
public DummySearchTree() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refresh() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<T> search(String pSearchText) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RefreshableSearchTree<ItemStack> getSearchTree(boolean tag) {
|
|
||||||
return new DummySearchTree<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canUse() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "Dummy";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
package org.embeddedt.modernfix.searchtree;
|
|
||||||
|
|
||||||
import mezz.jei.api.ingredients.ITypedIngredient;
|
|
||||||
import mezz.jei.gui.ingredients.IngredientFilter;
|
|
||||||
import mezz.jei.gui.ingredients.IngredientFilterApi;
|
|
||||||
import mezz.jei.library.runtime.JeiRuntime;
|
|
||||||
import net.minecraft.client.searchtree.RefreshableSearchTree;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses JEI to handle search tree lookups.
|
|
||||||
*/
|
|
||||||
public class JEIBackedSearchTree extends DummySearchTree<ItemStack> {
|
|
||||||
private final boolean filteringByTag;
|
|
||||||
private String lastSearchText = "";
|
|
||||||
private final List<ItemStack> listCache = new ArrayList<>();
|
|
||||||
|
|
||||||
private static final Field filterField;
|
|
||||||
private static final MethodHandle getIngredientListUncached;
|
|
||||||
|
|
||||||
static {
|
|
||||||
MethodHandle m;
|
|
||||||
Field f;
|
|
||||||
try {
|
|
||||||
Method jeiMethod = IngredientFilter.class.getDeclaredMethod("getIngredientListUncached", String.class);
|
|
||||||
jeiMethod.setAccessible(true);
|
|
||||||
m = MethodHandles.lookup().unreflect(jeiMethod);
|
|
||||||
f = IngredientFilterApi.class.getDeclaredField("ingredientFilter");
|
|
||||||
f.setAccessible(true);
|
|
||||||
} catch(ReflectiveOperationException | RuntimeException | NoClassDefFoundError e) {
|
|
||||||
m = null;
|
|
||||||
f = null;
|
|
||||||
}
|
|
||||||
getIngredientListUncached = m;
|
|
||||||
filterField = f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JEIBackedSearchTree(boolean filteringByTag) {
|
|
||||||
this.filteringByTag = filteringByTag;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public List<ItemStack> search(String pSearchText) {
|
|
||||||
Optional<JeiRuntime> runtime = JEIRuntimeCapturer.runtime();
|
|
||||||
if(runtime.isPresent()) {
|
|
||||||
IngredientFilterApi iFilterApi = (IngredientFilterApi)runtime.get().getIngredientFilter();
|
|
||||||
IngredientFilter filter;
|
|
||||||
try {
|
|
||||||
filter = (IngredientFilter)filterField.get(iFilterApi);
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
ModernFix.LOGGER.error(e);
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return this.searchJEI(filter, pSearchText);
|
|
||||||
} else {
|
|
||||||
/* Use the default, dummy implementation */
|
|
||||||
return super.search(pSearchText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ItemStack> searchJEI(IngredientFilter filter, String pSearchText) {
|
|
||||||
if(!pSearchText.equals(lastSearchText)) {
|
|
||||||
listCache.clear();
|
|
||||||
Stream<ITypedIngredient<?>> ingredients;
|
|
||||||
String finalSearchTerm = filteringByTag ? ("$" + pSearchText) : pSearchText;
|
|
||||||
try {
|
|
||||||
ingredients = (Stream<ITypedIngredient<?>>)getIngredientListUncached.invokeExact(filter, finalSearchTerm);
|
|
||||||
} catch(Throwable e) {
|
|
||||||
ModernFix.LOGGER.error("Error searching", e);
|
|
||||||
ingredients = Stream.empty();
|
|
||||||
}
|
|
||||||
ingredients.toList().forEach(ingredient -> {
|
|
||||||
if(ingredient.getIngredient() instanceof ItemStack) {
|
|
||||||
listCache.add((ItemStack)ingredient.getIngredient());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
lastSearchText = pSearchText;
|
|
||||||
}
|
|
||||||
return listCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() {
|
|
||||||
@Override
|
|
||||||
public RefreshableSearchTree<ItemStack> getSearchTree(boolean tag) {
|
|
||||||
return new JEIBackedSearchTree(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canUse() {
|
|
||||||
return ModernFixPlatformHooks.INSTANCE.modPresent("jei") && !ModernFixPlatformHooks.INSTANCE.modPresent("emi") && getIngredientListUncached != null && filterField != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "JEI";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
package org.embeddedt.modernfix.searchtree;
|
|
||||||
|
|
||||||
import mezz.jei.api.IModPlugin;
|
|
||||||
import mezz.jei.api.JeiPlugin;
|
|
||||||
import mezz.jei.api.runtime.IJeiRuntime;
|
|
||||||
import mezz.jei.library.runtime.JeiRuntime;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@JeiPlugin
|
|
||||||
public class JEIRuntimeCapturer implements IModPlugin {
|
|
||||||
private static JeiRuntime runtimeHandle = null;
|
|
||||||
|
|
||||||
public static Optional<JeiRuntime> runtime() {
|
|
||||||
return Optional.ofNullable(runtimeHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceLocation getPluginUid() {
|
|
||||||
return new ResourceLocation(ModernFix.MODID, "capturer");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRuntimeAvailable(IJeiRuntime jeiRuntime) {
|
|
||||||
runtimeHandle = (JeiRuntime)jeiRuntime;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRuntimeUnavailable() {
|
|
||||||
runtimeHandle = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,150 +0,0 @@
|
||||||
package org.embeddedt.modernfix.searchtree;
|
|
||||||
|
|
||||||
import com.google.common.base.Predicates;
|
|
||||||
import me.shedaniel.rei.api.client.registry.entry.EntryRegistry;
|
|
||||||
import me.shedaniel.rei.api.common.entry.EntryStack;
|
|
||||||
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
|
|
||||||
import me.shedaniel.rei.impl.client.search.AsyncSearchManager;
|
|
||||||
import me.shedaniel.rei.impl.common.entry.type.EntryRegistryImpl;
|
|
||||||
import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper;
|
|
||||||
import net.minecraft.client.searchtree.RefreshableSearchTree;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.invoke.MethodType;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.function.UnaryOperator;
|
|
||||||
|
|
||||||
public class REIBackedSearchTree extends DummySearchTree<ItemStack> {
|
|
||||||
private final AsyncSearchManager searchManager = createSearchManager();
|
|
||||||
|
|
||||||
private final boolean filteringByTag;
|
|
||||||
private String lastSearchText = "";
|
|
||||||
private final List<ItemStack> listCache = new ArrayList<>();
|
|
||||||
|
|
||||||
public REIBackedSearchTree(boolean filteringByTag) {
|
|
||||||
this.filteringByTag = filteringByTag;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public List<ItemStack> search(String pSearchText) {
|
|
||||||
if(true) {
|
|
||||||
return this.searchREI(pSearchText);
|
|
||||||
} else {
|
|
||||||
/* Use the default, dummy implementation */
|
|
||||||
return super.search(pSearchText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ItemStack> searchREI(String pSearchText) {
|
|
||||||
if(!pSearchText.equals(lastSearchText)) {
|
|
||||||
listCache.clear();
|
|
||||||
this.searchManager.updateFilter(pSearchText);
|
|
||||||
List stacks;
|
|
||||||
try {
|
|
||||||
stacks = this.searchManager.getNow();
|
|
||||||
} catch(RuntimeException e) {
|
|
||||||
ModernFix.LOGGER.error("Couldn't search for '" + pSearchText + "'", e);
|
|
||||||
stacks = Collections.emptyList();
|
|
||||||
}
|
|
||||||
for(Object o : stacks) {
|
|
||||||
EntryStack<?> stack;
|
|
||||||
if(o instanceof EntryStack<?>)
|
|
||||||
stack = (EntryStack<?>)o;
|
|
||||||
else if(o instanceof HashedEntryStackWrapper) {
|
|
||||||
stack = ((HashedEntryStackWrapper)o).unwrap();
|
|
||||||
} else {
|
|
||||||
ModernFix.LOGGER.error("Don't know how to handle {}", o.getClass().getName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(stack.getType() == VanillaEntryTypes.ITEM) {
|
|
||||||
listCache.add(stack.cheatsAs().getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastSearchText = pSearchText;
|
|
||||||
}
|
|
||||||
return listCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
||||||
private static AsyncSearchManager createSearchManager() {
|
|
||||||
Method m, normalizeMethod;
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
m = EntryRegistryImpl.class.getDeclaredMethod("getPreFilteredComplexList");
|
|
||||||
m.setAccessible(true);
|
|
||||||
normalizeMethod = HashedEntryStackWrapper.class.getDeclaredMethod("normalize");
|
|
||||||
normalizeMethod.setAccessible(true);
|
|
||||||
} catch(NoSuchMethodException e) {
|
|
||||||
m = EntryRegistryImpl.class.getDeclaredMethod("getPreFilteredList");
|
|
||||||
m.setAccessible(true);
|
|
||||||
normalizeMethod = EntryStack.class.getDeclaredMethod("normalize");
|
|
||||||
normalizeMethod.setAccessible(true);
|
|
||||||
}
|
|
||||||
final MethodHandle getListMethod = MethodHandles.publicLookup().unreflect(m);
|
|
||||||
final MethodHandle normalize = MethodHandles.publicLookup().unreflect(normalizeMethod);
|
|
||||||
final EntryRegistryImpl registry = (EntryRegistryImpl)EntryRegistry.getInstance();
|
|
||||||
Supplier stackListSupplier = () -> {
|
|
||||||
try {
|
|
||||||
return (List)getListMethod.invokeExact(registry);
|
|
||||||
} catch(Throwable e) {
|
|
||||||
if(e instanceof RuntimeException)
|
|
||||||
throw (RuntimeException)e;
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UnaryOperator normalizeOperator = o -> {
|
|
||||||
try {
|
|
||||||
return normalize.invoke(o);
|
|
||||||
} catch(Throwable e) {
|
|
||||||
if(e instanceof RuntimeException)
|
|
||||||
throw (RuntimeException)e;
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Supplier<Predicate<Boolean>> shouldShowStack = () -> {
|
|
||||||
return Predicates.alwaysTrue();
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
// Old constructor taking Supplier as first arg
|
|
||||||
MethodHandle cn = MethodHandles.publicLookup().findConstructor(AsyncSearchManager.class, MethodType.methodType(void.class, Supplier.class, Supplier.class, UnaryOperator.class));
|
|
||||||
return (AsyncSearchManager)cn.invoke(stackListSupplier, shouldShowStack, normalizeOperator);
|
|
||||||
} catch(NoSuchMethodException e) {
|
|
||||||
// New constructor taking Function as first arg
|
|
||||||
MethodHandle cn = MethodHandles.publicLookup().findConstructor(AsyncSearchManager.class, MethodType.methodType(void.class, Function.class, Supplier.class, UnaryOperator.class));
|
|
||||||
return (AsyncSearchManager)cn.invoke((Function<?, ?>)o -> stackListSupplier.get(), shouldShowStack, normalizeOperator);
|
|
||||||
}
|
|
||||||
} catch(Throwable mhThrowable) {
|
|
||||||
throw new ReflectiveOperationException(mhThrowable);
|
|
||||||
}
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() {
|
|
||||||
@Override
|
|
||||||
public RefreshableSearchTree<ItemStack> getSearchTree(boolean tag) {
|
|
||||||
return new REIBackedSearchTree(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canUse() {
|
|
||||||
return ModernFixPlatformHooks.INSTANCE.modPresent("roughlyenoughitems");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "REI";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
package org.embeddedt.modernfix.searchtree;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
|
|
||||||
import net.minecraft.client.searchtree.SearchTree;
|
|
||||||
import net.minecraft.world.item.Item;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class RecipeBookSearchTree extends DummySearchTree<RecipeCollection> {
|
|
||||||
private final SearchTree<ItemStack> stackCollector;
|
|
||||||
private Map<Item, List<RecipeCollection>> collectionsByItem = null;
|
|
||||||
private final List<RecipeCollection> allCollections;
|
|
||||||
|
|
||||||
public RecipeBookSearchTree(SearchTree<ItemStack> stackCollector, List<RecipeCollection> allCollections) {
|
|
||||||
this.stackCollector = stackCollector;
|
|
||||||
this.allCollections = allCollections;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<Item, List<RecipeCollection>> populateCollectionMap() {
|
|
||||||
Map<Item, List<RecipeCollection>> collections = this.collectionsByItem;
|
|
||||||
if(collections == null) {
|
|
||||||
collections = new Object2ObjectOpenHashMap<>();
|
|
||||||
Map<Item, List<RecipeCollection>> finalCollection = collections;
|
|
||||||
for(RecipeCollection collection : allCollections) {
|
|
||||||
collection.getRecipes().stream().map(recipe -> recipe.getResultItem(collection.registryAccess()).getItem()).distinct().forEach(item -> {
|
|
||||||
finalCollection.computeIfAbsent(item, k -> new ArrayList<>()).add(collection);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.collectionsByItem = collections;
|
|
||||||
}
|
|
||||||
return collections;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refresh() {
|
|
||||||
this.collectionsByItem = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<RecipeCollection> search(String pSearchText) {
|
|
||||||
// Avoid constructing the recipe collection map until the first real search
|
|
||||||
if(pSearchText.trim().length() == 0) {
|
|
||||||
return this.allCollections;
|
|
||||||
}
|
|
||||||
List<ItemStack> stacks = stackCollector.search(pSearchText);
|
|
||||||
Map<Item, List<RecipeCollection>> collections = this.populateCollectionMap();
|
|
||||||
return stacks.stream().map(ItemStack::getItem).distinct().flatMap(item -> collections.getOrDefault(item, Collections.emptyList()).stream()).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
package org.embeddedt.modernfix.searchtree;
|
|
||||||
|
|
||||||
import net.minecraft.client.searchtree.RefreshableSearchTree;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SearchTreeProviderRegistry {
|
|
||||||
private static final List<Provider> searchTreeProviders = new ArrayList<>();
|
|
||||||
|
|
||||||
public static synchronized Provider getSearchTreeProvider() {
|
|
||||||
for(Provider p : searchTreeProviders) {
|
|
||||||
if(p.canUse())
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("perf.blast_search_trees.force.Registry"))
|
|
||||||
return DummySearchTree.PROVIDER;
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized void register(Provider p) {
|
|
||||||
if(p.canUse())
|
|
||||||
searchTreeProviders.add(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface Provider {
|
|
||||||
RefreshableSearchTree<ItemStack> getSearchTree(boolean tag);
|
|
||||||
boolean canUse();
|
|
||||||
String getName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
package org.embeddedt.modernfix.structure;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.DataFixer;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
|
||||||
import net.minecraft.SharedConstants;
|
|
||||||
import net.minecraft.core.HolderGetter;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.NbtIo;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.util.datafix.DataFixTypes;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
|
||||||
import org.embeddedt.modernfix.util.FileUtil;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class CachingStructureManager {
|
|
||||||
private static ThreadLocal<MessageDigest> digestThreadLocal = ThreadLocal.withInitial(() -> {
|
|
||||||
try {
|
|
||||||
return MessageDigest.getInstance("SHA-256");
|
|
||||||
} catch(NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
private static final File STRUCTURE_CACHE_FOLDER = FileUtil.childFile(ModernFixPlatformHooks.INSTANCE.getGameDirectory().resolve("modernfix").resolve("structureCacheV1").toFile());
|
|
||||||
|
|
||||||
static {
|
|
||||||
STRUCTURE_CACHE_FOLDER.mkdirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static StructureTemplate readStructure(ResourceLocation location, DataFixer datafixer, InputStream stream, HolderGetter<Block> blockGetter) throws IOException {
|
|
||||||
CompoundTag tag = readStructureTag(location, datafixer, stream);
|
|
||||||
StructureTemplate template = new StructureTemplate();
|
|
||||||
template.load(blockGetter, tag);
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String encodeHex(byte[] byteArray) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for(byte b : byteArray) {
|
|
||||||
sb.append(String.format("%02x", b));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Set<String> laggyStructureMods = new ObjectOpenHashSet<>();
|
|
||||||
|
|
||||||
private static final int MAX_HASH_LENGTH = 9;
|
|
||||||
|
|
||||||
private static String truncateHash(String hash) {
|
|
||||||
return hash.substring(0, MAX_HASH_LENGTH + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CompoundTag readStructureTag(ResourceLocation location, DataFixer datafixer, InputStream stream) throws IOException {
|
|
||||||
byte[] structureBytes = toBytes(stream);
|
|
||||||
CompoundTag currentTag = NbtIo.readCompressed(new ByteArrayInputStream(structureBytes));
|
|
||||||
if (!currentTag.contains("DataVersion", 99)) {
|
|
||||||
currentTag.putInt("DataVersion", 500);
|
|
||||||
}
|
|
||||||
int currentDataVersion = currentTag.getInt("DataVersion");
|
|
||||||
int requiredMinimumDataVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
|
|
||||||
if(currentDataVersion < requiredMinimumDataVersion) {
|
|
||||||
/* Needs upgrade, try looking up from cache */
|
|
||||||
MessageDigest hasher = digestThreadLocal.get();
|
|
||||||
hasher.reset();
|
|
||||||
String hash = encodeHex(hasher.digest(structureBytes));
|
|
||||||
CompoundTag cachedUpgraded = getCachedUpgraded(location, truncateHash(hash));
|
|
||||||
if(cachedUpgraded == null)
|
|
||||||
cachedUpgraded = getCachedUpgraded(location, hash); /* pick up old cache */
|
|
||||||
if(cachedUpgraded != null && cachedUpgraded.getInt("DataVersion") == requiredMinimumDataVersion) {
|
|
||||||
ModernFix.LOGGER.debug("Using cached upgraded version of {}", location);
|
|
||||||
currentTag = cachedUpgraded;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
synchronized (laggyStructureMods) {
|
|
||||||
if(laggyStructureMods.add(location.getNamespace())) {
|
|
||||||
ModernFix.LOGGER.warn("The namespace {} contains an outdated structure file, which can cause worldgen lag. Please view debug.log for the full filename, determine which mod provides the structure, and report to the mod/datapack author, including the debug log.", location.getNamespace());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
ModernFix.LOGGER.debug("Structure {} is being run through DFU (hash {}), this will cause launch time delays", location, hash);
|
|
||||||
currentTag = DataFixTypes.STRUCTURE.update(datafixer, currentTag, currentDataVersion,
|
|
||||||
SharedConstants.getCurrentVersion().getDataVersion().getVersion());
|
|
||||||
currentTag.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion());
|
|
||||||
saveCachedUpgraded(location, hash, currentTag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return currentTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File getCachePath(ResourceLocation location, String hash) {
|
|
||||||
String fileName = location.getNamespace() + "_" + location.getPath().replace('/', '_') + "_" + hash + ".nbt";
|
|
||||||
return new File(STRUCTURE_CACHE_FOLDER, fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static synchronized CompoundTag getCachedUpgraded(ResourceLocation location, String hash) {
|
|
||||||
File theFile = getCachePath(location, hash);
|
|
||||||
try {
|
|
||||||
return NbtIo.readCompressed(theFile);
|
|
||||||
} catch(FileNotFoundException e) {
|
|
||||||
return null;
|
|
||||||
} catch(IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static synchronized void saveCachedUpgraded(ResourceLocation location, String hash, CompoundTag tagToSave) {
|
|
||||||
File theFile = getCachePath(location, truncateHash(hash));
|
|
||||||
try {
|
|
||||||
NbtIo.writeCompressed(tagToSave, theFile);
|
|
||||||
} catch(IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] toBytes(InputStream stream) throws IOException {
|
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
|
||||||
|
|
||||||
byte[] tmp = new byte[16384];
|
|
||||||
int n;
|
|
||||||
while ((n = stream.read(tmp, 0, tmp.length)) != -1) {
|
|
||||||
buffer.write(tmp, 0, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer.toByteArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
package org.embeddedt.modernfix.util;
|
|
||||||
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
|
|
||||||
public enum BakeReason {
|
|
||||||
FREEZE,
|
|
||||||
REMOTE_SNAPSHOT_INJECT,
|
|
||||||
LOCAL_SNAPSHOT_INJECT,
|
|
||||||
REVERT,
|
|
||||||
UNKNOWN;
|
|
||||||
private static BakeReason currentBakeReason = null;
|
|
||||||
private static boolean bakeReasonWarned = false;
|
|
||||||
|
|
||||||
public static BakeReason getCurrentBakeReason() {
|
|
||||||
if(currentBakeReason == null && !bakeReasonWarned) {
|
|
||||||
ModernFix.LOGGER.warn("No bake reason found, mixin probably not applied correctly", new IllegalStateException());
|
|
||||||
bakeReasonWarned = false;
|
|
||||||
}
|
|
||||||
return currentBakeReason;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setCurrentBakeReason(BakeReason reason) {
|
|
||||||
currentBakeReason = reason;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -15,4 +15,4 @@ public class CacheUtil {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ public class DummyServerConfiguration implements WorldData {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean getAllowCommands() {
|
public boolean isAllowCommands() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package org.embeddedt.modernfix.util;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||||
|
import org.embeddedt.modernfix.ModernFix;
|
||||||
|
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class LRUMap<K, V> extends Object2ObjectLinkedOpenHashMap<K, V> {
|
||||||
|
private Set<K> permanentEntries = Set.of();
|
||||||
|
public LRUMap(Map<K, V> map) {
|
||||||
|
super(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V put(K k, V v) {
|
||||||
|
return putAndMoveToLast(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(Object k) {
|
||||||
|
return getAndMoveToLast((K)k);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPermanentEntries(Set<K> permanentEntries) {
|
||||||
|
this.permanentEntries = permanentEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dropEntriesToMeetSize(int size) {
|
||||||
|
// Increase allowed size quota to include permanent entries
|
||||||
|
size += permanentEntries.size();
|
||||||
|
int prevSize = size();
|
||||||
|
if(size() > size) {
|
||||||
|
var iterator = entrySet().iterator();
|
||||||
|
while(size() > size && iterator.hasNext()) {
|
||||||
|
var entry = iterator.next();
|
||||||
|
if(!this.permanentEntries.contains(entry.getKey())) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ModernFixPlatformHooks.INSTANCE.isDevEnv()) {
|
||||||
|
ModernFix.LOGGER.warn("Trimmed map from {} to {} entries", prevSize, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
package org.embeddedt.modernfix.world;
|
package org.embeddedt.modernfix.world;
|
||||||
|
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.util.datafix.DataFixTypes;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import net.minecraft.world.level.saveddata.SavedData;
|
import net.minecraft.world.level.saveddata.SavedData;
|
||||||
|
|
@ -17,6 +20,11 @@ public class StrongholdLocationCache extends SavedData {
|
||||||
chunkPosList = new ArrayList<>();
|
chunkPosList = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SavedData.Factory<StrongholdLocationCache> factory(ServerLevel serverLevel) {
|
||||||
|
// FIXME datafixer will probably throw on update
|
||||||
|
return new SavedData.Factory<>(StrongholdLocationCache::new, StrongholdLocationCache::load, DataFixTypes.SAVED_DATA_FORCED_CHUNKS);
|
||||||
|
}
|
||||||
|
|
||||||
public List<ChunkPos> getChunkPosList() {
|
public List<ChunkPos> getChunkPosList() {
|
||||||
return new ArrayList<>(chunkPosList);
|
return new ArrayList<>(chunkPosList);
|
||||||
}
|
}
|
||||||
|
|
@ -26,7 +34,7 @@ public class StrongholdLocationCache extends SavedData {
|
||||||
this.setDirty();
|
this.setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StrongholdLocationCache load(CompoundTag arg) {
|
public static StrongholdLocationCache load(CompoundTag arg, HolderLookup.Provider provider) {
|
||||||
StrongholdLocationCache cache = new StrongholdLocationCache();
|
StrongholdLocationCache cache = new StrongholdLocationCache();
|
||||||
if(arg.contains("Positions", Tag.TAG_LONG_ARRAY)) {
|
if(arg.contains("Positions", Tag.TAG_LONG_ARRAY)) {
|
||||||
long[] positions = arg.getLongArray("Positions");
|
long[] positions = arg.getLongArray("Positions");
|
||||||
|
|
@ -38,7 +46,7 @@ public class StrongholdLocationCache extends SavedData {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompoundTag save(CompoundTag compoundTag) {
|
public CompoundTag save(CompoundTag compoundTag, HolderLookup.Provider provider) {
|
||||||
long[] serialized = new long[chunkPosList.size()];
|
long[] serialized = new long[chunkPosList.size()];
|
||||||
for(int i = 0; i < chunkPosList.size(); i++) {
|
for(int i = 0; i < chunkPosList.size(); i++) {
|
||||||
ChunkPos thePos = chunkPosList.get(i);
|
ChunkPos thePos = chunkPosList.get(i);
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,6 @@ accessible field net/minecraft/client/renderer/texture/Stitcher$Holder width I
|
||||||
accessible field net/minecraft/client/renderer/texture/Stitcher$Holder height I
|
accessible field net/minecraft/client/renderer/texture/Stitcher$Holder height I
|
||||||
accessible field net/minecraft/network/syncher/EntityDataAccessor id I
|
accessible field net/minecraft/network/syncher/EntityDataAccessor id I
|
||||||
mutable field net/minecraft/network/syncher/EntityDataAccessor id I
|
mutable field net/minecraft/network/syncher/EntityDataAccessor id I
|
||||||
accessible class net/minecraft/client/resources/model/ModelBakery$BlockStateDefinitionException
|
|
||||||
accessible field net/minecraft/network/syncher/SynchedEntityData itemsById Lit/unimi/dsi/fastutil/ints/Int2ObjectMap;
|
|
||||||
accessible field net/minecraft/network/syncher/SynchedEntityData ENTITY_ID_POOL Lit/unimi/dsi/fastutil/objects/Object2IntMap;
|
|
||||||
accessible field net/minecraft/network/syncher/SynchedEntityData lock Ljava/util/concurrent/locks/ReadWriteLock;
|
|
||||||
accessible method net/minecraft/Util makeExecutor (Ljava/lang/String;)Ljava/util/concurrent/ExecutorService;
|
accessible method net/minecraft/Util makeExecutor (Ljava/lang/String;)Ljava/util/concurrent/ExecutorService;
|
||||||
accessible field net/minecraft/server/level/ChunkMap updatingChunkMap Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
accessible field net/minecraft/server/level/ChunkMap updatingChunkMap Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
||||||
accessible field net/minecraft/server/level/ChunkMap visibleChunkMap Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
accessible field net/minecraft/server/level/ChunkMap visibleChunkMap Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
||||||
|
|
@ -44,13 +40,10 @@ accessible method net/minecraft/resources/ResourceKey <init> (Lnet/minecraft/res
|
||||||
accessible field net/minecraft/client/renderer/block/model/BlockModel GSON Lcom/google/gson/Gson;
|
accessible field net/minecraft/client/renderer/block/model/BlockModel GSON Lcom/google/gson/Gson;
|
||||||
accessible class net/minecraft/server/level/ChunkMap$DistanceManager
|
accessible class net/minecraft/server/level/ChunkMap$DistanceManager
|
||||||
accessible class net/minecraft/client/resources/model/ModelBakery$BakedCacheKey
|
accessible class net/minecraft/client/resources/model/ModelBakery$BakedCacheKey
|
||||||
accessible field net/minecraft/client/resources/model/ModelBakery bakedCache Ljava/util/Map;
|
|
||||||
accessible field net/minecraft/client/resources/model/ModelBakery ITEM_MODEL_GENERATOR Lnet/minecraft/client/renderer/block/model/ItemModelGenerator;
|
|
||||||
accessible method net/minecraft/client/resources/model/ModelBakery loadTopLevel (Lnet/minecraft/client/resources/model/ModelResourceLocation;)V
|
|
||||||
accessible field net/minecraft/client/resources/model/ModelBakery topLevelModels Ljava/util/Map;
|
|
||||||
accessible class net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl
|
|
||||||
accessible method net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl <init> (Lnet/minecraft/client/resources/model/ModelBakery;Ljava/util/function/BiFunction;Lnet/minecraft/resources/ResourceLocation;)V
|
|
||||||
accessible method net/minecraft/client/resources/model/ModelBakery$BakedCacheKey <init> (Lnet/minecraft/resources/ResourceLocation;Lcom/mojang/math/Transformation;Z)V
|
accessible method net/minecraft/client/resources/model/ModelBakery$BakedCacheKey <init> (Lnet/minecraft/resources/ResourceLocation;Lcom/mojang/math/Transformation;Z)V
|
||||||
|
accessible class net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl
|
||||||
|
accessible method net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl <init> (Lnet/minecraft/client/resources/model/ModelBakery;Lnet/minecraft/client/resources/model/ModelBakery$TextureGetter;Lnet/minecraft/client/resources/model/ModelResourceLocation;)V
|
||||||
|
accessible method net/minecraft/client/resources/model/ModelBakery getModel (Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/UnbakedModel;
|
||||||
accessible class net/minecraft/world/level/chunk/PalettedContainer$Data
|
accessible class net/minecraft/world/level/chunk/PalettedContainer$Data
|
||||||
accessible field net/minecraft/server/MinecraftServer resources Lnet/minecraft/server/MinecraftServer$ReloadableResources;
|
accessible field net/minecraft/server/MinecraftServer resources Lnet/minecraft/server/MinecraftServer$ReloadableResources;
|
||||||
accessible class net/minecraft/server/MinecraftServer$ReloadableResources
|
accessible class net/minecraft/server/MinecraftServer$ReloadableResources
|
||||||
|
|
@ -60,4 +53,5 @@ accessible field net/minecraft/client/renderer/block/model/multipart/MultiPart d
|
||||||
accessible field net/minecraft/client/renderer/block/model/ItemOverrides$BakedOverride model Lnet/minecraft/client/resources/model/BakedModel;
|
accessible field net/minecraft/client/renderer/block/model/ItemOverrides$BakedOverride model Lnet/minecraft/client/resources/model/BakedModel;
|
||||||
mutable field net/minecraft/client/renderer/block/model/ItemOverrides$BakedOverride model Lnet/minecraft/client/resources/model/BakedModel;
|
mutable field net/minecraft/client/renderer/block/model/ItemOverrides$BakedOverride model Lnet/minecraft/client/resources/model/BakedModel;
|
||||||
accessible field net/minecraft/client/renderer/entity/EnderDragonRenderer$DragonModel entity Lnet/minecraft/world/entity/boss/enderdragon/EnderDragon;
|
accessible field net/minecraft/client/renderer/entity/EnderDragonRenderer$DragonModel entity Lnet/minecraft/world/entity/boss/enderdragon/EnderDragon;
|
||||||
accessible method net/minecraft/world/level/block/state/StateDefinition appendPropertyCodec (Lcom/mojang/serialization/MapCodec;Ljava/util/function/Supplier;Ljava/lang/String;Lnet/minecraft/world/level/block/state/properties/Property;)Lcom/mojang/serialization/MapCodec;
|
accessible method net/minecraft/world/level/block/state/StateDefinition appendPropertyCodec (Lcom/mojang/serialization/MapCodec;Ljava/util/function/Supplier;Ljava/lang/String;Lnet/minecraft/world/level/block/state/properties/Property;)Lcom/mojang/serialization/MapCodec;
|
||||||
|
accessible class net/minecraft/client/multiplayer/SessionSearchTrees$Key
|
||||||
|
|
@ -29,13 +29,11 @@ configurations {
|
||||||
dependencies {
|
dependencies {
|
||||||
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||||
testImplementation "net.fabricmc:fabric-loader-junit:${rootProject.fabric_loader_version}"
|
testImplementation "net.fabricmc:fabric-loader-junit:${rootProject.fabric_loader_version}"
|
||||||
include(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:${rootProject.mixinextras_version}")))
|
|
||||||
|
|
||||||
modCompileOnly(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modCompileOnly(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
modCompileOnly(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modCompileOnly(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
modCompileOnly(fabricApi.module("fabric-command-api-v2", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modCompileOnly(fabricApi.module("fabric-command-api-v2", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
modCompileOnly(fabricApi.module("fabric-model-loading-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modCompileOnly(fabricApi.module("fabric-model-loading-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
modCompileOnly(fabricApi.module("fabric-models-v0", "0.84.0+1.20.1")) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
|
||||||
modCompileOnly(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modCompileOnly(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
modCompileOnly(fabricApi.module("fabric-data-generation-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modCompileOnly(fabricApi.module("fabric-data-generation-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
if(project.use_fabric_api_at_runtime.toBoolean()) {
|
if(project.use_fabric_api_at_runtime.toBoolean()) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package org.embeddedt.modernfix.fabric.mappings;
|
||||||
import net.fabricmc.loader.api.*;
|
import net.fabricmc.loader.api.*;
|
||||||
import net.fabricmc.loader.impl.launch.FabricLauncherBase;
|
import net.fabricmc.loader.impl.launch.FabricLauncherBase;
|
||||||
import net.fabricmc.loader.impl.launch.MappingConfiguration;
|
import net.fabricmc.loader.impl.launch.MappingConfiguration;
|
||||||
import net.fabricmc.mapping.tree.TinyMappingFactory;
|
|
||||||
import org.embeddedt.modernfix.util.CommonModUtil;
|
import org.embeddedt.modernfix.util.CommonModUtil;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
|
@ -28,7 +27,9 @@ public class MappingsClearer {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static void clear() {
|
public static void clear() {
|
||||||
if(FabricLoader.getInstance().isDevelopmentEnvironment())
|
// TODO: Port this to Fabric Loader 0.15
|
||||||
|
|
||||||
|
if(true) //FabricLoader.getInstance().isDevelopmentEnvironment())
|
||||||
return; // never do this in dev
|
return; // never do this in dev
|
||||||
|
|
||||||
Optional<Version> loaderVersion = FabricLoader.getInstance().getModContainer("fabricloader").map(m -> m.getMetadata().getVersion());
|
Optional<Version> loaderVersion = FabricLoader.getInstance().getModContainer("fabricloader").map(m -> m.getMetadata().getVersion());
|
||||||
|
|
@ -50,7 +51,7 @@ public class MappingsClearer {
|
||||||
MappingConfiguration config = FabricLauncherBase.getLauncher().getMappingConfiguration();
|
MappingConfiguration config = FabricLauncherBase.getLauncher().getMappingConfiguration();
|
||||||
Field mappingsField = MappingConfiguration.class.getDeclaredField("mappings");
|
Field mappingsField = MappingConfiguration.class.getDeclaredField("mappings");
|
||||||
mappingsField.setAccessible(true);
|
mappingsField.setAccessible(true);
|
||||||
mappingsField.set(config, TinyMappingFactory.EMPTY_TREE);
|
//mappingsField.set(config, TinyMappingFactory.EMPTY_TREE);
|
||||||
|
|
||||||
// clear useless intermediary->intermediary mappings
|
// clear useless intermediary->intermediary mappings
|
||||||
MappingResolver resolver = FabricLoader.getInstance().getMappingResolver();
|
MappingResolver resolver = FabricLoader.getInstance().getMappingResolver();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package org.embeddedt.modernfix.fabric.mixin.core;
|
||||||
|
|
||||||
|
import net.minecraft.client.multiplayer.ClientConfigurationPacketListenerImpl;
|
||||||
|
import org.embeddedt.modernfix.ModernFixClientFabric;
|
||||||
|
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
@Mixin(ClientConfigurationPacketListenerImpl.class)
|
||||||
|
@ClientOnlyMixin
|
||||||
|
public class ClientCommonPacketListenerImplMixin {
|
||||||
|
@Inject(method = "handleUpdateTags", at = @At("RETURN"))
|
||||||
|
private void signalTags(CallbackInfo ci) {
|
||||||
|
ModernFixClientFabric.commonMod.onTagsUpdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
@Mixin(MinecraftServer.class)
|
@Mixin(MinecraftServer.class)
|
||||||
@ClientOnlyMixin
|
@ClientOnlyMixin
|
||||||
public class ClientMinecraftServerMixin {
|
public class ClientMinecraftServerMixin {
|
||||||
@Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getMillis()J", ordinal = 0))
|
@Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getNanos()J", ordinal = 0))
|
||||||
private void markServerStarted(CallbackInfo ci) {
|
private void markServerStarted(CallbackInfo ci) {
|
||||||
ModernFixClient.INSTANCE.onServerStarted((MinecraftServer)(Object)this);
|
ModernFixClient.INSTANCE.onServerStarted((MinecraftServer)(Object)this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,4 @@ public class ClientPlayNetHandlerMixin {
|
||||||
private void signalRecipes(CallbackInfo ci) {
|
private void signalRecipes(CallbackInfo ci) {
|
||||||
ModernFixClientFabric.commonMod.onRecipesUpdated();
|
ModernFixClientFabric.commonMod.onRecipesUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "handleUpdateTags", at = @At("RETURN"))
|
|
||||||
private void signalTags(CallbackInfo ci) {
|
|
||||||
ModernFixClientFabric.commonMod.onTagsUpdated();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ public class MinecraftServerMixin {
|
||||||
ModernFixFabric.theServer = new WeakReference<>((MinecraftServer)(Object)this);
|
ModernFixFabric.theServer = new WeakReference<>((MinecraftServer)(Object)this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getMillis()J", ordinal = 0))
|
@Inject(method = "runServer", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getNanos()J", ordinal = 0))
|
||||||
private void hookServerStarted(CallbackInfo ci) {
|
private void hookServerStarted(CallbackInfo ci) {
|
||||||
ModernFix.INSTANCE.onServerStarted();
|
ModernFix.INSTANCE.onServerStarted();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources;
|
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
|
||||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
|
||||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
|
||||||
import net.minecraft.client.resources.model.ModelBaker;
|
|
||||||
import net.minecraft.client.resources.model.ModelState;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelBaker;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
|
|
||||||
@Mixin(ItemOverrides.class)
|
|
||||||
@ClientOnlyMixin
|
|
||||||
public class ItemOverridesFabricMixin {
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason servers insist on generating invalid item overrides that have missing models
|
|
||||||
*/
|
|
||||||
@WrapOperation(method = "bakeModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBaker;bake(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/resources/model/ModelState;)Lnet/minecraft/client/resources/model/BakedModel;"))
|
|
||||||
private BakedModel bake(ModelBaker instance, ResourceLocation resourceLocation, ModelState modelState, Operation<BakedModel> original) {
|
|
||||||
boolean prevState = ((IExtendedModelBaker)instance).throwOnMissingModel(false);
|
|
||||||
try {
|
|
||||||
return original.call(instance, resourceLocation, modelState);
|
|
||||||
} finally {
|
|
||||||
((IExtendedModelBaker)instance).throwOnMissingModel(prevState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources;
|
|
||||||
|
|
||||||
import net.fabricmc.fabric.impl.client.model.ModelLoadingRegistryImpl;
|
|
||||||
import net.minecraft.client.resources.model.ModelBakery;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.annotation.RequiresMod;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
@Mixin(ModelLoadingRegistryImpl.LoaderInstance.class)
|
|
||||||
@RequiresMod("fabric-models-v0")
|
|
||||||
@ClientOnlyMixin
|
|
||||||
public class LoaderInstanceMixin {
|
|
||||||
@Redirect(method = "finish", at = @At(value = "FIELD", target = "Lnet/fabricmc/fabric/impl/client/model/ModelLoadingRegistryImpl$LoaderInstance;loader:Lnet/minecraft/client/resources/model/ModelBakery;"), require = 0)
|
|
||||||
private void keepLoader(ModelLoadingRegistryImpl.LoaderInstance instance, ModelBakery value) {
|
|
||||||
/* allow loading models to happen later */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,185 +0,0 @@
|
||||||
package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
|
||||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import net.minecraft.client.resources.model.*;
|
|
||||||
import net.minecraft.core.registries.BuiltInRegistries;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.ModernFixClient;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelBaker;
|
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
|
||||||
import org.embeddedt.modernfix.dynamicresources.ModelMissingException;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
@Mixin(value = ModelBakery.ModelBakerImpl.class, priority = 600)
|
|
||||||
@ClientOnlyMixin
|
|
||||||
public abstract class ModelBakerImplMixin implements IExtendedModelBaker {
|
|
||||||
private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
|
|
||||||
@Shadow @Final private ModelBakery field_40571;
|
|
||||||
|
|
||||||
@Shadow public abstract UnbakedModel getModel(ResourceLocation arg);
|
|
||||||
|
|
||||||
@Shadow @Final private Function<Material, TextureAtlasSprite> modelTextureGetter;
|
|
||||||
|
|
||||||
private static final MethodHandle blockStateLoaderHandle;
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
blockStateLoaderHandle = MethodHandles.lookup().unreflect(ModelBakery.class.getDeclaredMethod(
|
|
||||||
FabricLoader.getInstance().getMappingResolver().mapMethodName(
|
|
||||||
"intermediary",
|
|
||||||
"net.minecraft.client.resources.model.ModelBakery",
|
|
||||||
"method_4716",
|
|
||||||
"(Lnet/minecraft/world/level/block/state/BlockState;)V"
|
|
||||||
),
|
|
||||||
BlockState.class
|
|
||||||
));
|
|
||||||
} catch(ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean throwIfMissing;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean throwOnMissingModel(boolean flag) {
|
|
||||||
boolean old = throwIfMissing;
|
|
||||||
throwIfMissing = flag;
|
|
||||||
return old;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
|
|
||||||
private void obtainModel(ResourceLocation arg, CallbackInfoReturnable<UnbakedModel> cir) {
|
|
||||||
if(debugDynamicModelLoading)
|
|
||||||
ModernFix.LOGGER.info("Baking {}", arg);
|
|
||||||
IExtendedModelBakery extendedBakery = (IExtendedModelBakery)this.field_40571;
|
|
||||||
if(arg instanceof ModelResourceLocation && arg != ModelBakery.MISSING_MODEL_LOCATION) {
|
|
||||||
// synchronized because we use topLevelModels
|
|
||||||
synchronized (this.field_40571) {
|
|
||||||
/* to emulate vanilla model loading, treat as top-level */
|
|
||||||
Optional<Block> blockOpt = Objects.equals(((ModelResourceLocation)arg).getVariant(), "inventory") ? Optional.empty() : BuiltInRegistries.BLOCK.getOptional(new ResourceLocation(arg.getNamespace(), arg.getPath()));
|
|
||||||
boolean invalidMRL = false;
|
|
||||||
if(blockOpt.isPresent()) {
|
|
||||||
/* load via lambda for mods that expect blockstate to get loaded */
|
|
||||||
ImmutableList<BlockState> states;
|
|
||||||
try {
|
|
||||||
states = extendedBakery.getBlockStatesForMRL(blockOpt.get().getStateDefinition(), (ModelResourceLocation)arg);
|
|
||||||
} catch(RuntimeException e) {
|
|
||||||
states = ImmutableList.of();
|
|
||||||
invalidMRL = true;
|
|
||||||
// Fall back to getModel
|
|
||||||
cir.setReturnValue(this.field_40571.getModel(arg));
|
|
||||||
}
|
|
||||||
for(BlockState state : states) {
|
|
||||||
try {
|
|
||||||
blockStateLoaderHandle.invokeExact(this.field_40571, state);
|
|
||||||
} catch(Throwable e) {
|
|
||||||
ModernFix.LOGGER.error("Error loading model", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.field_40571.loadTopLevel((ModelResourceLocation)arg);
|
|
||||||
}
|
|
||||||
if(!invalidMRL) {
|
|
||||||
cir.setReturnValue(this.field_40571.topLevelModels.getOrDefault(arg, extendedBakery.mfix$getUnbakedMissingModel()));
|
|
||||||
// avoid leaks
|
|
||||||
this.field_40571.topLevelModels.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
cir.setReturnValue(this.field_40571.getModel(arg));
|
|
||||||
UnbakedModel toReplace = cir.getReturnValue();
|
|
||||||
if(true) {
|
|
||||||
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
|
|
||||||
try {
|
|
||||||
toReplace = integration.onUnbakedModelPreBake(arg, toReplace, this.field_40571);
|
|
||||||
} catch(RuntimeException e) {
|
|
||||||
ModernFix.LOGGER.error("Exception firing model pre-bake event for {}", arg, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cir.setReturnValue(toReplace);
|
|
||||||
cir.getReturnValue().resolveParents(this.field_40571::getModel);
|
|
||||||
if(cir.getReturnValue() == extendedBakery.mfix$getUnbakedMissingModel()) {
|
|
||||||
if(arg != ModelBakery.MISSING_MODEL_LOCATION) {
|
|
||||||
if(debugDynamicModelLoading)
|
|
||||||
ModernFix.LOGGER.warn("Model {} not present", arg);
|
|
||||||
if(throwIfMissing)
|
|
||||||
throw new ModelMissingException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@WrapOperation(method = "bake", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/UnbakedModel;bake(Lnet/minecraft/client/resources/model/ModelBaker;Ljava/util/function/Function;Lnet/minecraft/client/resources/model/ModelState;Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/client/resources/model/BakedModel;"))
|
|
||||||
private BakedModel callBakedModelIntegration(UnbakedModel unbakedModel, ModelBaker baker, Function<Material, TextureAtlasSprite> spriteGetter, ModelState state, ResourceLocation location, Operation<BakedModel> operation) {
|
|
||||||
BakedModel model = operation.call(unbakedModel, baker, spriteGetter, state, location);
|
|
||||||
|
|
||||||
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
|
|
||||||
model = integration.onBakedModelLoad(location, unbakedModel, model, state, this.field_40571, spriteGetter);
|
|
||||||
}
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason emulate old function, to allow injectors to work
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
@Overwrite
|
|
||||||
public BakedModel bake(ResourceLocation arg, ModelState arg2) {
|
|
||||||
ModelBakery.BakedCacheKey key = new ModelBakery.BakedCacheKey(arg, arg2.getRotation(), arg2.isUvLocked());
|
|
||||||
BakedModel existing = this.field_40571.bakedCache.get(key);
|
|
||||||
if (existing != null) {
|
|
||||||
return existing;
|
|
||||||
} else {
|
|
||||||
synchronized (this.field_40571) {
|
|
||||||
if(debugDynamicModelLoading)
|
|
||||||
ModernFix.LOGGER.info("Baking {}", arg);
|
|
||||||
UnbakedModel iunbakedmodel = this.getModel(arg);
|
|
||||||
// TODO: make sure parent resolution doesn't re-run many times
|
|
||||||
iunbakedmodel.resolveParents(this::getModel);
|
|
||||||
BakedModel ibakedmodel = null;
|
|
||||||
if (iunbakedmodel instanceof BlockModel) {
|
|
||||||
BlockModel blockmodel = (BlockModel)iunbakedmodel;
|
|
||||||
if (blockmodel.getRootModel() == ModelBakery.GENERATION_MARKER) {
|
|
||||||
ibakedmodel = ModelBakery.ITEM_MODEL_GENERATOR.generateBlockModel(this.modelTextureGetter, blockmodel).bake((ModelBaker)this, blockmodel, this.modelTextureGetter, arg2, arg, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(ibakedmodel == null) {
|
|
||||||
// leave the original assignment in the same spot so wrapping injectors work
|
|
||||||
// this means two bakes might happen for missing models, but not much we can do
|
|
||||||
ibakedmodel = iunbakedmodel.bake((ModelBaker)this, this.modelTextureGetter, arg2, arg);
|
|
||||||
IExtendedModelBakery extendedBakery = (IExtendedModelBakery)this.field_40571;
|
|
||||||
if(iunbakedmodel == extendedBakery.mfix$getUnbakedMissingModel()) {
|
|
||||||
// use a shared baked missing model
|
|
||||||
createBakedMissingModelIfNeeded(extendedBakery, iunbakedmodel, arg2, arg);
|
|
||||||
ibakedmodel = extendedBakery.getBakedMissingModel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.field_40571.bakedCache.put(key, ibakedmodel);
|
|
||||||
return ibakedmodel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
@ -1,328 +0,0 @@
|
||||||
package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources;
|
|
||||||
|
|
||||||
import com.google.common.cache.Cache;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
import com.google.common.cache.RemovalNotification;
|
|
||||||
import com.google.common.collect.ForwardingMap;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.color.block.BlockColors;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import net.minecraft.client.resources.model.*;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.block.state.StateDefinition;
|
|
||||||
import net.minecraft.world.level.block.state.properties.Property;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.ModernFixClient;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelBaker;
|
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
|
||||||
import org.embeddedt.modernfix.dynamicresources.DynamicBakedModelProvider;
|
|
||||||
import org.embeddedt.modernfix.dynamicresources.ModelBakeryHelpers;
|
|
||||||
import org.objectweb.asm.Opcodes;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Mutable;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.*;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
|
|
||||||
/* low priority so that our injectors are added after other mods' */
|
|
||||||
@Mixin(value = ModelBakery.class, priority = 1100)
|
|
||||||
@ClientOnlyMixin
|
|
||||||
public abstract class ModelBakeryMixin implements IExtendedModelBakery {
|
|
||||||
|
|
||||||
private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
|
|
||||||
|
|
||||||
@Shadow @Final @Mutable public Map<ResourceLocation, UnbakedModel> unbakedCache;
|
|
||||||
|
|
||||||
@Shadow @Final public static ModelResourceLocation MISSING_MODEL_LOCATION;
|
|
||||||
|
|
||||||
@Shadow @Final private Set<ResourceLocation> loadingStack;
|
|
||||||
|
|
||||||
@Shadow protected abstract void loadModel(ResourceLocation blockstateLocation) throws Exception;
|
|
||||||
|
|
||||||
@Shadow @Final @Mutable
|
|
||||||
private Map<ResourceLocation, BakedModel> bakedTopLevelModels;
|
|
||||||
|
|
||||||
@Shadow @Final @Mutable private Map<ModelBakery.BakedCacheKey, BakedModel> bakedCache;
|
|
||||||
|
|
||||||
@Shadow @Final @Mutable private BlockColors blockColors;
|
|
||||||
@Shadow @Final private static Logger LOGGER;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
public abstract void loadTopLevel(ModelResourceLocation modelResourceLocation);
|
|
||||||
|
|
||||||
@Shadow public abstract UnbakedModel getModel(ResourceLocation resourceLocation);
|
|
||||||
|
|
||||||
private Cache<ModelBakery.BakedCacheKey, BakedModel> loadedBakedModels;
|
|
||||||
|
|
||||||
private Cache<ResourceLocation, UnbakedModel> loadedModels;
|
|
||||||
|
|
||||||
private HashMap<ResourceLocation, UnbakedModel> smallLoadingCache = new HashMap<>();
|
|
||||||
|
|
||||||
private boolean ignoreModelLoad;
|
|
||||||
|
|
||||||
// disable fabric recursion
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private boolean fabric_enableGetOrLoadModelGuard;
|
|
||||||
|
|
||||||
|
|
||||||
@Redirect(method = "<init>", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/resources/model/ModelBakery;blockColors:Lnet/minecraft/client/color/block/BlockColors;"))
|
|
||||||
private void replaceTopLevelBakedModels(ModelBakery bakery, BlockColors val) {
|
|
||||||
// we can handle recursion in getModel without issues
|
|
||||||
fabric_enableGetOrLoadModelGuard = false;
|
|
||||||
this.blockColors = val;
|
|
||||||
this.loadedBakedModels = CacheBuilder.newBuilder()
|
|
||||||
.expireAfterAccess(ModelBakeryHelpers.MAX_MODEL_LIFETIME_SECS, TimeUnit.SECONDS)
|
|
||||||
.maximumSize(ModelBakeryHelpers.MAX_BAKED_MODEL_COUNT)
|
|
||||||
.concurrencyLevel(8)
|
|
||||||
.removalListener(this::onModelRemoved)
|
|
||||||
.softValues()
|
|
||||||
.build();
|
|
||||||
this.loadedModels = CacheBuilder.newBuilder()
|
|
||||||
.expireAfterAccess(ModelBakeryHelpers.MAX_MODEL_LIFETIME_SECS, TimeUnit.SECONDS)
|
|
||||||
.maximumSize(ModelBakeryHelpers.MAX_UNBAKED_MODEL_COUNT)
|
|
||||||
.concurrencyLevel(8)
|
|
||||||
.removalListener(this::onModelRemoved)
|
|
||||||
//.softValues()
|
|
||||||
.build();
|
|
||||||
this.bakedCache = loadedBakedModels.asMap();
|
|
||||||
ConcurrentMap<ResourceLocation, UnbakedModel> unbakedCacheBackingMap = loadedModels.asMap();
|
|
||||||
this.unbakedCache = new ForwardingMap<ResourceLocation, UnbakedModel>() {
|
|
||||||
@Override
|
|
||||||
protected Map<ResourceLocation, UnbakedModel> delegate() {
|
|
||||||
return unbakedCacheBackingMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UnbakedModel put(ResourceLocation key, UnbakedModel value) {
|
|
||||||
smallLoadingCache.put(key, value);
|
|
||||||
return super.put(key, value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.bakedTopLevelModels = new DynamicBakedModelProvider((ModelBakery)(Object)this, bakedCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", ordinal = 0))
|
|
||||||
private void ignoreFutureModelLoads(CallbackInfo ci) {
|
|
||||||
this.ignoreModelLoad = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private <K, V> void onModelRemoved(RemovalNotification<K, V> notification) {
|
|
||||||
if(!debugDynamicModelLoading)
|
|
||||||
return;
|
|
||||||
Object k = notification.getKey();
|
|
||||||
if(k == null)
|
|
||||||
return;
|
|
||||||
ResourceLocation rl;
|
|
||||||
boolean baked = false;
|
|
||||||
if(k instanceof ResourceLocation) {
|
|
||||||
rl = (ResourceLocation)k;
|
|
||||||
} else {
|
|
||||||
rl = ((ModelBakery.BakedCacheKey)k).id();
|
|
||||||
baked = true;
|
|
||||||
}
|
|
||||||
/* can fire when a model is replaced */
|
|
||||||
if(!baked && this.loadedModels.getIfPresent(rl) != null)
|
|
||||||
return;
|
|
||||||
ModernFix.LOGGER.warn("Evicted {} model {}", baked ? "baked" : "unbaked", rl);
|
|
||||||
}
|
|
||||||
|
|
||||||
private UnbakedModel missingModel;
|
|
||||||
|
|
||||||
private Set<ResourceLocation> blockStateFiles;
|
|
||||||
private Set<ResourceLocation> modelFiles;
|
|
||||||
|
|
||||||
@ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 0), index = 1)
|
|
||||||
private Object captureMissingModel(Object model) {
|
|
||||||
this.missingModel = (UnbakedModel)model;
|
|
||||||
this.blockStateFiles = new HashSet<>();
|
|
||||||
this.modelFiles = new HashSet<>();
|
|
||||||
return this.missingModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason don't actually load most models
|
|
||||||
*/
|
|
||||||
@Redirect(method = "*", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelBakery;loadTopLevel(Lnet/minecraft/client/resources/model/ModelResourceLocation;)V"))
|
|
||||||
private void addTopLevelFile(ModelBakery bakery, ModelResourceLocation location) {
|
|
||||||
if(location == MISSING_MODEL_LOCATION || !this.ignoreModelLoad) {
|
|
||||||
loadTopLevel(location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Map;forEach(Ljava/util/function/BiConsumer;)V", ordinal = 0))
|
|
||||||
private void fetchStaticDefinitions(Map<ResourceLocation, StateDefinition<Block, BlockState>> map, BiConsumer<ResourceLocation, StateDefinition<Block, BlockState>> func) {
|
|
||||||
/* no-op */
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;", ordinal = 0))
|
|
||||||
private ImmutableList<BlockState> fetchBlocks(StateDefinition<Block, BlockState> def) {
|
|
||||||
/* no-op */
|
|
||||||
return ImmutableList.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make a copy of the top-level model list to avoid CME if more models get loaded here.
|
|
||||||
*/
|
|
||||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Ljava/util/Map;values()Ljava/util/Collection;", ordinal = 0))
|
|
||||||
private Collection<?> copyTopLevelModelList(Map<?, ?> map) {
|
|
||||||
return new ArrayList<>(map.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
private BiFunction<ResourceLocation, Material, TextureAtlasSprite> textureGetter;
|
|
||||||
|
|
||||||
@Inject(method = "bakeModels", at = @At("HEAD"))
|
|
||||||
private void captureGetter(BiFunction<ResourceLocation, Material, TextureAtlasSprite> getter, CallbackInfo ci) {
|
|
||||||
this.ignoreModelLoad = false;
|
|
||||||
textureGetter = getter;
|
|
||||||
DynamicBakedModelProvider.currentInstance = (DynamicBakedModelProvider)this.bakedTopLevelModels;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = "bakeModels", at = @At(value = "INVOKE", target = "Ljava/util/Map;keySet()Ljava/util/Set;"))
|
|
||||||
private Set<ResourceLocation> skipBake(Map<ResourceLocation, UnbakedModel> instance) {
|
|
||||||
Set<ResourceLocation> modelSet = new HashSet<>(instance.keySet());
|
|
||||||
if(modelSet.size() > 0)
|
|
||||||
ModernFix.LOGGER.info("Early baking {} models", modelSet.size());
|
|
||||||
return modelSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use the already loaded missing model instead of the cache entry (which will probably get evicted).
|
|
||||||
*/
|
|
||||||
@Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;", ordinal = 1))
|
|
||||||
private Object getMissingModel(Map map, Object rl) {
|
|
||||||
if(rl == MISSING_MODEL_LOCATION && map == unbakedCache)
|
|
||||||
return missingModel;
|
|
||||||
return unbakedCache.get(rl);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ModifyVariable(method = "cacheAndQueueDependencies", at = @At("HEAD"), argsOnly = true)
|
|
||||||
private UnbakedModel fireUnbakedEvent(UnbakedModel model, ResourceLocation location) {
|
|
||||||
for(ModernFixClientIntegration integration : ModernFixClient.CLIENT_INTEGRATIONS) {
|
|
||||||
try {
|
|
||||||
model = integration.onUnbakedModelLoad(location, model, (ModelBakery)(Object)this);
|
|
||||||
} catch(RuntimeException e) {
|
|
||||||
ModernFix.LOGGER.error("Exception firing model load event for {}", location, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int mfix$nestedLoads = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author embeddedt
|
|
||||||
* @reason synchronize
|
|
||||||
*/
|
|
||||||
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
|
|
||||||
public void getOrLoadModelDynamic(ResourceLocation modelLocation, CallbackInfoReturnable<UnbakedModel> cir) {
|
|
||||||
if(modelLocation.equals(MISSING_MODEL_LOCATION)) {
|
|
||||||
cir.setReturnValue(missingModel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
UnbakedModel existing = this.unbakedCache.get(modelLocation);
|
|
||||||
if (existing != null) {
|
|
||||||
cir.setReturnValue(existing);
|
|
||||||
} else {
|
|
||||||
synchronized(this) {
|
|
||||||
if (this.loadingStack.contains(modelLocation)) {
|
|
||||||
throw new IllegalStateException("Circular reference while loading " + modelLocation);
|
|
||||||
} else {
|
|
||||||
this.loadingStack.add(modelLocation);
|
|
||||||
UnbakedModel iunbakedmodel = missingModel;
|
|
||||||
|
|
||||||
while(!this.loadingStack.isEmpty()) {
|
|
||||||
ResourceLocation resourcelocation = this.loadingStack.iterator().next();
|
|
||||||
|
|
||||||
mfix$nestedLoads++;
|
|
||||||
try {
|
|
||||||
existing = this.unbakedCache.get(resourcelocation);
|
|
||||||
if (existing == null) {
|
|
||||||
if(debugDynamicModelLoading)
|
|
||||||
LOGGER.info("Loading {}", resourcelocation);
|
|
||||||
this.loadModel(resourcelocation);
|
|
||||||
} else
|
|
||||||
smallLoadingCache.put(resourcelocation, existing);
|
|
||||||
} catch (ModelBakery.BlockStateDefinitionException var9) {
|
|
||||||
LOGGER.warn(var9.getMessage());
|
|
||||||
this.unbakedCache.put(resourcelocation, iunbakedmodel);
|
|
||||||
smallLoadingCache.put(resourcelocation, iunbakedmodel);
|
|
||||||
} catch (Exception var10) {
|
|
||||||
LOGGER.warn("Unable to load model: '{}' referenced from: {}: {}", resourcelocation, modelLocation, var10);
|
|
||||||
this.unbakedCache.put(resourcelocation, iunbakedmodel);
|
|
||||||
smallLoadingCache.put(resourcelocation, iunbakedmodel);
|
|
||||||
} finally {
|
|
||||||
mfix$nestedLoads--;
|
|
||||||
this.loadingStack.remove(resourcelocation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have to get the result from the temporary cache used for a model load
|
|
||||||
// As in pathological cases (e.g. Pedestals on 1.19) unbakedCache can lose
|
|
||||||
// the model immediately
|
|
||||||
UnbakedModel result = smallLoadingCache.getOrDefault(modelLocation, iunbakedmodel);
|
|
||||||
try {
|
|
||||||
// required as some mods (e.g. EBE) call bake directly on the returned model, without resolving parents themselves
|
|
||||||
result.resolveParents(this::getModel);
|
|
||||||
} catch(RuntimeException ignored) {}
|
|
||||||
// We are done with loading, so clear this cache to allow GC of any unneeded models
|
|
||||||
if(mfix$nestedLoads == 0)
|
|
||||||
smallLoadingCache.clear();
|
|
||||||
cir.setReturnValue(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T extends Comparable<T>, V extends T> BlockState setPropertyGeneric(BlockState state, Property<T> prop, Object o) {
|
|
||||||
return state.setValue(prop, (V)o);
|
|
||||||
}
|
|
||||||
@Redirect(method = "loadModel", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/StateDefinition;getPossibleStates()Lcom/google/common/collect/ImmutableList;"))
|
|
||||||
private ImmutableList<BlockState> loadOnlyRelevantBlockState(StateDefinition<Block, BlockState> stateDefinition, ResourceLocation location) {
|
|
||||||
if(!(location instanceof ModelResourceLocation) || Minecraft.getInstance().getOverlay() != null)
|
|
||||||
return stateDefinition.getPossibleStates();
|
|
||||||
return ModelBakeryHelpers.getBlockStatesForMRL(stateDefinition, (ModelResourceLocation)location);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BakedModel bakeDefault(ResourceLocation modelLocation, ModelState state) {
|
|
||||||
ModelBakery.BakedCacheKey key = new ModelBakery.BakedCacheKey(modelLocation, BlockModelRotation.X0_Y0.getRotation(), BlockModelRotation.X0_Y0.isUvLocked());
|
|
||||||
BakedModel m = loadedBakedModels.getIfPresent(key);
|
|
||||||
if(m != null)
|
|
||||||
return m;
|
|
||||||
ModelBakery self = (ModelBakery) (Object) this;
|
|
||||||
ModelBaker theBaker = self.new ModelBakerImpl(textureGetter, modelLocation);
|
|
||||||
((IExtendedModelBaker)theBaker).throwOnMissingModel(true);
|
|
||||||
synchronized(this) { m = theBaker.bake(modelLocation, state); }
|
|
||||||
if(m != null)
|
|
||||||
loadedBakedModels.put(key, m);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ImmutableList<BlockState> getBlockStatesForMRL(StateDefinition<Block, BlockState> stateDefinition, ModelResourceLocation location) {
|
|
||||||
return loadOnlyRelevantBlockState(stateDefinition, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnbakedModel mfix$getUnbakedMissingModel() {
|
|
||||||
return missingModel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,12 +9,10 @@ import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.fabricmc.loader.api.ModContainer;
|
import net.fabricmc.loader.api.ModContainer;
|
||||||
import net.fabricmc.loader.api.metadata.CustomValue;
|
import net.fabricmc.loader.api.metadata.CustomValue;
|
||||||
import net.fabricmc.loader.api.metadata.ModMetadata;
|
import net.fabricmc.loader.api.metadata.ModMetadata;
|
||||||
import net.minecraft.client.searchtree.SearchRegistry;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.world.item.CreativeModeTabs;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.embeddedt.modernfix.ModernFixFabric;
|
import org.embeddedt.modernfix.ModernFixFabric;
|
||||||
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
|
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
|
||||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||||
|
|
@ -24,9 +22,7 @@ import org.embeddedt.modernfix.util.CommonModUtil;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
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.Consumer;
|
||||||
|
|
||||||
public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks {
|
public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks {
|
||||||
|
|
@ -70,7 +66,7 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks {
|
||||||
return FabricLoader.getInstance().getGameDir();
|
return FabricLoader.getInstance().getGameDir();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacket(ServerPlayer player, Object packet) {
|
public void sendPacket(ServerPlayer player, CustomPacketPayload packet) {
|
||||||
//PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), packet);
|
//PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,13 +104,6 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks {
|
||||||
return modOptions;
|
return modOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerCreativeSearchTrees(SearchRegistry registry, SearchRegistry.TreeBuilderSupplier<ItemStack> nameSupplier, SearchRegistry.TreeBuilderSupplier<ItemStack> tagSupplier, BiConsumer<SearchRegistry.Key<ItemStack>, List<ItemStack>> populator) {
|
|
||||||
CreativeModeTabs.searchTab().setSearchTreeBuilder((list) -> {
|
|
||||||
populator.accept(SearchRegistry.CREATIVE_NAMES, list);
|
|
||||||
populator.accept(SearchRegistry.CREATIVE_TAGS, list);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onLaunchComplete() {
|
public void onLaunchComplete() {
|
||||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnFabric")) {
|
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnFabric")) {
|
||||||
CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.stop("launch"), "Failed to stop profiler");
|
CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.stop("launch"), "Failed to stop profiler");
|
||||||
|
|
|
||||||
|
|
@ -34,15 +34,15 @@
|
||||||
"modmenu": [ "org.embeddedt.modernfix.fabric.modmenu.ModernFixModMenuApiImpl" ],
|
"modmenu": [ "org.embeddedt.modernfix.fabric.modmenu.ModernFixModMenuApiImpl" ],
|
||||||
"preLaunch": [
|
"preLaunch": [
|
||||||
"org.embeddedt.modernfix.ModernFixPreLaunchFabric"
|
"org.embeddedt.modernfix.ModernFixPreLaunchFabric"
|
||||||
],
|
]
|
||||||
"jei_mod_plugin": [ "org.embeddedt.modernfix.searchtree.JEIRuntimeCapturer"]
|
|
||||||
},
|
},
|
||||||
"mixins": [
|
"mixins": [
|
||||||
"modernfix-fabric.mixins.json",
|
"modernfix-fabric.mixins.json",
|
||||||
"modernfix-common.mixins.json"
|
"modernfix-common.mixins.json"
|
||||||
],
|
],
|
||||||
"depends": {
|
"depends": {
|
||||||
"minecraft": ">=1.16.2"
|
"minecraft": ">=1.16.2",
|
||||||
|
"fabricloader": ">=0.15.0"
|
||||||
},
|
},
|
||||||
"breaks": {
|
"breaks": {
|
||||||
"dashloader": "<5.0.0-beta.1"
|
"dashloader": "<5.0.0-beta.1"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.world.level.EmptyBlockGetter;
|
import net.minecraft.world.level.EmptyBlockGetter;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.material.FluidState;
|
|
||||||
import org.embeddedt.modernfix.duck.IBlockState;
|
import org.embeddedt.modernfix.duck.IBlockState;
|
||||||
import org.embeddedt.modernfix.testing.util.BootstrapMinecraft;
|
import org.embeddedt.modernfix.testing.util.BootstrapMinecraft;
|
||||||
import org.junit.jupiter.api.*;
|
import org.junit.jupiter.api.*;
|
||||||
|
|
@ -79,6 +78,7 @@ public class BlockStateCacheTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
@Test
|
@Test
|
||||||
@Order(5)
|
@Order(5)
|
||||||
public void checkRecursiveFluidState() {
|
public void checkRecursiveFluidState() {
|
||||||
|
|
@ -93,4 +93,5 @@ public class BlockStateCacheTest {
|
||||||
// this should not throw
|
// this should not throw
|
||||||
state.getFluidState();
|
state.getFluidState();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package org.embeddedt.modernfix.testing.util;
|
package org.embeddedt.modernfix.testing.util;
|
||||||
|
|
||||||
import net.minecraft.DetectedVersion;
|
import net.minecraft.SharedConstants;
|
||||||
import net.minecraft.core.MappedRegistry;
|
import net.minecraft.core.MappedRegistry;
|
||||||
import net.minecraft.core.registries.BuiltInRegistries;
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.server.Bootstrap;
|
import net.minecraft.server.Bootstrap;
|
||||||
|
|
@ -18,7 +18,7 @@ import java.util.IdentityHashMap;
|
||||||
public class BootstrapMinecraftExtension implements Extension, BeforeAllCallback, AfterAllCallback {
|
public class BootstrapMinecraftExtension implements Extension, BeforeAllCallback, AfterAllCallback {
|
||||||
@Override
|
@Override
|
||||||
public void beforeAll(ExtensionContext context) throws Exception {
|
public void beforeAll(ExtensionContext context) throws Exception {
|
||||||
DetectedVersion.tryDetectVersion();
|
SharedConstants.tryDetectVersion();
|
||||||
Bootstrap.bootStrap();
|
Bootstrap.bootStrap();
|
||||||
// Allow blocks to be created in tests
|
// Allow blocks to be created in tests
|
||||||
Field field = MappedRegistry.class.getDeclaredField("unregisteredIntrusiveHolders");
|
Field field = MappedRegistry.class.getDeclaredField("unregisteredIntrusiveHolders");
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ dependencies {
|
||||||
modImplementation(fabricApi.module("fabric-models-v0", "0.84.0+1.20.1")) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modImplementation(fabricApi.module("fabric-models-v0", "0.84.0+1.20.1")) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
modImplementation(fabricApi.module("fabric-registry-sync-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modImplementation(fabricApi.module("fabric-registry-sync-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
modImplementation(fabricApi.module("fabric-renderer-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modImplementation(fabricApi.module("fabric-renderer-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
modImplementation(fabricApi.module("fabric-rendering-data-attachment-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modImplementation("net.fabricmc.fabric-api:fabric-rendering-data-attachment-v1:0.3.36+92a0d36777") { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
modImplementation(fabricApi.module("fabric-rendering-fluids-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modImplementation(fabricApi.module("fabric-rendering-fluids-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
modRuntimeOnly(fabricApi.module("fabric-renderer-indigo", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modRuntimeOnly(fabricApi.module("fabric-renderer-indigo", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||||
|
|
||||||
public class TestBlock extends Block {
|
public class TestBlock extends Block {
|
||||||
private static final BlockBehaviour.Properties PROPERTIES = BlockBehaviour.Properties.copy(Blocks.STONE);
|
private static final BlockBehaviour.Properties PROPERTIES = BlockBehaviour.Properties.ofFullCopy(Blocks.STONE);
|
||||||
|
|
||||||
public TestBlock() {
|
public TestBlock() {
|
||||||
super(PROPERTIES);
|
super(PROPERTIES);
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
loom.platform=forge
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user