diff options
Diffstat (limited to 'neoforge')
41 files changed, 2283 insertions, 0 deletions
diff --git a/neoforge/build.gradle b/neoforge/build.gradle new file mode 100644 index 000000000..a0747ec53 --- /dev/null +++ b/neoforge/build.gradle @@ -0,0 +1,243 @@ +plugins { + id "com.github.johnrengelman.shadow" version "7.0.0" +} + +architectury { + platformSetupLoomIde() + neoForge() +} + +configurations { + common + shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this. + compileClasspath.extendsFrom common + runtimeClasspath.extendsFrom common +} + +processResources { + filesMatching("META-INF/mods.toml") { + expand "version": project.version + } + inputs.property "version", project.version +} + +repositories { + maven { url "https://www.cursemaven.com" } + maven { url "https://dl.cloudsmith.io/public/geckolib3/geckolib/maven" } + maven { + name "Modmaven" + url "https://modmaven.dev/" + // For Gradle 5.1 and above, limit it to just AE2 + content { + includeGroup 'appeng' + } + } +} + +sourceSets { + serverComponent { + compileClasspath += main.compileClasspath + runtimeClasspath += main.runtimeClasspath + } +} + +processServerComponentResources { + filesMatching("META-INF/mods.toml") { + expand "version": project.version + } + inputs.property "version", project.version +} + +loom { +} + +def depProjects = [":api", ":runtime", ":default-plugin"] + +dependencies { + neoForge("net.neoforged:neoforge:${rootProject.neoforge_version}") + modApi("me.shedaniel.cloth:cloth-config-neoforge:${cloth_config_version}") + modApi("dev.architectury:architectury-neoforge:${architectury_version}") + + depProjects.forEach { + common(project(path: it, configuration: "namedElements")) { transitive false } + shadowCommon(project(path: it, configuration: "transformProductionNeoForge")) { transitive false } + } +} + +def modRuntime(str) { + dependencies.modLocalRuntime(str) +} + +shadowJar { + configurations = [project.configurations.shadowCommon] + archiveClassifier = "dev-shadow" +} + +remapJar { + input.set shadowJar.archiveFile + dependsOn shadowJar + archiveClassifier = null +} + +task renameJarForPublication(type: Zip, dependsOn: remapJar) { + from remapJar.archiveFile.map { zipTree(it) } + metadataCharset "UTF-8" + archiveExtension = "jar" + destinationDirectory = base.libsDirectory + archiveClassifier = project.name +} + +assemble.dependsOn renameJarForPublication + +jar { + archiveClassifier = "dev" +} + +java { + withSourcesJar() +} + +sourcesJar { + afterEvaluate { + depProjects.forEach { + def depSources = project(it).sourcesJar + dependsOn depSources + from depSources.archiveFile.map { zipTree(it) } + } + } +} + +task serverOnlyJar(type: Jar, dependsOn: [remapJar]) { + archiveClassifier = "server-only" + from(zipTree(remapJar.archiveFile.get().asFile)) { + exclude "META-INF/mods.toml", "mezz/**", "me/shedaniel/rei/forge/RoughlyEnoughItemsForge.class", "me/shedaniel/rei/forge/JEIStub.class" + } + from(sourceSets.serverComponent.output) +} + +tasks.build { + dependsOn tasks.serverOnlyJar +} + +components.java { + withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { + skip() + } +} + +publishing { + publications { + mavenNeoForge(MavenPublication) { + artifactId = rootProject.name + "-" + project.name + from components.java + } + ["api", "default-plugin"].forEach { projectName -> + def remapMojang = tasks.create("remapMojangJarFor$projectName", net.fabricmc.loom.task.RemapJarTask) { + inputFile = project(":" + projectName).tasks.jar.archiveFile + archiveClassifier = "mojang-remapped-$projectName" + targetNamespace = "mojang" + } + def remapMojangSourcesJar = tasks.create("remapMojangSourcesFor$projectName", net.fabricmc.loom.task.RemapSourcesJarTask) { + inputFile = project(":" + projectName).tasks.sourcesJar.archiveFile + archiveClassifier = "mojang-remapped-$projectName-sources" + targetNamespace = "mojang" + } + create(projectName + "NeoForge", MavenPublication.class) { publication -> + publication.artifactId = rootProject.name + "-" + projectName + "-neoforge" + project.afterEvaluate { + def project = project(":" + projectName) + remapMojang.inputFile = project.fakeForgeJar.archiveFile + publication.artifact(remapMojang) { classifier null } + publication.artifact(remapMojangSourcesJar) { + builtBy remapMojangSourcesJar + classifier "sources" + } + } + } + } + } +} + +unifiedPublishing { + project { + displayName = "[NeoForge $rootProject.supported_version] v$project.version" + releaseType = "beta" + gameVersions = [] + gameLoaders = ["neoforge"] + changelog = rootProject.releaseChangelog + + mainPublication renameJarForPublication + + relations { + depends { + curseforge = "architectury-api" + modrinth = "architectury-api" + } + depends { + curseforge = "cloth-config" + modrinth = "cloth-config" + } + } + + if (project.hasProperty("danielshe_curse_api_key") || System.getenv("danielshe_curse_api_key") != null) { + curseforge { + token = project.hasProperty("danielshe_curse_api_key") ? project.property("danielshe_curse_api_key") : System.getenv("danielshe_curse_api_key") + id = "310111" + gameVersions.addAll "Java 17" + gameVersions.addAll project.minecraft_version + + relations { + depends "roughly-enough-items-hacks" + } + } + } + + if (project.hasProperty("modrinth_key") || System.getenv("modrinth_key") != null) { + modrinth { + token = project.hasProperty("modrinth_key") ? project.property("modrinth_key") : System.getenv("modrinth_key") + id = "nfn13YXA" + version = "$project.version+$project.name" + gameVersions.addAll project.minecraft_version + } + } + } + project { + displayName = "[NeoForge $rootProject.supported_version] v$project.version" + releaseType = "release" + gameVersions = [] + gameLoaders = ["neoforge"] + changelog = rootProject.releaseChangelog + + mainPublication serverOnlyJar + + relations { + depends { + curseforge = "architectury-api" + modrinth = "architectury-api" + } + depends { + curseforge = "cloth-config" + modrinth = "cloth-config" + } + } + + if (project.hasProperty("danielshe_curse_api_key") || System.getenv("danielshe_curse_api_key") != null) { + curseforge { + token = project.hasProperty("danielshe_curse_api_key") ? project.property("danielshe_curse_api_key") : System.getenv("danielshe_curse_api_key") + id = "567899" + gameVersions.addAll "Java 17" + gameVersions.addAll project.minecraft_version + } + } + + if (project.hasProperty("modrinth_key") || System.getenv("modrinth_key") != null) { + modrinth { + releaseType = "release" + token = project.hasProperty("modrinth_key") ? project.property("modrinth_key") : System.getenv("modrinth_key") + id = "OM4ZYSws" + version = "$project.version+$project.name" + gameVersions.addAll project.minecraft_version + } + } + } +} diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties new file mode 100644 index 000000000..7da18ea6f --- /dev/null +++ b/neoforge/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge diff --git a/neoforge/src/main/java/me/shedaniel/rei/forge/AnnotationUtils.java b/neoforge/src/main/java/me/shedaniel/rei/forge/AnnotationUtils.java new file mode 100644 index 000000000..9a13e0cdc --- /dev/null +++ b/neoforge/src/main/java/me/shedaniel/rei/forge/AnnotationUtils.java @@ -0,0 +1,119 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.forge; + +import com.google.common.collect.Lists; +import me.shedaniel.rei.impl.init.PrimitivePlatformAdapter; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.fml.ModList; +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.fml.loading.moddiscovery.ModAnnotation; +import net.neoforged.neoforgespi.language.IModInfo; +import net.neoforged.neoforgespi.language.ModFileScanData; +import org.apache.commons.lang3.tuple.ImmutableTriple; +import org.apache.commons.lang3.tuple.Triple; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.util.TriConsumer; +import org.objectweb.asm.Type; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class AnnotationUtils { + public static final Logger LOGGER = LogManager.getFormatterLogger("REI"); + + public static <A, T> void scanAnnotation(Class<A> clazz, Predicate<Class<T>> predicate, TriConsumer<List<String>, Supplier<T>, Class<T>> consumer) { + scanAnnotation(Type.getType(clazz), predicate, consumer); + } + + public static <T> void scanAnnotation(Type annotationType, Predicate<Class<T>> predicate, TriConsumer<List<String>, Supplier<T>, Class<T>> consumer) { + List<Triple<List<String>, Supplier<T>, Class<T>>> instances = Lists.newArrayList(); + for (ModFileScanData data : ModList.get().getAllScanData()) { + List<String> modIds = data.getIModInfoData().stream() + .flatMap(info -> info.getMods().stream()) + .map(IModInfo::getModId) + .collect(Collectors.toList()); + out: + for (ModFileScanData.AnnotationData annotation : data.getAnnotations()) { + if (annotationType.equals(annotation.annotationType())) { + Object value = annotation.annotationData().get("value"); + boolean enabled; + + if (value instanceof Dist[]) { + enabled = Arrays.asList((Dist[]) value).contains(FMLEnvironment.dist); + } else if (value instanceof ModAnnotation.EnumHolder) { + enabled = Objects.equals(((ModAnnotation.EnumHolder) value).getValue(), FMLEnvironment.dist.name()); + } else if (value instanceof List) { + List<ModAnnotation.EnumHolder> holders = ((List<?>) value).stream().filter(o -> o instanceof ModAnnotation.EnumHolder) + .map(o -> (ModAnnotation.EnumHolder) o).toList(); + if (!holders.isEmpty()) { + enabled = holders.stream() + .anyMatch(o -> Objects.equals(o.getValue(), FMLEnvironment.dist.name())); + } else { + enabled = true; + } + } else { + enabled = true; + } + + if (!enabled) continue; + + try { + Class<T> clazz = (Class<T>) Class.forName(annotation.memberName()); + if (predicate.test(clazz)) { + instances.add(new ImmutableTriple<>(modIds, () -> { + try { + return clazz.getDeclaredConstructor().newInstance(); + } catch (Throwable throwable) { + LOGGER.error("Failed to load plugin: " + annotation.memberName(), throwable); + return null; + } + }, clazz)); + } + } catch (Throwable throwable) { + Throwable t = throwable; + while (t != null) { + if (t.getMessage() != null && t.getMessage().contains("invalid dist DEDICATED_SERVER") && !PrimitivePlatformAdapter.get().isClient()) { + LOGGER.warn("Plugin " + annotation.memberName() + " is attempting to load on the server, but is not compatible with the server. " + + "The mod should declare the environments it is compatible with in the @" + annotationType.getClassName() + " annotation."); + continue out; + } + t = t.getCause(); + } + LOGGER.error("Failed to load plugin: " + annotation.memberName(), throwable); + } + } + } + } + + for (Triple<List<String>, Supplier<T>, Class<T>> pair : instances) { + consumer.accept(pair.getLeft(), pair.getMiddle(), pair.getRight()); + } + } +} diff --git a/neoforge/src/main/java/me/shedaniel/rei/forge/PluginDetectorImpl.java b/neoforge/src/main/java/me/shedaniel/rei/forge/PluginDetectorImpl.java new file mode 100644 index 000000000..f96f0b409 --- /dev/null +++ b/neoforge/src/main/java/me/shedaniel/rei/forge/PluginDetectorImpl.java @@ -0,0 +1,237 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.forge; + +import com.google.common.base.Suppliers; +import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; +import me.shedaniel.rei.api.client.plugins.REIClientPlugin; +import me.shedaniel.rei.api.common.plugins.PluginManager; +import me.shedaniel.rei.api.common.plugins.PluginView; +import me.shedaniel.rei.api.common.plugins.REIPluginProvider; +import me.shedaniel.rei.api.common.plugins.REIServerPlugin; +import me.shedaniel.rei.impl.init.PluginDetector; +import me.shedaniel.rei.plugin.client.forge.DefaultClientPluginImpl; +import me.shedaniel.rei.plugin.client.runtime.DefaultClientRuntimePlugin; +import me.shedaniel.rei.plugin.client.runtime.HideIngredientsFromTagsPlugin; +import me.shedaniel.rei.plugin.common.forge.DefaultPluginImpl; +import me.shedaniel.rei.plugin.common.runtime.DefaultRuntimePlugin; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext; +import net.neoforged.fml.loading.FMLEnvironment; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.Supplier; + +public class PluginDetectorImpl implements PluginDetector { + private static <P extends me.shedaniel.rei.api.common.plugins.REIPlugin<?>> REIPluginProvider<P> wrapPlugin(List<String> modId, REIPluginProvider<P> plugin) { + return new REIPluginProvider<P>() { + final String nameSuffix = " [" + String.join(", ", modId) + "]"; + + @Override + public Collection<P> provide() { + return plugin.provide(); + } + + @Override + public Class<P> getPluginProviderClass() { + return plugin.getPluginProviderClass(); + } + + @Override + public String getPluginProviderName() { + return plugin.getPluginProviderName() + nameSuffix; + } + }; + } + + private static final Supplier<List<Map.Entry<REIPluginProvider<me.shedaniel.rei.api.common.plugins.REIPlugin<?>>, List<String>>>> loaderProvided = + Suppliers.memoize(() -> getPluginsLoader(REIPluginLoader.class)); + private static final Supplier<List<Map.Entry<REIPluginProvider<me.shedaniel.rei.api.common.plugins.REIPlugin<?>>, List<String>>>> loaderProvidedCommon = + Suppliers.memoize(() -> getPluginsLoader(REIPluginLoaderCommon.class)); + private static final Supplier<List<Map.Entry<REIPluginProvider<me.shedaniel.rei.api.common.plugins.REIPlugin<?>>, List<String>>>> loaderProvidedDist; + + static { + Supplier<List<Map.Entry<REIPluginProvider<me.shedaniel.rei.api.common.plugins.REIPlugin<?>>, List<String>>>> dist; + if (FMLEnvironment.dist == Dist.CLIENT) { + dist = Suppliers.memoize(() -> getPluginsLoader(REIPluginLoaderClient.class)); + } else if (FMLEnvironment.dist == Dist.DEDICATED_SERVER) { + dist = Suppliers.memoize(() -> getPluginsLoader(REIPluginLoaderDedicatedServer.class)); + } else throw new IllegalStateException("Unknown environment: " + FMLEnvironment.dist); + loaderProvidedDist = dist; + } + + @NotNull + private static <T, P extends me.shedaniel.rei.api.common.plugins.REIPlugin<?>> List<Map.Entry<REIPluginProvider<P>, List<String>>> getPluginsLoader(Class<T> annotation) { + List<Map.Entry<REIPluginProvider<P>, List<String>>> list = new ArrayList<>(); + AnnotationUtils.<T, REIPluginProvider<P>>scanAnnotation(annotation, REIPluginProvider.class::isAssignableFrom, (modId, provider, clazz) -> { + try { + list.add(new AbstractMap.SimpleEntry<>(provider.get(), modId)); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + }); + return list; + } + + @Override + public void detectServerPlugins() { + PluginView.getServerInstance().registerPlugin(wrapPlugin(Collections.singletonList("roughlyenoughitems"), new DefaultPluginImpl())); + PluginView.getServerInstance().registerPlugin(wrapPlugin(Collections.singletonList("roughlyenoughitems"), new DefaultRuntimePlugin())); + + // Legacy Annotation + AnnotationUtils.<REIPlugin, REIServerPlugin>scanAnnotation(REIPlugin.class, REIServerPlugin.class::isAssignableFrom, (modId, plugin, clazz) -> { + ((PluginView<REIServerPlugin>) PluginManager.getServerInstance()).registerPlugin(wrapPlugin(modId, plugin.get())); + }); + + // Common plugins + AnnotationUtils.<REIPluginCommon, REIServerPlugin>scanAnnotation(REIPluginCommon.class, REIServerPlugin.class::isAssignableFrom, (modId, plugin, clazz) -> { + ((PluginView<REIServerPlugin>) PluginManager.getServerInstance()).registerPlugin(wrapPlugin(modId, plugin.get())); + }); + + // Dist plugins + if (FMLEnvironment.dist == Dist.DEDICATED_SERVER) { + AnnotationUtils.<REIPluginDedicatedServer, REIServerPlugin>scanAnnotation(REIPluginDedicatedServer.class, REIServerPlugin.class::isAssignableFrom, (modId, plugin, clazz) -> { + ((PluginView<REIServerPlugin>) PluginManager.getServerInstance()).registerPlugin(wrapPlugin(modId, plugin.get())); + }); + } else { + AnnotationUtils.<REIPluginClient, REIServerPlugin>scanAnnotation(REIPluginClient.class, REIServerPlugin.class::isAssignableFrom, (modId, plugin, clazz) -> { + ((PluginView<REIServerPlugin>) PluginManager.getServerInstance()).registerPlugin(wrapPlugin(modId, plugin.get())); + }); + } + + // Loaders + for (Map.Entry<REIPluginProvider<me.shedaniel.rei.api.common.plugins.REIPlugin<?>>, List<String>> entry : loaderProvided.get()) { + ((PluginView<REIServerPlugin>) PluginManager.getServerInstance()).registerPlugin(wrapPlugin(entry.getValue(), wrapAndFilter(entry.getKey(), REIServerPlugin.class))); + } + for (Map.Entry<REIPluginProvider<me.shedaniel.rei.api.common.plugins.REIPlugin<?>>, List<String>> entry : loaderProvidedCommon.get()) { + ((PluginView<REIServerPlugin>) PluginManager.getServerInst |
