Merge 1.20.4 into 1.20.5

This commit is contained in:
embeddedt 2024-01-11 08:02:54 -05:00
commit b55ed00748
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
54 changed files with 801 additions and 176 deletions

View File

@ -0,0 +1,60 @@
plugins {
id 'com.github.johnrengelman.shadow'
id 'java-library'
id 'com.diffplug.spotless'
}
repositories {
mavenCentral()
maven { url uri("https://maven.fabricmc.net") }
maven { url "https://maven.neoforged.net/releases" }
}
dependencies {
annotationProcessor 'com.google.auto.service:auto-service:1.1.1'
compileOnly 'com.google.auto.service:auto-service:1.1.1'
implementation 'com.google.code.gson:gson:2.10.1'
shadow 'com.google.code.gson:gson:2.10.1'
implementation 'com.google.auto:auto-common:1.2.1'
shadow 'com.google.auto:auto-common:1.2.1'
implementation 'com.google.guava:guava:21.0'
shadow 'com.google.guava:guava:21.0'
implementation project(":annotations")
shadow project(":annotations")
// Shadow annotations
implementation 'net.fabricmc:sponge-mixin:0.12.5+'
implementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
implementation 'net.minecraftforge:mergetool:1.1.7'
implementation 'net.neoforged:mergetool:2.0.2'
}
tasks.withType(JavaCompile) {
options.compilerArgs += '--enable-preview'
options.release = 17
}
shadowJar {
dependencies {
include(dependency('net.fabricmc:sponge-mixin:'))
include(dependency('net.fabricmc:fabric-loader:'))
include(dependency(':mergetool:'))
}
// shadowJar bug
include '*.jar'
include 'META-INF/services/javax.annotation.processing.Processor'
include 'org/spongepowered/asm/mixin/Mixin.class'
include 'org/fury_phoenix/**/*'
include {it.getName() == 'OnlyIn.class'}
include {it.getName() == 'Dist.class'}
include {it.getName() == 'Environment.class'}
include {it.getName() == 'EnvType.class'}
}
spotless {
java {
removeUnusedImports()
}
}
version = '1.1.4'

View File

@ -0,0 +1,190 @@
package org.fury_phoenix.mixinAp.annotation;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import net.fabricmc.api.Environment;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.annotation.IgnoreMixin;
import org.fury_phoenix.mixinAp.util.TypedAccessorMap;
import org.spongepowered.asm.mixin.Mixin;
import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
import static java.util.AbstractMap.SimpleImmutableEntry;
public class ClientMixinValidator {
private final Messager messager;
private final Elements elemUtils;
private final Types types;
private final boolean debug;
private static final TypedAccessorMap<Annotation> markers = new TypedAccessorMap<>();
private static final Map.Entry<Class<Environment>, Function<? super Environment, ?>>
FabricAccessor = new SimpleImmutableEntry<>(Environment.class, Environment::value);
private static final Map.Entry<
Class<net.minecraftforge.api.distmarker.OnlyIn>,
Function<? super net.minecraftforge.api.distmarker.OnlyIn, ?>>
ForgeAccessor = new SimpleImmutableEntry<>(
net.minecraftforge.api.distmarker.OnlyIn.class,
net.minecraftforge.api.distmarker.OnlyIn::value
);
private static final Map.Entry<
Class<net.neoforged.api.distmarker.OnlyIn>,
Function<? super net.neoforged.api.distmarker.OnlyIn, ?>>
NeoForgeAccessor = new SimpleImmutableEntry<>(
net.neoforged.api.distmarker.OnlyIn.class,
net.neoforged.api.distmarker.OnlyIn::value
);
static {
markers.put(FabricAccessor);
markers.put(ForgeAccessor);
markers.put(NeoForgeAccessor);
}
private static final Collection<String> unannotatedClasses = new HashSet<>();
public ClientMixinValidator(ProcessingEnvironment env) {
debug = Boolean.valueOf(env.getOptions().get("org.fury_phoenix.mixinAp.validator.debug"));
messager = env.getMessager();
elemUtils = env.getElementUtils();
types = env.getTypeUtils();
}
public boolean validateMixin(TypeElement annotatedMixinClass) {
return targetsClient(annotatedMixinClass) &&
(annotatedMixinClass.getAnnotation(ClientOnlyMixin.class) == null);
}
public boolean targetsClient(TypeElement annotatedMixinClass) {
return targetsClient(getTargets(annotatedMixinClass)) &&
!isIgnored(annotatedMixinClass);
}
private boolean targetsClient(Collection<?> classTargets) {
return classTargets.stream().anyMatch(this::targetsClient);
}
private boolean targetsClient(Object classTarget) {
return switch (classTarget) {
case TypeElement te ->
isClientMarked(te);
case TypeMirror tm -> {
var el = types.asElement(tm);
yield el != null ? targetsClient(el) : warn("TypeMirror of " + tm);
}
// If you're using a dollar sign in class names you are insane
case String s -> {
var te =
elemUtils.getTypeElement(toSourceString(s.split("\\$")[0]));
yield te != null ? targetsClient(te) : warn(s);
}
default ->
throw new IllegalArgumentException("Unhandled type: "
+ classTarget.getClass() + "\n" + "Stringified contents: "
+ classTarget.toString());
};
}
private boolean isClientMarked(TypeElement te) {
for (var entry : markers.entrySet()) {
var marker = te.getAnnotation(entry.getKey());
if(marker == null) continue;
return entry.getValue().apply(marker).toString().equals("CLIENT");
}
if(debug && unannotatedClasses.add(te.toString())) {
messager.printMessage(Diagnostic.Kind.WARNING,
"No marker annotations present on " + te + "!");
}
return false;
}
private boolean isIgnored(TypeElement te) {
if(te.getAnnotation(IgnoreMixin.class) != null) {
messager.printMessage(Diagnostic.Kind.WARNING,
toSourceString(te.toString()) + " is ignored!");
return true;
}
return false;
}
private boolean warn(Object o) {
messager.printMessage(Diagnostic.Kind.WARNING,
toSourceString(o.toString()) + " can't be loaded, so it is skipped!");
return false;
}
public Map.Entry<? extends CharSequence, ? extends CharSequence>
getClientMixinEntry(TypeElement annotatedMixinClass) {
return new SimpleImmutableEntry<>(
annotatedMixinClass.getQualifiedName(),
getTargets(annotatedMixinClass)
.stream()
.filter(this::targetsClient)
.map(Object::toString)
.map(ClientMixinValidator::toSourceString)
.collect(Collectors.joining(", "))
);
}
private Collection<Object> getTargets(TypeElement annotatedMixinClass) {
Collection<? extends TypeMirror> clzsses = Set.of();
Collection<? extends String> imaginaries = Set.of();
TypeMirror MixinElement = elemUtils.getTypeElement(Mixin.class.getName()).asType();
for (var mirror : annotatedMixinClass.getAnnotationMirrors()) {
if(!types.isSameType(mirror.getAnnotationType(), MixinElement))
continue;
@SuppressWarnings("unchecked")
var wrappedClzss = (List<? extends AnnotationValue>)
getAnnotationValue(mirror, "value").getValue();
clzsses = wrappedClzss.stream()
.map(AnnotationValue::getValue)
.filter(o -> o instanceof TypeMirror)
.map(TypeMirror.class::cast)
.collect(Collectors.toSet());
@SuppressWarnings("unchecked")
var wrappedStrings = (List<? extends AnnotationValue>)
getAnnotationValue(mirror, "targets").getValue();
imaginaries = wrappedStrings.stream()
.map(AnnotationValue::getValue)
.map(String.class::cast)
.collect(Collectors.toSet());
}
return Stream.of(clzsses, imaginaries)
.flatMap(Collection::stream)
.collect(Collectors.toSet());
}
public static String toSourceString(String bytecodeName) {
return bytecodeName.replaceAll("\\/", ".");
}
}

