diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7c0c8b3e..6af25592 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,8 +18,13 @@ jobs: gradle-home-cache-cleanup: true gradle-home-cache-excludes: | gradle.properties - - run: ./gradlew clean build publishToMavenLocal --stacktrace + - name: Run Auto test Client + uses: modmuss50/xvfb-action@v1 + with: + run: ./gradlew runTestModClient + - run: ./gradlew clean build --stacktrace + - run: ./gradlew publishToMavenLocal --stacktrace - uses: actions/upload-artifact@v3 with: name: Maven Local - path: ~/.m2/repository \ No newline at end of file + path: ~/.m2/repository diff --git a/build.gradle.kts b/build.gradle.kts index 504dd7a3..84dc700c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,18 +1,32 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import me.modmuss50.mpp.ReleaseType +import net.minecraftforge.gradle.common.tasks.DownloadAssets import net.minecraftforge.gradle.common.util.MavenArtifactDownloader import net.minecraftforge.gradle.common.util.RunConfig import net.minecraftforge.gradle.userdev.tasks.JarJar import net.minecraftforge.gradle.userdev.util.MavenPomUtils -import net.minecraftforge.jarjar.metadata.* +import net.minecraftforge.jarjar.metadata.ContainedJarIdentifier +import net.minecraftforge.jarjar.metadata.ContainedJarMetadata +import net.minecraftforge.jarjar.metadata.ContainedVersion +import net.minecraftforge.jarjar.metadata.MetadataIOHandler import org.apache.maven.artifact.versioning.DefaultArtifactVersion import org.apache.maven.artifact.versioning.VersionRange +import org.yaml.snakeyaml.Yaml import java.nio.file.FileSystems import java.nio.file.Files import java.nio.file.StandardOpenOption import java.time.LocalDateTime import kotlin.io.path.inputStream +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath(group = "org.yaml", name = "snakeyaml", version = "2.2") + } +} + plugins { java `maven-publish` @@ -56,6 +70,7 @@ if (!PUBLISH_RELEASE_TYPE.isPresent) { println("Project version: $version") val mod: SourceSet by sourceSets.creating +val test: SourceSet by sourceSets val shade: Configuration by configurations.creating { isTransitive = false } val adapterData: Configuration by configurations.creating @@ -113,7 +128,7 @@ val modJar: JarJar by tasks.creating(JarJar::class) { false ) Files.deleteIfExists(metadataPath) - Files.write(metadataPath, MetadataIOHandler.toLines(metadata), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) + Files.write(metadataPath, MetadataIOHandler.toLines(metadata), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE) } } manifest.attributes( @@ -198,6 +213,20 @@ configurations { } } +repositories { + exclusiveContent { + forRepository { + maven { + url = uri("https://cursemaven.com") + } + } + forRepositories(fg.repository) + filter { + includeGroup("curse.maven") + } + } +} + sourceSets { main { runtimeClasspath = runtimeClasspath.minus(output).plus(files(fullJar)) @@ -231,8 +260,20 @@ minecraft { } } - create("client", config) + val clientConfig = create("client", config) create("server", config) + + create("testModClient") { + mods { + create("testconnector") { + sources(test) + } + } + args("--mixin.config", "connectortest.mixins.json") + args("--quickPlaySingleplayer", "ctest") + parent(clientConfig) + workingDirectory = project.file("run/test").canonicalPath + } } } @@ -255,6 +296,13 @@ repositories { name = "Su5eD" url = uri("https://maven.su5ed.dev/releases") } + exclusiveRepo("https://api.modrinth.com/maven/", "maven.modrinth") + maven { + url = uri("https://www.cursemaven.com") + content { + includeGroup("curse.maven") + } + } mavenLocal() } @@ -282,6 +330,8 @@ dependencies { "modCompileOnly"(sourceSets.main.get().output) "modCompileOnly"("io.github.llamalad7:mixinextras-common:${mixinextrasVersion}") + + runtimeOnly(fg.deobf("curse.maven:connector-extras-913445:5027683")) } tasks { @@ -312,6 +362,23 @@ tasks { dependsOn("reobfModJar", fullJar) } + val modDownload = register("resolveTestMods") { + doFirst { + val configFile = rootProject.file("testmods.yaml") + val data: List> = configFile.reader().use(Yaml()::load) + val deps = data.map { project.dependencies.create(it["maven"] as String) } + + val config = configurations.detachedConfiguration(*deps.toTypedArray()) + val files = config.resolve() + + val dir = project.file("run/test/mods").apply { + if (exists()) deleteRecursively() + mkdirs() + } + files.forEach { it.copyTo(dir.resolve(it.name)) } + } + } + configureEach { if (name == "prepareRuns") { dependsOn(fullJar) @@ -322,6 +389,13 @@ tasks { if (name == "reobfModJar") { mustRunAfter(modJar) } + if (name == "runTestModClient") { + dependsOn(modDownload) + } + if (name == "downloadAssets" && providers.environmentVariable("CI").isPresent) { + enabled = false + (this as DownloadAssets).output.mkdirs() + } } } @@ -388,3 +462,19 @@ publishing { } } } + +// Adapted from https://gist.github.com/pupnewfster/6c21401789ca6d74f9892be8c1c505c9 +fun RepositoryHandler.exclusiveRepo(location: String, vararg groups: String) { + exclusiveRepo(location) { + for (group in groups) { + includeGroup(group) + } + } +} + +fun RepositoryHandler.exclusiveRepo(location: String, config: Action) { + exclusiveContent { + forRepositories(maven(location), fg.repository) + filter(config) + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 423f4eb0..679d0e07 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,9 +4,9 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=true # Versions -versionConnector=1.0.0-beta.35 +versionConnector=1.0.0-beta.36 versionAdapter=1.11.19-1.20.1-20240126.215012 -versionAdapterDefinition=1.11.22 +versionAdapterDefinition=1.11.24 versionMc=1.20.1 versionForge=47.1.3 diff --git a/run/test/config/fml.toml b/run/test/config/fml.toml new file mode 100644 index 00000000..a7ef7888 --- /dev/null +++ b/run/test/config/fml.toml @@ -0,0 +1,25 @@ +#Early window height +earlyWindowHeight = 480 +#Early window framebuffer scale +earlyWindowFBScale = 1 +#Enable forge global version checking +versionCheck = true +#Early window provider +earlyWindowProvider = "fmlearlywindow" +#Early window width +earlyWindowWidth = 854 +#Early window starts maximized +earlyWindowMaximized = false +#Default config path for servers +defaultConfigPath = "defaultconfigs" +#Disables Optimized DFU client-side - already disabled on servers +disableOptimizedDFU = true +#Skip specific GL versions, may help with buggy graphics card drivers +earlyWindowSkipGLVersions = [] +#Should we control the window. Disabling this disables new GL features and can be bad for mods that rely on them. +earlyWindowControl = false +#Max threads for early initialization parallelism, -1 is based on processor count +maxThreads = -1 +#Squir? +earlyWindowSquir = false + diff --git a/src/main/java/dev/su5ed/sinytra/connector/locator/DependencyResolver.java b/src/main/java/dev/su5ed/sinytra/connector/locator/DependencyResolver.java index 5f2e9567..115ec8da 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/locator/DependencyResolver.java +++ b/src/main/java/dev/su5ed/sinytra/connector/locator/DependencyResolver.java @@ -45,12 +45,12 @@ public final class DependencyResolver { private static final Logger LOGGER = LogUtils.getLogger(); public static final VersionOverrides VERSION_OVERRIDES = new VersionOverrides(); - public static final Supplier DEPENDENCY_OVERRIDES = Suppliers.memoize(DependencyResolver::loadDependencyOverrides); - private static final GlobalModAliases GLOBAL_MOD_ALIASES = new GlobalModAliases(FMLPaths.CONFIGDIR.get(), ConnectorUtil.DEFAULT_GLOBAL_MOD_ALIASES); + public static final Supplier DEPENDENCY_OVERRIDES = Suppliers.memoize(() -> loadConfigFile("fabric_loader_dependencies.json", () -> new DependencyOverrides(FMLPaths.CONFIGDIR.get()))); + private static final Supplier GLOBAL_MOD_ALIASES = Suppliers.memoize(() -> loadConfigFile("connector_global_mod_aliases.json", () -> new GlobalModAliases(FMLPaths.CONFIGDIR.get(), ConnectorUtil.DEFAULT_GLOBAL_MOD_ALIASES))); public static List resolveDependencies(Collection keys, Multimap jars, Iterable loadedMods) { // Add global mod aliases - FabricLoaderImpl.INSTANCE.aliasMods(GLOBAL_MOD_ALIASES.getAliases()); + FabricLoaderImpl.INSTANCE.aliasMods(GLOBAL_MOD_ALIASES.get().getAliases()); BiMap jarToCandidate = HashBiMap.create(); // Fabric candidates List candidates = createCandidatesRecursive(keys, keys, jars, jarToCandidate); @@ -130,11 +130,11 @@ private static ModCandidate createFabricLoaderMod() { return ModCandidate.createBuiltin(builtinMod, VERSION_OVERRIDES, DEPENDENCY_OVERRIDES.get()); } - private static DependencyOverrides loadDependencyOverrides() { + private static T loadConfigFile(String name, Supplier supplier) { try { - return new DependencyOverrides(FMLPaths.CONFIGDIR.get()); - } catch (Exception e) { - throw ConnectorEarlyLoader.createGenericLoadingException(e, "Invalid config file fabric_loader_dependencies.json"); + return supplier.get(); + } catch (Throwable t) { + throw ConnectorEarlyLoader.createGenericLoadingException(t, "Invalid config file " + name); } } } diff --git a/src/main/java/dev/su5ed/sinytra/connector/service/FabricMixinBootstrap.java b/src/main/java/dev/su5ed/sinytra/connector/service/FabricMixinBootstrap.java index 678391cd..d5598e21 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/service/FabricMixinBootstrap.java +++ b/src/main/java/dev/su5ed/sinytra/connector/service/FabricMixinBootstrap.java @@ -18,10 +18,10 @@ import com.mojang.logging.LogUtils; import dev.su5ed.sinytra.connector.loader.ConnectorEarlyLoader; -import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.SemanticVersion; import net.fabricmc.loader.api.Version; import net.fabricmc.loader.api.metadata.ModDependency; +import net.fabricmc.loader.api.metadata.ModMetadata; import net.fabricmc.loader.api.metadata.version.VersionInterval; import net.fabricmc.loader.impl.FabricLoaderImpl; import net.minecraftforge.fml.loading.LoadingModList; @@ -72,7 +72,7 @@ public static void init() { } } - private static final class MixinConfigDecorator { + public static final class MixinConfigDecorator { private static final List VERSIONS = List.of( LoaderMixinVersionEntry.create("0.12.0-", FabricUtil.COMPATIBILITY_0_10_0) ); @@ -87,7 +87,7 @@ static void apply(Map configToModMap) { if (!mod.getMods().isEmpty()) { String modid = mod.getMods().get(0).getModId(); int compat = ConnectorEarlyLoader.isConnectorMod(modid) ? FabricLoaderImpl.INSTANCE.getModContainer(modid) - .map(MixinConfigDecorator::getMixinCompat) + .map(m -> getMixinCompat(m.getMetadata())) .orElse(FabricUtil.COMPATIBILITY_0_10_0) : FabricUtil.COMPATIBILITY_0_10_0; config.decorate(FabricUtil.KEY_COMPATIBILITY, compat); @@ -95,13 +95,13 @@ static void apply(Map configToModMap) { } } - private static int getMixinCompat(ModContainer mod) { + public static int getMixinCompat(ModMetadata metadata) { // infer from loader dependency by determining the least relevant loader version the mod accepts // AND any loader deps List reqIntervals = List.of(VersionInterval.INFINITE); - for (ModDependency dep : mod.getMetadata().getDependencies()) { + for (ModDependency dep : metadata.getDependencies()) { if (dep.getModId().equals("fabricloader") || dep.getModId().equals("fabric-loader")) { if (dep.getKind() == ModDependency.Kind.DEPENDS) { reqIntervals = VersionInterval.and(reqIntervals, dep.getVersionIntervals()); @@ -112,7 +112,7 @@ else if (dep.getKind() == ModDependency.Kind.BREAKS) { } } - if (reqIntervals.isEmpty()) throw new IllegalStateException("mod " + mod + " is incompatible with every loader version?"); // shouldn't get there + if (reqIntervals.isEmpty()) throw new IllegalStateException("mod " + metadata.getId() + " is incompatible with every loader version?"); // shouldn't get there Version minLoaderVersion = reqIntervals.get(0).getMin(); // it is sorted, to 0 has the absolute lower bound diff --git a/src/main/java/dev/su5ed/sinytra/connector/transformer/MixinPatchTransformer.java b/src/main/java/dev/su5ed/sinytra/connector/transformer/MixinPatchTransformer.java index b137b479..2851b55b 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/transformer/MixinPatchTransformer.java +++ b/src/main/java/dev/su5ed/sinytra/connector/transformer/MixinPatchTransformer.java @@ -11,13 +11,11 @@ import dev.su5ed.sinytra.adapter.patch.LVTOffsets; import dev.su5ed.sinytra.adapter.patch.api.ClassTransform; import dev.su5ed.sinytra.adapter.patch.api.MixinClassGenerator; -import dev.su5ed.sinytra.adapter.patch.api.MixinConstants; import dev.su5ed.sinytra.adapter.patch.api.Patch; import dev.su5ed.sinytra.adapter.patch.api.PatchContext; import dev.su5ed.sinytra.adapter.patch.api.PatchEnvironment; import dev.su5ed.sinytra.adapter.patch.fixes.FieldTypePatchTransformer; import dev.su5ed.sinytra.adapter.patch.fixes.FieldTypeUsageTransformer; -import dev.su5ed.sinytra.adapter.patch.transformer.ModifyMethodParams; import dev.su5ed.sinytra.adapter.patch.transformer.dynamic.DynamicAnonymousShadowFieldTypePatch; import dev.su5ed.sinytra.adapter.patch.transformer.dynamic.DynamicInheritedInjectionPointPatch; import dev.su5ed.sinytra.adapter.patch.transformer.dynamic.DynamicInjectorOrdinalPatch; @@ -29,18 +27,7 @@ import net.minecraftforge.forgespi.locating.IModFile; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldInsnNode; -import org.objectweb.asm.tree.InsnList; -import org.objectweb.asm.tree.InsnNode; -import org.objectweb.asm.tree.JumpInsnNode; -import org.objectweb.asm.tree.LabelNode; -import org.objectweb.asm.tree.MethodInsnNode; -import org.objectweb.asm.tree.TypeInsnNode; -import org.objectweb.asm.tree.VarInsnNode; import org.slf4j.Logger; import java.io.IOException; @@ -53,7 +40,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; -import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.stream.Stream; diff --git a/src/main/java/dev/su5ed/sinytra/connector/transformer/MixinPatches.java b/src/main/java/dev/su5ed/sinytra/connector/transformer/MixinPatches.java index 4e0c3813..f1ebde88 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/transformer/MixinPatches.java +++ b/src/main/java/dev/su5ed/sinytra/connector/transformer/MixinPatches.java @@ -2,6 +2,7 @@ import dev.su5ed.sinytra.adapter.patch.api.MixinConstants; import dev.su5ed.sinytra.adapter.patch.api.Patch; +import dev.su5ed.sinytra.adapter.patch.transformer.ModifyMethodAccess; import dev.su5ed.sinytra.adapter.patch.transformer.ModifyMethodParams; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @@ -348,6 +349,38 @@ public static List getPatches() { .ignoreOffset()) .targetMixinType(MixinConstants.REDIRECT) .build(), + Patch.builder() + .targetClass("net/minecraft/world/entity/LivingEntity") + .targetMethod("m_7023_") // travel + .targetMixinType(MixinConstants.MODIFY_VAR) + .targetAnnotationValues(handle -> handle.getNested("at") + .filter(at -> at.getValue("value") + .filter(h -> h.get().equals("CONSTANT")) + .isPresent()) + .flatMap(at -> at.getValue("args")) + .map(v -> (List) v.get()) + .map(v -> v.size() == 1 && v.get(0).equals("doubleValue=0.01")) + .orElse(false)) + .modifyMixinType(MixinConstants.WRAP_OPERATION, builder -> builder + .sameTarget() + .injectionPoint("INVOKE", "Lnet/minecraft/world/entity/ai/attributes/AttributeInstance;m_22135_()D") // getValue + .putValue("ordinal", 0)) + .transformParams(builder -> + builder.inject(0, Type.getType("Lnet/minecraft/world/entity/ai/attributes/AttributeInstance;")) + .inject(1, Type.getObjectType(MixinConstants.OPERATION_INTERNAL_NAME)) + .inline(2, adapter -> { + adapter.visitVarInsn(Opcodes.ALOAD, 2); + adapter.visitInsn(Opcodes.ICONST_1); + adapter.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + adapter.visitInsn(Opcodes.DUP); + adapter.visitInsn(Opcodes.ICONST_0); + adapter.visitVarInsn(Opcodes.ALOAD, 1); + adapter.visitInsn(Opcodes.AASTORE); + adapter.visitMethodInsn(Opcodes.INVOKEINTERFACE, "com/llamalad7/mixinextras/injector/wrapoperation/Operation", "call", "([Ljava/lang/Object;)Ljava/lang/Object;", true); + adapter.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Double"); + adapter.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false); + })) + .build(), Patch.builder() .targetClass("net/minecraft/server/level/ServerPlayerGameMode") .targetMethod("m_9280_") @@ -500,7 +533,9 @@ public static List getPatches() { methodNode.desc = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getArgumentTypes(methodNode.desc)); return Patch.Result.APPLY; }) - .build()); + .build(), + + buildReplayModPatches()); return patches.stream() .flatMap(p -> p instanceof List lst ? lst.stream() : Stream.of(p)) @@ -525,4 +560,46 @@ private static Patch buildGuiPatch(int minOrdinal, int maxOrdinal, String target }) .build(); } + + private static List buildReplayModPatches() { + return List.of( + Patch.builder() + .targetClass("net/minecraft/client/KeyboardHandler") + .targetMethod("method_1473") + .modifyTarget("lambda$charTyped$7(Lnet/minecraft/client/gui/screens/Screen;CI)V") + .modifyParams(builder -> builder.replace(0, Type.getType("Lnet/minecraft/client/gui/screens/Screen;"))) + .build(), + Patch.builder() + .targetClass("net/minecraft/client/KeyboardHandler") + .targetMethod("method_1458") + .modifyTarget("lambda$charTyped$6(Lnet/minecraft/client/gui/screens/Screen;II)V") + .modifyParams(builder -> builder.replace(0, Type.getType("Lnet/minecraft/client/gui/screens/Screen;"))) + .build(), + Patch.builder() + .targetClass("net/minecraft/client/KeyboardHandler") + .targetMethod("method_1454") + .modifyTarget("lambda$keyPress$5(ILnet/minecraft/client/gui/screens/Screen;[ZIII)V") + .transformParams(builder -> builder.swap(1, 2)) + .build(), + + // Their refmaps are so broken... + Patch.builder() + .targetClass("net/minecraft/client/MouseHandler") + .targetMethod("method_1611") + .modifyMethodAccess(new ModifyMethodAccess.AccessChange(false, Opcodes.ACC_STATIC)) + .modifyTarget("m_168084_") // lambda$onPress$0 + .build(), + Patch.builder() + .targetClass("net/minecraft/client/MouseHandler") + .targetMethod("method_1605") + .modifyMethodAccess(new ModifyMethodAccess.AccessChange(false, Opcodes.ACC_STATIC)) + .modifyTarget("m_168078_") // lambda$onPress$1 + .build(), + Patch.builder() + .targetClass("net/minecraft/client/MouseHandler") + .targetMethod("method_1602") + .modifyTarget("m_168072_") // lambda$onMove$11 + .build() + ); + } } diff --git a/src/main/java/dev/su5ed/sinytra/connector/transformer/jar/JarTransformInstance.java b/src/main/java/dev/su5ed/sinytra/connector/transformer/jar/JarTransformInstance.java index f27489d2..d3530fe1 100644 --- a/src/main/java/dev/su5ed/sinytra/connector/transformer/jar/JarTransformInstance.java +++ b/src/main/java/dev/su5ed/sinytra/connector/transformer/jar/JarTransformInstance.java @@ -13,6 +13,7 @@ import dev.su5ed.sinytra.adapter.patch.util.provider.ClassLookup; import dev.su5ed.sinytra.adapter.patch.util.provider.ZipClassLookup; import dev.su5ed.sinytra.connector.locator.EmbeddedDependencies; +import dev.su5ed.sinytra.connector.service.FabricMixinBootstrap; import dev.su5ed.sinytra.connector.transformer.AccessWidenerTransformer; import dev.su5ed.sinytra.connector.transformer.AccessorRedirectTransformer; import dev.su5ed.sinytra.connector.transformer.FieldToMethodTransformer; @@ -124,7 +125,8 @@ public void transformJar(File input, Path output, FabricModFileMetadata metadata List extraPatches = Stream.concat(this.adapterPatches.stream(), AccessorRedirectTransformer.PATCHES.stream()).toList(); ConnectorRefmapHolder refmapHolder = new ConnectorRefmapHolder(refmap.merged(), refmap.files()); - PatchEnvironment environment = PatchEnvironment.create(refmapHolder, this.cleanClassLookup, this.bfu.unwrap()); + int fabricLVTCompatibility = FabricMixinBootstrap.MixinConfigDecorator.getMixinCompat(metadata.modMetadata()); + PatchEnvironment environment = PatchEnvironment.create(refmapHolder, this.cleanClassLookup, this.bfu.unwrap(), fabricLVTCompatibility); MixinPatchTransformer patchTransformer = new MixinPatchTransformer(this.lvtOffsetsData, metadata.mixinPackages(), environment, extraPatches); RefmapRemapper refmapRemapper = new RefmapRemapper(metadata.visibleMixinConfigs(), refmap.files()); Renamer.Builder builder = Renamer.builder() diff --git a/src/mod/java/dev/su5ed/sinytra/connector/mod/ConnectorBootstrap.java b/src/mod/java/dev/su5ed/sinytra/connector/mod/ConnectorBootstrap.java index d330ad2c..842baf29 100644 --- a/src/mod/java/dev/su5ed/sinytra/connector/mod/ConnectorBootstrap.java +++ b/src/mod/java/dev/su5ed/sinytra/connector/mod/ConnectorBootstrap.java @@ -52,11 +52,15 @@ private static void registerCrashLogInfo() { }); } + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return !ConnectorEarlyLoader.hasEncounteredException(); + } + // We don't need any of the mixin stuff //@formatter:off @Override public void onLoad(String mixinPackage) {} @Override public String getRefMapperConfig() {return null;} - @Override public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {return true;} @Override public void acceptTargets(Set myTargets, Set otherTargets) {} @Override public List getMixins() {return null;} @Override public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {} diff --git a/src/test/java/org/sinytra/connector/test/ConnectorTest.java b/src/test/java/org/sinytra/connector/test/ConnectorTest.java new file mode 100644 index 00000000..1c37ea40 --- /dev/null +++ b/src/test/java/org/sinytra/connector/test/ConnectorTest.java @@ -0,0 +1,30 @@ +package org.sinytra.connector.test; + +import com.mojang.logging.LogUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.PauseScreen; +import net.minecraft.client.gui.screens.ReceivingLevelScreen; +import net.minecraftforge.client.event.ScreenEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.Mod; + +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +@Mod("testconnector") +public class ConnectorTest { + public ConnectorTest() { + MinecraftForge.EVENT_BUS.addListener((final ScreenEvent.Opening event) -> { + if (event.getNewScreen() instanceof PauseScreen) { + Minecraft.getInstance().setScreen(null); + Minecraft.getInstance().mouseHandler.grabMouse(); + } + }); + MinecraftForge.EVENT_BUS.addListener((final ScreenEvent.Closing event) -> { + if (event.getScreen() instanceof PauseScreen || event.getScreen() instanceof ReceivingLevelScreen) { + LogUtils.getLogger().info("Screen reset, scheduling shutdown in 5 seconds"); + Executors.newSingleThreadScheduledExecutor().schedule(() -> System.exit(0), 5, TimeUnit.SECONDS); + } + }); + } +} diff --git a/src/test/java/org/sinytra/connector/test/mixin/client/MinecraftMixin.java b/src/test/java/org/sinytra/connector/test/mixin/client/MinecraftMixin.java new file mode 100644 index 00000000..6e828ba5 --- /dev/null +++ b/src/test/java/org/sinytra/connector/test/mixin/client/MinecraftMixin.java @@ -0,0 +1,19 @@ +package org.sinytra.connector.test.mixin.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.ErrorScreen; +import net.minecraft.client.gui.screens.Screen; +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(Minecraft.class) +public class MinecraftMixin { + @Inject(method = "setScreen", at = @At("HEAD")) + private void onSetScreen(Screen screen, CallbackInfo ci) { + if (screen instanceof ErrorScreen) { + System.exit(1); + } + } +} diff --git a/src/test/java/org/sinytra/connector/test/mixin/client/QuickPlayMixin.java b/src/test/java/org/sinytra/connector/test/mixin/client/QuickPlayMixin.java new file mode 100644 index 00000000..be66e6f3 --- /dev/null +++ b/src/test/java/org/sinytra/connector/test/mixin/client/QuickPlayMixin.java @@ -0,0 +1,27 @@ +package org.sinytra.connector.test.mixin.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.quickplay.QuickPlay; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.Difficulty; +import net.minecraft.world.level.GameRules; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.LevelSettings; +import net.minecraft.world.level.WorldDataConfiguration; +import net.minecraft.world.level.levelgen.WorldOptions; +import net.minecraft.world.level.levelgen.presets.WorldPresets; +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(QuickPlay.class) +public class QuickPlayMixin { + @Inject(method = "joinSingleplayerWorld", at = @At("HEAD")) + private static void createWorld(Minecraft pMinecraft, String pLevelName, CallbackInfo ci) { + if (!pMinecraft.getLevelSource().levelExists(pLevelName)) { + pMinecraft.createWorldOpenFlows().createFreshLevel(pLevelName, new LevelSettings("Connector test", GameType.SURVIVAL, false, Difficulty.NORMAL, false, new GameRules(), WorldDataConfiguration.DEFAULT), + new WorldOptions("Connector Test".hashCode(), true, true), WorldPresets::createNormalWorldDimensions); + } + } +} diff --git a/src/test/resources/META-INF/MANIFEST.MF b/src/test/resources/META-INF/MANIFEST.MF new file mode 100644 index 00000000..0dea672f --- /dev/null +++ b/src/test/resources/META-INF/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1 +MixinConfigs: connectortest.mixins.json diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml new file mode 100644 index 00000000..b50f9a07 --- /dev/null +++ b/src/test/resources/META-INF/mods.toml @@ -0,0 +1,11 @@ +modLoader="javafml" +loaderVersion="[47,)" +license="MIT" +issueTrackerURL="https://github.com/Sinytra/Connector/issues" + +[[mods]] +modId="testconnector" +version="${file.jarVersion}" +displayName="Connector Test" +description=''' +''' diff --git a/src/test/resources/connectortest.mixins.json b/src/test/resources/connectortest.mixins.json new file mode 100644 index 00000000..cc53789a --- /dev/null +++ b/src/test/resources/connectortest.mixins.json @@ -0,0 +1,15 @@ +{ + "required": true, + "minVersion": "0.8.4", + "package": "org.sinytra.connector.test.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + ], + "client": [ + "client.MinecraftMixin", + "client.QuickPlayMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/src/test/resources/pack.mcmeta b/src/test/resources/pack.mcmeta new file mode 100644 index 00000000..d9989eb8 --- /dev/null +++ b/src/test/resources/pack.mcmeta @@ -0,0 +1,8 @@ +{ + "pack": { + "description": { + "text": "connector test resources" + }, + "pack_format": 15 + } +} diff --git a/testmods.yaml b/testmods.yaml new file mode 100644 index 00000000..97a0fed5 --- /dev/null +++ b/testmods.yaml @@ -0,0 +1,8 @@ +- id: fastback + homepage: https://modrinth.com/mod/fastback + maven: maven.modrinth:fastback:0.15.6+1.20.1-fabric + +# Requiring connector extras: +- id: beaconoverhaul + homepage: https://www.curseforge.com/minecraft/mc-mods/beaconoverhaul + maven: curse.maven:beaconoverhaul-430382:4715716