diff --git a/build.gradle b/build.gradle index e417f354..adabc626 100644 --- a/build.gradle +++ b/build.gradle @@ -8,9 +8,8 @@ buildscript { plugins { id "architectury-plugin" version "3.4-SNAPSHOT" - id "dev.architectury.loom" version "1.1-SNAPSHOT" apply false + id "dev.architectury.loom" version "1.4-SNAPSHOT" apply false id "org.cadixdev.licenser" version "0.6.1" - id "io.github.juuxel.loom-vineflower" version "1.11.0" apply false id "me.shedaniel.unified-publishing" version "0.1.+" apply false id "maven-publish" } @@ -21,7 +20,6 @@ architectury { subprojects { apply plugin: "dev.architectury.loom" - apply plugin: "io.github.juuxel.loom-vineflower" loom { silentMojangMappingsLicense() @@ -31,6 +29,10 @@ subprojects { minecraft "com.mojang:minecraft:${rootProject.architectury.minecraft}" mappings loom.officialMojangMappings() } + + repositories { + maven { url "https://maven.neoforged.net/releases/" } + } } allprojects { @@ -56,7 +58,7 @@ allprojects { tasks.withType(JavaCompile) { options.encoding = "UTF-8" - options.release = 16 + options.release = 17 } javadoc { diff --git a/common/build.gradle b/common/build.gradle index ecfa9e1d..da395085 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -9,7 +9,9 @@ dependencies { } architectury { - common(rootProject.platforms.split(",")) + common(rootProject.platforms.split(",")) { + it.platformPackage "neoforge", "forge" + } } /** diff --git a/common/src/main/java/dev/architectury/impl/RegistrySupplierImpl.java b/common/src/main/java/dev/architectury/impl/RegistrySupplierImpl.java new file mode 100644 index 00000000..dd11c205 --- /dev/null +++ b/common/src/main/java/dev/architectury/impl/RegistrySupplierImpl.java @@ -0,0 +1,98 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.impl; + +import com.mojang.datafixers.util.Either; +import dev.architectury.registry.registries.RegistrySupplier; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderOwner; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Stream; + +@ApiStatus.Internal +public interface RegistrySupplierImpl extends RegistrySupplier { + @Nullable + Holder getHolder(); + + @Override + default T value() { + return get(); + } + + @Override + default boolean isBound() { + return isPresent(); + } + + @Override + default boolean is(ResourceLocation resourceLocation) { + return getId().equals(resourceLocation); + } + + @Override + default boolean is(ResourceKey resourceKey) { + return getKey().equals(resourceKey); + } + + @Override + default boolean is(Predicate> predicate) { + return predicate.test(getKey()); + } + + @Override + default boolean is(TagKey tagKey) { + Holder holder = getHolder(); + return holder != null && holder.is(tagKey); + } + + @Override + default Stream> tags() { + Holder holder = getHolder(); + return holder != null ? holder.tags() : Stream.empty(); + } + + @Override + default Either, T> unwrap() { + return Either.left(getKey()); + } + + @Override + default Optional> unwrapKey() { + return Optional.of(getKey()); + } + + @Override + default Kind kind() { + return Kind.REFERENCE; + } + + @Override + default boolean canSerializeIn(HolderOwner holderOwner) { + Holder holder = getHolder(); + return holder != null && holder.canSerializeIn(holderOwner); + } +} diff --git a/common/src/main/java/dev/architectury/platform/Platform.java b/common/src/main/java/dev/architectury/platform/Platform.java index ae864ec4..2380d1e5 100644 --- a/common/src/main/java/dev/architectury/platform/Platform.java +++ b/common/src/main/java/dev/architectury/platform/Platform.java @@ -41,11 +41,25 @@ public final class Platform { return simpleLoaderCache == 0; } + @Deprecated(forRemoval = true) public static boolean isForge() { + return isForgeLike(); + } + + public static boolean isForgeLike() { + return isMinecraftForge() || isNeoForge(); + } + + public static boolean isMinecraftForge() { updateLoaderCache(); return simpleLoaderCache == 1; } + public static boolean isNeoForge() { + updateLoaderCache(); + return simpleLoaderCache == 2; + } + private static void updateLoaderCache() { if (simpleLoaderCache != -1) { return; @@ -54,6 +68,7 @@ public final class Platform { switch (ArchitecturyTarget.getCurrentTarget()) { case "fabric" -> simpleLoaderCache = 0; case "forge" -> simpleLoaderCache = 1; + case "neoforge" -> simpleLoaderCache = 2; } } diff --git a/common/src/main/java/dev/architectury/registry/registries/DeferredRegister.java b/common/src/main/java/dev/architectury/registry/registries/DeferredRegister.java index af660738..e96e1b8c 100644 --- a/common/src/main/java/dev/architectury/registry/registries/DeferredRegister.java +++ b/common/src/main/java/dev/architectury/registry/registries/DeferredRegister.java @@ -20,6 +20,8 @@ package dev.architectury.registry.registries; import com.google.common.base.Suppliers; +import dev.architectury.impl.RegistrySupplierImpl; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; @@ -90,16 +92,25 @@ public class DeferredRegister implements Iterable> { return registriesSupplier.get().get(key); } - private class Entry implements RegistrySupplier { + private class Entry implements RegistrySupplierImpl { private final ResourceLocation id; private final Supplier supplier; private RegistrySupplier value; + @Nullable + private Holder holder = null; public Entry(ResourceLocation id, Supplier supplier) { this.id = id; this.supplier = supplier; } + @Nullable + @Override + public Holder getHolder() { + if (holder != null) return holder; + return holder = getRegistrar().getHolder(getId()); + } + @Override public RegistrarManager getRegistrarManager() { return DeferredRegister.this.getRegistrarManager(); diff --git a/common/src/main/java/dev/architectury/registry/registries/Registrar.java b/common/src/main/java/dev/architectury/registry/registries/Registrar.java index 775c7e29..27865b73 100644 --- a/common/src/main/java/dev/architectury/registry/registries/Registrar.java +++ b/common/src/main/java/dev/architectury/registry/registries/Registrar.java @@ -19,6 +19,7 @@ package dev.architectury.registry.registries; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; @@ -68,6 +69,14 @@ public interface Registrar extends Iterable { ResourceKey> key(); + @Nullable + Holder getHolder(ResourceKey key); + + @Nullable + default Holder getHolder(ResourceLocation id) { + return getHolder(ResourceKey.create(key(), id)); + } + /** * Listens to when the registry entry is registered, and calls the given action. * Evaluates immediately if the entry is already registered. diff --git a/common/src/main/java/dev/architectury/registry/registries/RegistrySupplier.java b/common/src/main/java/dev/architectury/registry/registries/RegistrySupplier.java index ba82555b..dfa8c0a8 100644 --- a/common/src/main/java/dev/architectury/registry/registries/RegistrySupplier.java +++ b/common/src/main/java/dev/architectury/registry/registries/RegistrySupplier.java @@ -19,12 +19,13 @@ package dev.architectury.registry.registries; +import net.minecraft.core.Holder; import org.jetbrains.annotations.ApiStatus; import java.util.function.Consumer; @ApiStatus.NonExtendable -public interface RegistrySupplier extends DeferredSupplier { +public interface RegistrySupplier extends DeferredSupplier, Holder { RegistrarManager getRegistrarManager(); Registrar getRegistrar(); diff --git a/common/src/main/java/dev/architectury/utils/ArchitecturyConstants.java b/common/src/main/java/dev/architectury/utils/ArchitecturyConstants.java new file mode 100644 index 00000000..39e5d512 --- /dev/null +++ b/common/src/main/java/dev/architectury/utils/ArchitecturyConstants.java @@ -0,0 +1,24 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.utils; + +public class ArchitecturyConstants { + public static final String MOD_ID = "architectury"; +} diff --git a/fabric/src/main/java/dev/architectury/registry/registries/fabric/RegistrarManagerImpl.java b/fabric/src/main/java/dev/architectury/registry/registries/fabric/RegistrarManagerImpl.java index 4a42c09b..35d76807 100644 --- a/fabric/src/main/java/dev/architectury/registry/registries/fabric/RegistrarManagerImpl.java +++ b/fabric/src/main/java/dev/architectury/registry/registries/fabric/RegistrarManagerImpl.java @@ -23,6 +23,7 @@ import com.google.common.base.Objects; import com.google.common.base.Suppliers; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import dev.architectury.impl.RegistrySupplierImpl; import dev.architectury.registry.registries.Registrar; import dev.architectury.registry.registries.RegistrarBuilder; import dev.architectury.registry.registries.RegistrarManager; @@ -32,6 +33,7 @@ import dev.architectury.registry.registries.options.StandardRegistrarOption; import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; import net.fabricmc.fabric.api.event.registry.RegistryAttribute; import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback; +import net.minecraft.core.Holder; import net.minecraft.core.MappedRegistry; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; @@ -153,7 +155,17 @@ public class RegistrarManagerImpl { public RegistrySupplier delegate(ResourceLocation id) { Supplier value = Suppliers.memoize(() -> get(id)); RegistrarImpl registrar = this; - return new RegistrySupplier<>() { + return new RegistrySupplierImpl() { + @Nullable + Holder holder = null; + + @Nullable + @Override + public Holder getHolder() { + if (holder != null) return holder; + return holder = registrar.getHolder(getId()); + } + @Override public RegistrarManager getRegistrarManager() { return RegistrarManager.get(modId); @@ -259,6 +271,12 @@ public class RegistrarManagerImpl { return delegate.key(); } + @Override + @Nullable + public Holder getHolder(ResourceKey key) { + return delegate.getHolder(key).orElse(null); + } + @Override public Iterator iterator() { return delegate.iterator(); diff --git a/forge/build.gradle b/forge/build.gradle index 7c7d84f6..d9384cce 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -1,109 +1,28 @@ -plugins { - id "com.github.johnrengelman.shadow" version "7.1.2" - id "me.shedaniel.unified-publishing" -} - loom { accessWidenerPath = project(":common").loom.accessWidenerPath - - forge { - mixinConfig "architectury.mixins.json" - mixinConfig "architectury-common.mixins.json" - - convertAccessWideners = true - extraAccessWideners.add loom.accessWidenerPath.get().asFile.name - } } architectury { - platformSetupLoomIde() - forge() -} - -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 - developmentForge.extendsFrom common + forgeLike(["forge", "neoforge"]) { + it.platformPackage "neoforge", "forge" + it.remapForgeLike "net/minecraftforge/common/extensions/IForgeItem", "net/neoforged/neoforge/common/extensions/IItemExtension" + it.remapForgeLike "net/minecraftforge/client/event/TextureStitchEvent\$Post", "net/neoforged/neoforge/client/event/TextureAtlasStitchedEvent" + it.remapForgeLike "net/minecraftforge/fluids/ForgeFlowingFluid", "net/neoforged/neoforge/fluids/BaseFlowingFluid" + it.remapForgeLike "net/minecraftforge/fluids/ForgeFlowingFluid\$Properties", "net/neoforged/neoforge/fluids/BaseFlowingFluid\$Properties" + it.remapForgeLike "net/minecraftforge/common/ForgeHooks", "net/neoforged/neoforge/common/CommonHooks" + } } dependencies { forge "net.minecraftforge:forge:${rootProject.architectury.minecraft}-${rootProject.forge_version}" - common(project(path: ":common", configuration: "namedElements")) { transitive false } - shadowCommon(project(path: ":common", configuration: "transformProductionForge")) { transitive false } -} - -processResources { - filesMatching("META-INF/mods.toml") { - expand "version": project.version - } - inputs.property "META-INF/mods.toml", project.version -} - -shadowJar { - exclude "fabric.mod.json" - exclude "architectury-common.accessWidener" - exclude "architectury.common.json" - - configurations = [project.configurations.shadowCommon] - archiveClassifier = "dev-shadow" - - // Replace classes with forge's version - exclude "dev/architectury/core/block/ArchitecturyLiquidBlock.class" - exclude "dev/architectury/core/fluid/ArchitecturyFlowingFluid.class" - exclude 'dev/architectury/core/fluid/ArchitecturyFlowingFluid$Source.class' - exclude 'dev/architectury/core/fluid/ArchitecturyFlowingFluid$Flowing.class' - exclude 'dev/architectury/core/item/ArchitecturyBucketItem.class' - exclude 'dev/architectury/core/item/ArchitecturyMobBucketItem.class' - relocate "dev.architectury.core.block.forge.imitator", "dev.architectury.core.block" - relocate "dev.architectury.core.fluid.forge.imitator", "dev.architectury.core.fluid" - relocate "dev.architectury.core.item.forge.imitator", "dev.architectury.core.item" -} - -remapJar { - input.set shadowJar.archiveFile - dependsOn shadowJar - archiveClassifier = null -} - -task renameJarForPublication(type: Zip, dependsOn: remapJar) { - from remapJar.archiveFile.map { zipTree(it) } - archiveExtension = "jar" - metadataCharset "UTF-8" - destinationDirectory = base.libsDirectory - archiveClassifier = project.name -} - -assemble.dependsOn renameJarForPublication - -jar { - archiveClassifier = "dev" -} - -sourcesJar { - afterEvaluate { - [":common"].forEach { - def depSources = project(it).sourcesJar - dependsOn depSources - from(depSources.archiveFile.map { zipTree(it) }) { - exclude "architectury.accessWidener" - } - } - } -} - -components.java { - withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { - skip() - } + compileOnly(project(path: ":common", configuration: "namedElements")) { transitive false } } publishing { publications { mavenForge(MavenPublication) { - artifactId = rootProject.archivesBaseName + "-forge" + artifactId = rootProject.archivesBaseName + "-forge-like" from components.java } } @@ -120,33 +39,3 @@ publishing { } } } - -unifiedPublishing { - project { - displayName = "[MinecraftForge $rootProject.supported_version] v$project.version" - releaseType = "$rootProject.artifact_type" - changelog = releaseChangelog() - gameVersions = ["1.20.2"] - gameLoaders = ["forge"] - mainPublication renameJarForPublication - - var CURSE_API_KEY = project.findProperty("CURSE_API_KEY") ?: System.getenv("CURSE_API_KEY") - if (CURSE_API_KEY != null) { - curseforge { - token = CURSE_API_KEY - id = rootProject.curseforge_id - gameVersions.addAll "Java 17" - } - } - - var MODRINTH_TOKEN = project.findProperty("MODRINTH_TOKEN") ?: System.getenv("MODRINTH_TOKEN") - if (MODRINTH_TOKEN != null) { - modrinth { - token = MODRINTH_TOKEN - id = rootProject.modrinth_id - version = "$project.version+$project.name" - } - } - } -} - diff --git a/forge/src/main/java/dev/architectury/event/forge/EventHandlerImpl.java b/forge/src/main/java/dev/architectury/event/forge/EventHandlerImpl.java index 86137582..690f5c15 100644 --- a/forge/src/main/java/dev/architectury/event/forge/EventHandlerImpl.java +++ b/forge/src/main/java/dev/architectury/event/forge/EventHandlerImpl.java @@ -19,8 +19,8 @@ package dev.architectury.event.forge; -import dev.architectury.forge.ArchitecturyForge; -import dev.architectury.platform.forge.EventBuses; +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.MinecraftForge; @@ -29,23 +29,23 @@ public class EventHandlerImpl { @OnlyIn(Dist.CLIENT) public static void registerClient() { MinecraftForge.EVENT_BUS.register(EventHandlerImplClient.class); - EventBuses.onRegistered(ArchitecturyForge.MOD_ID, bus -> { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { bus.register(EventHandlerImplClient.ModBasedEventHandler.class); }); } public static void registerCommon() { MinecraftForge.EVENT_BUS.register(EventHandlerImplCommon.class); - EventBuses.onRegistered(ArchitecturyForge.MOD_ID, bus -> { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { bus.register(EventHandlerImplCommon.ModBasedEventHandler.class); }); } @OnlyIn(Dist.DEDICATED_SERVER) public static void registerServer() { - MinecraftForge.EVENT_BUS.register(EventHandlerImplServer.class); - EventBuses.onRegistered(ArchitecturyForge.MOD_ID, bus -> { - bus.register(EventHandlerImplServer.ModBasedEventHandler.class); - }); + // MinecraftForge.EVENT_BUS.register(EventHandlerImplServer.class); + // EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + // bus.register(EventHandlerImplServer.ModBasedEventHandler.class); + // }); } } diff --git a/forge/src/main/java/dev/architectury/event/forge/EventHandlerImplClient.java b/forge/src/main/java/dev/architectury/event/forge/EventHandlerImplClient.java index 643d4aa5..ab6cca72 100644 --- a/forge/src/main/java/dev/architectury/event/forge/EventHandlerImplClient.java +++ b/forge/src/main/java/dev/architectury/event/forge/EventHandlerImplClient.java @@ -24,6 +24,7 @@ import dev.architectury.event.EventResult; import dev.architectury.event.events.client.ClientChatEvent; import dev.architectury.event.events.client.*; import dev.architectury.event.events.common.InteractionEvent; +import dev.architectury.hooks.forgelike.ForgeLikeClientHooks; import dev.architectury.impl.ScreenAccessImpl; import dev.architectury.impl.TooltipEventColorContextImpl; import dev.architectury.impl.TooltipEventPositionContextImpl; @@ -92,8 +93,8 @@ public class EventHandlerImplClient { @SubscribeEvent(priority = EventPriority.HIGH) public static void eventRenderGameOverlayEvent(CustomizeGuiOverlayEvent.DebugText event) { if (Minecraft.getInstance().gui.getDebugOverlay().showDebugScreen()) { - ClientGuiEvent.DEBUG_TEXT_LEFT.invoker().gatherText(event.getLeft()); - ClientGuiEvent.DEBUG_TEXT_RIGHT.invoker().gatherText(event.getRight()); + ClientGuiEvent.DEBUG_TEXT_LEFT.invoker().gatherText(ForgeLikeClientHooks.getLeft(event)); + ClientGuiEvent.DEBUG_TEXT_RIGHT.invoker().gatherText(ForgeLikeClientHooks.getRight(event)); } } @@ -217,14 +218,12 @@ public class EventHandlerImplClient { @SubscribeEvent(priority = EventPriority.HIGH) public static void eventMouseScrollEvent(ScreenEvent.MouseScrolled.Pre event) { - if (ClientScreenInputEvent.MOUSE_SCROLLED_PRE.invoker().mouseScrolled(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getDeltaX(), event.getDeltaY()).isFalse()) { - event.setCanceled(true); - } + ForgeLikeClientHooks.preMouseScroll(event); } @SubscribeEvent(priority = EventPriority.HIGH) public static void eventMouseScrollEvent(ScreenEvent.MouseScrolled.Post event) { - ClientScreenInputEvent.MOUSE_SCROLLED_POST.invoker().mouseScrolled(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getDeltaX(), event.getDeltaY()); + ForgeLikeClientHooks.postMouseScroll(event); } @SubscribeEvent(priority = EventPriority.HIGH) @@ -301,9 +300,7 @@ public class EventHandlerImplClient { @SubscribeEvent(priority = EventPriority.HIGH) public static void eventInputEvent(InputEvent.MouseScrollingEvent event) { - if (ClientRawInputEvent.MOUSE_SCROLLED.invoker().mouseScrolled(Minecraft.getInstance(), event.getDeltaX(), event.getDeltaY()).isFalse()) { - event.setCanceled(true); - } + ForgeLikeClientHooks.inputMouseScroll(event); } @SubscribeEvent(priority = EventPriority.HIGH) @@ -325,10 +322,10 @@ public class EventHandlerImplClient { @OnlyIn(Dist.CLIENT) public static class ModBasedEventHandler { - @SubscribeEvent(priority = EventPriority.HIGH) - public static void eventTextureStitchEvent(TextureStitchEvent.Post event) { - // ClientTextureStitchEvent.POST.invoker().stitch(event.getAtlas()); - } + // @SubscribeEvent(priority = EventPriority.HIGH) + // public static void eventTextureStitchEvent(TextureStitchEvent.Post event) { + // ClientTextureStitchEvent.POST.invoker().stitch(event.getAtlas()); + // } @SubscribeEvent(priority = EventPriority.HIGH) public static void event(FMLClientSetupEvent event) { diff --git a/forge/src/main/java/dev/architectury/event/forge/LootTableModificationContextImpl.java b/forge/src/main/java/dev/architectury/event/forge/LootTableModificationContextImpl.java index a58c09de..a8c57862 100644 --- a/forge/src/main/java/dev/architectury/event/forge/LootTableModificationContextImpl.java +++ b/forge/src/main/java/dev/architectury/event/forge/LootTableModificationContextImpl.java @@ -57,20 +57,30 @@ final class LootTableModificationContextImpl implements LootEvent.LootTableModif throw new RuntimeException(e); } } catch (NoSuchFieldException ignored2) { - for (Field field : LootTable.class.getDeclaredFields()) { - if (field.getType().equals(List.class)) { - // This is probably the field - field.setAccessible(true); - try { - pools = (List) field.get(table); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); + try { + Field field = LootTable.class.getDeclaredField("pools"); + field.setAccessible(true); + try { + pools = (List) field.get(table); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } catch (NoSuchFieldException ignored3) { + for (Field field : LootTable.class.getDeclaredFields()) { + if (field.getType().equals(List.class)) { + // This is probably the field + field.setAccessible(true); + try { + pools = (List) field.get(table); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } } } - } - - if (pools == null) { - throw new RuntimeException("Unable to find pools field in LootTable!"); + + if (pools == null) { + throw new RuntimeException("Unable to find pools field in LootTable!"); + } } } } diff --git a/forge/src/main/java/dev/architectury/hooks/client/screen/forge/ScreenHooksImpl.java b/forge/src/main/java/dev/architectury/hooks/client/screen/forge/ScreenHooksImpl.java index 062bfff9..f7acc49c 100644 --- a/forge/src/main/java/dev/architectury/hooks/client/screen/forge/ScreenHooksImpl.java +++ b/forge/src/main/java/dev/architectury/hooks/client/screen/forge/ScreenHooksImpl.java @@ -24,9 +24,7 @@ import net.minecraft.client.gui.components.Renderable; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.screens.Screen; -import net.minecraftforge.fml.util.ObfuscationReflectionHelper; -import java.lang.reflect.InvocationTargetException; import java.util.List; public class ScreenHooksImpl { @@ -39,26 +37,14 @@ public class ScreenHooksImpl { } public static T addRenderableWidget(Screen screen, T widget) { - try { - return (T) ObfuscationReflectionHelper.findMethod(Screen.class, "m_142416_", GuiEventListener.class).invoke(screen, widget); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } + return screen.addRenderableWidget(widget); } public static T addRenderableOnly(Screen screen, T listener) { - try { - return (T) ObfuscationReflectionHelper.findMethod(Screen.class, "m_169394_", Renderable.class).invoke(screen, listener); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } + return screen.addRenderableOnly(listener); } public static T addWidget(Screen screen, T listener) { - try { - return (T) ObfuscationReflectionHelper.findMethod(Screen.class, "m_7787_", GuiEventListener.class).invoke(screen, listener); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } + return screen.addWidget(listener); } } diff --git a/forge/src/main/java/dev/architectury/hooks/forgelike/ForgeLikeClientHooks.java b/forge/src/main/java/dev/architectury/hooks/forgelike/ForgeLikeClientHooks.java new file mode 100644 index 00000000..0b5015d4 --- /dev/null +++ b/forge/src/main/java/dev/architectury/hooks/forgelike/ForgeLikeClientHooks.java @@ -0,0 +1,56 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.forgelike; + +import dev.architectury.injectables.annotations.ExpectPlatform; +import net.minecraftforge.client.event.CustomizeGuiOverlayEvent; +import net.minecraftforge.client.event.InputEvent; +import net.minecraftforge.client.event.ScreenEvent; +import org.jetbrains.annotations.ApiStatus; + +import java.util.List; + +@ApiStatus.Internal +public class ForgeLikeClientHooks { + @ExpectPlatform + public static void preMouseScroll(ScreenEvent.MouseScrolled.Pre event) { + throw new AssertionError(); + } + + @ExpectPlatform + public static void postMouseScroll(ScreenEvent.MouseScrolled.Post event) { + throw new AssertionError(); + } + + @ExpectPlatform + public static void inputMouseScroll(InputEvent.MouseScrollingEvent event) { + throw new AssertionError(); + } + + @ExpectPlatform + public static List getLeft(CustomizeGuiOverlayEvent.DebugText event) { + throw new AssertionError(); + } + + @ExpectPlatform + public static List getRight(CustomizeGuiOverlayEvent.DebugText event) { + throw new AssertionError(); + } +} diff --git a/forge/src/main/java/dev/architectury/hooks/forgelike/ForgeLikeHooks.java b/forge/src/main/java/dev/architectury/hooks/forgelike/ForgeLikeHooks.java new file mode 100644 index 00000000..0c743a92 --- /dev/null +++ b/forge/src/main/java/dev/architectury/hooks/forgelike/ForgeLikeHooks.java @@ -0,0 +1,36 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.forgelike; + +import com.mojang.serialization.Codec; +import dev.architectury.injectables.annotations.ExpectPlatform; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.common.world.BiomeModifier; +import org.jetbrains.annotations.ApiStatus; + +import java.util.function.Supplier; + +@ApiStatus.Internal +public class ForgeLikeHooks { + @ExpectPlatform + public static void registerBiomeModifier(ResourceLocation id, Supplier> codecSupplier) { + throw new AssertionError(); + } +} diff --git a/forge/src/main/java/dev/architectury/hooks/level/entity/forge/PlayerHooksImpl.java b/forge/src/main/java/dev/architectury/hooks/level/entity/forge/PlayerHooksImpl.java index 6eb9a1fe..720e778f 100644 --- a/forge/src/main/java/dev/architectury/hooks/level/entity/forge/PlayerHooksImpl.java +++ b/forge/src/main/java/dev/architectury/hooks/level/entity/forge/PlayerHooksImpl.java @@ -19,11 +19,11 @@ package dev.architectury.hooks.level.entity.forge; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.player.Player; -import net.minecraftforge.common.util.FakePlayer; public class PlayerHooksImpl { public static boolean isFake(Player playerEntity) { - return playerEntity instanceof FakePlayer; + return playerEntity instanceof ServerPlayer && playerEntity.getClass() != ServerPlayer.class; } } diff --git a/forge/src/main/java/dev/architectury/mixin/forge/MixinMinecraft.java b/forge/src/main/java/dev/architectury/mixin/forge/MixinMinecraft.java index 562414b1..841046a2 100644 --- a/forge/src/main/java/dev/architectury/mixin/forge/MixinMinecraft.java +++ b/forge/src/main/java/dev/architectury/mixin/forge/MixinMinecraft.java @@ -29,7 +29,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; // adopted from fabric @Mixin(Minecraft.class) public abstract class MixinMinecraft { - @Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/client/Minecraft;gameThread:Ljava/lang/Thread;", shift = At.Shift.AFTER, ordinal = 0), method = "run") + @Inject(at = @At(value = "INVOKE", target = "Ljava/lang/Runtime;getRuntime()Ljava/lang/Runtime;", ordinal = 0), method = "run") private void onStart(CallbackInfo ci) { ClientLifecycleEvent.CLIENT_STARTED.invoker().stateChanged((Minecraft) (Object) this); } diff --git a/forge/src/main/java/dev/architectury/platform/hooks/EventBusesHooks.java b/forge/src/main/java/dev/architectury/platform/hooks/EventBusesHooks.java new file mode 100644 index 00000000..81b172f7 --- /dev/null +++ b/forge/src/main/java/dev/architectury/platform/hooks/EventBusesHooks.java @@ -0,0 +1,41 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.platform.hooks; + +import dev.architectury.injectables.annotations.ExpectPlatform; +import net.minecraftforge.eventbus.api.IEventBus; + +import java.util.Optional; +import java.util.function.Consumer; + +public final class EventBusesHooks { + private EventBusesHooks() { + } + + @ExpectPlatform + public static void whenAvailable(String modId, Consumer busConsumer) { + throw new AssertionError(); + } + + @ExpectPlatform + public static Optional getModEventBus(String modId) { + throw new AssertionError(); + } +} diff --git a/forge/src/main/java/dev/architectury/plugin/forge/ArchitecturyMixinPlugin.java b/forge/src/main/java/dev/architectury/plugin/forge/ArchitecturyMixinPlugin.java index 59171e25..cd95d776 100644 --- a/forge/src/main/java/dev/architectury/plugin/forge/ArchitecturyMixinPlugin.java +++ b/forge/src/main/java/dev/architectury/plugin/forge/ArchitecturyMixinPlugin.java @@ -19,6 +19,7 @@ package dev.architectury.plugin.forge; +import dev.architectury.injectables.targets.ArchitecturyTarget; import org.objectweb.asm.tree.ClassNode; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; @@ -29,7 +30,7 @@ import java.util.Set; public class ArchitecturyMixinPlugin implements IMixinConfigPlugin { @Override public void onLoad(String mixinPackage) { - + } @Override @@ -44,21 +45,23 @@ public class ArchitecturyMixinPlugin implements IMixinConfigPlugin { @Override public void acceptTargets(Set myTargets, Set otherTargets) { - + } @Override public List getMixins() { - return null; + return ArchitecturyTarget.getCurrentTarget().equals("forge") + ? List.of() + : List.of("neoforge.MixinChunkSerializer"); } @Override public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - + } @Override public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - + } } diff --git a/forge/src/main/java/dev/architectury/registry/client/keymappings/forge/KeyMappingRegistryImpl.java b/forge/src/main/java/dev/architectury/registry/client/keymappings/forge/KeyMappingRegistryImpl.java index 1f51d8c7..6cb52ce9 100644 --- a/forge/src/main/java/dev/architectury/registry/client/keymappings/forge/KeyMappingRegistryImpl.java +++ b/forge/src/main/java/dev/architectury/registry/client/keymappings/forge/KeyMappingRegistryImpl.java @@ -19,14 +19,12 @@ package dev.architectury.registry.client.keymappings.forge; -import dev.architectury.forge.ArchitecturyForge; +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; import net.minecraft.client.Options; -import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.RegisterKeyMappingsEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -34,12 +32,17 @@ import org.apache.logging.log4j.Logger; import java.util.ArrayList; import java.util.List; -@Mod.EventBusSubscriber(modid = ArchitecturyForge.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) public class KeyMappingRegistryImpl { private static final Logger LOGGER = LogManager.getLogger(KeyMappingRegistryImpl.class); private static final List MAPPINGS = new ArrayList<>(); private static boolean eventCalled = false; + static { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(KeyMappingRegistryImpl::event); + }); + } + public static void register(KeyMapping mapping) { if (eventCalled) { Options options = Minecraft.getInstance().options; @@ -50,7 +53,6 @@ public class KeyMappingRegistryImpl { } } - @SubscribeEvent public static void event(RegisterKeyMappingsEvent event) { MAPPINGS.forEach(event::register); eventCalled = true; diff --git a/forge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityModelLayerRegistryImpl.java b/forge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityModelLayerRegistryImpl.java index ca20b084..95e3ff1f 100644 --- a/forge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityModelLayerRegistryImpl.java +++ b/forge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityModelLayerRegistryImpl.java @@ -19,8 +19,8 @@ package dev.architectury.registry.client.level.entity.forge; -import dev.architectury.forge.ArchitecturyForge; -import dev.architectury.platform.forge.EventBuses; +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; import net.minecraft.client.model.geom.ModelLayerLocation; import net.minecraft.client.model.geom.builders.LayerDefinition; import net.minecraftforge.client.event.EntityRenderersEvent; @@ -34,7 +34,7 @@ public class EntityModelLayerRegistryImpl { private static final Map> DEFINITIONS = new ConcurrentHashMap<>(); static { - EventBuses.onRegistered(ArchitecturyForge.MOD_ID, bus -> { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { bus.register(EntityModelLayerRegistryImpl.class); }); } diff --git a/forge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityRendererRegistryImpl.java b/forge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityRendererRegistryImpl.java index c47d9905..fa263c5b 100644 --- a/forge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityRendererRegistryImpl.java +++ b/forge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityRendererRegistryImpl.java @@ -19,8 +19,8 @@ package dev.architectury.registry.client.level.entity.forge; -import dev.architectury.forge.ArchitecturyForge; -import dev.architectury.platform.forge.EventBuses; +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; @@ -39,7 +39,7 @@ public class EntityRendererRegistryImpl { } static { - EventBuses.onRegistered(ArchitecturyForge.MOD_ID, bus -> { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { bus.register(EntityRendererRegistryImpl.class); }); } diff --git a/forge/src/main/java/dev/architectury/registry/client/particle/forge/ParticleProviderRegistryImpl.java b/forge/src/main/java/dev/architectury/registry/client/particle/forge/ParticleProviderRegistryImpl.java index b081a03e..b0ff7900 100644 --- a/forge/src/main/java/dev/architectury/registry/client/particle/forge/ParticleProviderRegistryImpl.java +++ b/forge/src/main/java/dev/architectury/registry/client/particle/forge/ParticleProviderRegistryImpl.java @@ -20,8 +20,9 @@ package dev.architectury.registry.client.particle.forge; import com.mojang.logging.LogUtils; -import dev.architectury.forge.ArchitecturyForge; +import dev.architectury.platform.hooks.EventBusesHooks; import dev.architectury.registry.client.particle.ParticleProviderRegistry; +import dev.architectury.utils.ArchitecturyConstants; import net.minecraft.client.Minecraft; import net.minecraft.client.particle.ParticleEngine; import net.minecraft.client.particle.ParticleProvider; @@ -31,21 +32,22 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleType; import net.minecraft.util.RandomSource; -import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.RegisterParticleProvidersEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; import org.slf4j.Logger; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -@Mod.EventBusSubscriber(modid = ArchitecturyForge.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD) public class ParticleProviderRegistryImpl { - public static final Logger LOGGER = LogUtils.getLogger(); + static { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(ParticleProviderRegistryImpl::onParticleFactoryRegister); + }); + } + private static final class ExtendedSpriteSetImpl implements ParticleProviderRegistry.ExtendedSpriteSet { private final ParticleEngine engine; private final SpriteSet delegate; @@ -105,7 +107,6 @@ public class ParticleProviderRegistryImpl { } } - @SubscribeEvent public static void onParticleFactoryRegister(RegisterParticleProvidersEvent event) { if (deferred != null) { ParticleProviderRegistrar registrar = ParticleProviderRegistrar.ofForge(event); diff --git a/forge/src/main/java/dev/architectury/registry/client/rendering/forge/ColorHandlerRegistryImpl.java b/forge/src/main/java/dev/architectury/registry/client/rendering/forge/ColorHandlerRegistryImpl.java index f473a186..15f32563 100644 --- a/forge/src/main/java/dev/architectury/registry/client/rendering/forge/ColorHandlerRegistryImpl.java +++ b/forge/src/main/java/dev/architectury/registry/client/rendering/forge/ColorHandlerRegistryImpl.java @@ -20,8 +20,8 @@ package dev.architectury.registry.client.rendering.forge; import com.google.common.collect.Lists; -import dev.architectury.forge.ArchitecturyForge; -import dev.architectury.platform.forge.EventBuses; +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; import net.minecraft.client.Minecraft; import net.minecraft.client.color.block.BlockColor; import net.minecraft.client.color.item.ItemColor; @@ -40,7 +40,7 @@ public class ColorHandlerRegistryImpl { private static final List[]>> BLOCK_COLORS = Lists.newArrayList(); static { - EventBuses.onRegistered(ArchitecturyForge.MOD_ID, bus -> { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { bus.register(ColorHandlerRegistryImpl.class); }); } diff --git a/forge/src/main/java/dev/architectury/registry/forge/CreativeTabRegistryImpl.java b/forge/src/main/java/dev/architectury/registry/forge/CreativeTabRegistryImpl.java index 7a3eedce..e7313b2e 100644 --- a/forge/src/main/java/dev/architectury/registry/forge/CreativeTabRegistryImpl.java +++ b/forge/src/main/java/dev/architectury/registry/forge/CreativeTabRegistryImpl.java @@ -22,21 +22,19 @@ package dev.architectury.registry.forge; import com.google.common.base.Suppliers; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; -import dev.architectury.forge.ArchitecturyForge; +import dev.architectury.platform.hooks.EventBusesHooks; import dev.architectury.registry.CreativeTabOutput; import dev.architectury.registry.CreativeTabRegistry; import dev.architectury.registry.registries.DeferredSupplier; +import dev.architectury.utils.ArchitecturyConstants; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.common.CreativeModeTabRegistry; import net.minecraftforge.common.util.MutableHashedLinkedMap; import net.minecraftforge.event.BuildCreativeModeTabContentsEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.ApiStatus; @@ -46,7 +44,6 @@ import java.util.*; import java.util.function.Consumer; import java.util.function.Supplier; -@Mod.EventBusSubscriber(modid = ArchitecturyForge.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) public class CreativeTabRegistryImpl { private static final Logger LOGGER = LogManager.getLogger(CreativeTabRegistryImpl.class); @@ -54,6 +51,10 @@ public class CreativeTabRegistryImpl { private static final Multimap> APPENDS = MultimapBuilder.hashKeys().arrayListValues().build(); static { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(CreativeTabRegistryImpl::event); + }); + BUILD_CONTENTS_LISTENERS.add(event -> { for (Map.Entry>> keyEntry : APPENDS.asMap().entrySet()) { Supplier> stacks = Suppliers.memoize(() -> keyEntry.getValue().stream() @@ -76,7 +77,6 @@ public class CreativeTabRegistryImpl { }); } - @SubscribeEvent public static void event(BuildCreativeModeTabContentsEvent event) { for (Consumer listener : BUILD_CONTENTS_LISTENERS) { listener.accept(event); diff --git a/forge/src/main/java/dev/architectury/registry/forge/ReloadListenerRegistryImpl.java b/forge/src/main/java/dev/architectury/registry/forge/ReloadListenerRegistryImpl.java index ab0c59f9..b48ca9ea 100644 --- a/forge/src/main/java/dev/architectury/registry/forge/ReloadListenerRegistryImpl.java +++ b/forge/src/main/java/dev/architectury/registry/forge/ReloadListenerRegistryImpl.java @@ -20,7 +20,8 @@ package dev.architectury.registry.forge; import com.google.common.collect.Lists; -import dev.architectury.forge.ArchitecturyForge; +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.PackType; @@ -28,18 +29,20 @@ import net.minecraft.server.packs.resources.PreparableReloadListener; import net.minecraft.server.packs.resources.ReloadableResourceManager; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.AddReloadListenerEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.List; -@Mod.EventBusSubscriber(modid = ArchitecturyForge.MOD_ID) public class ReloadListenerRegistryImpl { private static List serverDataReloadListeners = Lists.newArrayList(); + static { + MinecraftForge.EVENT_BUS.addListener(ReloadListenerRegistryImpl::addReloadListeners); + } + public static void register(PackType type, PreparableReloadListener listener, @Nullable ResourceLocation listenerId, Collection dependencies) { if (type == PackType.SERVER_DATA) { serverDataReloadListeners.add(listener); @@ -53,7 +56,6 @@ public class ReloadListenerRegistryImpl { ((ReloadableResourceManager) Minecraft.getInstance().getResourceManager()).registerReloadListener(listener); } - @SubscribeEvent public static void addReloadListeners(AddReloadListenerEvent event) { for (PreparableReloadListener listener : serverDataReloadListeners) { event.addListener(listener); diff --git a/forge/src/main/java/dev/architectury/registry/level/biome/forge/BiomeModificationsImpl.java b/forge/src/main/java/dev/architectury/registry/level/biome/forge/BiomeModificationsImpl.java index 48d0b4b0..efc5233a 100644 --- a/forge/src/main/java/dev/architectury/registry/level/biome/forge/BiomeModificationsImpl.java +++ b/forge/src/main/java/dev/architectury/registry/level/biome/forge/BiomeModificationsImpl.java @@ -21,10 +21,10 @@ package dev.architectury.registry.level.biome.forge; import com.google.common.collect.Lists; import com.mojang.serialization.Codec; -import dev.architectury.forge.ArchitecturyForge; +import dev.architectury.hooks.forgelike.ForgeLikeHooks; import dev.architectury.hooks.level.biome.*; -import dev.architectury.platform.forge.EventBuses; import dev.architectury.registry.level.biome.BiomeModifications.BiomeContext; +import dev.architectury.utils.ArchitecturyConstants; import dev.architectury.utils.GameInstance; import net.minecraft.core.Holder; import net.minecraft.core.Registry; @@ -42,8 +42,6 @@ import net.minecraft.world.level.levelgen.GenerationStep; import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraftforge.common.world.*; -import net.minecraftforge.registries.ForgeRegistries; -import net.minecraftforge.registries.RegisterEvent; import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.Nullable; @@ -64,14 +62,8 @@ public class BiomeModificationsImpl { private static Codec noneBiomeModCodec = null; public static void init() { - EventBuses.onRegistered(ArchitecturyForge.MOD_ID, bus -> { - bus.addListener(event -> { - event.register(ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, registry -> { - registry.register(new ResourceLocation(ArchitecturyForge.MOD_ID, "none_biome_mod_codec"), - noneBiomeModCodec = Codec.unit(BiomeModifierImpl.INSTANCE)); - }); - }); - }); + ForgeLikeHooks.registerBiomeModifier(new ResourceLocation(ArchitecturyConstants.MOD_ID, "none_biome_mod_codec"), + () -> noneBiomeModCodec = Codec.unit(BiomeModifierImpl.INSTANCE)); } public static void addProperties(Predicate predicate, BiConsumer modifier) { diff --git a/forge/src/main/java/dev/architectury/registry/level/entity/forge/EntityAttributeRegistryImpl.java b/forge/src/main/java/dev/architectury/registry/level/entity/forge/EntityAttributeRegistryImpl.java index cad28083..56a61b68 100644 --- a/forge/src/main/java/dev/architectury/registry/level/entity/forge/EntityAttributeRegistryImpl.java +++ b/forge/src/main/java/dev/architectury/registry/level/entity/forge/EntityAttributeRegistryImpl.java @@ -19,8 +19,8 @@ package dev.architectury.registry.level.entity.forge; -import dev.architectury.forge.ArchitecturyForge; -import dev.architectury.platform.forge.EventBuses; +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; @@ -39,7 +39,7 @@ public class EntityAttributeRegistryImpl { } static { - EventBuses.onRegistered(ArchitecturyForge.MOD_ID, bus -> { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { bus.register(EntityAttributeRegistryImpl.class); }); } diff --git a/forge/src/main/java/dev/architectury/registry/level/entity/trade/forge/TradeRegistryImpl.java b/forge/src/main/java/dev/architectury/registry/level/entity/trade/forge/TradeRegistryImpl.java index 2867a12a..00d88602 100644 --- a/forge/src/main/java/dev/architectury/registry/level/entity/trade/forge/TradeRegistryImpl.java +++ b/forge/src/main/java/dev/architectury/registry/level/entity/trade/forge/TradeRegistryImpl.java @@ -21,23 +21,25 @@ package dev.architectury.registry.level.entity.trade.forge; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import dev.architectury.forge.ArchitecturyForge; import net.minecraft.core.NonNullList; import net.minecraft.world.entity.npc.VillagerProfession; import net.minecraft.world.entity.npc.VillagerTrades; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.village.VillagerTradesEvent; import net.minecraftforge.event.village.WandererTradesEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; import java.util.*; -@Mod.EventBusSubscriber(modid = ArchitecturyForge.MOD_ID) public class TradeRegistryImpl { private static final Map>> TRADES_TO_ADD = new HashMap<>(); private static final List WANDERER_TRADER_TRADES_GENERIC = new ArrayList<>(); private static final List WANDERER_TRADER_TRADES_RARE = new ArrayList<>(); + static { + MinecraftForge.EVENT_BUS.addListener(TradeRegistryImpl::onTradeRegistering); + MinecraftForge.EVENT_BUS.addListener(TradeRegistryImpl::onWanderingTradeRegistering); + } + public static void registerVillagerTrade0(VillagerProfession profession, int level, VillagerTrades.ItemListing... trades) { Int2ObjectMap> tradesForProfession = TRADES_TO_ADD.computeIfAbsent(profession, $ -> new Int2ObjectOpenHashMap<>()); List tradesForLevel = tradesForProfession.computeIfAbsent(level, $ -> new ArrayList<>()); @@ -52,7 +54,6 @@ public class TradeRegistryImpl { } } - @SubscribeEvent public static void onTradeRegistering(VillagerTradesEvent event) { Int2ObjectMap> trades = TRADES_TO_ADD.get(event.getType()); @@ -63,7 +64,6 @@ public class TradeRegistryImpl { } } - @SubscribeEvent public static void onWanderingTradeRegistering(WandererTradesEvent event) { if (!WANDERER_TRADER_TRADES_GENERIC.isEmpty()) { event.getGenericTrades().addAll(WANDERER_TRADER_TRADES_GENERIC); diff --git a/forge/src/main/resources/architectury.mixins.json b/forge/src/main/resources/architectury.mixins.json index 4242d539..2ebafd4d 100644 --- a/forge/src/main/resources/architectury.mixins.json +++ b/forge/src/main/resources/architectury.mixins.json @@ -9,7 +9,6 @@ "MixinMinecraft" ], "mixins": [ - "MixinChunkSerializer", "MixinEntitySpawnExtension", "MixinFallingBlockEntity", "MixinItemExtension", diff --git a/gradle.properties b/gradle.properties index 5426fbf0..9196dffd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,8 @@ fabric_loader_version=0.14.23 fabric_api_version=0.90.3+1.20.3 mod_menu_version=7.0.0 -forge_version=48.0.1 +forge_version=48.0.38 +neoforge_version=20.2.59-beta curseforge_id=419699 modrinth_id=lhGA9TYQ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f72df95a..e411586a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/minecraftforge/build.gradle b/minecraftforge/build.gradle new file mode 100644 index 00000000..2d870236 --- /dev/null +++ b/minecraftforge/build.gradle @@ -0,0 +1,155 @@ +plugins { + id "com.github.johnrengelman.shadow" version "7.1.2" + id "me.shedaniel.unified-publishing" +} + +loom { + accessWidenerPath = project(":common").loom.accessWidenerPath + + forge { + mixinConfig "architectury.mixins.json" + mixinConfig "architectury-common.mixins.json" + mixinConfig "architectury-forge.mixins.json" + + convertAccessWideners = true + extraAccessWideners.add loom.accessWidenerPath.get().asFile.name + } +} + +architectury { + platformSetupLoomIde() + forge() +} + +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 + developmentForge.extendsFrom common +} + +dependencies { + forge "net.minecraftforge:forge:${rootProject.architectury.minecraft}-${rootProject.forge_version}" + + common(project(path: ":common", configuration: "namedElements")) { transitive false } + common(project(path: ":forge", configuration: "namedElements")) { transitive false } + shadowCommon(project(path: ":common", configuration: "transformProductionForge")) { transitive false } + shadowCommon(project(path: ":forge", configuration: "transformProductionForge")) { transitive false } +} + +processResources { + filesMatching("META-INF/mods.toml") { + expand "version": project.version + } + inputs.property "META-INF/mods.toml", project.version +} + +shadowJar { + exclude "fabric.mod.json" + exclude "architectury-common.accessWidener" + exclude "architectury.common.json" + + configurations = [project.configurations.shadowCommon] + archiveClassifier = "dev-shadow" + + // Replace classes with forge's version + exclude "dev/architectury/core/block/ArchitecturyLiquidBlock.class" + exclude "dev/architectury/core/fluid/ArchitecturyFlowingFluid.class" + exclude 'dev/architectury/core/fluid/ArchitecturyFlowingFluid$Source.class' + exclude 'dev/architectury/core/fluid/ArchitecturyFlowingFluid$Flowing.class' + exclude 'dev/architectury/core/item/ArchitecturyBucketItem.class' + exclude 'dev/architectury/core/item/ArchitecturyMobBucketItem.class' + relocate "dev.architectury.core.block.forge.imitator", "dev.architectury.core.block" + relocate "dev.architectury.core.fluid.forge.imitator", "dev.architectury.core.fluid" + relocate "dev.architectury.core.item.forge.imitator", "dev.architectury.core.item" +} + +remapJar { + input.set shadowJar.archiveFile + dependsOn shadowJar + archiveClassifier = null +} + +task renameJarForPublication(type: Zip, dependsOn: remapJar) { + from remapJar.archiveFile.map { zipTree(it) } + archiveExtension = "jar" + metadataCharset "UTF-8" + destinationDirectory = base.libsDirectory + archiveClassifier = project.name +} + +assemble.dependsOn renameJarForPublication + +jar { + archiveClassifier = "dev" +} + +sourcesJar { + afterEvaluate { + [":common"].forEach { + def depSources = project(it).sourcesJar + dependsOn depSources + from(depSources.archiveFile.map { zipTree(it) }) { + exclude "architectury.accessWidener" + } + } + } +} + +components.java { + withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { + skip() + } +} + +publishing { + publications { + mavenMinecraftForge(MavenPublication) { + artifactId = rootProject.archivesBaseName + "-forge" + from components.java + } + } + + repositories { + if (System.getenv("MAVEN_PASS") != null) { + maven { + url = "https://deploy.shedaniel.me/" + credentials { + username = "shedaniel" + password = System.getenv("MAVEN_PASS") + } + } + } + } +} + +unifiedPublishing { + project { + displayName = "[MinecraftForge $rootProject.supported_version] v$project.version" + releaseType = "$rootProject.artifact_type" + changelog = releaseChangelog() + gameVersions = ["1.20.2"] + gameLoaders = ["forge"] + mainPublication renameJarForPublication + + var CURSE_API_KEY = project.findProperty("CURSE_API_KEY") ?: System.getenv("CURSE_API_KEY") + if (CURSE_API_KEY != null) { + curseforge { + token = CURSE_API_KEY + id = rootProject.curseforge_id + gameVersions.addAll "Java 17" + } + } + + var MODRINTH_TOKEN = project.findProperty("MODRINTH_TOKEN") ?: System.getenv("MODRINTH_TOKEN") + if (MODRINTH_TOKEN != null) { + modrinth { + token = MODRINTH_TOKEN + id = rootProject.modrinth_id + version = "$project.version+$project.name" + } + } + } +} + diff --git a/minecraftforge/gradle.properties b/minecraftforge/gradle.properties new file mode 100644 index 00000000..32f842a6 --- /dev/null +++ b/minecraftforge/gradle.properties @@ -0,0 +1 @@ +loom.platform=forge \ No newline at end of file diff --git a/forge/src/main/java/dev/architectury/forge/ArchitecturyForge.java b/minecraftforge/src/main/java/dev/architectury/forge/ArchitecturyForge.java similarity index 86% rename from forge/src/main/java/dev/architectury/forge/ArchitecturyForge.java rename to minecraftforge/src/main/java/dev/architectury/forge/ArchitecturyForge.java index ed8c789a..3c574a84 100644 --- a/forge/src/main/java/dev/architectury/forge/ArchitecturyForge.java +++ b/minecraftforge/src/main/java/dev/architectury/forge/ArchitecturyForge.java @@ -19,18 +19,17 @@ package dev.architectury.forge; -import dev.architectury.platform.forge.EventBuses; import dev.architectury.event.EventHandler; +import dev.architectury.platform.forge.EventBuses; import dev.architectury.registry.level.biome.forge.BiomeModificationsImpl; +import dev.architectury.utils.ArchitecturyConstants; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -@Mod(ArchitecturyForge.MOD_ID) +@Mod(ArchitecturyConstants.MOD_ID) public class ArchitecturyForge { - public static final String MOD_ID = "architectury"; - public ArchitecturyForge() { - EventBuses.registerModEventBus(ArchitecturyForge.MOD_ID, FMLJavaModLoadingContext.get().getModEventBus()); + EventBuses.registerModEventBus(ArchitecturyConstants.MOD_ID, FMLJavaModLoadingContext.get().getModEventBus()); EventHandler.init(); BiomeModificationsImpl.init(); } diff --git a/minecraftforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeClientHooksImpl.java b/minecraftforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeClientHooksImpl.java new file mode 100644 index 00000000..61573c8e --- /dev/null +++ b/minecraftforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeClientHooksImpl.java @@ -0,0 +1,55 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.forgelike.forge; + +import dev.architectury.event.events.client.ClientRawInputEvent; +import dev.architectury.event.events.client.ClientScreenInputEvent; +import net.minecraft.client.Minecraft; +import net.minecraftforge.client.event.CustomizeGuiOverlayEvent; +import net.minecraftforge.client.event.InputEvent; +import net.minecraftforge.client.event.ScreenEvent; + +import java.util.List; + +public class ForgeLikeClientHooksImpl { + public static void preMouseScroll(ScreenEvent.MouseScrolled.Pre event) { + if (ClientScreenInputEvent.MOUSE_SCROLLED_PRE.invoker().mouseScrolled(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getDeltaX(), event.getDeltaY()).isFalse()) { + event.setCanceled(true); + } + } + + public static void postMouseScroll(ScreenEvent.MouseScrolled.Post event) { + ClientScreenInputEvent.MOUSE_SCROLLED_POST.invoker().mouseScrolled(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getDeltaX(), event.getDeltaY()); + } + + public static void inputMouseScroll(InputEvent.MouseScrollingEvent event) { + if (ClientRawInputEvent.MOUSE_SCROLLED.invoker().mouseScrolled(Minecraft.getInstance(), event.getDeltaX(), event.getDeltaY()).isFalse()) { + event.setCanceled(true); + } + } + + public static List getLeft(CustomizeGuiOverlayEvent.DebugText event) { + return event.getLeft(); + } + + public static List getRight(CustomizeGuiOverlayEvent.DebugText event) { + return event.getRight(); + } +} diff --git a/minecraftforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeHooksImpl.java b/minecraftforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeHooksImpl.java new file mode 100644 index 00000000..fbb4b4fb --- /dev/null +++ b/minecraftforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeHooksImpl.java @@ -0,0 +1,42 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.forgelike.forge; + +import com.mojang.serialization.Codec; +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.common.world.BiomeModifier; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.RegisterEvent; + +import java.util.function.Supplier; + +public class ForgeLikeHooksImpl { + public static void registerBiomeModifier(ResourceLocation id, Supplier> codecSupplier) { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(event -> { + event.register(ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, registry -> { + registry.register(id, codecSupplier.get()); + }); + }); + }); + } +} diff --git a/forge/src/main/java/dev/architectury/mixin/forge/MixinChunkSerializer.java b/minecraftforge/src/main/java/dev/architectury/mixin/forge/minecraftforge/MixinChunkSerializer.java similarity index 98% rename from forge/src/main/java/dev/architectury/mixin/forge/MixinChunkSerializer.java rename to minecraftforge/src/main/java/dev/architectury/mixin/forge/minecraftforge/MixinChunkSerializer.java index fa0ef07b..488e6281 100644 --- a/forge/src/main/java/dev/architectury/mixin/forge/MixinChunkSerializer.java +++ b/minecraftforge/src/main/java/dev/architectury/mixin/forge/minecraftforge/MixinChunkSerializer.java @@ -17,7 +17,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package dev.architectury.mixin.forge; +package dev.architectury.mixin.forge.minecraftforge; import dev.architectury.event.forge.EventHandlerImplCommon; import net.minecraft.nbt.CompoundTag; diff --git a/forge/src/main/java/dev/architectury/networking/forge/ClientNetworkingManager.java b/minecraftforge/src/main/java/dev/architectury/networking/forge/ClientNetworkingManager.java similarity index 100% rename from forge/src/main/java/dev/architectury/networking/forge/ClientNetworkingManager.java rename to minecraftforge/src/main/java/dev/architectury/networking/forge/ClientNetworkingManager.java diff --git a/forge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java b/minecraftforge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java similarity index 99% rename from forge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java rename to minecraftforge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java index 3f1d2170..635d6410 100644 --- a/forge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java +++ b/minecraftforge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java @@ -22,7 +22,6 @@ package dev.architectury.networking.forge; import com.google.common.collect.*; import com.mojang.logging.LogUtils; -import dev.architectury.forge.ArchitecturyForge; import dev.architectury.networking.NetworkManager; import dev.architectury.networking.NetworkManager.NetworkReceiver; import dev.architectury.networking.transformers.PacketSink; @@ -52,7 +51,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.function.Consumer; -@Mod.EventBusSubscriber(modid = ArchitecturyForge.MOD_ID) +@Mod.EventBusSubscriber(modid = "architectury") public class NetworkManagerImpl { public static void registerReceiver(NetworkManager.Side side, ResourceLocation id, List packetTransformers, NetworkReceiver receiver) { Objects.requireNonNull(id, "Cannot register receiver with a null ID!"); diff --git a/forge/src/main/java/dev/architectury/platform/forge/EventBuses.java b/minecraftforge/src/main/java/dev/architectury/platform/forge/EventBuses.java similarity index 100% rename from forge/src/main/java/dev/architectury/platform/forge/EventBuses.java rename to minecraftforge/src/main/java/dev/architectury/platform/forge/EventBuses.java diff --git a/forge/src/main/java/dev/architectury/platform/forge/PlatformImpl.java b/minecraftforge/src/main/java/dev/architectury/platform/forge/PlatformImpl.java similarity index 100% rename from forge/src/main/java/dev/architectury/platform/forge/PlatformImpl.java rename to minecraftforge/src/main/java/dev/architectury/platform/forge/PlatformImpl.java diff --git a/minecraftforge/src/main/java/dev/architectury/platform/hooks/forge/EventBusesHooksImpl.java b/minecraftforge/src/main/java/dev/architectury/platform/hooks/forge/EventBusesHooksImpl.java new file mode 100644 index 00000000..e7142fb5 --- /dev/null +++ b/minecraftforge/src/main/java/dev/architectury/platform/hooks/forge/EventBusesHooksImpl.java @@ -0,0 +1,36 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.platform.hooks.forge; + +import dev.architectury.platform.forge.EventBuses; +import net.minecraftforge.eventbus.api.IEventBus; + +import java.util.Optional; +import java.util.function.Consumer; + +public class EventBusesHooksImpl { + public static void whenAvailable(String modId, Consumer busConsumer) { + EventBuses.onRegistered(modId, busConsumer); + } + + public static Optional getModEventBus(String modId) { + return EventBuses.getModEventBus(modId); + } +} diff --git a/forge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java b/minecraftforge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java similarity index 92% rename from forge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java rename to minecraftforge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java index 4f454f52..a688b5b8 100644 --- a/forge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java +++ b/minecraftforge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java @@ -23,13 +23,15 @@ import com.google.common.base.Objects; import com.google.common.base.Suppliers; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; -import dev.architectury.platform.forge.EventBuses; +import dev.architectury.impl.RegistrySupplierImpl; +import dev.architectury.platform.hooks.EventBusesHooks; import dev.architectury.registry.registries.Registrar; import dev.architectury.registry.registries.RegistrarBuilder; import dev.architectury.registry.registries.RegistrarManager; import dev.architectury.registry.registries.RegistrySupplier; import dev.architectury.registry.registries.options.RegistrarOption; import dev.architectury.registry.registries.options.StandardRegistrarOption; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceKey; @@ -123,7 +125,7 @@ public class RegistrarManagerImpl { public RegistryProviderImpl(String modId) { this.modId = modId; this.eventBus = Suppliers.memoize(() -> { - IEventBus eventBus = EventBuses.getModEventBus(modId).orElseThrow(() -> new IllegalStateException("Can't get event bus for mod '" + modId + "' because it was not registered!")); + IEventBus eventBus = EventBusesHooks.getModEventBus(modId).orElseThrow(() -> new IllegalStateException("Can't get event bus for mod '" + modId + "' because it was not registered!")); eventBus.register(new EventListener()); return eventBus; }); @@ -322,7 +324,17 @@ public class RegistrarManagerImpl { public RegistrySupplier delegate(ResourceLocation id) { Supplier value = Suppliers.memoize(() -> get(id)); Registrar registrar = this; - return new RegistrySupplier<>() { + return new RegistrySupplierImpl() { + @Nullable + Holder holder = null; + + @Nullable + @Override + public Holder getHolder() { + if (holder != null) return holder; + return holder = registrar.getHolder(getId()); + } + @Override public RegistrarManager getRegistrarManager() { return RegistrarManager.get(modId); @@ -432,6 +444,12 @@ public class RegistrarManagerImpl { return delegate.key(); } + @Override + @Nullable + public Holder getHolder(ResourceKey key) { + return delegate.getHolder(key).orElse(null); + } + @Override public Iterator iterator() { return delegate.iterator(); @@ -462,7 +480,17 @@ public class RegistrarManagerImpl { public RegistrySupplier delegate(ResourceLocation id) { Supplier value = Suppliers.memoize(() -> get(id)); Registrar registrar = this; - return new RegistrySupplier<>() { + return new RegistrySupplierImpl() { + @Nullable + Holder holder = null; + + @Nullable + @Override + public Holder getHolder() { + if (holder != null) return holder; + return holder = registrar.getHolder(getId()); + } + @Override public RegistrarManager getRegistrarManager() { return RegistrarManager.get(modId); @@ -519,7 +547,17 @@ public class RegistrarManagerImpl { Data data = (Data) registry.computeIfAbsent(key(), type -> new Data<>()); data.registerForForge(delegate, id, objectArr, supplier); Registrar registrar = this; - return new RegistrySupplier<>() { + return new RegistrySupplierImpl() { + @Nullable + Holder holder = null; + + @Nullable + @Override + public Holder getHolder() { + if (holder != null) return holder; + return holder = getRegistrar().getHolder(getId()); + } + @Override public RegistrarManager getRegistrarManager() { return RegistrarManager.get(modId); @@ -625,6 +663,12 @@ public class RegistrarManagerImpl { return ResourceKey.createRegistryKey(delegate.getRegistryName()); } + @Override + @Nullable + public Holder getHolder(ResourceKey key) { + return delegate.getHolder(key).orElse(null); + } + @Override public Iterator iterator() { return delegate.iterator(); @@ -668,7 +712,17 @@ public class RegistrarManagerImpl { @Override public RegistrySupplier delegate(ResourceLocation id) { if (isReady()) return delegate.get().delegate(id); - return new RegistrySupplier<>() { + return new RegistrySupplierImpl() { + @Nullable + Holder holder = null; + + @Nullable + @Override + public Holder getHolder() { + if (holder != null || !isReady()) return holder; + return holder = delegate.get().getHolder(getId()); + } + @Override public RegistrarManager getRegistrarManager() { return RegistrarManager.get(modId); @@ -761,6 +815,12 @@ public class RegistrarManagerImpl { return isReady() ? delegate.get().key() : ResourceKey.createRegistryKey(registryId); } + @Override + @Nullable + public Holder getHolder(ResourceKey key) { + return isReady() ? delegate.get().getHolder(key) : null; + } + @Override public void listen(ResourceLocation id, Consumer callback) { if (isReady()) { diff --git a/forge/src/main/resources/META-INF/mods.toml b/minecraftforge/src/main/resources/META-INF/mods.toml similarity index 100% rename from forge/src/main/resources/META-INF/mods.toml rename to minecraftforge/src/main/resources/META-INF/mods.toml diff --git a/minecraftforge/src/main/resources/architectury-forge.mixins.json b/minecraftforge/src/main/resources/architectury-forge.mixins.json new file mode 100644 index 00000000..a95e797e --- /dev/null +++ b/minecraftforge/src/main/resources/architectury-forge.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "package": "dev.architectury.mixin.forge", + "compatibilityLevel": "JAVA_16", + "minVersion": "0.8", + "client": [ + ], + "mixins": [ + "minecraftforge.MixinChunkSerializer" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/forge/src/main/resources/icon.png b/minecraftforge/src/main/resources/icon.png similarity index 100% rename from forge/src/main/resources/icon.png rename to minecraftforge/src/main/resources/icon.png diff --git a/forge/src/main/resources/pack.mcmeta b/minecraftforge/src/main/resources/pack.mcmeta similarity index 100% rename from forge/src/main/resources/pack.mcmeta rename to minecraftforge/src/main/resources/pack.mcmeta diff --git a/neoforge/build.gradle b/neoforge/build.gradle new file mode 100644 index 00000000..f9b51c05 --- /dev/null +++ b/neoforge/build.gradle @@ -0,0 +1,156 @@ +plugins { + id "com.github.johnrengelman.shadow" version "7.1.2" + id "me.shedaniel.unified-publishing" +} + +loom { + accessWidenerPath = project(":common").loom.accessWidenerPath +} + +architectury { + platformSetupLoomIde() + neoForge { + platformPackage = "forge" + remapForgeLike "net/minecraftforge/common/extensions/IForgeItem", "net/neoforged/neoforge/common/extensions/IItemExtension" + remapForgeLike "net/minecraftforge/client/event/TextureStitchEvent\$Post", "net/neoforged/neoforge/client/event/TextureAtlasStitchedEvent" + remapForgeLike "net/minecraftforge/fluids/ForgeFlowingFluid", "net/neoforged/neoforge/fluids/BaseFlowingFluid" + remapForgeLike "net/minecraftforge/fluids/ForgeFlowingFluid\$Properties", "net/neoforged/neoforge/fluids/BaseFlowingFluid\$Properties" + remapForgeLike "net/minecraftforge/common/ForgeHooks", "net/neoforged/neoforge/common/CommonHooks" + } +} + +configurations { + common + forgeLike + shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this. + compileClasspath.extendsFrom common, forgeLike + runtimeClasspath.extendsFrom common, forgeLike + developmentNeoForge.extendsFrom common + developmentForgeLike.extendsFrom forgeLike +} + +dependencies { + neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}" + + common(project(path: ":common", configuration: "namedElements")) { transitive false } + forgeLike(project(path: ":forge", configuration: "namedElements")) { transitive false } + shadowCommon(project(path: ":common", configuration: "transformProductionNeoForge")) { transitive false } + shadowCommon(project(path: ":forge", configuration: "transformProductionNeoForge")) { transitive false } +} + +processResources { + filesMatching("META-INF/mods.toml") { + expand "version": project.version + } + inputs.property "META-INF/mods.toml", project.version +} + +shadowJar { + exclude "fabric.mod.json" + exclude "architectury-common.accessWidener" + exclude "architectury.common.json" + + configurations = [project.configurations.shadowCommon] + archiveClassifier = "dev-shadow" + + // Replace classes with forge's version + exclude "dev/architectury/core/block/ArchitecturyLiquidBlock.class" + exclude "dev/architectury/core/fluid/ArchitecturyFlowingFluid.class" + exclude 'dev/architectury/core/fluid/ArchitecturyFlowingFluid$Source.class' + exclude 'dev/architectury/core/fluid/ArchitecturyFlowingFluid$Flowing.class' + exclude 'dev/architectury/core/item/ArchitecturyBucketItem.class' + exclude 'dev/architectury/core/item/ArchitecturyMobBucketItem.class' + relocate "dev.architectury.core.block.forge.imitator", "dev.architectury.core.block" + relocate "dev.architectury.core.fluid.forge.imitator", "dev.architectury.core.fluid" + relocate "dev.architectury.core.item.forge.imitator", "dev.architectury.core.item" +} + +remapJar { + input.set shadowJar.archiveFile + dependsOn shadowJar + archiveClassifier = null + atAccessWideners.add "architectury.accessWidener" +} + +task renameJarForPublication(type: Zip, dependsOn: remapJar) { + from remapJar.archiveFile.map { zipTree(it) } + archiveExtension = "jar" + metadataCharset "UTF-8" + destinationDirectory = base.libsDirectory + archiveClassifier = project.name +} + +assemble.dependsOn renameJarForPublication + +jar { + archiveClassifier = "dev" +} + +sourcesJar { + afterEvaluate { + [":common"].forEach { + def depSources = project(it).sourcesJar + dependsOn depSources + from(depSources.archiveFile.map { zipTree(it) }) { + exclude "architectury.accessWidener" + } + } + } +} + +components.java { + withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) { + skip() + } +} + +publishing { + publications { + mavenNeoForge(MavenPublication) { + artifactId = rootProject.archivesBaseName + "-neoforge" + from components.java + } + } + + repositories { + if (System.getenv("MAVEN_PASS") != null) { + maven { + url = "https://deploy.shedaniel.me/" + credentials { + username = "shedaniel" + password = System.getenv("MAVEN_PASS") + } + } + } + } +} + +unifiedPublishing { + project { + displayName = "[NeoForge $rootProject.supported_version] v$project.version" + releaseType = "$rootProject.artifact_type" + changelog = releaseChangelog() + gameVersions = ["1.20.2"] + gameLoaders = ["neoforge"] + mainPublication renameJarForPublication + + var CURSE_API_KEY = project.findProperty("CURSE_API_KEY") ?: System.getenv("CURSE_API_KEY") + if (CURSE_API_KEY != null) { + curseforge { + token = CURSE_API_KEY + id = rootProject.curseforge_id + gameVersions.addAll "Java 17" + } + } + + var MODRINTH_TOKEN = project.findProperty("MODRINTH_TOKEN") ?: System.getenv("MODRINTH_TOKEN") + if (MODRINTH_TOKEN != null) { + modrinth { + token = MODRINTH_TOKEN + id = rootProject.modrinth_id + version = "$project.version+$project.name" + } + } + } +} + diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties new file mode 100644 index 00000000..2914393d --- /dev/null +++ b/neoforge/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge \ No newline at end of file diff --git a/neoforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeClientHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeClientHooksImpl.java new file mode 100644 index 00000000..208d0a82 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeClientHooksImpl.java @@ -0,0 +1,55 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.forgelike.forge; + +import dev.architectury.event.events.client.ClientRawInputEvent; +import dev.architectury.event.events.client.ClientScreenInputEvent; +import net.minecraft.client.Minecraft; +import net.neoforged.neoforge.client.event.CustomizeGuiOverlayEvent; +import net.neoforged.neoforge.client.event.InputEvent; +import net.neoforged.neoforge.client.event.ScreenEvent; + +import java.util.List; + +public class ForgeLikeClientHooksImpl { + public static void preMouseScroll(ScreenEvent.MouseScrolled.Pre event) { + if (ClientScreenInputEvent.MOUSE_SCROLLED_PRE.invoker().mouseScrolled(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getScrollDeltaX(), event.getScrollDeltaY()).isFalse()) { + event.setCanceled(true); + } + } + + public static void postMouseScroll(ScreenEvent.MouseScrolled.Post event) { + ClientScreenInputEvent.MOUSE_SCROLLED_POST.invoker().mouseScrolled(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getScrollDeltaX(), event.getScrollDeltaY()); + } + + public static void inputMouseScroll(InputEvent.MouseScrollingEvent event) { + if (ClientRawInputEvent.MOUSE_SCROLLED.invoker().mouseScrolled(Minecraft.getInstance(), event.getScrollDeltaX(), event.getScrollDeltaY()).isFalse()) { + event.setCanceled(true); + } + } + + public static List getLeft(CustomizeGuiOverlayEvent.DebugText event) { + return event.getLeft(); + } + + public static List getRight(CustomizeGuiOverlayEvent.DebugText event) { + return event.getRight(); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeHooksImpl.java new file mode 100644 index 00000000..8d656007 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeHooksImpl.java @@ -0,0 +1,42 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.forgelike.forge; + +import com.mojang.serialization.Codec; +import dev.architectury.platform.hooks.forge.EventBusesHooksImpl; +import dev.architectury.utils.ArchitecturyConstants; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.neoforge.common.world.BiomeModifier; +import net.neoforged.neoforge.registries.NeoForgeRegistries; +import net.neoforged.neoforge.registries.RegisterEvent; + +import java.util.function.Supplier; + +public class ForgeLikeHooksImpl { + public static void registerBiomeModifier(ResourceLocation id, Supplier> codecSupplier) { + EventBusesHooksImpl.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(event -> { + event.register(NeoForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, registry -> { + registry.register(id, codecSupplier.get()); + }); + }); + }); + } +} diff --git a/neoforge/src/main/java/dev/architectury/mixin/forge/neoforge/MixinChunkSerializer.java b/neoforge/src/main/java/dev/architectury/mixin/forge/neoforge/MixinChunkSerializer.java new file mode 100644 index 00000000..741ae0fb --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/mixin/forge/neoforge/MixinChunkSerializer.java @@ -0,0 +1,64 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.mixin.forge.neoforge; + +import dev.architectury.event.forge.EventHandlerImplCommon; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.ai.village.poi.PoiManager; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.ProtoChunk; +import net.minecraft.world.level.chunk.storage.ChunkSerializer; +import net.neoforged.bus.api.Event; +import net.neoforged.neoforge.event.level.ChunkDataEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyArg; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.lang.ref.WeakReference; + +@Mixin(ChunkSerializer.class) +public class MixinChunkSerializer { + @Unique + private static ThreadLocal> level = new ThreadLocal<>(); + + @Inject(method = "read", at = @At("HEAD")) + private static void read(ServerLevel worldIn, PoiManager arg2, ChunkPos arg3, CompoundTag arg4, CallbackInfoReturnable cir) { + level.set(new WeakReference<>(worldIn)); + } + + @ModifyArg(method = "read", at = @At(value = "INVOKE", + ordinal = 1, + target = "Lnet/neoforged/bus/api/IEventBus;post(Lnet/neoforged/bus/api/Event;)Lnet/neoforged/bus/api/Event;"), + index = 0) + private static Event modifyProtoChunkLevel(Event event) { + // We should get this PRed to Forge + WeakReference levelRef = level.get(); + if (levelRef != null && event instanceof ChunkDataEvent.Load) { + ChunkDataEvent.Load load = (ChunkDataEvent.Load) event; + ((EventHandlerImplCommon.LevelEventAttachment) load).architectury$attachLevel(levelRef.get()); + } + level.remove(); + return event; + } +} diff --git a/neoforge/src/main/java/dev/architectury/neoforge/ArchitecturyNeoForge.java b/neoforge/src/main/java/dev/architectury/neoforge/ArchitecturyNeoForge.java new file mode 100644 index 00000000..a07507d4 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/neoforge/ArchitecturyNeoForge.java @@ -0,0 +1,33 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.neoforge; + +import dev.architectury.event.EventHandler; +import dev.architectury.registry.level.biome.forge.BiomeModificationsImpl; +import dev.architectury.utils.ArchitecturyConstants; +import net.neoforged.fml.common.Mod; + +@Mod(ArchitecturyConstants.MOD_ID) +public class ArchitecturyNeoForge { + public ArchitecturyNeoForge() { + EventHandler.init(); + BiomeModificationsImpl.init(); + } +} diff --git a/neoforge/src/main/java/dev/architectury/networking/forge/ClientNetworkingManager.java b/neoforge/src/main/java/dev/architectury/networking/forge/ClientNetworkingManager.java new file mode 100644 index 00000000..0c15977b --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/networking/forge/ClientNetworkingManager.java @@ -0,0 +1,63 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.networking.forge; + +import dev.architectury.networking.NetworkManager; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.network.PlayNetworkDirection; + +import java.util.Collections; +import java.util.Set; + +@OnlyIn(Dist.CLIENT) +public class ClientNetworkingManager { + public static void initClient() { + NetworkManagerImpl.CHANNEL.addListener(NetworkManagerImpl.createPacketHandler(PlayNetworkDirection.PLAY_TO_CLIENT, NetworkManagerImpl.S2C_TRANSFORMERS)); + NeoForge.EVENT_BUS.register(ClientNetworkingManager.class); + + NetworkManagerImpl.registerS2CReceiver(NetworkManagerImpl.SYNC_IDS, Collections.emptyList(), (buffer, context) -> { + Set receivables = NetworkManagerImpl.serverReceivables; + int size = buffer.readInt(); + receivables.clear(); + for (int i = 0; i < size; i++) { + receivables.add(buffer.readResourceLocation()); + } + context.queue(() -> { + NetworkManager.sendToServer(NetworkManagerImpl.SYNC_IDS, NetworkManagerImpl.sendSyncPacket(NetworkManagerImpl.C2S)); + }); + }); + } + + public static Player getClientPlayer() { + return Minecraft.getInstance().player; + } + + @SubscribeEvent + public static void loggedOut(ClientPlayerNetworkEvent.LoggingOut event) { + NetworkManagerImpl.serverReceivables.clear(); + } +} diff --git a/neoforge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java b/neoforge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java new file mode 100644 index 00000000..05241d3f --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java @@ -0,0 +1,203 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.networking.forge; + + +import com.google.common.collect.*; +import com.mojang.logging.LogUtils; +import dev.architectury.networking.NetworkManager; +import dev.architectury.networking.NetworkManager.NetworkReceiver; +import dev.architectury.networking.transformers.PacketSink; +import dev.architectury.networking.transformers.PacketTransformer; +import dev.architectury.utils.ArchitecturyConstants; +import dev.architectury.utils.Env; +import io.netty.buffer.Unpooled; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.DistExecutor; +import net.neoforged.fml.LogicalSide; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; +import net.neoforged.neoforge.network.*; +import net.neoforged.neoforge.network.event.EventNetworkChannel; +import org.slf4j.Logger; + +import java.util.*; +import java.util.function.Consumer; + +@Mod.EventBusSubscriber(modid = ArchitecturyConstants.MOD_ID) +public class NetworkManagerImpl { + public static void registerReceiver(NetworkManager.Side side, ResourceLocation id, List packetTransformers, NetworkReceiver receiver) { + Objects.requireNonNull(id, "Cannot register receiver with a null ID!"); + packetTransformers = Objects.requireNonNullElse(packetTransformers, List.of()); + Objects.requireNonNull(receiver, "Cannot register a null receiver!"); + if (side == NetworkManager.Side.C2S) { + registerC2SReceiver(id, packetTransformers, receiver); + } else if (side == NetworkManager.Side.S2C) { + registerS2CReceiver(id, packetTransformers, receiver); + } + } + + public static Packet toPacket(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buffer) { + FriendlyByteBuf packetBuffer = new FriendlyByteBuf(Unpooled.buffer()); + packetBuffer.writeResourceLocation(id); + packetBuffer.writeBytes(buffer); + return (side == NetworkManager.Side.C2S ? PlayNetworkDirection.PLAY_TO_SERVER : PlayNetworkDirection.PLAY_TO_CLIENT).buildPacket(new INetworkDirection.PacketData(packetBuffer, 0), CHANNEL_ID); + } + + public static void collectPackets(PacketSink sink, NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf) { + PacketTransformer transformer = side == NetworkManager.Side.C2S ? C2S_TRANSFORMERS.get(id) : S2C_TRANSFORMERS.get(id); + if (transformer != null) { + transformer.outbound(side, id, buf, (side1, id1, buf1) -> { + sink.accept(toPacket(side1, id1, buf1)); + }); + } else { + sink.accept(toPacket(side, id, buf)); + } + } + + private static final Logger LOGGER = LogUtils.getLogger(); + private static final ResourceLocation CHANNEL_ID = new ResourceLocation("architectury:network"); + static final ResourceLocation SYNC_IDS = new ResourceLocation("architectury:sync_ids"); + static final EventNetworkChannel CHANNEL = NetworkRegistry.ChannelBuilder.named(CHANNEL_ID).networkProtocolVersion(() -> "").clientAcceptedVersions(version -> true).serverAcceptedVersions(version -> true).eventNetworkChannel(); + static final Map S2C = Maps.newHashMap(); + static final Map C2S = Maps.newHashMap(); + static final Map S2C_TRANSFORMERS = Maps.newHashMap(); + static final Map C2S_TRANSFORMERS = Maps.newHashMap(); + static final Set serverReceivables = Sets.newHashSet(); + private static final Multimap clientReceivables = Multimaps.newMultimap(Maps.newHashMap(), Sets::newHashSet); + + static { + CHANNEL.addListener(createPacketHandler(PlayNetworkDirection.PLAY_TO_SERVER, C2S_TRANSFORMERS)); + + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> ClientNetworkingManager::initClient); + + registerC2SReceiver(SYNC_IDS, Collections.emptyList(), (buffer, context) -> { + Set receivables = (Set) clientReceivables.get(context.getPlayer()); + int size = buffer.readInt(); + receivables.clear(); + for (int i = 0; i < size; i++) { + receivables.add(buffer.readResourceLocation()); + } + }); + } + + static Consumer createPacketHandler(INetworkDirection direction, Map map) { + return event -> { + NetworkEvent.Context context = event.getSource(); + if (context.getDirection() != direction) return; + if (context.getPacketHandled()) return; + FriendlyByteBuf buffer = event.getPayload(); + if (buffer == null) return; + ResourceLocation type = buffer.readResourceLocation(); + PacketTransformer transformer = map.get(type); + + if (transformer != null) { + NetworkManager.Side side = context.getDirection().getReceptionSide() == LogicalSide.CLIENT ? NetworkManager.Side.S2C : NetworkManager.Side.C2S; + NetworkManager.PacketContext packetContext = new NetworkManager.PacketContext() { + @Override + public Player getPlayer() { + return getEnvironment() == Env.CLIENT ? getClientPlayer() : context.getSender(); + } + + @Override + public void queue(Runnable runnable) { + context.enqueueWork(runnable); + } + + @Override + public Env getEnvironment() { + return context.getDirection().getReceptionSide() == LogicalSide.CLIENT ? Env.CLIENT : Env.SERVER; + } + + private Player getClientPlayer() { + return DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> ClientNetworkingManager::getClientPlayer); + } + }; + transformer.inbound(side, type, buffer, packetContext, (side1, id1, buf1) -> { + NetworkReceiver networkReceiver = side == NetworkManager.Side.C2S ? C2S.get(id1) : S2C.get(id1); + if (networkReceiver == null) { + throw new IllegalArgumentException("Network Receiver not found! " + id1); + } + networkReceiver.receive(buf1, packetContext); + }); + } else { + LOGGER.error("Unknown message ID: " + type); + } + + context.setPacketHandled(true); + }; + } + + @OnlyIn(Dist.CLIENT) + public static void registerS2CReceiver(ResourceLocation id, List packetTransformers, NetworkReceiver receiver) { + LOGGER.info("Registering S2C receiver with id {}", id); + S2C.put(id, receiver); + PacketTransformer transformer = PacketTransformer.concat(packetTransformers); + S2C_TRANSFORMERS.put(id, transformer); + } + + public static void registerC2SReceiver(ResourceLocation id, List packetTransformers, NetworkReceiver receiver) { + LOGGER.info("Registering C2S receiver with id {}", id); + C2S.put(id, receiver); + PacketTransformer transformer = PacketTransformer.concat(packetTransformers); + C2S_TRANSFORMERS.put(id, transformer); + } + + public static boolean canServerReceive(ResourceLocation id) { + return serverReceivables.contains(id); + } + + public static boolean canPlayerReceive(ServerPlayer player, ResourceLocation id) { + return clientReceivables.get(player).contains(id); + } + + public static Packet createAddEntityPacket(Entity entity) { + return NetworkHooks.getEntitySpawningPacket(entity); + } + + static FriendlyByteBuf sendSyncPacket(Map map) { + List availableIds = Lists.newArrayList(map.keySet()); + FriendlyByteBuf packetBuffer = new FriendlyByteBuf(Unpooled.buffer()); + packetBuffer.writeInt(availableIds.size()); + for (ResourceLocation availableId : availableIds) { + packetBuffer.writeResourceLocation(availableId); + } + return packetBuffer; + } + + @SubscribeEvent + public static void loggedIn(PlayerEvent.PlayerLoggedInEvent event) { + NetworkManager.sendToPlayer((ServerPlayer) event.getEntity(), SYNC_IDS, sendSyncPacket(C2S)); + } + + @SubscribeEvent + public static void loggedOut(PlayerEvent.PlayerLoggedOutEvent event) { + clientReceivables.removeAll(event.getEntity()); + } +} diff --git a/neoforge/src/main/java/dev/architectury/platform/forge/PlatformImpl.java b/neoforge/src/main/java/dev/architectury/platform/forge/PlatformImpl.java new file mode 100644 index 00000000..214b7ab3 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/platform/forge/PlatformImpl.java @@ -0,0 +1,185 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.platform.forge; + +import dev.architectury.platform.Mod; +import dev.architectury.utils.Env; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.ModList; +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.fml.loading.FMLPaths; +import net.neoforged.fml.loading.moddiscovery.ModFileInfo; +import net.neoforged.neoforge.client.ConfigScreenHandler; +import net.neoforged.neoforgespi.language.IModFileInfo; +import net.neoforged.neoforgespi.language.IModInfo; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +public class PlatformImpl { + private static final Map mods = new ConcurrentHashMap<>(); + + public static Path getGameFolder() { + return FMLPaths.GAMEDIR.get(); + } + + public static Path getConfigFolder() { + return FMLPaths.CONFIGDIR.get(); + } + + public static Path getModsFolder() { + return FMLPaths.MODSDIR.get(); + } + + public static Env getEnvironment() { + return Env.fromPlatform(getEnv()); + } + + public static Dist getEnv() { + return FMLEnvironment.dist; + } + + public static boolean isModLoaded(String id) { + return ModList.get().isLoaded(id); + } + + public static Mod getMod(String id) { + return mods.computeIfAbsent(id, ModImpl::new); + } + + public static Collection getMods() { + for (IModInfo mod : ModList.get().getMods()) { + getMod(mod.getModId()); + } + return mods.values(); + } + + public static Collection getModIds() { + return ModList.get().getMods().stream().map(IModInfo::getModId).collect(Collectors.toList()); + } + + public static boolean isDevelopmentEnvironment() { + return !FMLLoader.isProduction(); + } + + private static class ModImpl implements Mod { + private final ModContainer container; + private final IModInfo info; + + public ModImpl(String id) { + this.container = ModList.get().getModContainerById(id).orElseThrow(); + this.info = ModList.get().getMods().stream() + .filter(modInfo -> Objects.equals(modInfo.getModId(), id)) + .findAny() + .orElseThrow(); + } + + @Override + @NotNull + public String getModId() { + return info.getModId(); + } + + @Override + @NotNull + public String getVersion() { + return info.getVersion().toString(); + } + + @Override + @NotNull + public String getName() { + return info.getDisplayName(); + } + + @Override + @NotNull + public String getDescription() { + return info.getDescription(); + } + + @Override + public Optional getLogoFile(int i) { + return this.info.getLogoFile(); + } + + @Override + public List getFilePaths() { + return List.of(getFilePath()); + } + + @Override + public Path getFilePath() { + return this.info.getOwningFile().getFile().getSecureJar().getRootPath(); + } + + @Override + public Optional findResource(String... path) { + return Optional.of(this.info.getOwningFile().getFile().findResource(path)).filter(Files::exists); + } + + @Override + public Collection getAuthors() { + Optional optional = this.info.getConfig().getConfigElement("authors") + .map(String::valueOf); + return optional.isPresent() ? Collections.singleton(optional.get()) : Collections.emptyList(); + } + + @Override + public @Nullable Collection getLicense() { + return Collections.singleton(this.info.getOwningFile().getLicense()); + } + + @Override + public Optional getHomepage() { + return this.info.getConfig().getConfigElement("displayURL") + .map(String::valueOf); + } + + @Override + public Optional getSources() { + return Optional.empty(); + } + + @Override + public Optional getIssueTracker() { + IModFileInfo owningFile = this.info.getOwningFile(); + if (owningFile instanceof ModFileInfo info) { + return Optional.ofNullable(info.getIssueURL()) + .map(URL::toString); + } + return Optional.empty(); + } + + @Override + public void registerConfigurationScreen(ConfigurationScreenProvider configurationScreenProvider) { + container.registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class, () -> + new ConfigScreenHandler.ConfigScreenFactory((minecraft, screen) -> configurationScreenProvider.provide(screen))); + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/platform/hooks/forge/EventBusesHooksImpl.java b/neoforge/src/main/java/dev/architectury/platform/hooks/forge/EventBusesHooksImpl.java new file mode 100644 index 00000000..00b6b129 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/platform/hooks/forge/EventBusesHooksImpl.java @@ -0,0 +1,39 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.platform.hooks.forge; + +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.ModList; + +import java.util.Optional; +import java.util.function.Consumer; + +public class EventBusesHooksImpl { + public static void whenAvailable(String modId, Consumer busConsumer) { + IEventBus bus = getModEventBus(modId).orElseThrow(() -> new IllegalStateException("Mod '" + modId + "' is not available!")); + busConsumer.accept(bus); + } + + public static Optional getModEventBus(String modId) { + return ModList.get().getModContainerById(modId) + .map(ModContainer::getEventBus); + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java b/neoforge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java new file mode 100644 index 00000000..e51de44a --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java @@ -0,0 +1,426 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.registries.forge; + +import com.google.common.base.Objects; +import com.google.common.base.Suppliers; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import dev.architectury.impl.RegistrySupplierImpl; +import dev.architectury.platform.hooks.forge.EventBusesHooksImpl; +import dev.architectury.registry.registries.Registrar; +import dev.architectury.registry.registries.RegistrarBuilder; +import dev.architectury.registry.registries.RegistrarManager; +import dev.architectury.registry.registries.RegistrySupplier; +import dev.architectury.registry.registries.options.RegistrarOption; +import dev.architectury.registry.registries.options.StandardRegistrarOption; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.bus.api.EventPriority; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.registries.NewRegistryEvent; +import net.neoforged.neoforge.registries.RegisterEvent; +import net.neoforged.neoforge.registries.RegistryBuilder; +import org.apache.commons.lang3.mutable.Mutable; +import org.apache.commons.lang3.mutable.MutableObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class RegistrarManagerImpl { + private static final Logger LOGGER = LogManager.getLogger(RegistrarManagerImpl.class); + private static final Multimap, Consumer> LISTENERS = HashMultimap.create(); + + private static void listen(ResourceKey resourceKey, ResourceLocation id, Consumer listener) { + LISTENERS.put(new RegistryEntryId<>(resourceKey, id), listener); + } + + public static RegistrarManager.RegistryProvider _get(String modId) { + return new RegistryProviderImpl(modId); + } + + public static class Data { + private boolean registered = false; + private final Map> objects = new LinkedHashMap<>(); + + public void register(Registry registry, ResourceLocation location, Mutable object, Supplier reference) { + if (!registered) { + objects.put(location, () -> { + T value = reference.get(); + object.setValue(value); + return value; + }); + } else { + ResourceKey> resourceKey = registry.key(); + T value = reference.get(); + Registry.register(registry, location, value); + object.setValue(value); + + RegistryEntryId registryEntryId = new RegistryEntryId<>(resourceKey, location); + for (Consumer consumer : LISTENERS.get(registryEntryId)) { + ((Consumer) consumer).accept(value); + } + LISTENERS.removeAll(registryEntryId); + } + } + } + + public record RegistryEntryId(ResourceKey registryKey, ResourceLocation id) { + @Override + public String toString() { + return "Registry Entry [%s / %s]".formatted(registryKey.location(), id); + } + } + + public static class RegistryProviderImpl implements RegistrarManager.RegistryProvider { + private static final Map>, Registrar> CUSTOM_REGS = new HashMap<>(); + private final String modId; + private final Supplier eventBus; + private final Map>, Data> registry = new HashMap<>(); + private final Multimap>, Consumer>> listeners = HashMultimap.create(); + + @Nullable + private List> newRegistries = new ArrayList<>(); + + public RegistryProviderImpl(String modId) { + this.modId = modId; + this.eventBus = Suppliers.memoize(() -> { + IEventBus eventBus = EventBusesHooksImpl.getModEventBus(modId).orElseThrow(() -> new IllegalStateException("Can't get event bus for mod '" + modId + "' because it was not registered!")); + eventBus.register(new EventListener()); + return eventBus; + }); + } + + private void updateEventBus() { + synchronized (eventBus) { + // Make sure that the eventbus is setup + this.eventBus.get(); + } + } + + @Override + public Registrar get(ResourceKey> registryKey) { + updateEventBus(); + Registry registry = (Registry) BuiltInRegistries.REGISTRY.get(registryKey.location()); + if (registry != null) { + return get(registry); + } + Registrar customReg = RegistryProviderImpl.CUSTOM_REGS.get(registryKey); + if (customReg != null) return (Registrar) customReg; + throw new IllegalArgumentException("Registry " + registryKey + " does not exist!"); + } + + @Override + public Registrar get(Registry registry) { + updateEventBus(); + return new RegistrarImpl<>(modId, this.registry, registry); + } + + @Override + public void forRegistry(ResourceKey> key, Consumer> consumer) { + this.listeners.put((ResourceKey>) (ResourceKey>) key, + (Consumer>) (Consumer>) consumer); + } + + @Override + public RegistrarBuilder builder(Class type, ResourceLocation registryId) { + return new RegistryBuilderWrapper<>(this, new RegistryBuilder<>(ResourceKey.createRegistryKey(registryId))); + } + + public class EventListener { + @SubscribeEvent + public void handleEvent(RegisterEvent event) { + for (Map.Entry>, Data> typeDataEntry : RegistryProviderImpl.this.registry.entrySet()) { + if (typeDataEntry.getKey().equals(event.getRegistryKey())) { + //noinspection rawtypes + registerFor(event, (ResourceKey) typeDataEntry.getKey(), typeDataEntry.getValue()); + } + } + } + + public void registerFor(RegisterEvent event, ResourceKey> resourceKey, Data data) { + event.register(resourceKey, registry -> { + data.registered = true; + + for (Map.Entry> entry : data.objects.entrySet()) { + ResourceLocation location = entry.getKey(); + T value = entry.getValue().get(); + registry.register(location, value); + + RegistryEntryId registryEntryId = new RegistryEntryId<>(resourceKey, location); + for (Consumer consumer : LISTENERS.get(registryEntryId)) { + ((Consumer) consumer).accept(value); + } + LISTENERS.removeAll(registryEntryId); + } + + data.objects.clear(); + Registrar archRegistry = get(event.getRegistry()); + + for (Map.Entry>, Consumer>> entry : listeners.entries()) { + if (entry.getKey().location().equals(resourceKey.location())) { + entry.getValue().accept(archRegistry); + } + } + }); + } + + @SubscribeEvent(priority = EventPriority.LOWEST) + public void handleEventPost(RegisterEvent event) { + Registrar archRegistry = get(event.getRegistry()); + + List> toRemove = new ArrayList<>(); + + for (Map.Entry, Collection>> entry : LISTENERS.asMap().entrySet()) { + if (entry.getKey().registryKey.equals(event.getRegistryKey())) { + if (archRegistry.contains(entry.getKey().id)) { + Object value = archRegistry.get(entry.getKey().id); + for (Consumer consumer : entry.getValue()) { + ((Consumer) consumer).accept(value); + } + toRemove.add(entry.getKey()); + } else { + LOGGER.warn("Registry entry listened {} was not realized!", entry.getKey()); + } + } + } + + for (RegistryEntryId id : toRemove) { + LISTENERS.removeAll(id); + } + } + + @SubscribeEvent + public void handleEvent(NewRegistryEvent event) { + if (newRegistries != null) { + for (Registry registry : newRegistries) { + event.register(registry); + } + newRegistries = null; + } + } + } + } + + public static class RegistryBuilderWrapper implements RegistrarBuilder { + private final RegistryProviderImpl provider; + private final RegistryBuilder builder; + private boolean syncToClients = false; + + public RegistryBuilderWrapper(RegistryProviderImpl provider, RegistryBuilder builder) { + this.provider = provider; + this.builder = builder; + } + + @Override + public Registrar build() { + builder.sync(syncToClients); + if (provider.newRegistries == null) { + throw new IllegalStateException("Cannot create registries when registries are already aggregated!"); + } + var registry = builder.create(); + var registrar = provider.get(registry); + provider.newRegistries.add(registry); + //noinspection rawtypes + RegistryProviderImpl.CUSTOM_REGS.put((ResourceKey) registrar.key(), registrar); + return registrar; + } + + @Override + public RegistrarBuilder option(RegistrarOption option) { + if (option == StandardRegistrarOption.SYNC_TO_CLIENTS) { + this.syncToClients = true; + } + return this; + } + } + + public static class RegistrarImpl implements Registrar { + private final String modId; + private final Registry delegate; + private final Map>, Data> registry; + + public RegistrarImpl(String modId, Map>, Data> registry, Registry delegate) { + this.modId = modId; + this.registry = registry; + this.delegate = delegate; + } + + @Override + public RegistrySupplier delegate(ResourceLocation id) { + Supplier value = Suppliers.memoize(() -> get(id)); + return asSupplier(id, this, () -> contains(id), value); + } + + @Override + public RegistrySupplier register(ResourceLocation id, Supplier supplier) { + Data data = (Data) registry.computeIfAbsent(key(), type -> new Data<>()); + Mutable object = new MutableObject<>(); + data.register(delegate, id, object, supplier); + return asSupplier(id, (Registrar) this, () -> object.getValue() != null, object::getValue); + } + + private RegistrySupplier asSupplier(ResourceLocation id, Registrar registrar, BooleanSupplier isPresent, Supplier object) { + return new RegistrySupplierImpl() { + @Nullable + Holder holder = null; + + @Nullable + @Override + public Holder getHolder() { + if (holder != null) return holder; + return holder = registrar.getHolder(getId()); + } + + @Override + public RegistrarManager getRegistrarManager() { + return RegistrarManager.get(modId); + } + + @Override + public Registrar getRegistrar() { + return registrar; + } + + @Override + public ResourceLocation getRegistryId() { + return delegate.key().location(); + } + + @Override + public ResourceLocation getId() { + return id; + } + + @Override + public boolean isPresent() { + return isPresent.getAsBoolean(); + } + + @Override + public E get() { + E value = (E) object.get(); + if (value == null) { + throw new NullPointerException("Value missing: " + this.getId() + "@" + getRegistryId()); + } + return value; + } + + @Override + public int hashCode() { + return Objects.hashCode(getRegistryId(), getId()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof RegistrySupplier other)) return false; + return other.getRegistryId().equals(getRegistryId()) && other.getId().equals(getId()); + } + + @Override + public String toString() { + return getRegistryId() + "@" + id.toString(); + } + }; + } + + @Override + @Nullable + public ResourceLocation getId(T obj) { + return delegate.getKey(obj); + } + + @Override + public int getRawId(T obj) { + return delegate.getId(obj); + } + + @Override + public Optional> getKey(T t) { + return delegate.getResourceKey(t); + } + + @Override + @Nullable + public T get(ResourceLocation id) { + return delegate.get(id); + } + + @Override + public T byRawId(int rawId) { + return delegate.byId(rawId); + } + + @Override + public boolean contains(ResourceLocation resourceLocation) { + return delegate.keySet().contains(resourceLocation); + } + + @Override + public boolean containsValue(T t) { + return delegate.getResourceKey(t).isPresent(); + } + + @Override + public Set getIds() { + return delegate.keySet(); + } + + @Override + public Set, T>> entrySet() { + return delegate.entrySet(); + } + + @Override + public ResourceKey> key() { + return delegate.key(); + } + + @Override + @Nullable + public Holder getHolder(ResourceKey key) { + return delegate.getHolder(key).orElse(null); + } + + @Override + public Iterator iterator() { + return delegate.iterator(); + } + + @Override + public void listen(ResourceLocation id, Consumer callback) { + if (contains(id)) { + callback.accept(get(id)); + } else { + RegistrarManagerImpl.listen(key(), id, callback); + } + } + } +} diff --git a/neoforge/src/main/resources/META-INF/mods.toml b/neoforge/src/main/resources/META-INF/mods.toml new file mode 100644 index 00000000..f620b277 --- /dev/null +++ b/neoforge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,35 @@ +modLoader = "javafml" +loaderVersion = "[1,)" +issueTrackerURL = "https://github.com/shedaniel/architectury/issues" +license = "GNU LGPLv3" + +[[mods]] +modId = "architectury" +version = "${version}" +displayName = "Architectury" +authors = "shedaniel" +description = ''' +A intermediary api aimed to ease developing multiplatform mods. +''' +logoFile = "icon.png" +license = "LGPL-3" + +[[dependencies.architectury]] +modId = "minecraft" +mandatory = true +versionRange = "[1.20.2,)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.architectury]] +modId = "neoforge" +mandatory = true +versionRange = "[20.2.58,)" +ordering = "NONE" +side = "BOTH" + +[[mixins]] +config = "architectury.mixins.json" + +[[mixins]] +config = "architectury-common.mixins.json" diff --git a/neoforge/src/main/resources/icon.png b/neoforge/src/main/resources/icon.png new file mode 100644 index 00000000..812d0b57 Binary files /dev/null and b/neoforge/src/main/resources/icon.png differ diff --git a/neoforge/src/main/resources/pack.mcmeta b/neoforge/src/main/resources/pack.mcmeta new file mode 100644 index 00000000..aca6d140 --- /dev/null +++ b/neoforge/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Architectury", + "pack_format": 6 + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 0f4b94fd..7f514a9e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,8 +14,11 @@ if (JavaVersion.current().ordinal() + 1 < 17) { include("common") include("fabric") //include("forge") +//include("minecraftforge") +//include("neoforge") include("testmod-common") include("testmod-fabric") //include("testmod-forge") +//include("testmod-neoforge") rootProject.name = "architectury" diff --git a/testmod-forge/build.gradle b/testmod-forge/build.gradle index d54ae33e..4a102223 100644 --- a/testmod-forge/build.gradle +++ b/testmod-forge/build.gradle @@ -8,7 +8,7 @@ loom { mods { forge { - sourceSet project(":forge").sourceSets.main + sourceSet project(":minecraftforge").sourceSets.main } } @@ -32,7 +32,8 @@ configurations { dependencies { forge "net.minecraftforge:forge:${gradle.rootProject.architectury.minecraft}-${rootProject.forge_version}" - implementation project(path: ":forge", configuration: "namedElements") + implementation project(path: ":minecraftforge", configuration: "namedElements") common(project(path: ":common", configuration: "namedElements")) { transitive false } common(project(path: ":testmod-common", configuration: "namedElements")) { transitive false } + common(project(path: ":forge", configuration: "namedElements")) { transitive false } } diff --git a/testmod-neoforge/build.gradle b/testmod-neoforge/build.gradle new file mode 100644 index 00000000..962f05b7 --- /dev/null +++ b/testmod-neoforge/build.gradle @@ -0,0 +1,44 @@ +plugins { + id "com.github.johnrengelman.shadow" version "7.0.0" + id "com.matthewprenger.cursegradle" +} + +loom { + accessWidenerPath = project(":common").loom.accessWidenerPath + + mods { + forge { + sourceSet project(":neoforge").sourceSets.main + } + } +} + +architectury { + platformSetupLoomIde() + neoForge { + platformPackage = "forge" + remapForgeLike "net/minecraftforge/common/extensions/IForgeItem", "net/neoforged/neoforge/common/extensions/IItemExtension" + remapForgeLike "net/minecraftforge/client/event/TextureStitchEvent\$Post", "net/neoforged/neoforge/client/event/TextureAtlasStitchedEvent" + remapForgeLike "net/minecraftforge/fluids/ForgeFlowingFluid", "net/neoforged/neoforge/fluids/BaseFlowingFluid" + remapForgeLike "net/minecraftforge/fluids/ForgeFlowingFluid\$Properties", "net/neoforged/neoforge/fluids/BaseFlowingFluid\$Properties" + remapForgeLike "net/minecraftforge/common/ForgeHooks", "net/neoforged/neoforge/common/CommonHooks" + } +} + +configurations { + common + forgeLike + compileClasspath.extendsFrom common, forgeLike + runtimeClasspath.extendsFrom common, forgeLike + developmentNeoForge.extendsFrom common + developmentForgeLike.extendsFrom forgeLike +} + +dependencies { + neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}" + + implementation(project(path: ":neoforge", configuration: "namedElements")) { transitive false } + common(project(path: ":common", configuration: "namedElements")) { transitive false } + common(project(path: ":testmod-common", configuration: "namedElements")) { transitive false } + forgeLike(project(path: ":forge", configuration: "namedElements")) { transitive false } +} diff --git a/testmod-neoforge/gradle.properties b/testmod-neoforge/gradle.properties new file mode 100644 index 00000000..2914393d --- /dev/null +++ b/testmod-neoforge/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge \ No newline at end of file diff --git a/testmod-neoforge/src/main/java/dev/architectury/test/forge/TestModForge.java b/testmod-neoforge/src/main/java/dev/architectury/test/forge/TestModForge.java new file mode 100644 index 00000000..83f85ca9 --- /dev/null +++ b/testmod-neoforge/src/main/java/dev/architectury/test/forge/TestModForge.java @@ -0,0 +1,30 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.test.forge; + +import dev.architectury.test.TestMod; +import net.neoforged.fml.common.Mod; + +@Mod(TestMod.MOD_ID) +public class TestModForge { + public TestModForge() { + TestMod.initialize(); + } +} diff --git a/testmod-neoforge/src/main/resources/META-INF/mods.toml b/testmod-neoforge/src/main/resources/META-INF/mods.toml new file mode 100644 index 00000000..3eba0bc3 --- /dev/null +++ b/testmod-neoforge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,14 @@ +modLoader = "javafml" +loaderVersion = "[1,)" +issueTrackerURL = "https://github.com/shedaniel/architectury/issues" +license = "LGPL-3" + +[[mods]] +modId = "architectury_test" +version = "${file.jarVersion}" +displayName = "Architectury Test" +authors = "shedaniel" +description = ''' +A intermediary api aimed to ease developing multiplatform mods. +''' +license = "LGPL-3" \ No newline at end of file diff --git a/testmod-neoforge/src/main/resources/pack.mcmeta b/testmod-neoforge/src/main/resources/pack.mcmeta new file mode 100644 index 00000000..610d07f3 --- /dev/null +++ b/testmod-neoforge/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "Architectury Test", + "pack_format": 6 + } +} \ No newline at end of file