View File

@ -0,0 +1,115 @@
package org.fury_phoenix.mixinAp.annotation;
import com.google.auto.service.AutoService;
import com.google.common.base.Throwables;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import org.fury_phoenix.mixinAp.config.MixinConfig;
@SupportedAnnotationTypes({"org.spongepowered.asm.mixin.Mixin", "org.embeddedt.modernfix.annotation.ClientOnlyMixin"})
@SupportedOptions({"rootProject.name", "project.name", "org.fury_phoenix.mixinAp.validator.debug"})
@SupportedSourceVersion(SourceVersion.RELEASE_17)
@AutoService(Processor.class)
public class MixinProcessor extends AbstractProcessor {
// Remember to call toString when using aliases
private static final Map<String, String> aliases = Map.of(
"Mixin", "mixins",
"ClientOnlyMixin", "client"
);
private final Map<String, List<String>> mixinConfigList = new HashMap<>();
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
try {
if(roundEnv.processingOver()){
filterMixinSets();
// create record for serialization, compute package name
String packageName = Optional.ofNullable(mixinConfigList.get("mixins"))
.orElse(mixinConfigList.get("client"))
.get(0).split("(?<=mixin)")[0];
finalizeMixinConfig();
new MixinConfig(packageName,
mixinConfigList.get("mixins"),
mixinConfigList.get("client")
).generateMixinConfig(processingEnv);
} else {
processMixins(annotations, roundEnv);
}
} catch (Exception e) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Fatal error:" +
Throwables.getStackTraceAsString(e));
throw new RuntimeException(e);
// Halt the AP to prevent nonsense errors
}
return false;
}
private void processMixins(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (TypeElement annotation : annotations) {
Set<? extends Element> annotatedMixins = roundEnv.getElementsAnnotatedWith(annotation);
Stream<TypeElement> mixinStream =
annotatedMixins.stream()
.map(TypeElement.class::cast);
validateCommonMixins(annotation, mixinStream);
List<String> mixins =
annotatedMixins.stream()
.map(TypeElement.class::cast)
.map(TypeElement::toString)
.collect(Collectors.toList());
mixinConfigList.putIfAbsent(aliases.get(annotation.getSimpleName().toString()), mixins);
}
}
private void filterMixinSets() {
List<String> commonSet = mixinConfigList.get("mixins");
if(commonSet == null) return;
commonSet.removeAll(mixinConfigList.get("client"));
}
private void validateCommonMixins(TypeElement annotation, Stream<TypeElement> mixins) {
if(!annotation.getSimpleName().toString().equals("Mixin"))
return;
ClientMixinValidator validator = new ClientMixinValidator(processingEnv);
// The implementation may throw a CME
mixins.sequential()
.filter(validator::validateMixin)
.map(validator::getClientMixinEntry)
.forEach(this::logClientClassTarget);
}
private void logClientClassTarget(Map.Entry<? extends CharSequence, ? extends CharSequence> mixin) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
"Mixin " + mixin.getKey() + " targets client-side classes: " + mixin.getValue());
}
private void finalizeMixinConfig() {
// relativize class names
for(var list : mixinConfigList.values()) {
list.replaceAll(className -> className.split("(?<=mixin.)")[1]);
}
}
}

View File

@ -0,0 +1,65 @@
package org.fury_phoenix.mixinAp.config;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import java.util.Optional;
import javax.annotation.processing.ProcessingEnvironment;
import javax.tools.StandardLocation;
public record MixinConfig(
boolean required,
String minVersion,
@SerializedName("package")
String packageName,
String plugin,
String compatabilityLevel,
@SerializedName("mixins")
List<String> commonMixins,
@SerializedName("client")
List<String> clientMixins,
InjectorOptions injectors, OverwriteOptions overwrites
) {
public MixinConfig(String packageName, List<String> commonMixins, List<String> clientMixins) {
this(true, "0.8", packageName, "org.embeddedt.modernfix.core.ModernFixMixinPlugin", "JAVA_8",
commonMixins, clientMixins, InjectorOptions.DEFAULT, OverwriteOptions.DEFAULT);
}
public record InjectorOptions(int defaultRequire) {
public static final InjectorOptions DEFAULT = new InjectorOptions(1);
}
public record OverwriteOptions(boolean conformVisibility) {
public static final OverwriteOptions DEFAULT = new OverwriteOptions(true);
}
public void generateMixinConfig(ProcessingEnvironment env) throws IOException {
try (
Writer mixinConfigWriter = env.getFiler()
.createResource(StandardLocation.SOURCE_OUTPUT, "",
MixinConfig.computeMixinConfigPath(
Optional.of(env.getOptions().get("rootProject.name")),
Optional.ofNullable(env.getOptions().get("project.name"))
)
).openWriter()
) {
String mixinConfig = new GsonBuilder()
.setPrettyPrinting()
.create()
.toJson(this);
mixinConfigWriter.write(mixinConfig);
mixinConfigWriter.write("\n");
} catch (IOException e) { throw e; }
}
private static String computeMixinConfigPath(Optional<String> rootProjectName, Optional<String> projectName) {
return "resources/" +
rootProjectName.get() +
(projectName.isPresent() ? "-" : "") +
projectName.orElse("") +
".mixins.json";
}
}

