Merge remote-tracking branch 'origin/1.18' into 1.19.2
This commit is contained in:
commit
9ce029b35d
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -4,6 +4,8 @@ libs
|
||||||
media
|
media
|
||||||
classes/
|
classes/
|
||||||
.architectury-transformer/
|
.architectury-transformer/
|
||||||
|
fabric/fabricloader.log
|
||||||
|
fabric/test_run
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
CHANGELOG.md
|
CHANGELOG.md
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
plugins {
|
plugins {
|
||||||
id "architectury-plugin" version "3.4-SNAPSHOT"
|
id "architectury-plugin" version "3.4-SNAPSHOT"
|
||||||
id "dev.architectury.loom" version "1.1-SNAPSHOT" apply false
|
id "dev.architectury.loom" version "1.2-SNAPSHOT" apply false
|
||||||
id "maven-publish"
|
id "maven-publish"
|
||||||
id 'com.matthewprenger.cursegradle' version '1.4.0' apply false
|
id 'com.matthewprenger.cursegradle' version '1.4.0' apply false
|
||||||
id 'com.palantir.git-version' version '1.0.0'
|
id 'com.palantir.git-version' version '1.0.0'
|
||||||
|
|
@ -93,7 +93,7 @@ allprojects {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
subprojects {
|
configure(subprojects.findAll {it.name == "common" || it.name == "forge" || it.name == "fabric"}) {
|
||||||
apply plugin: "dev.architectury.loom"
|
apply plugin: "dev.architectury.loom"
|
||||||
|
|
||||||
loom {
|
loom {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package org.embeddedt.modernfix.common.mixin.perf.deduplicate_climate_parameters;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.biome.Climate;
|
||||||
|
import org.embeddedt.modernfix.dedup.ClimateCache;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
@Mixin({ Climate.Parameter.class, Climate.ParameterPoint.class })
|
||||||
|
public class ParameterMixin {
|
||||||
|
@Redirect(method = "*", at = @At(value = "NEW", target = "net/minecraft/world/level/biome/Climate$Parameter"), require = 0)
|
||||||
|
private static Climate.Parameter internParameterStatic(long min, long max) {
|
||||||
|
return ClimateCache.MFIX_INTERNER.intern(new Climate.Parameter(min, max));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,11 @@ public abstract class BlockStateBaseMixin implements IBlockState {
|
||||||
cacheInvalid = true;
|
cacheInvalid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCacheInvalid() {
|
||||||
|
return cacheInvalid;
|
||||||
|
}
|
||||||
|
|
||||||
private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockStateBase base) {
|
private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockStateBase base) {
|
||||||
if(cacheInvalid) {
|
if(cacheInvalid) {
|
||||||
// Ensure that only one block's cache is built at a time
|
// Ensure that only one block's cache is built at a time
|
||||||
|
|
|
||||||
|
|
@ -311,19 +311,21 @@ public class ModernFixEarlyConfig {
|
||||||
public static ModernFixEarlyConfig load(File file) {
|
public static ModernFixEarlyConfig load(File file) {
|
||||||
ModernFixEarlyConfig config = new ModernFixEarlyConfig(file);
|
ModernFixEarlyConfig config = new ModernFixEarlyConfig(file);
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
if(file.exists()) {
|
if(!Boolean.getBoolean("modernfix.ignoreConfigForTesting")) {
|
||||||
try (FileInputStream fin = new FileInputStream(file)){
|
if(file.exists()) {
|
||||||
props.load(fin);
|
try (FileInputStream fin = new FileInputStream(file)){
|
||||||
} catch (IOException e) {
|
props.load(fin);
|
||||||
throw new RuntimeException("Could not load config file", e);
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Could not load config file", e);
|
||||||
|
}
|
||||||
|
config.readProperties(props);
|
||||||
}
|
}
|
||||||
config.readProperties(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
config.save();
|
config.save();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.warn("Could not write configuration file", e);
|
LOGGER.warn("Could not write configuration file", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.embeddedt.modernfix.dedup;
|
||||||
|
|
||||||
|
import com.google.common.collect.Interner;
|
||||||
|
import com.google.common.collect.Interners;
|
||||||
|
import net.minecraft.world.level.biome.Climate;
|
||||||
|
|
||||||
|
public class ClimateCache {
|
||||||
|
public static final Interner<Climate.Parameter> MFIX_INTERNER = Interners.newStrongInterner();
|
||||||
|
}
|
||||||
|
|
@ -3,4 +3,5 @@ package org.embeddedt.modernfix.duck;
|
||||||
|
|
||||||
public interface IBlockState {
|
public interface IBlockState {
|
||||||
void clearCache();
|
void clearCache();
|
||||||
|
boolean isCacheInvalid();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ public class ClassInfoManager {
|
||||||
if(entry.getKey().equals("java/lang/Object"))
|
if(entry.getKey().equals("java/lang/Object"))
|
||||||
return false;
|
return false;
|
||||||
ClassInfo mixinClz = entry.getValue();
|
ClassInfo mixinClz = entry.getValue();
|
||||||
|
if(mixinClz == null)
|
||||||
|
return true;
|
||||||
try {
|
try {
|
||||||
if(mixinClz.isMixin()) {
|
if(mixinClz.isMixin()) {
|
||||||
// clear classNode in MixinInfo.State
|
// clear classNode in MixinInfo.State
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
plugins {
|
plugins {
|
||||||
id "com.github.johnrengelman.shadow" version "7.1.2"
|
id "com.github.johnrengelman.shadow" version "7.1.2"
|
||||||
|
id 'com.adarshr.test-logger' version '3.2.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
architectury {
|
architectury {
|
||||||
|
|
@ -22,10 +23,16 @@ configurations {
|
||||||
|
|
||||||
include.extendsFrom modIncludeImplementation
|
include.extendsFrom modIncludeImplementation
|
||||||
modImplementation.extendsFrom modIncludeImplementation
|
modImplementation.extendsFrom modIncludeImplementation
|
||||||
|
|
||||||
|
testAgent {
|
||||||
|
canBeConsumed = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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}"
|
||||||
|
|
||||||
modIncludeImplementation(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modIncludeImplementation(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
modIncludeImplementation(fabricApi.module("fabric-lifecycle-events-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modIncludeImplementation(fabricApi.module("fabric-lifecycle-events-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
modIncludeImplementation(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
modIncludeImplementation(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
||||||
|
|
@ -40,7 +47,33 @@ dependencies {
|
||||||
// modApi "me.shedaniel:architectury-fabric:${rootProject.architectury_version}"
|
// modApi "me.shedaniel:architectury-fabric:${rootProject.architectury_version}"
|
||||||
|
|
||||||
common(project(path: ":common", configuration: "namedElements")) { transitive false }
|
common(project(path: ":common", configuration: "namedElements")) { transitive false }
|
||||||
shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) { transitive false }
|
testImplementation(shadowCommon(project(path: ":common", configuration: "transformProductionFabric"))) { transitive false }
|
||||||
|
|
||||||
|
testImplementation(platform("org.junit:junit-bom:${project.junit_version}"))
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter-api")
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter-params")
|
||||||
|
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
||||||
|
testImplementation("org.junit.platform:junit-platform-launcher")
|
||||||
|
testImplementation("org.assertj:assertj-core:3.19.0")
|
||||||
|
testImplementation("com.google.guava:guava-testlib:21.0")
|
||||||
|
testImplementation("org.mockito:mockito-junit-jupiter:5.3.1")
|
||||||
|
|
||||||
|
testAgent(project("path": ":test_agent", "configuration": "agentJar"))
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
def runDir = file('test_run')
|
||||||
|
doFirst() {
|
||||||
|
runDir.mkdir()
|
||||||
|
}
|
||||||
|
workingDir = runDir
|
||||||
|
systemProperty 'modernfix.ignoreConfigForTesting', 'true'
|
||||||
|
|
||||||
|
// inject our custom agent to fix #817
|
||||||
|
FileCollection agentFile = configurations.getByName("testAgent")
|
||||||
|
jvmArgs "-javaagent:${agentFile.singleFile.absolutePath}"
|
||||||
|
dependsOn(agentFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
processResources {
|
||||||
|
|
@ -55,18 +88,18 @@ shadowJar {
|
||||||
exclude "architectury.common.json"
|
exclude "architectury.common.json"
|
||||||
|
|
||||||
configurations = [project.configurations.shadowCommon]
|
configurations = [project.configurations.shadowCommon]
|
||||||
classifier "dev-shadow"
|
archiveClassifier.set("dev-shadow")
|
||||||
}
|
}
|
||||||
|
|
||||||
remapJar {
|
remapJar {
|
||||||
injectAccessWidener = true
|
injectAccessWidener = true
|
||||||
input.set shadowJar.archiveFile
|
input.set shadowJar.archiveFile
|
||||||
dependsOn shadowJar
|
dependsOn shadowJar
|
||||||
classifier null
|
archiveClassifier.set(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
classifier "dev"
|
archiveClassifier.set("dev")
|
||||||
}
|
}
|
||||||
|
|
||||||
components.java {
|
components.java {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@ import org.embeddedt.modernfix.util.CommonModUtil;
|
||||||
public class ModernFixPreLaunchFabric implements PreLaunchEntrypoint {
|
public class ModernFixPreLaunchFabric implements PreLaunchEntrypoint {
|
||||||
@Override
|
@Override
|
||||||
public void onPreLaunch() {
|
public void onPreLaunch() {
|
||||||
|
if(ModernFixMixinPlugin.instance == null) {
|
||||||
|
System.err.println("Mixin plugin not loaded yet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnFabric")) {
|
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_launch.OnFabric")) {
|
||||||
CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.start("launch"), "Failed to start profiler");
|
CommonModUtil.runWithoutCrash(() -> SparkLaunchProfiler.start("launch"), "Failed to start profiler");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package net.minecraft.world.level.block.state;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.EmptyBlockGetter;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import org.embeddedt.modernfix.duck.IBlockState;
|
||||||
|
import org.embeddedt.modernfix.testing.util.BootstrapMinecraft;
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
@BootstrapMinecraft
|
||||||
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
|
public class BlockStateCacheTest {
|
||||||
|
@BeforeEach
|
||||||
|
public void rebuildCache() {
|
||||||
|
Blocks.rebuildCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initially, the cache should be invalid, and null.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Order(1)
|
||||||
|
public void testCacheNullInitially() {
|
||||||
|
BlockState stoneBlock = Blocks.STONE.defaultBlockState();
|
||||||
|
assertTrue(((IBlockState)stoneBlock).isCacheInvalid());
|
||||||
|
assertNull(stoneBlock.cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When an API that needs the cache is called, it should be built and the invalid flag
|
||||||
|
* becomes false.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Order(2)
|
||||||
|
public void testCacheBuiltByRequest() {
|
||||||
|
BlockState stoneBlock = Blocks.STONE.defaultBlockState();
|
||||||
|
stoneBlock.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
||||||
|
assertFalse(((IBlockState)stoneBlock).isCacheInvalid());
|
||||||
|
assertNotNull(stoneBlock.cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a second rebuild occurs, the invalid flag should be set to true, but the old cache
|
||||||
|
* is not set to null, in order to prevent NPEs if a second thread is accessing the cache
|
||||||
|
* when this takes place.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Order(3)
|
||||||
|
public void testCacheInvalidatedByLateRebuild() {
|
||||||
|
BlockState stoneBlock = Blocks.STONE.defaultBlockState();
|
||||||
|
stoneBlock.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
||||||
|
Blocks.rebuildCache();
|
||||||
|
assertTrue(((IBlockState)stoneBlock).isCacheInvalid());
|
||||||
|
assertNotNull(stoneBlock.cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.embeddedt.modernfix.testing.util;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@ExtendWith({ BootstrapMinecraftExtension.class })
|
||||||
|
public @interface BootstrapMinecraft {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.embeddedt.modernfix.testing.util;
|
||||||
|
|
||||||
|
import net.minecraft.DetectedVersion;
|
||||||
|
import net.minecraft.server.Bootstrap;
|
||||||
|
import org.junit.jupiter.api.extension.AfterAllCallback;
|
||||||
|
import org.junit.jupiter.api.extension.BeforeAllCallback;
|
||||||
|
import org.junit.jupiter.api.extension.Extension;
|
||||||
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple extension to run vanilla bootstrap, inspired by AE2.
|
||||||
|
*/
|
||||||
|
public class BootstrapMinecraftExtension implements Extension, BeforeAllCallback, AfterAllCallback {
|
||||||
|
@Override
|
||||||
|
public void beforeAll(ExtensionContext context) throws Exception {
|
||||||
|
DetectedVersion.tryDetectVersion();
|
||||||
|
Bootstrap.bootStrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterAll(ExtensionContext context) throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -61,17 +61,17 @@ shadowJar {
|
||||||
exclude "architectury.common.json"
|
exclude "architectury.common.json"
|
||||||
|
|
||||||
configurations = [project.configurations.shadowCommon]
|
configurations = [project.configurations.shadowCommon]
|
||||||
classifier "dev-shadow"
|
archiveClassifier.set("dev-shadow")
|
||||||
}
|
}
|
||||||
|
|
||||||
remapJar {
|
remapJar {
|
||||||
input.set shadowJar.archiveFile
|
input.set shadowJar.archiveFile
|
||||||
dependsOn shadowJar
|
dependsOn shadowJar
|
||||||
classifier null
|
archiveClassifier.set(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
classifier "dev"
|
archiveClassifier.set("dev")
|
||||||
manifest {
|
manifest {
|
||||||
attributes([
|
attributes([
|
||||||
"Specification-Title" : "modernfix",
|
"Specification-Title" : "modernfix",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
package org.embeddedt.modernfix.forge.dynresources;
|
||||||
|
|
||||||
|
import com.google.common.collect.ForwardingMap;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.common.graph.GraphBuilder;
|
||||||
|
import com.google.common.graph.MutableGraph;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraftforge.fml.ModContainer;
|
||||||
|
import net.minecraftforge.fml.ModList;
|
||||||
|
import net.minecraftforge.forgespi.language.IModInfo;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a list of all known default block/item models in the game, and provides a namespaced version
|
||||||
|
* of the model registry that emulates vanilla keySet behavior.
|
||||||
|
*/
|
||||||
|
public class ModelBakeEventHelper {
|
||||||
|
private final Map<ResourceLocation, BakedModel> modelRegistry;
|
||||||
|
private final Set<ResourceLocation> topLevelModelLocations;
|
||||||
|
private final MutableGraph<String> dependencyGraph;
|
||||||
|
public ModelBakeEventHelper(Map<ResourceLocation, BakedModel> modelRegistry) {
|
||||||
|
this.modelRegistry = modelRegistry;
|
||||||
|
this.topLevelModelLocations = new HashSet<>(modelRegistry.keySet());
|
||||||
|
for(Block block : ForgeRegistries.BLOCKS) {
|
||||||
|
for(BlockState state : block.getStateDefinition().getPossibleStates()) {
|
||||||
|
topLevelModelLocations.add(ModelLocationCache.get(state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(Item item : ForgeRegistries.ITEMS) {
|
||||||
|
topLevelModelLocations.add(ModelLocationCache.get(item));
|
||||||
|
}
|
||||||
|
this.dependencyGraph = GraphBuilder.undirected().build();
|
||||||
|
ModList.get().forEachModContainer((id, mc) -> {
|
||||||
|
this.dependencyGraph.addNode(id);
|
||||||
|
});
|
||||||
|
for(String id : this.dependencyGraph.nodes()) {
|
||||||
|
Optional<? extends ModContainer> mContainer = ModList.get().getModContainerById(id);
|
||||||
|
if(mContainer.isPresent()) {
|
||||||
|
for(IModInfo.ModVersion version : mContainer.get().getModInfo().getDependencies()) {
|
||||||
|
this.dependencyGraph.putEdge(id, version.getModId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<ResourceLocation, BakedModel> wrapRegistry(String modId) {
|
||||||
|
final Set<String> modIdsToInclude = new HashSet<>();
|
||||||
|
modIdsToInclude.add(modId);
|
||||||
|
try {
|
||||||
|
modIdsToInclude.addAll(this.dependencyGraph.adjacentNodes(modId));
|
||||||
|
} catch(IllegalArgumentException ignored) { /* sanity check */ }
|
||||||
|
modIdsToInclude.remove("minecraft");
|
||||||
|
Set<ResourceLocation> ourModelLocations = Sets.filter(this.topLevelModelLocations, loc -> modIdsToInclude.contains(loc.getNamespace()));
|
||||||
|
return new ForwardingMap<ResourceLocation, BakedModel>() {
|
||||||
|
@Override
|
||||||
|
protected Map<ResourceLocation, BakedModel> delegate() {
|
||||||
|
return modelRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ResourceLocation> keySet() {
|
||||||
|
return ourModelLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(@Nullable Object key) {
|
||||||
|
return ourModelLocations.contains(key) || super.containsKey(key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
package org.embeddedt.modernfix.forge.mixin.perf.dynamic_resources;
|
||||||
|
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraftforge.client.ForgeHooksClient;
|
||||||
|
import net.minecraftforge.client.event.ModelEvent;
|
||||||
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
|
import net.minecraftforge.fml.ModContainer;
|
||||||
|
import net.minecraftforge.fml.ModList;
|
||||||
|
import net.minecraftforge.fml.ModLoader;
|
||||||
|
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||||
|
import org.embeddedt.modernfix.forge.dynresources.ModelBakeEventHelper;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mixin(ForgeHooksClient.class)
|
||||||
|
public class ForgeHooksClientMixin {
|
||||||
|
/**
|
||||||
|
* Generate a more realistic keySet that contains every item and block model location, to help with mod compat.
|
||||||
|
*/
|
||||||
|
@Redirect(method = "onModelBake", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/fml/ModLoader;postEvent(Lnet/minecraftforge/eventbus/api/Event;)V"))
|
||||||
|
private static void postNamespacedKeySetEvent(ModLoader loader, Event event) {
|
||||||
|
if(!ModLoader.isLoadingStateValid())
|
||||||
|
return;
|
||||||
|
ModelEvent.BakingCompleted bakeEvent = ((ModelEvent.BakingCompleted)event);
|
||||||
|
ModelBakeEventHelper helper = new ModelBakeEventHelper(bakeEvent.getModels());
|
||||||
|
Method acceptEv = ObfuscationReflectionHelper.findMethod(ModContainer.class, "acceptEvent", Event.class);
|
||||||
|
ModList.get().forEachModContainer((id, mc) -> {
|
||||||
|
Map<ResourceLocation, BakedModel> newRegistry = helper.wrapRegistry(id);
|
||||||
|
ModelEvent.BakingCompleted postedEvent = new ModelEvent.BakingCompleted(bakeEvent.getModelManager(), newRegistry, bakeEvent.getModelBakery());
|
||||||
|
try {
|
||||||
|
acceptEv.invoke(mc, postedEvent);
|
||||||
|
} catch(ReflectiveOperationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,6 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
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.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import team.chisel.ctm.CTM;
|
import team.chisel.ctm.CTM;
|
||||||
import team.chisel.ctm.client.model.AbstractCTMBakedModel;
|
import team.chisel.ctm.client.model.AbstractCTMBakedModel;
|
||||||
|
|
@ -35,9 +34,9 @@ public abstract class TextureMetadataHandlerMixin implements ModernFixClientInte
|
||||||
ModernFixClient.CLIENT_INTEGRATIONS.add(this);
|
ModernFixClient.CLIENT_INTEGRATIONS.add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Redirect(method = "onModelBake", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/BakedModel;isCustomRenderer()Z"))
|
@Inject(method = "onModelBake", at = @At("HEAD"), cancellable = true, remap = false)
|
||||||
private boolean checkModelValid(BakedModel model) {
|
private void noIteration(CallbackInfo ci) {
|
||||||
return model == null || model.isCustomRenderer();
|
ci.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
# Done to increase the memory available to gradle.
|
# Done to increase the memory available to gradle.
|
||||||
org.gradle.jvmargs=-Xmx2G
|
org.gradle.jvmargs=-Xmx2G
|
||||||
|
|
||||||
|
junit_version=5.10.0-M1
|
||||||
|
|
||||||
mod_id=modernfix
|
mod_id=modernfix
|
||||||
minecraft_version=1.19.2
|
minecraft_version=1.19.2
|
||||||
enabled_platforms=fabric,forge
|
enabled_platforms=fabric,forge
|
||||||
|
|
@ -14,7 +16,7 @@ kubejs_version=1902.6.0-build.142
|
||||||
rhino_version=1902.2.2-build.268
|
rhino_version=1902.2.2-build.268
|
||||||
supported_minecraft_versions=1.19.2
|
supported_minecraft_versions=1.19.2
|
||||||
|
|
||||||
fabric_loader_version=0.14.18
|
fabric_loader_version=0.14.21
|
||||||
fabric_api_version=0.76.0+1.19.2
|
fabric_api_version=0.76.0+1.19.2
|
||||||
|
|
||||||
modmenu_version=4.1.2
|
modmenu_version=4.1.2
|
||||||
|
|
|
||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ pluginManagement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
include("test_agent")
|
||||||
include("common")
|
include("common")
|
||||||
include("fabric")
|
include("fabric")
|
||||||
include("forge")
|
include("forge")
|
||||||
|
|
|
||||||
86
test_agent/build.gradle
Normal file
86
test_agent/build.gradle
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
plugins {
|
||||||
|
//id 'com.github.johnrengelman.shadow' version '7.1.2'
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
group 'org.embeddedt'
|
||||||
|
archivesBaseName = 'modernfix-test-agent'
|
||||||
|
version '1.0'
|
||||||
|
|
||||||
|
sourceCompatibility = '1.8'
|
||||||
|
targetCompatibility = '1.8'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
|
maven {
|
||||||
|
name = 'forge'
|
||||||
|
url = 'https://maven.minecraftforge.net/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
relocate 'net.bytebuddy.agent', 'org.embeddedt.modernfix.testing.shadow.bytebuddyagent'
|
||||||
|
relocate 'org.objectweb.asm', 'org.embeddedt.modernfix.testing.shadow.asm'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
project.configurations.implementation.canBeResolved = true
|
||||||
|
configurations = [project.configurations.implementation]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
dependencies {
|
||||||
|
compileOnly "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||||
|
implementation "org.ow2.asm:asm-tree:9.1"
|
||||||
|
implementation "org.ow2.asm:asm-commons:9.1"
|
||||||
|
implementation "org.ow2.asm:asm-util:9.1"
|
||||||
|
|
||||||
|
//implementation('net.bytebuddy:byte-buddy-agent:1.12.22')
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
// ensure that the encoding is set to UTF-8, no matter what the system default is
|
||||||
|
// this fixes some edge cases with special characters not displaying correctly
|
||||||
|
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
|
||||||
|
// If Javadoc is generated, this must be specified in that task too.
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
manifest {
|
||||||
|
attributes(
|
||||||
|
"Premain-Class": "org.embeddedt.modernfix.testing.Agent",
|
||||||
|
"Can-Redefine-Classes": false,
|
||||||
|
"Can-Set-Native-Method-Prefix": false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
archiveBaseName.set('modernfix-test-agent')
|
||||||
|
archiveClassifier.set('')
|
||||||
|
archiveVersion.set('v1')
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
agentJar {
|
||||||
|
canBeConsumed = true
|
||||||
|
canBeResolved = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
artifacts {
|
||||||
|
agentJar(jar)
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
project.tasks.shadowJar.dependsOn build
|
||||||
|
defaultTasks 'shadowJar'
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package org.embeddedt.modernfix.testing;
|
||||||
|
|
||||||
|
import org.objectweb.asm.ClassReader;
|
||||||
|
import org.objectweb.asm.ClassWriter;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.ClassNode;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
|
||||||
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
|
import java.lang.instrument.IllegalClassFormatException;
|
||||||
|
import java.lang.instrument.Instrumentation;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
public class Agent {
|
||||||
|
/**
|
||||||
|
* Simple agent that transforms Fabric Loader to never mark game JARs as system libraries.
|
||||||
|
*
|
||||||
|
* Ugly, but usable workaround for <a href="https://github.com/FabricMC/fabric-loader/issues/817">issue #817</a>
|
||||||
|
* on the Loader bug tracker.
|
||||||
|
*/
|
||||||
|
public static void premain(String args, Instrumentation instrumentation) {
|
||||||
|
instrumentation.addTransformer(new ClassFileTransformer() {
|
||||||
|
@Override
|
||||||
|
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
|
||||||
|
if(className.equals("net/fabricmc/loader/impl/game/LibClassifier")) {
|
||||||
|
ClassNode node = new ClassNode();
|
||||||
|
ClassReader reader = new ClassReader(classfileBuffer);
|
||||||
|
reader.accept(node, 0);
|
||||||
|
for(MethodNode m : node.methods) {
|
||||||
|
if(m.name.equals("<init>")) {
|
||||||
|
ListIterator<AbstractInsnNode> iter = m.instructions.iterator();
|
||||||
|
int addMatches = 0;
|
||||||
|
while(iter.hasNext()) {
|
||||||
|
AbstractInsnNode n = iter.next();
|
||||||
|
if(n instanceof MethodInsnNode) {
|
||||||
|
MethodInsnNode invokeNode = (MethodInsnNode)n;
|
||||||
|
if(invokeNode.name.equals("add") && invokeNode.owner.equals("java/util/Set") && invokeNode.desc.equals("(Ljava/lang/Object;)Z")) {
|
||||||
|
addMatches++;
|
||||||
|
if(addMatches == 2) {
|
||||||
|
iter.set(new MethodInsnNode(Opcodes.INVOKESTATIC, "org/embeddedt/modernfix/testing/AgentHooks", "addLibraryWithCheck", "(Ljava/util/Set;Ljava/lang/Object;)Z", false));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||||
|
node.accept(writer);
|
||||||
|
byte[] finalArray = writer.toByteArray();
|
||||||
|
//dumpDebugClass(className, finalArray);
|
||||||
|
return finalArray;
|
||||||
|
}
|
||||||
|
return classfileBuffer;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.embeddedt.modernfix.testing;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class AgentHooks {
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes" })
|
||||||
|
public static boolean addLibraryWithCheck(Set pathSet, Object path) {
|
||||||
|
boolean shouldAdd;
|
||||||
|
if(path instanceof Path) {
|
||||||
|
shouldAdd = !((Path)path).toString().contains("minecraft-merged");
|
||||||
|
} else
|
||||||
|
shouldAdd = true;
|
||||||
|
return shouldAdd && pathSet.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user