Merge remote-tracking branch 'origin/1.20.2' into 1.20.3

# Conflicts:
#	gradle.properties
#	settings.gradle
This commit is contained in:
shedaniel
2023-11-21 00:20:59 +08:00
70 changed files with 2223 additions and 260 deletions

View File

@@ -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 {

View File

@@ -9,7 +9,9 @@ dependencies {
}
architectury {
common(rootProject.platforms.split(","))
common(rootProject.platforms.split(",")) {
it.platformPackage "neoforge", "forge"
}
}
/**

View File

@@ -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<T> extends RegistrySupplier<T> {
@Nullable
Holder<T> 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<T> resourceKey) {
return getKey().equals(resourceKey);
}
@Override
default boolean is(Predicate<ResourceKey<T>> predicate) {
return predicate.test(getKey());
}
@Override
default boolean is(TagKey<T> tagKey) {
Holder<T> holder = getHolder();
return holder != null && holder.is(tagKey);
}
@Override
default Stream<TagKey<T>> tags() {
Holder<T> holder = getHolder();
return holder != null ? holder.tags() : Stream.empty();
}
@Override
default Either<ResourceKey<T>, T> unwrap() {
return Either.left(getKey());
}
@Override
default Optional<ResourceKey<T>> unwrapKey() {
return Optional.of(getKey());
}
@Override
default Kind kind() {
return Kind.REFERENCE;
}
@Override
default boolean canSerializeIn(HolderOwner<T> holderOwner) {
Holder<T> holder = getHolder();
return holder != null && holder.canSerializeIn(holderOwner);
}
}

View File

@@ -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;
}
}

View File

@@ -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<T> implements Iterable<RegistrySupplier<T>> {
return registriesSupplier.get().get(key);
}
private class Entry<R> implements RegistrySupplier<R> {
private class Entry<R> implements RegistrySupplierImpl<R> {
private final ResourceLocation id;
private final Supplier<R> supplier;
private RegistrySupplier<R> value;
@Nullable
private Holder<R> holder = null;
public Entry(ResourceLocation id, Supplier<R> supplier) {
this.id = id;
this.supplier = supplier;
}
@Nullable
@Override
public Holder<R> getHolder() {
if (holder != null) return holder;
return holder = getRegistrar().getHolder(getId());
}
@Override
public RegistrarManager getRegistrarManager() {
return DeferredRegister.this.getRegistrarManager();

View File

@@ -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<T> extends Iterable<T> {
ResourceKey<? extends Registry<T>> key();
@Nullable
Holder<T> getHolder(ResourceKey<T> key);
@Nullable
default Holder<T> 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.

View File

@@ -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<T> extends DeferredSupplier<T> {
public interface RegistrySupplier<T> extends DeferredSupplier<T>, Holder<T> {
RegistrarManager getRegistrarManager();
Registrar<T> getRegistrar();

View File

@@ -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";
}

View File

@@ -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<T> delegate(ResourceLocation id) {
Supplier<T> value = Suppliers.memoize(() -> get(id));
RegistrarImpl<T> registrar = this;
return new RegistrySupplier<>() {
return new RegistrySupplierImpl<T>() {
@Nullable
Holder<T> holder = null;
@Nullable
@Override
public Holder<T> 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<T> getHolder(ResourceKey<T> key) {
return delegate.getHolder(key).orElse(null);
}
@Override
public Iterator<T> iterator() {
return delegate.iterator();

View File

@@ -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"
}
}
}
}

View File

@@ -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);
// });
}
}

View File

@@ -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) {

View File

@@ -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<LootPool>) field.get(table);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
try {
Field field = LootTable.class.getDeclaredField("pools");
field.setAccessible(true);
try {
pools = (List<LootPool>) 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<LootPool>) 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!");
}
}
}
}

View File

@@ -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 extends AbstractWidget & Renderable & NarratableEntry> 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 extends Renderable> 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 extends GuiEventListener & NarratableEntry> 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);
}
}

View File

@@ -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<String> getLeft(CustomizeGuiOverlayEvent.DebugText event) {
throw new AssertionError();
}
@ExpectPlatform
public static List<String> getRight(CustomizeGuiOverlayEvent.DebugText event) {
throw new AssertionError();
}
}

View File

@@ -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<Codec<? extends BiomeModifier>> codecSupplier) {
throw new AssertionError();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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<IEventBus> busConsumer) {
throw new AssertionError();
}
@ExpectPlatform
public static Optional<IEventBus> getModEventBus(String modId) {
throw new AssertionError();
}
}

View File

@@ -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<String> myTargets, Set<String> otherTargets) {
}
@Override
public List<String> 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) {
}
}

View File

@@ -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<KeyMapping> 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;

View File

@@ -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<ModelLayerLocation, Supplier<LayerDefinition>> DEFINITIONS = new ConcurrentHashMap<>();
static {
EventBuses.onRegistered(ArchitecturyForge.MOD_ID, bus -> {
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.register(EntityModelLayerRegistryImpl.class);
});
}

View File

@@ -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);
});
}

View File

@@ -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);

View File

@@ -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<Pair<BlockColor, Supplier<? extends Block>[]>> BLOCK_COLORS = Lists.newArrayList();
static {
EventBuses.onRegistered(ArchitecturyForge.MOD_ID, bus -> {
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.register(ColorHandlerRegistryImpl.class);
});
}

View File

@@ -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<TabKey, Supplier<ItemStack>> APPENDS = MultimapBuilder.hashKeys().arrayListValues().build();
static {
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.addListener(CreativeTabRegistryImpl::event);
});
BUILD_CONTENTS_LISTENERS.add(event -> {
for (Map.Entry<TabKey, Collection<Supplier<ItemStack>>> keyEntry : APPENDS.asMap().entrySet()) {
Supplier<List<ItemStack>> stacks = Suppliers.memoize(() -> keyEntry.getValue().stream()
@@ -76,7 +77,6 @@ public class CreativeTabRegistryImpl {
});
}
@SubscribeEvent
public static void event(BuildCreativeModeTabContentsEvent event) {
for (Consumer<BuildCreativeModeTabContentsEvent> listener : BUILD_CONTENTS_LISTENERS) {
listener.accept(event);

View File

@@ -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<PreparableReloadListener> serverDataReloadListeners = Lists.newArrayList();
static {
MinecraftForge.EVENT_BUS.addListener(ReloadListenerRegistryImpl::addReloadListeners);
}
public static void register(PackType type, PreparableReloadListener listener, @Nullable ResourceLocation listenerId, Collection<ResourceLocation> 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);

View File

@@ -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<BiomeModifierImpl> noneBiomeModCodec = null;
public static void init() {
EventBuses.onRegistered(ArchitecturyForge.MOD_ID, bus -> {
bus.<RegisterEvent>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<BiomeContext> predicate, BiConsumer<BiomeContext, BiomeProperties.Mutable> modifier) {

View File

@@ -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);
});
}

View File

@@ -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<VillagerProfession, Int2ObjectMap<List<VillagerTrades.ItemListing>>> TRADES_TO_ADD = new HashMap<>();
private static final List<VillagerTrades.ItemListing> WANDERER_TRADER_TRADES_GENERIC = new ArrayList<>();
private static final List<VillagerTrades.ItemListing> 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<List<VillagerTrades.ItemListing>> tradesForProfession = TRADES_TO_ADD.computeIfAbsent(profession, $ -> new Int2ObjectOpenHashMap<>());
List<VillagerTrades.ItemListing> tradesForLevel = tradesForProfession.computeIfAbsent(level, $ -> new ArrayList<>());
@@ -52,7 +54,6 @@ public class TradeRegistryImpl {
}
}
@SubscribeEvent
public static void onTradeRegistering(VillagerTradesEvent event) {
Int2ObjectMap<List<VillagerTrades.ItemListing>> 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);

View File

@@ -9,7 +9,6 @@
"MixinMinecraft"
],
"mixins": [
"MixinChunkSerializer",
"MixinEntitySpawnExtension",
"MixinFallingBlockEntity",
"MixinItemExtension",

View File

@@ -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

View File

@@ -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

155
minecraftforge/build.gradle Normal file
View File

@@ -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"
}
}
}
}

View File

@@ -0,0 +1 @@
loom.platform=forge

View File

@@ -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();
}

View File

@@ -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<String> getLeft(CustomizeGuiOverlayEvent.DebugText event) {
return event.getLeft();
}
public static List<String> getRight(CustomizeGuiOverlayEvent.DebugText event) {
return event.getRight();
}
}

View File

@@ -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<Codec<? extends BiomeModifier>> codecSupplier) {
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.<RegisterEvent>addListener(event -> {
event.register(ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, registry -> {
registry.register(id, codecSupplier.get());
});
});
});
}
}

View File

@@ -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;

View File

@@ -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<PacketTransformer> packetTransformers, NetworkReceiver receiver) {
Objects.requireNonNull(id, "Cannot register receiver with a null ID!");

View File

@@ -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<IEventBus> busConsumer) {
EventBuses.onRegistered(modId, busConsumer);
}
public static Optional<IEventBus> getModEventBus(String modId) {
return EventBuses.getModEventBus(modId);
}
}

View File

@@ -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<T> delegate(ResourceLocation id) {
Supplier<T> value = Suppliers.memoize(() -> get(id));
Registrar<T> registrar = this;
return new RegistrySupplier<>() {
return new RegistrySupplierImpl<T>() {
@Nullable
Holder<T> holder = null;
@Nullable
@Override
public Holder<T> 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<T> getHolder(ResourceKey<T> key) {
return delegate.getHolder(key).orElse(null);
}
@Override
public Iterator<T> iterator() {
return delegate.iterator();
@@ -462,7 +480,17 @@ public class RegistrarManagerImpl {
public RegistrySupplier<T> delegate(ResourceLocation id) {
Supplier<T> value = Suppliers.memoize(() -> get(id));
Registrar<T> registrar = this;
return new RegistrySupplier<>() {
return new RegistrySupplierImpl<T>() {
@Nullable
Holder<T> holder = null;
@Nullable
@Override
public Holder<T> 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<T> data = (Data<T>) registry.computeIfAbsent(key(), type -> new Data<>());
data.registerForForge(delegate, id, objectArr, supplier);
Registrar<T> registrar = this;
return new RegistrySupplier<>() {
return new RegistrySupplierImpl<E>() {
@Nullable
Holder<E> holder = null;
@Nullable
@Override
public Holder<E> 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<T> getHolder(ResourceKey<T> key) {
return delegate.getHolder(key).orElse(null);
}
@Override
public Iterator<T> iterator() {
return delegate.iterator();
@@ -668,7 +712,17 @@ public class RegistrarManagerImpl {
@Override
public RegistrySupplier<T> delegate(ResourceLocation id) {
if (isReady()) return delegate.get().delegate(id);
return new RegistrySupplier<>() {
return new RegistrySupplierImpl<T>() {
@Nullable
Holder<T> holder = null;
@Nullable
@Override
public Holder<T> 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<T> getHolder(ResourceKey<T> key) {
return isReady() ? delegate.get().getHolder(key) : null;
}
@Override
public void listen(ResourceLocation id, Consumer<T> callback) {
if (isReady()) {

View File

@@ -0,0 +1,14 @@
{
"required": true,
"package": "dev.architectury.mixin.forge",
"compatibilityLevel": "JAVA_16",
"minVersion": "0.8",
"client": [
],
"mixins": [
"minecraftforge.MixinChunkSerializer"
],
"injectors": {
"defaultRequire": 1
}
}

View File

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB

156
neoforge/build.gradle Normal file
View File

@@ -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"
}
}
}
}

View File

@@ -0,0 +1 @@
loom.platform=neoforge

View File

@@ -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<String> getLeft(CustomizeGuiOverlayEvent.DebugText event) {
return event.getLeft();
}
public static List<String> getRight(CustomizeGuiOverlayEvent.DebugText event) {
return event.getRight();
}
}

View File

@@ -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<Codec<? extends BiomeModifier>> codecSupplier) {
EventBusesHooksImpl.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.<RegisterEvent>addListener(event -> {
event.register(NeoForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, registry -> {
registry.register(id, codecSupplier.get());
});
});
});
}
}

View File

@@ -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<WeakReference<ServerLevel>> level = new ThreadLocal<>();
@Inject(method = "read", at = @At("HEAD"))
private static void read(ServerLevel worldIn, PoiManager arg2, ChunkPos arg3, CompoundTag arg4, CallbackInfoReturnable<ProtoChunk> 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<ServerLevel> 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;
}
}

View File

@@ -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();
}
}

View File

@@ -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<ResourceLocation> 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();
}
}

View File

@@ -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<PacketTransformer> 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<ResourceLocation, NetworkReceiver> S2C = Maps.newHashMap();
static final Map<ResourceLocation, NetworkReceiver> C2S = Maps.newHashMap();
static final Map<ResourceLocation, PacketTransformer> S2C_TRANSFORMERS = Maps.newHashMap();
static final Map<ResourceLocation, PacketTransformer> C2S_TRANSFORMERS = Maps.newHashMap();
static final Set<ResourceLocation> serverReceivables = Sets.newHashSet();
private static final Multimap<Player, ResourceLocation> 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<ResourceLocation> receivables = (Set<ResourceLocation>) clientReceivables.get(context.getPlayer());
int size = buffer.readInt();
receivables.clear();
for (int i = 0; i < size; i++) {
receivables.add(buffer.readResourceLocation());
}
});
}
static <T extends NetworkEvent> Consumer<T> createPacketHandler(INetworkDirection<?> direction, Map<ResourceLocation, PacketTransformer> 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<PacketTransformer> 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<PacketTransformer> 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<ClientGamePacketListener> createAddEntityPacket(Entity entity) {
return NetworkHooks.getEntitySpawningPacket(entity);
}
static FriendlyByteBuf sendSyncPacket(Map<ResourceLocation, NetworkReceiver> map) {
List<ResourceLocation> 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());
}
}

View File

@@ -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<String, Mod> 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<Mod> getMods() {
for (IModInfo mod : ModList.get().getMods()) {
getMod(mod.getModId());
}
return mods.values();
}
public static Collection<String> 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<String> getLogoFile(int i) {
return this.info.getLogoFile();
}
@Override
public List<Path> getFilePaths() {
return List.of(getFilePath());
}
@Override
public Path getFilePath() {
return this.info.getOwningFile().getFile().getSecureJar().getRootPath();
}
@Override
public Optional<Path> findResource(String... path) {
return Optional.of(this.info.getOwningFile().getFile().findResource(path)).filter(Files::exists);
}
@Override
public Collection<String> getAuthors() {
Optional<String> optional = this.info.getConfig().getConfigElement("authors")
.map(String::valueOf);
return optional.isPresent() ? Collections.singleton(optional.get()) : Collections.emptyList();
}
@Override
public @Nullable Collection<String> getLicense() {
return Collections.singleton(this.info.getOwningFile().getLicense());
}
@Override
public Optional<String> getHomepage() {
return this.info.getConfig().getConfigElement("displayURL")
.map(String::valueOf);
}
@Override
public Optional<String> getSources() {
return Optional.empty();
}
@Override
public Optional<String> 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)));
}
}
}

View File

@@ -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<IEventBus> busConsumer) {
IEventBus bus = getModEventBus(modId).orElseThrow(() -> new IllegalStateException("Mod '" + modId + "' is not available!"));
busConsumer.accept(bus);
}
public static Optional<IEventBus> getModEventBus(String modId) {
return ModList.get().getModContainerById(modId)
.map(ModContainer::getEventBus);
}
}

View File

@@ -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<RegistryEntryId<?>, 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<T> {
private boolean registered = false;
private final Map<ResourceLocation, Supplier<? extends T>> objects = new LinkedHashMap<>();
public void register(Registry<T> registry, ResourceLocation location, Mutable<T> object, Supplier<? extends T> reference) {
if (!registered) {
objects.put(location, () -> {
T value = reference.get();
object.setValue(value);
return value;
});
} else {
ResourceKey<? extends Registry<T>> 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<Object>) consumer).accept(value);
}
LISTENERS.removeAll(registryEntryId);
}
}
}
public record RegistryEntryId<T>(ResourceKey<T> 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<ResourceKey<Registry<?>>, Registrar<?>> CUSTOM_REGS = new HashMap<>();
private final String modId;
private final Supplier<IEventBus> eventBus;
private final Map<ResourceKey<? extends Registry<?>>, Data<?>> registry = new HashMap<>();
private final Multimap<ResourceKey<Registry<?>>, Consumer<Registrar<?>>> listeners = HashMultimap.create();
@Nullable
private List<Registry<?>> 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 <T> Registrar<T> get(ResourceKey<Registry<T>> registryKey) {
updateEventBus();
Registry<T> registry = (Registry<T>) BuiltInRegistries.REGISTRY.get(registryKey.location());
if (registry != null) {
return get(registry);
}
Registrar<?> customReg = RegistryProviderImpl.CUSTOM_REGS.get(registryKey);
if (customReg != null) return (Registrar<T>) customReg;
throw new IllegalArgumentException("Registry " + registryKey + " does not exist!");
}
@Override
public <T> Registrar<T> get(Registry<T> registry) {
updateEventBus();
return new RegistrarImpl<>(modId, this.registry, registry);
}
@Override
public <T> void forRegistry(ResourceKey<Registry<T>> key, Consumer<Registrar<T>> consumer) {
this.listeners.put((ResourceKey<Registry<?>>) (ResourceKey<? extends Registry<?>>) key,
(Consumer<Registrar<?>>) (Consumer<? extends Registrar<?>>) consumer);
}
@Override
public <T> RegistrarBuilder<T> builder(Class<T> type, ResourceLocation registryId) {
return new RegistryBuilderWrapper<>(this, new RegistryBuilder<>(ResourceKey.createRegistryKey(registryId)));
}
public class EventListener {
@SubscribeEvent
public void handleEvent(RegisterEvent event) {
for (Map.Entry<ResourceKey<? extends Registry<?>>, Data<?>> typeDataEntry : RegistryProviderImpl.this.registry.entrySet()) {
if (typeDataEntry.getKey().equals(event.getRegistryKey())) {
//noinspection rawtypes
registerFor(event, (ResourceKey) typeDataEntry.getKey(), typeDataEntry.getValue());
}
}
}
public <T> void registerFor(RegisterEvent event, ResourceKey<? extends Registry<T>> resourceKey, Data<T> data) {
event.register(resourceKey, registry -> {
data.registered = true;
for (Map.Entry<ResourceLocation, Supplier<? extends T>> 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<Object>) consumer).accept(value);
}
LISTENERS.removeAll(registryEntryId);
}
data.objects.clear();
Registrar<?> archRegistry = get(event.getRegistry());
for (Map.Entry<ResourceKey<Registry<?>>, Consumer<Registrar<?>>> 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<RegistryEntryId<?>> toRemove = new ArrayList<>();
for (Map.Entry<RegistryEntryId<?>, Collection<Consumer<?>>> 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<Object>) 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<T> implements RegistrarBuilder<T> {
private final RegistryProviderImpl provider;
private final RegistryBuilder<T> builder;
private boolean syncToClients = false;
public RegistryBuilderWrapper(RegistryProviderImpl provider, RegistryBuilder<T> builder) {
this.provider = provider;
this.builder = builder;
}
@Override
public Registrar<T> 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<T> option(RegistrarOption option) {
if (option == StandardRegistrarOption.SYNC_TO_CLIENTS) {
this.syncToClients = true;
}
return this;
}
}
public static class RegistrarImpl<T> implements Registrar<T> {
private final String modId;
private final Registry<T> delegate;
private final Map<ResourceKey<? extends Registry<?>>, Data<?>> registry;
public RegistrarImpl(String modId, Map<ResourceKey<? extends Registry<?>>, Data<?>> registry, Registry<T> delegate) {
this.modId = modId;
this.registry = registry;
this.delegate = delegate;
}
@Override
public RegistrySupplier<T> delegate(ResourceLocation id) {
Supplier<T> value = Suppliers.memoize(() -> get(id));
return asSupplier(id, this, () -> contains(id), value);
}
@Override
public <E extends T> RegistrySupplier<E> register(ResourceLocation id, Supplier<E> supplier) {
Data<T> data = (Data<T>) registry.computeIfAbsent(key(), type -> new Data<>());
Mutable<T> object = new MutableObject<>();
data.register(delegate, id, object, supplier);
return asSupplier(id, (Registrar<E>) this, () -> object.getValue() != null, object::getValue);
}
private <E extends T> RegistrySupplier<E> asSupplier(ResourceLocation id, Registrar<E> registrar, BooleanSupplier isPresent, Supplier<T> object) {
return new RegistrySupplierImpl<E>() {
@Nullable
Holder<E> holder = null;
@Nullable
@Override
public Holder<E> getHolder() {
if (holder != null) return holder;
return holder = registrar.getHolder(getId());
}
@Override
public RegistrarManager getRegistrarManager() {
return RegistrarManager.get(modId);
}
@Override
public Registrar<E> 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<ResourceKey<T>> 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<ResourceLocation> getIds() {
return delegate.keySet();
}
@Override
public Set<Map.Entry<ResourceKey<T>, T>> entrySet() {
return delegate.entrySet();
}
@Override
public ResourceKey<? extends Registry<T>> key() {
return delegate.key();
}
@Override
@Nullable
public Holder<T> getHolder(ResourceKey<T> key) {
return delegate.getHolder(key).orElse(null);
}
@Override
public Iterator<T> iterator() {
return delegate.iterator();
}
@Override
public void listen(ResourceLocation id, Consumer<T> callback) {
if (contains(id)) {
callback.accept(get(id));
} else {
RegistrarManagerImpl.listen(key(), id, callback);
}
}
}
}

View File

@@ -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"

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View File

@@ -0,0 +1,6 @@
{
"pack": {
"description": "Architectury",
"pack_format": 6
}
}

View File

@@ -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"

View File

@@ -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 }
}

View File

@@ -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 }
}

View File

@@ -0,0 +1 @@
loom.platform=neoforge

View File

@@ -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();
}
}

View File

@@ -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"

View File

@@ -0,0 +1,6 @@
{
"pack": {
"description": "Architectury Test",
"pack_format": 6
}
}