View File

@ -0,0 +1,37 @@
package org.fury_phoenix.mixinAp.util;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import static java.util.Map.Entry;
/**
* Type-safe heterogenous map of accessors
* @author Fury_Phoenix
* @reason Type-safety since K, V of Map are non-identical
* @param <SuperType> The supertype of desired types.
* This is useful in cases such as <A extends Annotation>.
*/
public class TypedAccessorMap<SuperType> {
private final Map<Class<? extends SuperType>, Function<Object, ?>> typedAccessors = new HashMap<>();
public <T extends SuperType> void put(Class<T> key, Function<? super T, ?> func) {
Objects.requireNonNull(func);
typedAccessors.put(Objects.requireNonNull(key), o -> func.apply(key.cast(o)));
}
public <T extends SuperType> void put(Entry<Class<T>, Function<? super T, ?>> entry) {
put(entry.getKey(), entry.getValue());
}
public <T extends SuperType> Function<Object, ?> get(Class<T> key) {
return typedAccessors.get(key);
}
public Set<Entry<Class<? extends SuperType>, Function<Object, ?>>> entrySet() {
return typedAccessors.entrySet();
}
}

6
annotations/build.gradle Normal file
View File

@ -0,0 +1,6 @@
plugins {
id 'modernfix.common-conventions'
id 'java-library'
}
version = '1.1.0'

View File

@ -0,0 +1,11 @@
package org.embeddedt.modernfix.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface IgnoreMixin {
}

View File

@ -7,7 +7,7 @@ plugins {
id 'org.ajoberstar.grgit' version '5.2.0'
id 'se.bjurr.gitchangelog.git-changelog-gradle-plugin' version '1.79.0'
id "com.modrinth.minotaur" version "2.+" apply false
id("com.diffplug.spotless") version "6.18.0" apply false
id("com.diffplug.spotless") version "6.18.0" apply false
id 'modernfix.common-conventions' apply false
}

View File

@ -1,3 +1,3 @@
plugins {
id 'groovy-gradle-plugin'
}
}

View File

@ -77,5 +77,4 @@ repositories {
maven {
url 'https://maven.terraformersmc.com/releases'
}
maven { url = "https://jitpack.io" }
}
}

View File

@ -16,37 +16,26 @@ dependencies {
parchment("org.parchmentmc.data:parchment-${minecraft_version}:${parchment_version}@zip")
}
}
implementation project(":annotations")
annotationProcessor project(path: ":annotation-processor", configuration: 'shadow')
}
project.sourceSets {
main.resources.srcDirs += [layout.buildDirectory.dir("generated/sources/annotationProcessor/java/main/resources")]
}
// hack to shut up gradle about the hack to include generated resources
tasks {
processResources {
def mixinFileList = []
def mixinDirectory = file("src/main/java/org/embeddedt/modernfix/" + project.name + "/mixin")
fileTree(mixinDirectory).visit { FileVisitDetails details ->
if(details.file.isFile()) {
def fileName = mixinDirectory.relativePath(details.file).toString().replaceFirst(/\.java$/, "").replace('/', '.')
mixinFileList << fileName
}
}
dependsOn compileJava
}
}
def mixinClassesStringB = new StringBuilder()
for(int i = 0; i < mixinFileList.size(); i++) {
mixinClassesStringB.append(" \"")
mixinClassesStringB.append(mixinFileList.get(i))
mixinClassesStringB.append('"')
if(i < (mixinFileList.size() - 1))
mixinClassesStringB.append(',')
mixinClassesStringB.append('\n')
}
def replacements = [
mixin_classes: mixinClassesStringB.toString()
]
inputs.properties replacements
def filePattern = "modernfix-" + project.name + ".mixins.json"
filesMatching(filePattern) {
expand replacements
tasks.withType(JavaCompile) {
options.fork = true
options.forkOptions.jvmArgs << '--enable-preview'
configure(options) {
if (!name.toLowerCase().contains('test')) {
options.compilerArgs << "-ArootProject.name=${rootProject.name}" << "-Aproject.name=${project.name}"
}
}
}
}

View File

@ -7,6 +7,7 @@ import net.minecraft.client.gui.font.providers.GlyphProviderDefinition;
import net.minecraft.client.gui.font.providers.UnihexProvider;
import net.minecraft.server.packs.resources.ResourceManager;
import org.embeddedt.modernfix.ModernFix;
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;
@ -16,6 +17,7 @@ import java.io.IOException;
import java.lang.reflect.Constructor;
@Mixin(UnihexProvider.Definition.class)
@ClientOnlyMixin
public class UnihexProviderDefinitionMixin {
@Inject(method = "unpack", at = @At("HEAD"), cancellable = true)
private void disableProvider(CallbackInfoReturnable<Either<GlyphProviderDefinition.Loader, GlyphProviderDefinition.Reference>> cir) {

View File

@ -7,6 +7,7 @@ import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.dynamicresources.DynamicModelCache;
import org.embeddedt.modernfix.dynamicresources.ModelLocationCache;
import org.embeddedt.modernfix.util.DynamicInt2ObjectMap;
@ -19,6 +20,7 @@ import java.util.HashMap;
import java.util.Map;
@Mixin(ItemModelShaper.class)
@ClientOnlyMixin
public abstract class ItemModelShaperMixin {
@Shadow public abstract ModelManager getModelManager();

View File

@ -0,0 +1,26 @@
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 = "bakeModel", 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);
}
}
}

View File

@ -11,6 +11,7 @@ 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.util.LambdaMap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@ -26,6 +27,7 @@ import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@Mixin(ModelManager.class)
@ClientOnlyMixin
public class ModelManagerMixin {
@Redirect(method = "reload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/resources/model/ModelManager;loadBlockModels(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
private CompletableFuture<Map<ResourceLocation, BlockModel>> deferBlockModelLoad(ResourceManager manager, Executor executor) {

View File

@ -1,6 +1,7 @@
package org.embeddedt.modernfix.common.mixin.perf.faster_item_rendering;
import net.minecraft.client.renderer.GameRenderer;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.render.RenderState;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@ -8,6 +9,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameRenderer.class)
@ClientOnlyMixin
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))
private void markRenderingLevel(CallbackInfo ci) {

View File

@ -11,6 +11,7 @@ import net.minecraft.client.resources.model.SimpleBakedModel;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.ItemStack;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.render.FastItemRenderType;
import org.embeddedt.modernfix.render.RenderState;
import org.embeddedt.modernfix.render.SimpleItemModelView;
@ -21,6 +22,7 @@ import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = ItemRenderer.class, priority = 600)
@ClientOnlyMixin
public abstract class ItemRendererMixin {
private ItemDisplayContext transformType;
private final SimpleItemModelView modelView = new SimpleItemModelView();

View File

@ -26,6 +26,8 @@ import java.util.*;
import java.util.function.BooleanSupplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class ModernFixEarlyConfig {
private static final Logger LOGGER = LogManager.getLogger("ModernFixConfig");
@ -85,7 +87,10 @@ public class ModernFixEarlyConfig {
continue;
try(Reader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
JsonObject configObject = (JsonObject)new JsonParser().parse(reader);
JsonArray mixinList = configObject.getAsJsonArray("mixins");
List<JsonElement> mixinList = Stream.of("mixins", "client")
.map(key -> Optional.ofNullable(configObject.getAsJsonArray(key)))
.flatMap(arr -> arr.map(jsonElements -> StreamSupport.stream(jsonElements.spliterator(), false)).orElseGet(Stream::of))
.collect(Collectors.toList());
String packageName = configObject.get("package").getAsString().replace('.', '/');
for(JsonElement mixin : mixinList) {
mixinPaths.add(packageName + "/" + mixin.getAsString().replace('.', '/') + ".class");
@ -212,6 +217,7 @@ public class ModernFixEarlyConfig {
disableIfModPresent("mixin.bugfix.remove_block_chunkloading", "performant");
disableIfModPresent("mixin.bugfix.paper_chunk_patches", "c2me");
disableIfModPresent("mixin.bugfix.preserve_early_window_pos", "better_loading_screen");
disableIfModPresent("mixin.perf.dynamic_dfu", "litematica");
disableIfModPresent("mixin.perf.cache_strongholds", "littletiles", "c2me");
// content overlap
disableIfModPresent("mixin.perf.deduplicate_wall_shapes", "dashloader");

View File

@ -3,6 +3,7 @@ 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
*/
void throwOnMissingModel();
boolean throwOnMissingModel(boolean flag);
}

View File

@ -59,7 +59,7 @@ public class ModelBakeryHelpers {
}
private static <T extends Comparable<T>> T getValueHelper(Property<T> property, String value) {
return property.getValue(value).orElse((T) null);
return property.getValue(value).orElse(null);
}
private static final Splitter COMMA_SPLITTER = Splitter.on(',');

View File

@ -2,7 +2,9 @@ package org.embeddedt.modernfix.packet;
import com.mojang.datafixers.util.Pair;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import org.embeddedt.modernfix.ModernFix;
@ -10,7 +12,8 @@ import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
public class EntityIDSyncPacket {
public class EntityIDSyncPacket implements CustomPacketPayload {
public static final ResourceLocation ID = new ResourceLocation(ModernFix.MODID, "entity_id_sync");
private Map<Class<? extends Entity>, List<Pair<String, Integer>>> map;
public EntityIDSyncPacket(Map<Class<? extends Entity>, List<Pair<String, Integer>>> map) {
@ -21,11 +24,8 @@ public class EntityIDSyncPacket {
return this.map;
}
public EntityIDSyncPacket() {
this.map = new HashMap<>();
}
public void serialize(FriendlyByteBuf buf) {
@Override
public void write(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());
@ -38,8 +38,8 @@ public class EntityIDSyncPacket {
}
@SuppressWarnings("unchecked")
public static EntityIDSyncPacket deserialize(FriendlyByteBuf buf) {
EntityIDSyncPacket self = new EntityIDSyncPacket();
public EntityIDSyncPacket(FriendlyByteBuf buf) {
EntityIDSyncPacket self = this;
int numEntityClasses = buf.readVarInt();
for(int i = 0; i < numEntityClasses; i++) {
String clzName = buf.readUtf();
@ -73,6 +73,10 @@ public class EntityIDSyncPacket {
ModernFix.LOGGER.error("Error deserializing packet", e);
}
}
return self;
}
@Override
public ResourceLocation id() {
return ID;
}
}

View File

@ -4,6 +4,7 @@ import com.google.common.collect.Multimap;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.client.searchtree.SearchRegistry;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
@ -39,7 +40,7 @@ public interface ModernFixPlatformHooks {
Path getGameDirectory();
void sendPacket(ServerPlayer player, Object packet);
void sendPacket(ServerPlayer player, CustomPacketPayload packet);
Multimap<String, String> getCustomModOptions();

View File

@ -15,9 +15,8 @@ public class RegistryStorage {
public static <T> BiMap<ResourceKey<T>, DirectStorageRegistryObject> createKeyStorage(ResourceKey<? extends Registry<T>> registryKey, BiMap<ResourceLocation, DirectStorageRegistryObject> storage) {
if(storage instanceof DirectStorageBiMap) {
DirectStorageBiMap<ResourceLocation, DirectStorageRegistryObject> directStorageBiMap = (DirectStorageBiMap<ResourceLocation, DirectStorageRegistryObject>)storage;
// silently ignore put/putAll calls on this map
return new TransformingBiMap<ResourceLocation, DirectStorageRegistryObject, ResourceKey<T>, DirectStorageRegistryObject>(directStorageBiMap, loc -> ResourceKey.create(registryKey, loc), ResourceKey::location, Function.identity(), Function.identity()) {
return new TransformingBiMap<ResourceLocation, DirectStorageRegistryObject, ResourceKey<T>, DirectStorageRegistryObject>(storage, loc -> ResourceKey.create(registryKey, loc), ResourceKey::location, Function.identity(), Function.identity()) {
@Override
public DirectStorageRegistryObject put(ResourceKey<T> key, DirectStorageRegistryObject value) {
return null;

View File

@ -3,16 +3,22 @@ package org.embeddedt.modernfix.util;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.logging.ILogger;
import org.spongepowered.asm.logging.LoggerAdapterDefault;
import org.spongepowered.asm.mixin.MixinEnvironment;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import org.spongepowered.asm.mixin.transformer.ClassInfo;
import org.spongepowered.asm.service.MixinServiceAbstract;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class ClassInfoManager {
private static boolean hasRun = false;
private static final List<Runnable> loggersToRestore = new ArrayList<>();
public static void clear() {
if (!ModernFixMixinPlugin.instance.isOptionEnabled("perf.clear_mixin_classinfo.ClassInfoManager") || hasRun)
return;
@ -25,11 +31,33 @@ public class ClassInfoManager {
return f;
}
private static void changeLoggerAndRestoreLater(Map<String, ILogger> map, ILogger newLogger) {
ILogger oldLogger = map.put("mixin.audit", newLogger);
loggersToRestore.add(() -> map.put("mixin.audit", oldLogger));
}
private static void disableLoggers() throws ReflectiveOperationException {
// Disable default audit logger
Field loggersField = accessible(MixinServiceAbstract.class.getDeclaredField("loggers"));
changeLoggerAndRestoreLater((Map<String, ILogger>)loggersField.get(null), new LoggerAdapterDefault("mixin.audit"));
Class<?> fabricLogger = null;
try {
fabricLogger = Class.forName("net.fabricmc.loader.impl.knot.MixinLogger");
} catch(Throwable e) {
// Probably not Fabric
return;
}
// Disable Fabric audit logger
loggersField = accessible(fabricLogger.getDeclaredField("LOGGER_MAP"));
changeLoggerAndRestoreLater((Map<String, ILogger>)loggersField.get(null), new LoggerAdapterDefault("mixin.audit"));
}
private static void doClear() {
Map<String, ClassInfo> classInfoCache;
Field mixinField, stateField, classNodeField, methodsField, fieldsField;
Class<?> stateClz;
try {
disableLoggers();
Field field = accessible(ClassInfo.class.getDeclaredField("cache"));
classInfoCache = (Map<String, ClassInfo>) field.get(null);
mixinField = accessible(ClassInfo.class.getDeclaredField("mixin"));
@ -70,6 +98,9 @@ public class ClassInfoManager {
} catch (RuntimeException e) {
e.printStackTrace();
}
// Put back the old logger
loggersToRestore.forEach(Runnable::run);
loggersToRestore.clear();
ModernFix.LOGGER.warn("Cleared mixin data structures");
}
}

View File

@ -1,16 +0,0 @@
{
"required": true,
"minVersion": "0.8",
"package": "org.embeddedt.modernfix.common.mixin",
"plugin": "org.embeddedt.modernfix.core.ModernFixMixinPlugin",
"compatibilityLevel": "JAVA_17",
"mixins": [
${mixin_classes}
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"conformVisibility": true
}
}

View File

@ -1,5 +1,5 @@
plugins {
id "com.github.johnrengelman.shadow" version "7.1.2"
id "com.github.johnrengelman.shadow"
id 'com.adarshr.test-logger' version '3.2.0'
id "modernfix.mod-common-conventions"
id "modernfix.platform-conventions"
@ -63,6 +63,7 @@ dependencies {
common(project(path: ":common", configuration: "namedElements")) { transitive false }
testImplementation(shadowCommon(project(path: ":common", configuration: "transformProductionFabric"))) { transitive false }
shadowCommon(project(path: ":annotations"))
testImplementation(platform("org.junit:junit-bom:${project.junit_version}"))
testImplementation("org.junit.jupiter:junit-jupiter-api")

View File

@ -2,12 +2,14 @@ package org.embeddedt.modernfix.fabric.mixin.core;
import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl;
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(ClientCommonPacketListenerImpl.class)
@ClientOnlyMixin
public class ClientCommonPacketListenerImplMixin {
@Inject(method = "handleUpdateTags", at = @At("RETURN"))
private void signalTags(CallbackInfo ci) {

View File

@ -0,0 +1,31 @@
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);
}
}
}

View File

@ -11,6 +11,7 @@ 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;
@ -29,6 +30,7 @@ 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;
@ -57,8 +59,10 @@ public abstract class ModelBakerImplMixin implements IExtendedModelBaker {
private boolean throwIfMissing;
@Override
public void throwOnMissingModel() {
throwIfMissing = true;
public boolean throwOnMissingModel(boolean flag) {
boolean old = throwIfMissing;
throwIfMissing = flag;
return old;
}
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)

View File

@ -310,7 +310,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
return m;
ModelBakery self = (ModelBakery) (Object) this;
ModelBaker theBaker = self.new ModelBakerImpl(textureGetter, modelLocation);
((IExtendedModelBaker)theBaker).throwOnMissingModel();
((IExtendedModelBaker)theBaker).throwOnMissingModel(true);
synchronized(this) { m = theBaker.bake(modelLocation, state); }
if(m != null)
loadedBakedModels.put(key, m);

View File

@ -11,6 +11,7 @@ import net.fabricmc.loader.api.metadata.CustomValue;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.minecraft.client.searchtree.SearchRegistry;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.CreativeModeTabs;
@ -72,7 +73,7 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks {
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);
}

View File

@ -1,16 +0,0 @@
{
"required": true,
"package": "org.embeddedt.modernfix.fabric.mixin",
"plugin": "org.embeddedt.modernfix.core.ModernFixMixinPlugin",
"compatibilityLevel": "JAVA_8",
"minVersion": "0.8",
"mixins": [
${mixin_classes}
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"conformVisibility": true
}
}

View File

@ -41,3 +41,5 @@ processResources {
}
}
// Make genSources do nothing in this project
project.gradle.startParameter.excludedTaskNames.add("genSources")

View File

@ -5,6 +5,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.world.level.ChunkPos;
import net.minecraftforge.client.model.data.ModelDataManager;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
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;
@ -12,6 +13,7 @@ import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
@ -24,12 +26,14 @@ import java.util.function.Function;
public abstract class ModelDataManagerMixin {
@Shadow protected abstract void refreshAt(ChunkPos chunk);
@Shadow @Final private Map<ChunkPos, Set<BlockPos>> needModelDataRefresh;
/**
* Make the set of positions to refresh a real concurrent hash set rather than relying on synchronizedSet,
* because the returned iterator won't be thread-safe otherwise. See https://github.com/AppliedEnergistics/Applied-Energistics-2/issues/7511
*/
@ModifyArg(method = "requestRefresh", at = @At(value = "INVOKE", target = "Ljava/util/Map;computeIfAbsent(Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;", ordinal = 0), index = 1, remap = false)
private static Function<ChunkPos, Set<BlockPos>> changeTypeOfSetUsed(Function<ChunkPos, Set<BlockPos>> mappingFunction) {
private Function<ChunkPos, Set<BlockPos>> changeTypeOfSetUsed(Function<ChunkPos, Set<BlockPos>> mappingFunction) {
return pos -> Collections.newSetFromMap(new ConcurrentHashMap<>());
}
@ -37,7 +41,8 @@ public abstract class ModelDataManagerMixin {
private void onlyRefreshOnMainThread(ModelDataManager instance, ChunkPos pos) {
// Only refresh model data on the main thread. This prevents calling getBlockEntity from worker threads
// which could cause weird CMEs or other behavior.
if(Minecraft.getInstance().isSameThread()) {
// Avoid the loop if no model data needs to be refreshed, to prevent unnecessary allocation.
if(Minecraft.getInstance().isSameThread() && !needModelDataRefresh.isEmpty()) {
// Refresh the given chunk, and all its neighbors. This is less efficient than the default code
// but we have no choice since we need to not do refreshing on workers, and blocks might
// try to access model data in neighboring chunks.

View File

@ -0,0 +1,24 @@
package org.embeddedt.modernfix.forge.mixin.perf.forge_cap_retrieval;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.LivingEntity;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import javax.annotation.Nullable;
@Mixin(LivingEntity.class)
public class LivingEntityMixin {
/**
* @author embeddedt (issue noted by XFactHD)
* @reason check capability equality before checking that entity is alive, the latter requires a lot more
* indirection
*/
@Redirect(method = "getCapability", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;isAlive()Z"))
private <T> boolean checkAliveAfterCap(LivingEntity entity, Capability<T> capability, @Nullable Direction facing) {
return capability == ForgeCapabilities.ITEM_HANDLER && entity.isAlive();
}
}

View File

@ -2,12 +2,12 @@
org.gradle.jvmargs=-Xmx2G
junit_version=5.10.0-M1
mixinextras_version=0.3.1
mixinextras_version=0.3.2
mod_id=modernfix
minecraft_version=23w51a
enabled_platforms=fabric
forge_version=20.4.22-beta
forge_version=20.4.70-beta
# parchment_version=2023.07.09
refined_storage_version=4392788
jei_version=16.0.0.28
@ -28,3 +28,6 @@ diagonal_fences_version=4558828
spark_version=4587310
use_fabric_api_at_runtime=false
# Look up maven coordinates when changing shadow_version
shadow_version=7.1.2

View File

@ -1,5 +1,5 @@
plugins {
id "com.github.johnrengelman.shadow" version "7.1.2"
id "com.github.johnrengelman.shadow"
id "modernfix.mod-common-conventions"
id "modernfix.platform-conventions"
}
@ -35,8 +35,7 @@ repositories {
dependencies {
neoForge "net.neoforged:neoforge:${rootProject.forge_version}"
//implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-common:${rootProject.mixinextras_version}"))
//implementation(include("com.github.llamalad7.mixinextras:mixinextras-forge:${rootProject.mixinextras_version}"))
// Remove the next line if you don't want to depend on the API
// modApi "me.shedaniel:architectury-forge:${rootProject.architectury_version}"
@ -65,6 +64,8 @@ dependencies {
common(project(path: ":common", configuration: "namedElements")) { transitive false }
shadowCommon(project(path: ":common", configuration: "transformProductionNeoForge")) { transitive = false }
shadowCommon(project(path: ":annotations"))
forgeRuntimeLibrary(project(path: ":annotations"))
}
processResources {

View File

@ -21,6 +21,7 @@ import org.embeddedt.modernfix.util.ForwardingInclDefaultsMap;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiFunction;
/**
* Stores a list of all known default block/item models in the game, and provides a namespaced version
@ -28,7 +29,12 @@ import java.util.*;
*/
public class ModelBakeEventHelper {
// TODO: make into config option
private static final Set<String> INCOMPATIBLE_MODS = ImmutableSet.of("industrialforegoing", "vampirism", "elevatorid", "embers");
private static final Set<String> INCOMPATIBLE_MODS = ImmutableSet.of(
"industrialforegoing",
"mekanism",
"vampirism",
"elevatorid",
"embers");
private final Map<ResourceLocation, BakedModel> modelRegistry;
private final Set<ResourceLocation> topLevelModelLocations;
private final MutableGraph<String> dependencyGraph;
@ -79,7 +85,7 @@ public class ModelBakeEventHelper {
private void logWarning() {
if(!WARNED_MOD_IDS.add(modId))
return;
ModernFix.LOGGER.warn("Mod '{}' is accessing Map#keySet/entrySet/values on the model registry map inside its event handler." +
ModernFix.LOGGER.warn("Mod '{}' is accessing Map#keySet/entrySet/values/replaceAll on the model registry map inside its event handler." +
" This probably won't work as expected with dynamic resources on. Prefer using Map#get/put and constructing ModelResourceLocations another way.", modId);
}
@ -100,6 +106,12 @@ public class ModelBakeEventHelper {
logWarning();
return super.values();
}
@Override
public void replaceAll(BiFunction<? super ResourceLocation, ? super BakedModel, ? extends BakedModel> function) {
logWarning();
super.replaceAll(function);
}
};
}
@ -139,6 +151,32 @@ public class ModelBakeEventHelper {
public boolean containsKey(@Nullable Object key) {
return ourModelLocations.contains(key) || super.containsKey(key);
}
@Override
public void replaceAll(BiFunction<? super ResourceLocation, ? super BakedModel, ? extends BakedModel> function) {
ModernFix.LOGGER.warn("Mod '{}' is calling replaceAll on the model registry. Some hacks will be used to keep this fast, but they may not be 100% compatible.", modId);
List<ResourceLocation> locations = new ArrayList<>(keySet());
for(ResourceLocation location : locations) {
/*
* Fetching every model is insanely slow. So we call the function with a null object first, since it
* probably isn't expecting that. If we get an exception thrown, or it returns nonnull, then we know
* it actually cares about the given model.
*/
boolean needsReplacement;
try {
needsReplacement = function.apply(location, null) != null;
} catch(Throwable e) {
needsReplacement = true;
}
if(needsReplacement) {
BakedModel existing = get(location);
BakedModel replacement = function.apply(location, existing);
if(replacement != existing) {
put(location, replacement);
}
}
}
}
};
}
}

View File

@ -1,15 +1,9 @@
package org.embeddedt.modernfix.neoforge.mixin.bugfix.recipe_book_type_desync;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.stats.RecipeBookSettings;
import net.minecraft.world.inventory.RecipeBookType;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
import org.embeddedt.modernfix.neoforge.packet.NetworkUtils;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@ -36,12 +30,14 @@ public class RecipeBookSettingsMixin {
}
mfix$maxVanillaOrdinal = ord;
}
/*
@Redirect(method = "read(Lnet/minecraft/network/FriendlyByteBuf;)Lnet/minecraft/stats/RecipeBookSettings;", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/FriendlyByteBuf;readBoolean()Z"))
private static boolean useDefaultBooleanIfVanilla(FriendlyByteBuf buf, @Local(ordinal = 0) RecipeBookType type) {
if(type.ordinal() >= (mfix$maxVanillaOrdinal + 1) && NetworkUtils.isCurrentlyVanilla) {
if(type.ordinal() >= (mfix$maxVanillaOrdinal + 1)) {
ModernFix.LOGGER.warn("Not reading recipe book data for type '{}' as we are using vanilla connection", type.name());
return false; // skip actually reading buffer
}
return buf.readBoolean();
}
*/
}

View File

@ -1,7 +1,6 @@
package org.embeddedt.modernfix.neoforge.mixin.core;
import net.minecraft.server.Bootstrap;
import net.neoforged.neoforge.network.NetworkConstants;
import org.embeddedt.modernfix.util.TimeFormatter;
import org.slf4j.Logger;
import org.spongepowered.asm.mixin.Final;
@ -25,10 +24,4 @@ public class BootstrapMixin {
LOGGER.info("ModernFix reached bootstrap stage ({} after launch)", TimeFormatter.formatNanos(ManagementFactory.getRuntimeMXBean().getUptime() * 1000L * 1000L));
}
}
/* for https://github.com/MinecraftForge/MinecraftForge/issues/9505 */
@Inject(method = "bootStrap", at = @At("RETURN"))
private static void doClassloadHack(CallbackInfo ci) {
NetworkConstants.init();
}
}

View File

@ -1,17 +0,0 @@
package org.embeddedt.modernfix.neoforge.mixin.core;
import net.minecraft.network.Connection;
import net.neoforged.neoforge.network.NetworkHooks;
import org.embeddedt.modernfix.neoforge.packet.NetworkUtils;
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(NetworkHooks.class)
public abstract class NetworkHooksMixin {
@Inject(method = "handleClientLoginSuccess", at = @At("RETURN"), remap = false)
private static void setVanillaGlobalFlag(Connection manager, CallbackInfo ci) {
NetworkUtils.isCurrentlyVanilla = NetworkHooks.isVanillaConnection(manager);
}
}

View File

@ -1,5 +1,6 @@
package org.embeddedt.modernfix.neoforge.mixin.perf.dynamic_resources;
import com.google.common.base.Stopwatch;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.bus.api.Event;
@ -9,6 +10,7 @@ import net.neoforged.fml.ModLoader;
import net.neoforged.fml.util.ObfuscationReflectionHelper;
import net.neoforged.neoforge.client.ClientHooks;
import net.neoforged.neoforge.client.event.ModelEvent;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.neoforge.dynresources.ModelBakeEventHelper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@ -16,6 +18,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Mixin(ClientHooks.class)
public class ForgeHooksClientMixin {
@ -32,11 +35,16 @@ public class ForgeHooksClientMixin {
ModList.get().forEachModContainer((id, mc) -> {
Map<ResourceLocation, BakedModel> newRegistry = helper.wrapRegistry(id);
ModelEvent.ModifyBakingResult postedEvent = new ModelEvent.ModifyBakingResult(newRegistry, bakeEvent.getModelBakery());
Stopwatch timer = Stopwatch.createStarted();
try {
acceptEv.invoke(mc, postedEvent);
} catch(ReflectiveOperationException e) {
e.printStackTrace();
}
timer.stop();
if(timer.elapsed(TimeUnit.SECONDS) >= 1) {
ModernFix.LOGGER.warn("Mod '{}' took {} in the model bake event", id, timer);
}
});
}
}

View File

@ -0,0 +1,34 @@
package org.embeddedt.modernfix.neoforge.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.renderer.texture.TextureAtlasSprite;
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;
import java.util.function.Function;
@Mixin(ItemOverrides.class)
@ClientOnlyMixin
public class ItemOverridesForgeMixin {
/**
* @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;Ljava/util/function/Function;)Lnet/minecraft/client/resources/model/BakedModel;"), remap = false)
private BakedModel bake(ModelBaker instance, ResourceLocation resourceLocation, ModelState modelState, Function<ResourceLocation, TextureAtlasSprite> spriteGetter, Operation<BakedModel> original) {
boolean prevState = ((IExtendedModelBaker)instance).throwOnMissingModel(false);
try {
return original.call(instance, resourceLocation, modelState, spriteGetter);
} finally {
((IExtendedModelBaker)instance).throwOnMissingModel(prevState);
}
}
}

View File

@ -8,6 +8,7 @@ import net.minecraft.client.resources.model.*;
import net.minecraft.resources.ResourceLocation;
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;
@ -23,6 +24,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.function.Function;
@Mixin(value = ModelBakery.ModelBakerImpl.class, priority = 600)
@ClientOnlyMixin
public abstract class ModelBakerImplMixin implements IModelBakerImpl, IExtendedModelBaker {
private static final boolean debugDynamicModelLoading = Boolean.getBoolean("modernfix.debugDynamicModelLoading");
@Shadow @Final private ModelBakery field_40571;
@ -39,8 +41,10 @@ public abstract class ModelBakerImplMixin implements IModelBakerImpl, IExtendedM
private boolean throwIfMissing;
@Override
public void throwOnMissingModel() {
throwIfMissing = true;
public boolean throwOnMissingModel(boolean flag) {
boolean old = throwIfMissing;
throwIfMissing = flag;
return old;
}
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)

View File

@ -316,7 +316,7 @@ public abstract class ModelBakeryMixin implements IExtendedModelBakery {
return m;
ModelBakery self = (ModelBakery) (Object) this;
ModelBaker theBaker = self.new ModelBakerImpl(textureGetter, modelLocation);
((IExtendedModelBaker)theBaker).throwOnMissingModel();
((IExtendedModelBaker)theBaker).throwOnMissingModel(true);
synchronized(this) { m = theBaker.bake(modelLocation, state, theBaker.getModelTextureGetter()); }
if(m != null)
loadedBakedModels.put(key, m);

View File

@ -1,5 +0,0 @@
package org.embeddedt.modernfix.neoforge.packet;
public class NetworkUtils {
public static boolean isCurrentlyVanilla;
}

View File

@ -1,33 +1,24 @@
package org.embeddedt.modernfix.neoforge.packet;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.fml.DistExecutor;
import net.neoforged.neoforge.network.NetworkEvent;
import net.neoforged.neoforge.network.NetworkRegistry;
import net.neoforged.neoforge.network.simple.SimpleChannel;
import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlerEvent;
import net.neoforged.neoforge.network.handling.PlayPayloadContext;
import net.neoforged.neoforge.network.registration.IPayloadRegistrar;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.ModernFixClient;
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
public class PacketHandler {
private static final String PROTOCOL_VERSION = "1";
public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(
new ResourceLocation(ModernFix.MODID, "main"),
() -> PROTOCOL_VERSION,
NetworkRegistry.acceptMissingOr(PROTOCOL_VERSION),
NetworkRegistry.acceptMissingOr(PROTOCOL_VERSION)
);
private static void registerPackets(final RegisterPayloadHandlerEvent event) {
final IPayloadRegistrar registrar = event.registrar(ModernFix.MODID).optional();
registrar.play(EntityIDSyncPacket.ID, EntityIDSyncPacket::new, PacketHandler::handleSyncPacket);
}
public static void register() {
int id = 1;
INSTANCE.registerMessage(id++, EntityIDSyncPacket.class, EntityIDSyncPacket::serialize, EntityIDSyncPacket::deserialize, PacketHandler::handleSyncPacket);
FMLJavaModLoadingContext.get().getModEventBus().addListener(PacketHandler::registerPackets);
}
private static void handleSyncPacket(EntityIDSyncPacket packet, NetworkEvent.Context contextSupplier) {
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
contextSupplier.enqueueWork(() -> ModernFixClient.handleEntityIDSync(packet));
contextSupplier.setPacketHandled(true);
});
private static void handleSyncPacket(EntityIDSyncPacket packet, PlayPayloadContext context) {
context.workHandler().execute(() -> ModernFixClient.handleEntityIDSync(packet));
}
}

View File

@ -5,6 +5,7 @@ import com.google.common.collect.Multimap;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.client.searchtree.SearchRegistry;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.CreativeModeTab;
@ -24,7 +25,6 @@ import org.embeddedt.modernfix.api.constants.IntegrationConstants;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.neoforge.config.NightConfigFixer;
import org.embeddedt.modernfix.neoforge.init.ModernFixForge;
import org.embeddedt.modernfix.neoforge.packet.PacketHandler;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
import org.embeddedt.modernfix.spark.SparkLaunchProfiler;
import org.embeddedt.modernfix.util.CommonModUtil;
@ -87,8 +87,8 @@ public class ModernFixPlatformHooksImpl implements ModernFixPlatformHooks {
return FMLPaths.GAMEDIR.get();
}
public void sendPacket(ServerPlayer player, Object packet) {
PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), packet);
public void sendPacket(ServerPlayer player, CustomPacketPayload packet) {
PacketDistributor.PLAYER.with(player).send(packet);
}
public void injectPlatformSpecificHacks() {

View File

@ -45,9 +45,9 @@ Egregious, yet effective performance improvements for modern Minecraft
# the modid of the dependency
modId = "neoforge" #mandatory
# Does this dependency have to exist - if not, ordering below must be specified
mandatory = true #mandatory
type = "required" #mandatory
# The version range of the dependency
versionRange = "[20.4.22-beta,)" #mandatory
versionRange = "[20.4.70-beta,)" #mandatory
# An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory
ordering = "NONE"
# Side this dependency is applied on - BOTH, CLIENT or SERVER
@ -55,14 +55,14 @@ side = "BOTH"
# Here's another dependency
[[dependencies.modernfix]]
modId = "minecraft"
mandatory = true
type = "required"
# This version range declares a minimum of the current minecraft version up to but not including the next major version
versionRange = "[1.20, 1.21)"
ordering = "NONE"
side = "BOTH"
[[dependencies.modernfix]]
modId = "jei"
mandatory = false
type = "optional"
# This version range declares a minimum of the current minecraft version up to but not including the next major version
versionRange = "[13,)"
ordering = "BEFORE"

View File

@ -1,16 +0,0 @@
{
"required": true,
"package": "org.embeddedt.modernfix.neoforge.mixin",
"plugin": "org.embeddedt.modernfix.core.ModernFixMixinPlugin",
"compatibilityLevel": "JAVA_8",
"minVersion": "0.8",
"mixins": [
${mixin_classes}
],
"injectors": {
"defaultRequire": 1
},
"overwrites": {
"conformVisibility": true
}
}

View File

@ -5,13 +5,20 @@ pluginManagement {
maven { url "https://maven.architectury.dev/" }
maven { url "https://maven.minecraftforge.net/" }
}
resolutionStrategy {
eachPlugin {
if (requested.id.id == "com.github.johnrengelman.shadow") {
useModule("gradle.plugin.com.github.johnrengelman:shadow:${shadow_version}")
}
}
}
}
include("annotation-processor")
include("annotations")
include("test_agent")
include("common")
startParameter.excludedTaskNames.add(':fabric:testmod:genSources')
def current_platforms = getProperty("enabled_platforms").tokenize(',')
current_platforms.each { it ->
def platform_name = it.trim()