From f2bf71d94029954b8ec80d872093e19e82779e17 Mon Sep 17 00:00:00 2001 From: BlayTheNinth <1933180+BlayTheNinth@users.noreply.github.com> Date: Sat, 11 Nov 2023 17:25:30 +0100 Subject: [PATCH] Add NeoForge support --- build.gradle | 4 +- fabric/dependencies.gradle | 4 +- forge/build.gradle | 2 +- gradle.properties | 4 +- .../api/block/entity/BalmBlockEntityBase.java | 98 +++++ .../blay09/mods/balm/api/event/BalmEvent.java | 7 + .../blay09/mods/balm/mixin/EntityMixin.java | 38 ++ .../mods/balm/mixin/MinecraftMixin.java | 18 + .../mods/balm/mixin/ModelBakeryMixin.java | 22 + .../mods/balm/neoforge/DeferredRegisters.java | 43 ++ .../mods/balm/neoforge/NeoForgeBalm.java | 32 ++ .../mods/balm/neoforge/NeoForgeBalmHooks.java | 132 ++++++ .../balm/neoforge/NeoForgeBalmRegistries.java | 89 ++++ .../balm/neoforge/NeoForgeBalmRuntime.java | 226 ++++++++++ .../neoforge/NeoForgeBalmRuntimeFactory.java | 11 + .../neoforge/block/NeoForgeBalmBlocks.java | 42 ++ .../entity/NeoForgeBalmBlockEntities.java | 38 ++ .../neoforge/client/NeoForgeBalmClient.java | 11 + .../client/NeoForgeBalmClientRuntime.java | 64 +++ .../NeoForgeBalmClientRuntimeFactory.java | 11 + .../keymappings/NeoForgeBalmKeyMappings.java | 120 ++++++ .../client/rendering/NeoForgeBalmModels.java | 199 +++++++++ .../rendering/NeoForgeBalmRenderers.java | 141 ++++++ .../rendering/NeoForgeBalmTextures.java | 6 + .../rendering/NeoForgeCachedDynamicModel.java | 66 +++ .../client/screen/NeoForgeBalmScreens.java | 65 +++ .../command/NeoForgeBalmCommands.java | 27 ++ .../neoforge/config/NeoForgeBalmConfig.java | 225 ++++++++++ .../neoforge/container/BalmInvWrapper.java | 23 + .../energy/NeoForgeEnergyStorage.java | 43 ++ .../neoforge/entity/NeoForgeBalmEntities.java | 66 +++ .../event/NeoForgeBalmClientEvents.java | 401 ++++++++++++++++++ .../event/NeoForgeBalmCommonEvents.java | 258 +++++++++++ .../neoforge/event/NeoForgeBalmEvents.java | 105 +++++ .../neoforge/fluid/NeoForgeFluidTank.java | 55 +++ .../balm/neoforge/item/NeoForgeBalmItems.java | 90 ++++ .../balm/neoforge/menu/NeoForgeBalmMenus.java | 27 ++ .../network/NeoForgeBalmNetworking.java | 189 +++++++++ .../neoforge/network/NetworkChannels.java | 38 ++ .../provider/NeoForgeBalmProviders.java | 44 ++ .../neoforge/sound/NeoForgeBalmSounds.java | 19 + .../neoforge/stats/NeoForgeBalmStats.java | 47 ++ .../neoforge/world/BalmBiomeModifier.java | 25 ++ .../neoforge/world/BiomeModification.java | 30 ++ .../neoforge/world/NeoForgeBalmWorldGen.java | 115 +++++ .../net/blay09/mods/balm/api/BalmProxy.java | 4 + .../mods/balm/api/client/BalmClientProxy.java | 5 + shared/src/main/resources/balm.mixins.json | 16 + 48 files changed, 3339 insertions(+), 6 deletions(-) create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/api/block/entity/BalmBlockEntityBase.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/api/event/BalmEvent.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/mixin/EntityMixin.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/mixin/MinecraftMixin.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/mixin/ModelBakeryMixin.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/DeferredRegisters.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalm.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmHooks.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmRegistries.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmRuntime.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmRuntimeFactory.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/block/NeoForgeBalmBlocks.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/block/entity/NeoForgeBalmBlockEntities.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/NeoForgeBalmClient.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/NeoForgeBalmClientRuntime.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/NeoForgeBalmClientRuntimeFactory.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/keymappings/NeoForgeBalmKeyMappings.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeBalmModels.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeBalmRenderers.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeBalmTextures.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeCachedDynamicModel.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/screen/NeoForgeBalmScreens.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/command/NeoForgeBalmCommands.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/config/NeoForgeBalmConfig.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/container/BalmInvWrapper.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/energy/NeoForgeEnergyStorage.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/entity/NeoForgeBalmEntities.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/event/NeoForgeBalmClientEvents.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/event/NeoForgeBalmCommonEvents.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/event/NeoForgeBalmEvents.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/fluid/NeoForgeFluidTank.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/item/NeoForgeBalmItems.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/menu/NeoForgeBalmMenus.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/network/NeoForgeBalmNetworking.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/network/NetworkChannels.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/provider/NeoForgeBalmProviders.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/sound/NeoForgeBalmSounds.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/stats/NeoForgeBalmStats.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/world/BalmBiomeModifier.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/world/BiomeModification.java create mode 100644 neoforge/src/main/java/net/blay09/mods/balm/neoforge/world/NeoForgeBalmWorldGen.java create mode 100644 shared/src/main/resources/balm.mixins.json diff --git a/build.gradle b/build.gradle index b4c665b6..e76fe496 100644 --- a/build.gradle +++ b/build.gradle @@ -83,7 +83,9 @@ subprojects { "license": license, "description": project.description, "neoforge_version": neoforge_version, - "neoforge_loader_version_range": neoforge_loader_version_range + "neoforge_version_range": neoforge_version_range, + "neoforge_loader_version_range": neoforge_loader_version_range, + "pack_format_number": pack_format_number ] filesMatching(['pack.mcmeta', 'fabric.mod.json', 'META-INF/mods.toml', '*.mixins.json']) { diff --git a/fabric/dependencies.gradle b/fabric/dependencies.gradle index 8b8582e2..c9c9780b 100644 --- a/fabric/dependencies.gradle +++ b/fabric/dependencies.gradle @@ -13,8 +13,8 @@ dependencies { modRuntimeOnly "mezz.jei:jei-$jei_minecraft_version-fabric:$jei_version" } - modCompileOnly "me.shedaniel.cloth:cloth-config-fabric:11.0.99" - modCompileOnly "com.terraformersmc:modmenu:$modmenu_version" + modImplementation "com.terraformersmc:modmenu:$modmenu_version" + modCompileOnly "me.shedaniel.cloth:cloth-config-fabric:11.0.99" modCompileOnly("de.siphalor:amecsapi-1.20:1.5.2+mc1.20-pre1") } \ No newline at end of file diff --git a/forge/build.gradle b/forge/build.gradle index f22d7e0c..5626f4bd 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -108,7 +108,7 @@ sourceSets.main.resources.srcDir 'src/generated/resources' dependencies { minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" - annotationProcessor 'org.spongepowered:mixin:0.8.5-SNAPSHOT:processor' + annotationProcessor "org.spongepowered:mixin:${mixin_version}:processor" compileOnly project(":shared") compileOnly "org.jetbrains:annotations:22.0.0" diff --git a/gradle.properties b/gradle.properties index faf16d1a..fc9cc056 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # Mod mod_id=balm mod_name=Balm -description=Abstraction Layer (but not really)? for Blay's multiplatform mods +description=Abstraction Layer (but not really) for Blay's multiplatform mods version = 8.0.1 group=net.blay09.mods license=All Rights Reserved @@ -40,7 +40,7 @@ jei_minecraft_version=1.19.2 jei_version=11.4.0.288 rei_version=10.0.581 mixin_version=0.8.5 -modmenu_version=7.2.2 +modmenu_version=8.0.0 # Gradle org.gradle.jvmargs=-Xmx3G diff --git a/neoforge/src/main/java/net/blay09/mods/balm/api/block/entity/BalmBlockEntityBase.java b/neoforge/src/main/java/net/blay09/mods/balm/api/block/entity/BalmBlockEntityBase.java new file mode 100644 index 00000000..36af75c1 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/api/block/entity/BalmBlockEntityBase.java @@ -0,0 +1,98 @@ +package net.blay09.mods.balm.api.block.entity; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import com.mojang.datafixers.util.Pair; +import net.blay09.mods.balm.api.Balm; +import net.blay09.mods.balm.api.energy.EnergyStorage; +import net.blay09.mods.balm.api.fluid.FluidTank; +import net.blay09.mods.balm.api.provider.BalmProvider; +import net.blay09.mods.balm.api.provider.BalmProviderHolder; +import net.blay09.mods.balm.neoforge.container.BalmInvWrapper; +import net.blay09.mods.balm.neoforge.energy.NeoForgeEnergyStorage; +import net.blay09.mods.balm.neoforge.fluid.NeoForgeFluidTank; +import net.blay09.mods.balm.neoforge.provider.NeoForgeBalmProviders; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.Container; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.common.capabilities.Capabilities; +import net.neoforged.neoforge.common.capabilities.Capability; +import net.neoforged.neoforge.common.util.LazyOptional; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class BalmBlockEntityBase extends BlockEntity { + + private final Map, LazyOptional> capabilities = new HashMap<>(); + private final Table, Direction, LazyOptional> sidedCapabilities = HashBasedTable.create(); + private boolean capabilitiesInitialized; + + public BalmBlockEntityBase(BlockEntityType blockEntityType, BlockPos blockPos, BlockState blockState) { + super(blockEntityType, blockPos, blockState); + } + + private void addCapabilities(BalmProvider provider, Map, LazyOptional> capabilities) { + NeoForgeBalmProviders forgeProviders = (NeoForgeBalmProviders) Balm.getProviders(); + Capability capability = forgeProviders.getCapability(provider.getProviderClass()); + capabilities.put(capability, LazyOptional.of(provider::getInstance)); + + if (provider.getProviderClass() == Container.class) { + capabilities.put(Capabilities.ITEM_HANDLER, LazyOptional.of(() -> new BalmInvWrapper((Container) provider.getInstance()))); + } else if (provider.getProviderClass() == FluidTank.class) { + capabilities.put(Capabilities.FLUID_HANDLER, LazyOptional.of(() -> new NeoForgeFluidTank((FluidTank) provider.getInstance()))); + } else if (provider.getProviderClass() == EnergyStorage.class) { + capabilities.put(Capabilities.ENERGY, LazyOptional.of(() -> new NeoForgeEnergyStorage((EnergyStorage) provider.getInstance()))); + } + } + + @SuppressWarnings("unchecked") + public T getProvider(Class clazz) { + NeoForgeBalmProviders forgeProviders = (NeoForgeBalmProviders) Balm.getProviders(); + Capability capability = forgeProviders.getCapability(clazz); + return (T) getCapability(capability).resolve().orElse(null); + } + + @NotNull + @Override + public LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { + if (!capabilitiesInitialized) { + List providers = new ArrayList<>(); + buildProviders(providers); + + for (Object holder : providers) { + BalmProviderHolder providerHolder = (BalmProviderHolder) holder; + for (BalmProvider provider : providerHolder.getProviders()) { + addCapabilities(provider, capabilities); + } + + for (Pair> pair : providerHolder.getSidedProviders()) { + Direction direction = pair.getFirst(); + BalmProvider provider = pair.getSecond(); + Map, LazyOptional> sidedCapabilities = this.sidedCapabilities.column(direction); + addCapabilities(provider, sidedCapabilities); + } + } + capabilitiesInitialized = true; + } + + LazyOptional result = null; + if (side != null) { + result = sidedCapabilities.get(cap, side); + } + if (result == null) { + result = capabilities.get(cap); + } + return result != null ? result.cast() : super.getCapability(cap, side); + } + + protected abstract void buildProviders(List providers); + +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/api/event/BalmEvent.java b/neoforge/src/main/java/net/blay09/mods/balm/api/event/BalmEvent.java new file mode 100644 index 00000000..9a7eadac --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/api/event/BalmEvent.java @@ -0,0 +1,7 @@ +package net.blay09.mods.balm.api.event; + +import net.neoforged.bus.api.Event; +import net.neoforged.bus.api.ICancellableEvent; + +public class BalmEvent extends Event implements ICancellableEvent { +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/mixin/EntityMixin.java b/neoforge/src/main/java/net/blay09/mods/balm/mixin/EntityMixin.java new file mode 100644 index 00000000..7cfe267d --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/mixin/EntityMixin.java @@ -0,0 +1,38 @@ +package net.blay09.mods.balm.mixin; + +import net.blay09.mods.balm.api.entity.BalmEntity; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(Entity.class) +public class EntityMixin implements BalmEntity { + + private CompoundTag fabricBalmData = new CompoundTag(); + + @Inject(method = "load(Lnet/minecraft/nbt/CompoundTag;)V", at = @At("HEAD")) + private void load(CompoundTag compound, CallbackInfo callbackInfo) { + if (compound.contains("BalmData")) { + fabricBalmData = compound.getCompound("BalmData"); + } + } + + @Inject(method = "saveWithoutId(Lnet/minecraft/nbt/CompoundTag;)Lnet/minecraft/nbt/CompoundTag;", at = @At("HEAD")) + private void saveWithoutId(CompoundTag compound, CallbackInfoReturnable callbackInfo) { + compound.put("BalmData", fabricBalmData); + } + + @Override + public CompoundTag getFabricBalmData() { + return fabricBalmData; + } + + @Override + public void setFabricBalmData(CompoundTag tag) { + this.fabricBalmData = tag; + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/mixin/MinecraftMixin.java b/neoforge/src/main/java/net/blay09/mods/balm/mixin/MinecraftMixin.java new file mode 100644 index 00000000..f1222b0c --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/mixin/MinecraftMixin.java @@ -0,0 +1,18 @@ +package net.blay09.mods.balm.mixin; + +import net.blay09.mods.balm.api.Balm; +import net.blay09.mods.balm.api.event.client.ClientStartedEvent; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Minecraft.class) +public class MinecraftMixin { + @Inject(method = "run()V", at = @At("HEAD")) + void run(CallbackInfo callbackInfo) { + final ClientStartedEvent event = new ClientStartedEvent(Minecraft.getInstance()); + Balm.getEvents().fireEvent(event); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/mixin/ModelBakeryMixin.java b/neoforge/src/main/java/net/blay09/mods/balm/mixin/ModelBakeryMixin.java new file mode 100644 index 00000000..30d2349b --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/mixin/ModelBakeryMixin.java @@ -0,0 +1,22 @@ +package net.blay09.mods.balm.mixin; + +import net.blay09.mods.balm.api.client.BalmClient; +import net.blay09.mods.balm.neoforge.client.rendering.NeoForgeBalmModels; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.Material; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.resources.ResourceLocation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.function.BiFunction; + +@Mixin(ModelBakery.class) +public class ModelBakeryMixin { + @Inject(method = "bakeModels(Ljava/util/function/BiFunction;)V", at = @At("RETURN")) + private void apply(BiFunction spriteBiFunction, CallbackInfo callbackInfo) { + ((NeoForgeBalmModels) BalmClient.getModels()).onBakeModels((ModelBakery) (Object) this, spriteBiFunction); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/DeferredRegisters.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/DeferredRegisters.java new file mode 100644 index 00000000..afeeb650 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/DeferredRegisters.java @@ -0,0 +1,43 @@ +package net.blay09.mods.balm.neoforge; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import com.google.common.collect.Tables; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.neoforged.neoforge.registries.IForgeRegistry; + +import java.util.Collection; + +public class DeferredRegisters { + private static final Table, String, DeferredRegister> deferredRegisters = Tables.synchronizedTable(HashBasedTable.create()); + + public static DeferredRegister get(IForgeRegistry registry, String modId) { + return get(registry.getRegistryKey(), modId); + } + + @SuppressWarnings("unchecked") + public static DeferredRegister get(ResourceKey> registry, String modId) { + DeferredRegister register = deferredRegisters.get(registry, modId); + if (register == null) { + register = DeferredRegister.create(registry, modId); + deferredRegisters.put(registry, modId, register); + } + + return (DeferredRegister) register; + } + + public static Collection> getByModId(String modId) { + return deferredRegisters.column(modId).values(); + } + + public static void register(String modId, IEventBus modEventBus) { + synchronized (deferredRegisters) { + for (DeferredRegister deferredRegister : DeferredRegisters.getByModId(modId)) { + deferredRegister.register(modEventBus); + } + } + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalm.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalm.java new file mode 100644 index 00000000..af5ad82b --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalm.java @@ -0,0 +1,32 @@ +package net.blay09.mods.balm.neoforge; + +import net.neoforged.fml.common.Mod; + +@Mod("balm") +public class NeoForgeBalm { + + public NeoForgeBalm() { + // ((AbstractBalmConfig) Balm.getConfig()).initialize(); + // ExampleConfig.initialize(); + + // ForgeBalmWorldGen.initializeBalmBiomeModifiers(); + // FMLJavaModLoadingContext.get().getModEventBus().addListener(ForgeBalmClient::onInitializeClient); + + // ForgeBalmProviders providers = (ForgeBalmProviders) Balm.getProviders(); + // providers.register(IItemHandler.class, new CapabilityToken<>() { + // }); + // providers.register(IFluidHandler.class, new CapabilityToken<>() { + // }); + // providers.register(IFluidHandlerItem.class, new CapabilityToken<>() { + // }); + // providers.register(IEnergyStorage.class, new CapabilityToken<>() { + // }); + // providers.register(Container.class, new CapabilityToken<>() { + // }); + // providers.register(FluidTank.class, new CapabilityToken<>() { + // }); + // providers.register(EnergyStorage.class, new CapabilityToken<>() { + // }); + } + +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmHooks.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmHooks.java new file mode 100644 index 00000000..753da570 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmHooks.java @@ -0,0 +1,132 @@ +package net.blay09.mods.balm.neoforge; + +import net.blay09.mods.balm.api.BalmHooks; +import net.blay09.mods.balm.api.entity.BalmEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.RandomSource; +import net.minecraft.world.Container; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Pose; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BoneMealItem; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.phys.BlockHitResult; +import net.neoforged.bus.api.Event; +import net.neoforged.neoforge.common.CommonHooks; +import net.neoforged.neoforge.common.ToolActions; +import net.neoforged.neoforge.common.util.FakePlayer; +import net.neoforged.neoforge.event.EventHooks; +import net.neoforged.neoforge.fluids.FluidUtil; +import net.neoforged.neoforge.items.ItemHandlerHelper; +import net.neoforged.neoforge.server.ServerLifecycleHooks; +import org.jetbrains.annotations.Nullable; + +public class NeoForgeBalmHooks implements BalmHooks { + @Override + public boolean blockGrowFeature(Level level, RandomSource random, BlockPos pos, @Nullable Holder> holder) { + return !EventHooks.blockGrowFeature(level, random, pos, holder).getResult().equals(Event.Result.DENY); + } + + @Override + public boolean growCrop(ItemStack itemStack, Level level, BlockPos pos, Player player) { + if (player != null) { + return BoneMealItem.applyBonemeal(itemStack, level, pos, player); + } else { + return BoneMealItem.growCrop(itemStack, level, pos); + } + } + + @Override + public CompoundTag getPersistentData(Entity entity) { + CompoundTag persistentData = entity.getPersistentData(); + if (entity instanceof Player) { + CompoundTag persistedTag = persistentData.getCompound(Player.PERSISTED_NBT_TAG); + persistentData.put(Player.PERSISTED_NBT_TAG, persistedTag); + persistentData = persistedTag; + } + + CompoundTag balmData = persistentData.getCompound("BalmData"); + if (balmData.size() == 0) { + // If we have no data, try to import from Fabric in case the world was migrated + balmData = ((BalmEntity) entity).getFabricBalmData(); + } + persistentData.put("BalmData", balmData); + + return balmData; + } + + @Override + public void curePotionEffects(LivingEntity entity, ItemStack curativeItem) { + entity.curePotionEffects(curativeItem); + } + + @Override + public boolean isFakePlayer(Player player) { + return player instanceof FakePlayer; + } + + @Override + public ItemStack getCraftingRemainingItem(ItemStack itemStack) { + return itemStack.getCraftingRemainingItem(); + } + + @Override + public DyeColor getColor(ItemStack itemStack) { + return DyeColor.getColor(itemStack); + } + + @Override + public boolean canItemsStack(ItemStack first, ItemStack second) { + return ItemHandlerHelper.canItemStacksStack(first, second); + } + + @Override + public int getBurnTime(ItemStack itemStack) { + return CommonHooks.getBurnTime(itemStack, RecipeType.SMELTING); + } + + @Override + public void firePlayerCraftingEvent(Player player, ItemStack crafted, Container craftMatrix) { + EventHooks.firePlayerCraftingEvent(player, crafted, craftMatrix); + } + + @Override + public boolean useFluidTank(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult) { + return FluidUtil.interactWithFluidHandler(player, hand, level, pos, hitResult.getDirection()); + } + + @Override + public boolean isShield(ItemStack itemStack) { + return itemStack.getItem().canPerformAction(itemStack, ToolActions.SHIELD_BLOCK); + } + + @Override + public boolean isRepairable(ItemStack itemStack) { + return itemStack.isRepairable(); + } + + @Override + public void setForcedPose(Player player, Pose pose) { + player.setForcedPose(pose); + } + + @Override + public MinecraftServer getServer() { + return ServerLifecycleHooks.getCurrentServer(); + } + + @Override + public double getBlockReachDistance(Player player) { + return player.getBlockReach(); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmRegistries.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmRegistries.java new file mode 100644 index 00000000..d3e88845 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmRegistries.java @@ -0,0 +1,89 @@ +package net.blay09.mods.balm.neoforge; + +import net.blay09.mods.balm.api.BalmRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.ItemTags; +import net.minecraft.tags.TagKey; +import net.minecraft.world.effect.MobEffect; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.ai.attributes.Attribute; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.material.Fluid; +import net.neoforged.neoforge.common.NeoForgeMod; +import net.neoforged.neoforge.registries.ForgeRegistries; + +import java.util.Collection; + +public class NeoForgeBalmRegistries implements BalmRegistries { + @Override + public ResourceLocation getKey(Item item) { + return ForgeRegistries.ITEMS.getKey(item); + } + + @Override + public ResourceLocation getKey(Block block) { + return ForgeRegistries.BLOCKS.getKey(block); + } + + @Override + public ResourceLocation getKey(Fluid fluid) { + return ForgeRegistries.FLUIDS.getKey(fluid); + } + + @Override + public ResourceLocation getKey(EntityType entityType) { + return ForgeRegistries.ENTITY_TYPES.getKey(entityType); + } + + @Override + public ResourceLocation getKey(MenuType menuType) { + return ForgeRegistries.MENU_TYPES.getKey(menuType); + } + + @Override + public Collection getItemKeys() { + return ForgeRegistries.ITEMS.getKeys(); + } + + @Override + public Item getItem(ResourceLocation key) { + return ForgeRegistries.ITEMS.getValue(key); + } + + @Override + public Block getBlock(ResourceLocation key) { + return ForgeRegistries.BLOCKS.getValue(key); + } + + @Override + public Fluid getFluid(ResourceLocation key) { + return ForgeRegistries.FLUIDS.getValue(key); + } + + @Override + public MobEffect getMobEffect(ResourceLocation key) { + return ForgeRegistries.MOB_EFFECTS.getValue(key); + } + + @Override + public TagKey getItemTag(ResourceLocation key) { + return ItemTags.create(key); + } + + @Override + public void enableMilkFluid() { + NeoForgeMod.enableMilkFluid(); + } + + @Override + public Fluid getMilkFluid() { + return NeoForgeMod.MILK.get(); + } + + @Override + public Attribute getAttribute(ResourceLocation key) { + return ForgeRegistries.ATTRIBUTES.getValue(key); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmRuntime.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmRuntime.java new file mode 100644 index 00000000..26f3fc53 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmRuntime.java @@ -0,0 +1,226 @@ +package net.blay09.mods.balm.neoforge; + +import net.blay09.mods.balm.api.BalmHooks; +import net.blay09.mods.balm.api.BalmRegistries; +import net.blay09.mods.balm.api.BalmRuntime; +import net.blay09.mods.balm.api.block.BalmBlockEntities; +import net.blay09.mods.balm.api.block.BalmBlocks; +import net.blay09.mods.balm.api.command.BalmCommands; +import net.blay09.mods.balm.api.config.BalmConfig; +import net.blay09.mods.balm.api.entity.BalmEntities; +import net.blay09.mods.balm.api.event.BalmEvents; +import net.blay09.mods.balm.api.item.BalmItems; +import net.blay09.mods.balm.api.loot.BalmLootTables; +import net.blay09.mods.balm.api.menu.BalmMenus; +import net.blay09.mods.balm.api.network.BalmNetworking; +import net.blay09.mods.balm.api.provider.BalmProviders; +import net.blay09.mods.balm.api.proxy.ProxyResolutionException; +import net.blay09.mods.balm.api.proxy.SidedProxy; +import net.blay09.mods.balm.api.sound.BalmSounds; +import net.blay09.mods.balm.api.stats.BalmStats; +import net.blay09.mods.balm.api.world.BalmWorldGen; +import net.blay09.mods.balm.common.CommonBalmLootTables; +import net.blay09.mods.balm.neoforge.block.NeoForgeBalmBlocks; +import net.blay09.mods.balm.neoforge.block.entity.NeoForgeBalmBlockEntities; +import net.blay09.mods.balm.neoforge.command.NeoForgeBalmCommands; +import net.blay09.mods.balm.neoforge.config.NeoForgeBalmConfig; +import net.blay09.mods.balm.neoforge.entity.NeoForgeBalmEntities; +import net.blay09.mods.balm.neoforge.event.NeoForgeBalmCommonEvents; +import net.blay09.mods.balm.neoforge.event.NeoForgeBalmEvents; +import net.blay09.mods.balm.neoforge.item.NeoForgeBalmItems; +import net.blay09.mods.balm.neoforge.menu.NeoForgeBalmMenus; +import net.blay09.mods.balm.neoforge.network.NeoForgeBalmNetworking; +import net.blay09.mods.balm.neoforge.provider.NeoForgeBalmProviders; +import net.blay09.mods.balm.neoforge.sound.NeoForgeBalmSounds; +import net.blay09.mods.balm.neoforge.stats.NeoForgeBalmStats; +import net.blay09.mods.balm.neoforge.world.NeoForgeBalmWorldGen; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.PreparableReloadListener; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.ResourceManagerReloadListener; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModList; +import net.neoforged.fml.event.lifecycle.FMLLoadCompleteEvent; +import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext; +import net.neoforged.fml.loading.FMLEnvironment; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.AddReloadListenerEvent; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class NeoForgeBalmRuntime implements BalmRuntime { + private final BalmWorldGen worldGen = new NeoForgeBalmWorldGen(); + private final BalmBlocks blocks = new NeoForgeBalmBlocks(); + private final BalmBlockEntities blockEntities = new NeoForgeBalmBlockEntities(); + private final NeoForgeBalmEvents events = new NeoForgeBalmEvents(); + private final BalmItems items = new NeoForgeBalmItems(); + private final BalmMenus menus = new NeoForgeBalmMenus(); + private final BalmNetworking networking = new NeoForgeBalmNetworking(); + private final BalmConfig config = new NeoForgeBalmConfig(); + private final BalmHooks hooks = new NeoForgeBalmHooks(); + private final BalmRegistries registries = new NeoForgeBalmRegistries(); + private final BalmSounds sounds = new NeoForgeBalmSounds(); + private final BalmEntities entities = new NeoForgeBalmEntities(); + private final BalmProviders providers = new NeoForgeBalmProviders(); + private final BalmCommands commands = new NeoForgeBalmCommands(); + private final BalmLootTables lootTables = new CommonBalmLootTables(); + private final BalmStats stats = new NeoForgeBalmStats(); + + private final List addonClasses = new ArrayList<>(); + + public NeoForgeBalmRuntime() { + NeoForgeBalmCommonEvents.registerEvents(events); + } + + @Override + public BalmConfig getConfig() { + return config; + } + + @Override + public BalmEvents getEvents() { + return events; + } + + @Override + public BalmWorldGen getWorldGen() { + return worldGen; + } + + @Override + public BalmBlocks getBlocks() { + return blocks; + } + + @Override + public BalmBlockEntities getBlockEntities() { + return blockEntities; + } + + @Override + public BalmItems getItems() { + return items; + } + + @Override + public BalmMenus getMenus() { + return menus; + } + + @Override + public BalmNetworking getNetworking() { + return networking; + } + + @Override + public BalmHooks getHooks() { + return hooks; + } + + @Override + public BalmRegistries getRegistries() { + return registries; + } + + @Override + public BalmSounds getSounds() { + return sounds; + } + + @Override + public BalmEntities getEntities() { + return entities; + } + + @Override + public BalmProviders getProviders() { + return providers; + } + + @Override + public BalmCommands getCommands() { + return commands; + } + + @Override + public BalmLootTables getLootTables() { + return lootTables; + } + + @Override + public BalmStats getStats() { + return stats; + } + + @Override + public boolean isModLoaded(String modId) { + return ModList.get().isLoaded(modId); + } + + @Override + public String getModName(String modId) { + return ModList.get().getModContainerById(modId).map(it -> it.getModInfo().getDisplayName()).orElse(modId); + } + + @Override + public void initialize(String modId, Runnable initializer) { + ((NeoForgeBalmItems) items).register(); + ((NeoForgeBalmEntities) entities).register(); + ((NeoForgeBalmWorldGen) worldGen).register(); + ((NeoForgeBalmStats) stats).register(); + + initializer.run(); + + IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); + modEventBus.addListener((FMLLoadCompleteEvent event) -> initializeAddons()); + DeferredRegisters.register(modId, modEventBus); + } + + @Override + public void initializeIfLoaded(String modId, String className) { + if (isModLoaded(modId)) { + addonClasses.add(className); + } + } + + @Override + public SidedProxy sidedProxy(String commonName, String clientName) { + SidedProxy proxy = new SidedProxy<>(commonName, clientName); + try { + if (FMLEnvironment.dist.isClient()) { + + proxy.resolveClient(); + } else { + proxy.resolveCommon(); + } + } catch (ProxyResolutionException e) { + throw new RuntimeException(e); + } + + return proxy; + } + + private void initializeAddons() { + for (String addonClass : addonClasses) { + try { + Class.forName(addonClass).getConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) { + e.printStackTrace(); + } + } + } + + @Override + public void addServerReloadListener(ResourceLocation identifier, PreparableReloadListener reloadListener) { + NeoForge.EVENT_BUS.addListener((AddReloadListenerEvent event) -> event.addListener(reloadListener)); + } + + @Override + public void addServerReloadListener(ResourceLocation identifier, Consumer reloadListener) { + NeoForge.EVENT_BUS.addListener((AddReloadListenerEvent event) -> event.addListener((ResourceManagerReloadListener) reloadListener::accept)); + } + +} \ No newline at end of file diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmRuntimeFactory.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmRuntimeFactory.java new file mode 100644 index 00000000..6ccc32e9 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/NeoForgeBalmRuntimeFactory.java @@ -0,0 +1,11 @@ +package net.blay09.mods.balm.neoforge; + +import net.blay09.mods.balm.api.BalmRuntime; +import net.blay09.mods.balm.api.BalmRuntimeFactory; + +public class NeoForgeBalmRuntimeFactory implements BalmRuntimeFactory { + @Override + public BalmRuntime create() { + return new NeoForgeBalmRuntime(); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/block/NeoForgeBalmBlocks.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/block/NeoForgeBalmBlocks.java new file mode 100644 index 00000000..b9fa7c36 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/block/NeoForgeBalmBlocks.java @@ -0,0 +1,42 @@ +package net.blay09.mods.balm.neoforge.block; + +import net.blay09.mods.balm.api.Balm; +import net.blay09.mods.balm.neoforge.DeferredRegisters; +import net.blay09.mods.balm.api.DeferredObject; +import net.blay09.mods.balm.api.block.BalmBlocks; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.neoforged.neoforge.registries.ForgeRegistries; +import net.neoforged.neoforge.registries.RegistryObject; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Supplier; + +public class NeoForgeBalmBlocks implements BalmBlocks { + @Override + public BlockBehaviour.Properties blockProperties() { + return BlockBehaviour.Properties.of(); + } + + @Override + public DeferredObject registerBlock(Supplier supplier, ResourceLocation identifier) { + DeferredRegister register = DeferredRegisters.get(ForgeRegistries.BLOCKS, identifier.getNamespace()); + RegistryObject registryObject = register.register(identifier.getPath(), supplier); + return new DeferredObject<>(identifier, registryObject, registryObject::isPresent); + } + + @Override + public DeferredObject registerBlockItem(Supplier supplier, ResourceLocation identifier, @Nullable ResourceLocation creativeTab) { + return Balm.getItems().registerItem(supplier::get, identifier, creativeTab); + } + + @Override + public void register(Supplier blockSupplier, Supplier blockItemSupplier, ResourceLocation identifier, @Nullable ResourceLocation creativeTab) { + registerBlock(blockSupplier, identifier); + registerBlockItem(blockItemSupplier, identifier, creativeTab); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/block/entity/NeoForgeBalmBlockEntities.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/block/entity/NeoForgeBalmBlockEntities.java new file mode 100644 index 00000000..8aa96a68 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/block/entity/NeoForgeBalmBlockEntities.java @@ -0,0 +1,38 @@ +package net.blay09.mods.balm.neoforge.block.entity; + +import net.blay09.mods.balm.api.DeferredObject; +import net.blay09.mods.balm.api.block.BalmBlockEntities; +import net.blay09.mods.balm.api.block.entity.BalmBlockEntityFactory; +import net.blay09.mods.balm.neoforge.DeferredRegisters; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.neoforged.neoforge.registries.ForgeRegistries; +import net.neoforged.neoforge.registries.RegistryObject; + +import java.util.Arrays; +import java.util.function.Supplier; + +public class NeoForgeBalmBlockEntities implements BalmBlockEntities { + @Override + public DeferredObject> registerBlockEntity(ResourceLocation identifier, BalmBlockEntityFactory factory, Supplier blocks) { + DeferredRegister> register = DeferredRegisters.get(ForgeRegistries.BLOCK_ENTITY_TYPES, identifier.getNamespace()); + RegistryObject> registryObject = register.register(identifier.getPath(), () -> { + Block[] resolvedBlocks = blocks.get(); + return BlockEntityType.Builder.of(factory::create, resolvedBlocks).build(null); + }); + return new DeferredObject<>(identifier, registryObject, registryObject::isPresent); + } + + @Override + public DeferredObject> registerBlockEntity(ResourceLocation identifier, BalmBlockEntityFactory factory, DeferredObject... blocks) { + DeferredRegister> register = DeferredRegisters.get(ForgeRegistries.BLOCK_ENTITY_TYPES, identifier.getNamespace()); + RegistryObject> registryObject = register.register(identifier.getPath(), () -> { + Block[] resolvedBlocks = Arrays.stream(blocks).map(DeferredObject::get).toArray(Block[]::new); + return BlockEntityType.Builder.of(factory::create, resolvedBlocks).build(null); + }); + return new DeferredObject<>(identifier, registryObject, registryObject::isPresent); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/NeoForgeBalmClient.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/NeoForgeBalmClient.java new file mode 100644 index 00000000..bb476df2 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/NeoForgeBalmClient.java @@ -0,0 +1,11 @@ +package net.blay09.mods.balm.neoforge.client; + +import net.blay09.mods.balm.api.Balm; +import net.blay09.mods.balm.api.event.client.DisconnectedFromServerEvent; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; + +public class NeoForgeBalmClient { + public static void onInitializeClient(FMLClientSetupEvent setupEvent) { + Balm.getEvents().onEvent(DisconnectedFromServerEvent.class, event -> Balm.getConfig().resetToBackingConfigs()); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/NeoForgeBalmClientRuntime.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/NeoForgeBalmClientRuntime.java new file mode 100644 index 00000000..fb420d1f --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/NeoForgeBalmClientRuntime.java @@ -0,0 +1,64 @@ +package net.blay09.mods.balm.neoforge.client; + +import net.blay09.mods.balm.api.Balm; +import net.blay09.mods.balm.api.client.BalmClientRuntime; +import net.blay09.mods.balm.api.client.keymappings.BalmKeyMappings; +import net.blay09.mods.balm.api.client.rendering.BalmModels; +import net.blay09.mods.balm.api.client.rendering.BalmRenderers; +import net.blay09.mods.balm.api.client.rendering.BalmTextures; +import net.blay09.mods.balm.api.client.screen.BalmScreens; +import net.blay09.mods.balm.neoforge.event.NeoForgeBalmEvents; +import net.blay09.mods.balm.neoforge.client.keymappings.NeoForgeBalmKeyMappings; +import net.blay09.mods.balm.neoforge.client.rendering.NeoForgeBalmModels; +import net.blay09.mods.balm.neoforge.client.rendering.NeoForgeBalmRenderers; +import net.blay09.mods.balm.neoforge.client.rendering.NeoForgeBalmTextures; +import net.blay09.mods.balm.neoforge.client.screen.NeoForgeBalmScreens; +import net.blay09.mods.balm.neoforge.event.NeoForgeBalmClientEvents; + +public class NeoForgeBalmClientRuntime implements BalmClientRuntime { + + private final BalmRenderers renderers = new NeoForgeBalmRenderers(); + private final BalmTextures textures = new NeoForgeBalmTextures(); + private final BalmScreens screens = new NeoForgeBalmScreens(); + private final BalmKeyMappings keyMappings = new NeoForgeBalmKeyMappings(); + private final BalmModels models = new NeoForgeBalmModels(); + + public NeoForgeBalmClientRuntime() { + NeoForgeBalmClientEvents.registerEvents(((NeoForgeBalmEvents) Balm.getEvents())); + } + + @Override + public BalmRenderers getRenderers() { + return renderers; + } + + @Override + public BalmTextures getTextures() { + return textures; + } + + @Override + public BalmScreens getScreens() { + return screens; + } + + @Override + public BalmModels getModels() { + return models; + } + + @Override + public BalmKeyMappings getKeyMappings() { + return keyMappings; + } + + @Override + public void initialize(String modId, Runnable initializer) { + ((NeoForgeBalmRenderers) renderers).register(); + ((NeoForgeBalmScreens) screens).register(); + ((NeoForgeBalmModels) models).register(); + ((NeoForgeBalmKeyMappings) keyMappings).register(); + + initializer.run(); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/NeoForgeBalmClientRuntimeFactory.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/NeoForgeBalmClientRuntimeFactory.java new file mode 100644 index 00000000..77c91df3 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/NeoForgeBalmClientRuntimeFactory.java @@ -0,0 +1,11 @@ +package net.blay09.mods.balm.neoforge.client; + +import net.blay09.mods.balm.api.client.BalmClientRuntime; +import net.blay09.mods.balm.api.client.BalmClientRuntimeFactory; + +public class NeoForgeBalmClientRuntimeFactory implements BalmClientRuntimeFactory { + @Override + public BalmClientRuntime create() { + return new NeoForgeBalmClientRuntime(); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/keymappings/NeoForgeBalmKeyMappings.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/keymappings/NeoForgeBalmKeyMappings.java new file mode 100644 index 00000000..ce2fd669 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/keymappings/NeoForgeBalmKeyMappings.java @@ -0,0 +1,120 @@ +package net.blay09.mods.balm.neoforge.client.keymappings; + +import com.mojang.blaze3d.platform.InputConstants; +import net.blay09.mods.balm.api.client.keymappings.KeyConflictContext; +import net.blay09.mods.balm.api.client.keymappings.KeyModifier; +import net.blay09.mods.balm.api.client.keymappings.KeyModifiers; +import net.blay09.mods.balm.common.client.keymappings.CommonBalmKeyMappings; +import net.minecraft.client.KeyMapping; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext; +import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; +import net.neoforged.neoforge.client.settings.IKeyConflictContext; +import org.jetbrains.annotations.Nullable; + +public class NeoForgeBalmKeyMappings extends CommonBalmKeyMappings { + private static class Registrations { + public final List keyMappings = new ArrayList<>(); + + @SubscribeEvent + public void registerKeyMappings(RegisterKeyMappingsEvent event) { + keyMappings.forEach(event::register); + } + } + + private final Map registrations = new ConcurrentHashMap<>(); + + @Override + public KeyMapping registerKeyMapping(String name, KeyConflictContext conflictContext, KeyModifier modifier, InputConstants.Type type, int keyCode, String category) { + KeyMapping keyMapping = new KeyMapping(name, toForge(conflictContext), toForge(modifier), type, keyCode, category); + getActiveRegistrations().keyMappings.add(keyMapping); + return keyMapping; + } + + @Override + public KeyMapping registerKeyMapping(String name, KeyConflictContext conflictContext, KeyModifiers modifiers, InputConstants.Type type, int keyCode, String category) { + var keyModifiers = modifiers.asList(); + var mainModifier = !keyModifiers.isEmpty() ? keyModifiers.get(0) : KeyModifier.NONE; + KeyMapping keyMapping = new KeyMapping(name, toForge(conflictContext), toForge(mainModifier), type, keyCode, category); + getActiveRegistrations().keyMappings.add(keyMapping); + if (keyModifiers.size() > 1) { + registerModifierKeyMappings(keyMapping, conflictContext, keyModifiers.subList(1, keyModifiers.size())); + } + if (modifiers.hasCustomModifiers()) { + registerCustomModifierKeyMappings(keyMapping, conflictContext, modifiers.getCustomModifiers()); + } + return keyMapping; + } + + @Override + public boolean isActiveAndMatches(@Nullable KeyMapping keyMapping, InputConstants.Key input) { + return isActive(keyMapping) && keyMapping.isActiveAndMatches(input); + } + + @Override + public boolean isActiveAndMatches(@Nullable KeyMapping keyMapping, int keyCode, int scanCode) { + return isActive(keyMapping) && keyMapping.isActiveAndMatches(InputConstants.getKey(keyCode, scanCode)); + } + + @Override + public boolean isActiveAndMatches(@Nullable KeyMapping keyMapping, InputConstants.Type type, int keyCode, int scanCode) { + if (!isActive(keyMapping)) { + return false; + } + + return type == InputConstants.Type.MOUSE ? keyMapping.isActiveAndMatches(InputConstants.Type.MOUSE.getOrCreate(keyCode)) : keyMapping.isActiveAndMatches( + InputConstants.getKey(keyCode, scanCode)); + } + + private boolean isActiveAndMatchesStrictModifier(@Nullable KeyMapping keyMapping, int keyCode, int scanCode) { + if (!isActive(keyMapping)) { + return false; + } + + if (keyMapping.getKeyModifier() == net.neoforged.neoforge.client.settings.KeyModifier.NONE) { + if (net.neoforged.neoforge.client.settings.KeyModifier.SHIFT.isActive(keyMapping.getKeyConflictContext()) + || net.neoforged.neoforge.client.settings.KeyModifier.CONTROL.isActive(keyMapping.getKeyConflictContext()) + || net.neoforged.neoforge.client.settings.KeyModifier.ALT.isActive(keyMapping.getKeyConflictContext())) { + return false; + } + } + + return keyMapping.matches(keyCode, scanCode); + } + + @Override + protected boolean isContextActive(KeyMapping keyMapping) { + return keyMapping.getKeyConflictContext().isActive(); + } + + private static IKeyConflictContext toForge(KeyConflictContext context) { + return switch (context) { + case UNIVERSAL -> net.neoforged.neoforge.client.settings.KeyConflictContext.UNIVERSAL; + case GUI -> net.neoforged.neoforge.client.settings.KeyConflictContext.GUI; + case INGAME -> net.neoforged.neoforge.client.settings.KeyConflictContext.IN_GAME; + }; + } + + private static net.neoforged.neoforge.client.settings.KeyModifier toForge(KeyModifier modifier) { + return switch (modifier) { + case SHIFT -> net.neoforged.neoforge.client.settings.KeyModifier.SHIFT; + case CONTROL -> net.neoforged.neoforge.client.settings.KeyModifier.CONTROL; + case ALT -> net.neoforged.neoforge.client.settings.KeyModifier.ALT; + default -> net.neoforged.neoforge.client.settings.KeyModifier.NONE; + }; + } + + public void register() { + FMLJavaModLoadingContext.get().getModEventBus().register(getActiveRegistrations()); + } + + private Registrations getActiveRegistrations() { + return registrations.computeIfAbsent(ModLoadingContext.get().getActiveNamespace(), it -> new Registrations()); + } + +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeBalmModels.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeBalmModels.java new file mode 100644 index 00000000..862993cb --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeBalmModels.java @@ -0,0 +1,199 @@ +package net.blay09.mods.balm.neoforge.client.rendering; + +import com.mojang.datafixers.util.Pair; +import com.mojang.logging.LogUtils; +import com.mojang.math.Transformation; +import net.blay09.mods.balm.api.DeferredObject; +import net.blay09.mods.balm.api.client.rendering.BalmModels; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.BlockModelShaper; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext; +import net.neoforged.neoforge.client.event.ModelEvent; +import net.neoforged.neoforge.client.model.SimpleModelState; +import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; +import org.slf4j.Logger; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +public class NeoForgeBalmModels implements BalmModels { + + private static final Logger LOGGER = LogUtils.getLogger(); + + private abstract static class DeferredModel extends DeferredObject { + public DeferredModel(ResourceLocation identifier) { + super(identifier); + } + + public void resolveAndSet(ModelBakery modelBakery, Map modelRegistry, BiFunction spriteBiFunction) { + try { + set(resolve(modelBakery, modelRegistry, spriteBiFunction)); + } catch (Exception exception) { + LOGGER.warn("Unable to bake model: '{}':", getIdentifier(), exception); + set(modelBakery.getBakedTopLevelModels().get(ModelBakery.MISSING_MODEL_LOCATION)); + } + } + + public abstract BakedModel resolve(ModelBakery modelBakery, Map modelRegistry, BiFunction spriteBiFunction); + } + + public final List modelsToBake = Collections.synchronizedList(new ArrayList<>()); + + private static class Registrations { + public final List additionalModels = new ArrayList<>(); + public final List, Supplier>> overrides = new ArrayList<>(); + + private BiFunction spriteBiFunction; + + public void setSpriteBiFunction(BiFunction spriteBiFunction) { + this.spriteBiFunction = spriteBiFunction; + } + + @SubscribeEvent + public void onRegisterAdditionalModels(ModelEvent.RegisterAdditional event) { + additionalModels.forEach(it -> event.register(it.getIdentifier())); + } + + @SubscribeEvent + public void onModelBakingCompleted(ModelEvent.ModifyBakingResult event) { + for (Pair, Supplier> override : overrides) { + Block block = override.getFirst().get(); + BakedModel bakedModel = override.getSecond().get(); + block.getStateDefinition().getPossibleStates().forEach(state -> { + ModelResourceLocation modelLocation = BlockModelShaper.stateToModelLocation(state); + event.getModels().put(modelLocation, bakedModel); + }); + } + } + + @SubscribeEvent + public void onModelBakingCompleted(ModelEvent.BakingCompleted event) { + for (DeferredModel deferredModel : additionalModels) { + deferredModel.resolveAndSet(event.getModelBakery(), event.getModels(), spriteBiFunction); + } + + spriteBiFunction = null; + } + } + + private final Map registrations = new ConcurrentHashMap<>(); + private ModelBakery modelBakery; + + public void onBakeModels(ModelBakery modelBakery, BiFunction spriteBiFunction) { + this.modelBakery = modelBakery; + registrations.values().forEach(it -> it.setSpriteBiFunction(spriteBiFunction)); + + synchronized (modelsToBake) { + for (DeferredModel deferredModel : modelsToBake) { + deferredModel.resolveAndSet(modelBakery, modelBakery.getBakedTopLevelModels(), spriteBiFunction); + } + } + } + + @Override + public DeferredObject loadModel(ResourceLocation identifier) { + DeferredModel deferredModel = new DeferredModel(identifier) { + @Override + public BakedModel resolve(ModelBakery bakery, Map modelRegistry, BiFunction spriteBiFunction) { + return modelRegistry.get(getIdentifier()); + } + }; + getActiveRegistrations().additionalModels.add(deferredModel); + return deferredModel; + } + + @Override + public DeferredObject bakeModel(ResourceLocation identifier, UnbakedModel model) { + DeferredModel deferredModel = new DeferredModel(identifier) { + @Override + public BakedModel resolve(ModelBakery bakery, Map modelRegistry, BiFunction spriteBiFunction) { + ModelBaker baker = createBaker(identifier, spriteBiFunction); + return model.bake(baker, baker.getModelTextureGetter(), getModelState(Transformation.identity()), identifier); + } + }; + modelsToBake.add(deferredModel); + return deferredModel; + } + + @Override + public DeferredObject retexture(ResourceLocation identifier, Map textureMap) { + DeferredModel deferredModel = new DeferredModel(identifier) { + @Override + public BakedModel resolve(ModelBakery bakery, Map modelRegistry, BiFunction spriteBiFunction) { + UnbakedModel model = retexture(bakery, identifier, textureMap); + ModelBaker baker = createBaker(identifier, spriteBiFunction); + return model.bake(baker, baker.getModelTextureGetter(), getModelState(Transformation.identity()), identifier); + } + }; + modelsToBake.add(deferredModel); + return deferredModel; + } + + @Override + public DeferredObject loadDynamicModel(ResourceLocation identifier, @Nullable Function modelFunction, @Nullable Function> textureMapFunction, @Nullable BiConsumer transformFunction, List renderTypes) { + Function effectiveModelFunction = modelFunction != null ? modelFunction : (it -> identifier); + + DeferredModel deferredModel = new DeferredModel(identifier) { + @Override + public BakedModel resolve(ModelBakery bakery, Map modelRegistry, BiFunction spriteBiFunction) { + return new NeoForgeCachedDynamicModel(bakery, effectiveModelFunction, null, textureMapFunction, transformFunction, renderTypes, identifier); + } + }; + modelsToBake.add(deferredModel); + return deferredModel; + } + + @Override + public void overrideModel(Supplier block, Supplier model) { + getActiveRegistrations().overrides.add(Pair.of(block, model)); + } + + @Override + public ModelState getModelState(Transformation transformation) { + return new SimpleModelState(transformation); + } + + @Override + public UnbakedModel getUnbakedModelOrMissing(ResourceLocation location) { + return modelBakery.getModel(location); + } + + @Override + public UnbakedModel getUnbakedMissingModel() { + return modelBakery.getModel(ModelBakery.MISSING_MODEL_LOCATION); + } + + public void register() { + FMLJavaModLoadingContext.get().getModEventBus().register(getActiveRegistrations()); + } + + private Registrations getActiveRegistrations() { + return registrations.computeIfAbsent(ModLoadingContext.get().getActiveNamespace(), it -> new Registrations()); + } + + @Override + public ModelBaker createBaker(ResourceLocation location, BiFunction spriteBiFunction) { + try { + Class clazz = Class.forName("net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl"); + Constructor constructor = clazz.getDeclaredConstructor(ModelBakery.class, BiFunction.class, ResourceLocation.class); + constructor.setAccessible(true); + return (ModelBaker) constructor.newInstance(modelBakery, spriteBiFunction, location); + } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Balm failed to create model baker", e); + } + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeBalmRenderers.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeBalmRenderers.java new file mode 100644 index 00000000..53ae7c8b --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeBalmRenderers.java @@ -0,0 +1,141 @@ +package net.blay09.mods.balm.neoforge.client.rendering; + +import com.mojang.datafixers.util.Pair; +import net.blay09.mods.balm.api.client.rendering.BalmRenderers; +import net.minecraft.client.color.block.BlockColor; +import net.minecraft.client.color.item.ItemColor; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext; +import net.neoforged.neoforge.client.event.EntityRenderersEvent; +import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +public class NeoForgeBalmRenderers implements BalmRenderers { + + private static class ColorRegistration { + private final THandler color; + private final Supplier objects; + + public ColorRegistration(THandler color, Supplier objects) { + this.color = color; + this.objects = objects; + } + + public THandler getColor() { + return color; + } + + public Supplier getObjects() { + return objects; + } + } + + private static class Registrations { + public final Map> layerDefinitions = new HashMap<>(); + public final List>, BlockEntityRendererProvider>> blockEntityRenderers = new ArrayList<>(); + public final List>, EntityRendererProvider>> entityRenderers = new ArrayList<>(); + public final List> blockColors = new ArrayList<>(); + public final List> itemColors = new ArrayList<>(); + + @SubscribeEvent + public void setupClient(FMLClientSetupEvent event) { + } + + @SubscribeEvent + public void initRenderers(EntityRenderersEvent.RegisterRenderers event) { + for (Pair>, BlockEntityRendererProvider> entry : blockEntityRenderers) { + event.registerBlockEntityRenderer(entry.getFirst().get(), entry.getSecond()); + } + + for (Pair>, EntityRendererProvider> entry : entityRenderers) { + event.registerEntityRenderer(entry.getFirst().get(), entry.getSecond()); + } + } + + @SubscribeEvent + public void initLayerDefinitions(EntityRenderersEvent.RegisterLayerDefinitions event) { + for (Map.Entry> entry : layerDefinitions.entrySet()) { + event.registerLayerDefinition(entry.getKey(), entry.getValue()); + } + } + + @SubscribeEvent + public void initBlockColors(RegisterColorHandlersEvent.Block event) { + for (ColorRegistration blockColor : blockColors) { + event.register(blockColor.getColor(), blockColor.getObjects().get()); + } + } + + @SubscribeEvent + public void initItemColors(RegisterColorHandlersEvent.Item event) { + for (ColorRegistration itemColor : itemColors) { + event.register(itemColor.getColor(), itemColor.getObjects().get()); + } + } + } + + private final Map registrations = new ConcurrentHashMap<>(); + + @Override + public ModelLayerLocation registerModel(ResourceLocation location, Supplier layerDefinition) { + ModelLayerLocation modelLayerLocation = new ModelLayerLocation(location, "main"); + getActiveRegistrations().layerDefinitions.put(modelLayerLocation, layerDefinition); + return modelLayerLocation; + } + + @Override + @SuppressWarnings("unchecked") + public void registerEntityRenderer(Supplier> type, EntityRendererProvider provider) { + getActiveRegistrations().entityRenderers.add(Pair.of(type::get, (EntityRendererProvider) provider)); + } + + @Override + @SuppressWarnings("unchecked") + public void registerBlockEntityRenderer(Supplier> type, BlockEntityRendererProvider provider) { + getActiveRegistrations().blockEntityRenderers.add(Pair.of(type::get, (BlockEntityRendererProvider) provider)); + } + + @Override + public void registerBlockColorHandler(BlockColor color, Supplier blocks) { + getActiveRegistrations().blockColors.add(new ColorRegistration<>(color, blocks)); + } + + @Override + public void registerItemColorHandler(ItemColor color, Supplier items) { + getActiveRegistrations().itemColors.add(new ColorRegistration<>(color, items)); + } + + @Override + public void setBlockRenderType(Supplier block, RenderType renderType) { + // Do nothing in Forge. Forge unfortunately changes the Vanilla model format, + // so we have to have both this call (for Fabric) and change the JSON (for Forge). + } + + public void register() { + FMLJavaModLoadingContext.get().getModEventBus().register(getActiveRegistrations()); + } + + private Registrations getActiveRegistrations() { + return registrations.computeIfAbsent(ModLoadingContext.get().getActiveNamespace(), it -> new Registrations()); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeBalmTextures.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeBalmTextures.java new file mode 100644 index 00000000..38b9cccf --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeBalmTextures.java @@ -0,0 +1,6 @@ +package net.blay09.mods.balm.neoforge.client.rendering; + +import net.blay09.mods.balm.api.client.rendering.BalmTextures; + +public class NeoForgeBalmTextures implements BalmTextures { +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeCachedDynamicModel.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeCachedDynamicModel.java new file mode 100644 index 00000000..11065ac0 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/rendering/NeoForgeCachedDynamicModel.java @@ -0,0 +1,66 @@ +package net.blay09.mods.balm.neoforge.client.rendering; + +import net.blay09.mods.balm.common.client.rendering.AbstractCachedDynamicModel; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.RandomSource; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.client.ChunkRenderTypeSet; +import net.neoforged.neoforge.client.model.data.ModelData; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; + +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Predicate; + +public class NeoForgeCachedDynamicModel extends AbstractCachedDynamicModel { + + private final List renderTypes; + private ChunkRenderTypeSet cachedChunkRenderTypeSet; + + public NeoForgeCachedDynamicModel(ModelBakery modelBakery, Function baseModelFunction, @Nullable List, BakedModel>> parts, @Nullable Function> textureMapFunction, @Nullable BiConsumer transformFunction, List renderTypes, ResourceLocation location) { + super(modelBakery, baseModelFunction, parts, textureMapFunction, transformFunction, renderTypes, location); + this.renderTypes = renderTypes; + } + + @Override + public List getItemRenderTypes(ItemStack itemStack, boolean fabulous) { + return renderTypes; + } + + @Override + public List getRenderTypes(ItemStack itemStack, boolean fabulous) { + List result = getItemRenderTypes(itemStack, fabulous); + if (result.isEmpty()) { + return super.getRenderTypes(itemStack, fabulous); + } + return result; + } + + @Override + public List getBlockRenderTypes(BlockState state, RandomSource rand) { + return renderTypes; + } + + @Override + public ChunkRenderTypeSet getRenderTypes(@NotNull BlockState state, @NotNull RandomSource rand, @NotNull ModelData data) { + if (cachedChunkRenderTypeSet == null) { + List result = getBlockRenderTypes(state, rand); + if (!result.isEmpty()) { + cachedChunkRenderTypeSet = ChunkRenderTypeSet.of(result); + } else { + cachedChunkRenderTypeSet = super.getRenderTypes(state, rand, data); + } + } + + return cachedChunkRenderTypeSet; + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/screen/NeoForgeBalmScreens.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/screen/NeoForgeBalmScreens.java new file mode 100644 index 00000000..ca1b31e3 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/client/screen/NeoForgeBalmScreens.java @@ -0,0 +1,65 @@ +package net.blay09.mods.balm.neoforge.client.screen; + +import com.mojang.datafixers.util.Pair; +import net.blay09.mods.balm.api.client.screen.BalmScreenFactory; +import net.blay09.mods.balm.api.client.screen.BalmScreens; +import net.blay09.mods.balm.mixin.ScreenAccessor; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.screens.MenuScreens; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.MenuAccess; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +public class NeoForgeBalmScreens implements BalmScreens { + + private static class Registrations { + public final List>, BalmScreenFactory>> menuTypes = new ArrayList<>(); + + @SubscribeEvent + @SuppressWarnings({"rawtypes", "unchecked"}) + public void setupClient(FMLClientSetupEvent event) { + for (Pair>, BalmScreenFactory> entry : menuTypes) { + registerScreenImmediate(entry.getFirst()::get, (BalmScreenFactory) entry.getSecond()); // I hate Java generics. + } + } + } + + private final Map registrations = new ConcurrentHashMap<>(); + + @Override + public > void registerScreen(Supplier> type, BalmScreenFactory screenFactory) { + getActiveRegistrations().menuTypes.add(Pair.of(type::get, screenFactory)); + } + + private static > void registerScreenImmediate(Supplier> type, BalmScreenFactory screenFactory) { + MenuScreens.register(type.get(), screenFactory::create); + } + + @Override + public AbstractWidget addRenderableWidget(Screen screen, AbstractWidget widget) { + ScreenAccessor accessor = ((ScreenAccessor) screen); + accessor.balm_getChildren().add(widget); + accessor.balm_getRenderables().add(widget); + accessor.balm_getNarratables().add(widget); + return widget; + } + + public void register() { + FMLJavaModLoadingContext.get().getModEventBus().register(getActiveRegistrations()); + } + + private Registrations getActiveRegistrations() { + return registrations.computeIfAbsent(ModLoadingContext.get().getActiveNamespace(), it -> new Registrations()); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/command/NeoForgeBalmCommands.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/command/NeoForgeBalmCommands.java new file mode 100644 index 00000000..cd3c9f47 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/command/NeoForgeBalmCommands.java @@ -0,0 +1,27 @@ +package net.blay09.mods.balm.neoforge.command; + +import com.mojang.brigadier.CommandDispatcher; +import net.blay09.mods.balm.api.command.BalmCommands; +import net.minecraft.commands.CommandSourceStack; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.RegisterCommandsEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class NeoForgeBalmCommands implements BalmCommands { + + private final List>> commands = new ArrayList<>(); + + public NeoForgeBalmCommands() { + NeoForge.EVENT_BUS.addListener((RegisterCommandsEvent event) -> { + commands.forEach(it -> it.accept(event.getDispatcher())); + }); + } + + @Override + public void register(Consumer> initializer) { + commands.add(initializer); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/config/NeoForgeBalmConfig.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/config/NeoForgeBalmConfig.java new file mode 100644 index 00000000..1a209206 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/config/NeoForgeBalmConfig.java @@ -0,0 +1,225 @@ +package net.blay09.mods.balm.neoforge.config; + +import net.blay09.mods.balm.api.Balm; +import net.blay09.mods.balm.api.config.AbstractBalmConfig; +import net.blay09.mods.balm.api.config.BalmConfigData; +import net.blay09.mods.balm.api.config.Comment; +import net.blay09.mods.balm.api.config.ExpectedType; +import net.blay09.mods.balm.api.event.ConfigReloadedEvent; +import net.blay09.mods.balm.api.network.ConfigReflection; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.config.IConfigSpec; +import net.neoforged.fml.config.ModConfig; +import net.neoforged.fml.event.config.ModConfigEvent; +import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext; +import net.neoforged.fml.loading.FMLPaths; +import net.neoforged.neoforge.common.ModConfigSpec; +import net.neoforged.neoforge.server.ServerLifecycleHooks; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class NeoForgeBalmConfig extends AbstractBalmConfig { + + private final Logger logger = LogManager.getLogger(); + private final Map, ModConfig> configs = new HashMap<>(); + private final Map, BalmConfigData> configData = new HashMap<>(); + + private IConfigSpec createConfigSpec(Class clazz) { + ModConfigSpec.Builder builder = new ModConfigSpec.Builder(); + try { + buildConfigSpec("", builder, clazz); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("Config spec generation unexpectedly failed.", e); + } + return builder.build(); + } + + private void buildConfigSpec(String parentPath, ModConfigSpec.Builder builder, Class clazz) throws IllegalAccessException { + List fields = ConfigReflection.getAllFields(clazz); + Object defaults = createConfigDataInstance(clazz); + for (Field field : fields) { + Class type = field.getType(); + Object defaultValue = field.get(defaults); + String path = parentPath + field.getName(); + + Comment comment = field.getAnnotation(Comment.class); + if (comment != null) { + builder.comment(comment.value()); + } + + if (String.class.isAssignableFrom(type)) { + builder.define(path, (String) defaultValue); + } else if (List.class.isAssignableFrom(type)) { + ExpectedType expectedType = field.getAnnotation(ExpectedType.class); + if (expectedType == null) { + logger.warn("Config field without expected type, will not validate list content ({} in {})", field.getName(), clazz.getName()); + } + + builder.defineListAllowEmpty(Arrays.asList(path.split("\\.")), + () -> ((List) defaultValue), + it -> expectedType == null || expectedType.value().isAssignableFrom(it.getClass()) || (expectedType.value().isEnum() && Arrays.stream( + expectedType.value().getEnumConstants()).anyMatch(constant -> constant.toString().equals(it)))); + } else if (Enum.class.isAssignableFrom(type)) { + builder.defineEnum(path, (Enum) defaultValue); + } else if (int.class.isAssignableFrom(type)) { + builder.defineInRange(path, (int) defaultValue, Integer.MIN_VALUE, Integer.MAX_VALUE); + } else if (float.class.isAssignableFrom(type)) { + builder.defineInRange(path, (float) defaultValue, -Float.MAX_VALUE, Float.MAX_VALUE); + } else if (double.class.isAssignableFrom(type)) { + builder.defineInRange(path, (double) defaultValue, -Double.MAX_VALUE, Double.MAX_VALUE); + } else if (boolean.class.isAssignableFrom(type)) { + builder.define(path, (boolean) defaultValue); + } else if (long.class.isAssignableFrom(type)) { + builder.defineInRange(path, (long) defaultValue, Long.MIN_VALUE, Long.MAX_VALUE); + } else { + buildConfigSpec(path + ".", builder, type); + } + } + } + + private T readConfigValues(Class clazz, ModConfig config) { + T instance = createConfigDataInstance(clazz); + try { + readConfigValues("", instance, config); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return instance; + } + + private void readConfigValues(String parentPath, T instance, ModConfig config) throws IllegalAccessException { + List fields = ConfigReflection.getAllFields(instance.getClass()); + for (Field field : fields) { + String path = parentPath + field.getName(); + boolean hasValue = config.getConfigData().contains(path); + Class type = field.getType(); + try { + if (hasValue && Integer.TYPE.isAssignableFrom(type)) { + field.set(instance, config.getConfigData().getInt(path)); + } else if (hasValue && Long.TYPE.isAssignableFrom(type)) { + field.set(instance, config.getConfigData().getLong(path)); + } else if (hasValue && Float.TYPE.isAssignableFrom(type)) { + Object value = config.getConfigData().get(path); + if (value instanceof Double doubleValue) { + field.set(instance, doubleValue.floatValue()); + } else if (value instanceof Float floatValue) { + field.set(instance, floatValue); + } else { + logger.error("Invalid config value for " + path + ", expected " + type.getName() + " but got " + value.getClass()); + } + } else if (hasValue && Double.TYPE.isAssignableFrom(type)) { + Object value = config.getConfigData().get(path); + if (value instanceof Double doubleValue) { + field.set(instance, doubleValue); + } else if (value instanceof Float floatValue) { + field.set(instance, floatValue.doubleValue()); + } else { + logger.error("Invalid config value for " + path + ", expected " + type.getName() + " but got " + value.getClass()); + } + } else if (hasValue && (type.isPrimitive() || String.class.isAssignableFrom(type) || List.class.isAssignableFrom(type))) { + Object raw = config.getConfigData().getRaw(path); + if (raw != null) { + try { + field.set(instance, raw); + } catch (IllegalArgumentException e) { + logger.error("Invalid config value for " + path + ", expected " + type.getName() + " but got " + raw.getClass()); + } + } else { + logger.error("Null config value for " + path + ", falling back to default"); + } + } else if (hasValue && type.isEnum()) { + Enum value = config.getConfigData().getEnum(path, (Class) type); + field.set(instance, value); + } else { + readConfigValues(path + ".", field.get(instance), config); + } + } catch (Exception e) { + logger.error("Unexpected error loading config value for " + path + ", falling back to default", e); + } + } + } + + private void writeConfigValues(ModConfig config, T configData) { + try { + writeConfigValues("", config, configData); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + private void writeConfigValues(String parentPath, ModConfig config, T instance) throws IllegalAccessException { + List fields = ConfigReflection.getAllFields(instance.getClass()); + for (Field field : fields) { + String path = parentPath + field.getName(); + Class type = field.getType(); + Object value = field.get(instance); + if (type.isPrimitive() || Enum.class.isAssignableFrom(type) || String.class.isAssignableFrom(type) || List.class.isAssignableFrom(type)) { + config.getConfigData().set(path, value); + } else { + writeConfigValues(path + ".", config, field.get(instance)); + } + } + } + + @Override + public T initializeBackingConfig(Class clazz) { + IConfigSpec configSpec = createConfigSpec(clazz); + ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, configSpec); + + FMLJavaModLoadingContext.get().getModEventBus().addListener((ModConfigEvent.Loading event) -> { + configs.put(clazz, event.getConfig()); + T newConfigData = readConfigValues(clazz, event.getConfig()); + configData.put(clazz, newConfigData); + + setActiveConfig(clazz, newConfigData); + }); + + FMLJavaModLoadingContext.get().getModEventBus().addListener((ModConfigEvent.Reloading event) -> { + configs.put(clazz, event.getConfig()); + T newConfigData = readConfigValues(clazz, event.getConfig()); + configData.put(clazz, newConfigData); + + // Only rewrite active configs with reload if we're the hosting server or there is no syncing + // TODO would be good if this still applied non-synced properties + boolean hasSyncMessage = getConfigSyncMessageFactory(clazz) != null; + boolean isHostingServer = ServerLifecycleHooks.getCurrentServer() != null; + boolean isIngame = Balm.getProxy().isIngame(); + if (!hasSyncMessage || isHostingServer || !isIngame) { + setActiveConfig(clazz, newConfigData); + } + + Balm.getEvents().fireEvent(new ConfigReloadedEvent()); + }); + + T initialData = createConfigDataInstance(clazz); + configData.put(clazz, initialData); + return initialData; + } + + @Override + @SuppressWarnings("unchecked") + public T getBackingConfig(Class clazz) { + return (T) configData.get(clazz); + } + + @Override + public void saveBackingConfig(Class clazz) { + ModConfig modConfig = configs.get(clazz); + if (modConfig != null) { + writeConfigValues(modConfig, configData.get(clazz)); + modConfig.save(); + } + } + + @Override + public File getConfigDir() { + return FMLPaths.CONFIGDIR.get().toFile(); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/container/BalmInvWrapper.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/container/BalmInvWrapper.java new file mode 100644 index 00000000..8ebbb31e --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/container/BalmInvWrapper.java @@ -0,0 +1,23 @@ +package net.blay09.mods.balm.neoforge.container; + +import net.blay09.mods.balm.api.container.ExtractionAwareContainer; +import net.minecraft.world.Container; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.items.wrapper.InvWrapper; + +public class BalmInvWrapper extends InvWrapper { + public BalmInvWrapper(Container inv) { + super(inv); + } + + @Override + public ItemStack extractItem(int slot, int amount, boolean simulate) { + if (getInv() instanceof ExtractionAwareContainer extractionAwareContainer) { + if (!extractionAwareContainer.canExtractItem(slot)) { + return ItemStack.EMPTY; + } + } + + return super.extractItem(slot, amount, simulate); + } +} \ No newline at end of file diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/energy/NeoForgeEnergyStorage.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/energy/NeoForgeEnergyStorage.java new file mode 100644 index 00000000..a201474e --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/energy/NeoForgeEnergyStorage.java @@ -0,0 +1,43 @@ +package net.blay09.mods.balm.neoforge.energy; + +import net.blay09.mods.balm.api.energy.EnergyStorage; +import net.neoforged.neoforge.energy.IEnergyStorage; + +public class NeoForgeEnergyStorage implements IEnergyStorage { + + private final EnergyStorage energyStorage; + + public NeoForgeEnergyStorage(EnergyStorage energyStorage) { + this.energyStorage = energyStorage; + } + + @Override + public int receiveEnergy(int maxReceive, boolean simulate) { + return energyStorage.fill(maxReceive, simulate); + } + + @Override + public int extractEnergy(int maxExtract, boolean simulate) { + return energyStorage.drain(maxExtract, simulate); + } + + @Override + public int getEnergyStored() { + return energyStorage.getEnergy(); + } + + @Override + public int getMaxEnergyStored() { + return energyStorage.getCapacity(); + } + + @Override + public boolean canExtract() { + return energyStorage.canDrain(); + } + + @Override + public boolean canReceive() { + return energyStorage.canFill(); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/entity/NeoForgeBalmEntities.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/entity/NeoForgeBalmEntities.java new file mode 100644 index 00000000..dc7360f9 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/entity/NeoForgeBalmEntities.java @@ -0,0 +1,66 @@ +package net.blay09.mods.balm.neoforge.entity; + +import net.blay09.mods.balm.api.DeferredObject; +import net.blay09.mods.balm.api.entity.BalmEntities; +import net.blay09.mods.balm.neoforge.DeferredRegisters; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext; +import net.neoforged.neoforge.event.entity.EntityAttributeCreationEvent; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.neoforged.neoforge.registries.ForgeRegistries; +import net.neoforged.neoforge.registries.RegistryObject; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +public class NeoForgeBalmEntities implements BalmEntities { + + private static class Registrations { + public final Map, AttributeSupplier> attributeSuppliers = new HashMap<>(); + + @SubscribeEvent + @SuppressWarnings("unchecked") + public void registerAttributes(EntityAttributeCreationEvent event) { + for (Map.Entry, AttributeSupplier> entry : attributeSuppliers.entrySet()) { + event.put((EntityType) entry.getKey(), entry.getValue()); + } + } + } + + private final Map registrations = new ConcurrentHashMap<>(); + + @Override + public DeferredObject> registerEntity(ResourceLocation identifier, EntityType.Builder typeBuilder) { + DeferredRegister> register = DeferredRegisters.get(ForgeRegistries.ENTITY_TYPES, identifier.getNamespace()); + RegistryObject> registryObject = register.register(identifier.getPath(), () -> typeBuilder.build(identifier.toString())); + return new DeferredObject<>(identifier, registryObject, registryObject::isPresent); + } + + @Override + public DeferredObject> registerEntity(ResourceLocation identifier, EntityType.Builder typeBuilder, Supplier attributeBuilder) { + final DeferredRegister> register = DeferredRegisters.get(ForgeRegistries.ENTITY_TYPES, identifier.getNamespace()); + final Registrations registrations = getActiveRegistrations(); + final RegistryObject> registryObject = register.register(identifier.getPath(), () -> { + EntityType entityType = typeBuilder.build(identifier.toString()); + registrations.attributeSuppliers.put(entityType, attributeBuilder.get().build()); + return entityType; + }); + return new DeferredObject<>(identifier, registryObject, registryObject::isPresent); + } + + public void register() { + FMLJavaModLoadingContext.get().getModEventBus().register(getActiveRegistrations()); + } + + private Registrations getActiveRegistrations() { + return registrations.computeIfAbsent(ModLoadingContext.get().getActiveNamespace(), it -> new Registrations()); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/event/NeoForgeBalmClientEvents.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/event/NeoForgeBalmClientEvents.java new file mode 100644 index 00000000..cc3ed0a7 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/event/NeoForgeBalmClientEvents.java @@ -0,0 +1,401 @@ +package net.blay09.mods.balm.neoforge.event; + +import net.blay09.mods.balm.api.event.client.RecipesUpdatedEvent; +import net.blay09.mods.balm.api.event.TickPhase; +import net.blay09.mods.balm.api.event.TickType; +import net.blay09.mods.balm.api.event.client.*; +import net.blay09.mods.balm.api.event.client.RenderHandEvent; +import net.blay09.mods.balm.api.event.client.screen.*; +import net.minecraft.client.Minecraft; +import net.minecraft.core.RegistryAccess; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.event.lifecycle.FMLLoadCompleteEvent; +import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext; +import net.neoforged.neoforge.client.event.*; +import net.neoforged.neoforge.client.gui.overlay.NamedGuiOverlay; +import net.neoforged.neoforge.client.gui.overlay.VanillaGuiOverlay; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.TickEvent; +import org.jetbrains.annotations.Nullable; + +public class NeoForgeBalmClientEvents { + + public static void registerEvents(NeoForgeBalmEvents events) { + events.registerTickEvent(TickType.Client, TickPhase.Start, (ClientTickHandler handler) -> { + NeoForge.EVENT_BUS.addListener((TickEvent.ClientTickEvent orig) -> { + if (orig.phase == TickEvent.Phase.START) { + handler.handle(Minecraft.getInstance()); + } + }); + }); + events.registerTickEvent(TickType.Client, TickPhase.End, (ClientTickHandler handler) -> { + NeoForge.EVENT_BUS.addListener((TickEvent.ClientTickEvent orig) -> { + if (orig.phase == TickEvent.Phase.END) { + handler.handle(Minecraft.getInstance()); + } + }); + }); + events.registerTickEvent(TickType.ClientLevel, TickPhase.Start, (ClientLevelTickHandler handler) -> { + NeoForge.EVENT_BUS.addListener((TickEvent.ClientTickEvent orig) -> { + if (orig.phase == TickEvent.Phase.START) { + handler.handle(Minecraft.getInstance().level); + } + }); + }); + events.registerTickEvent(TickType.ClientLevel, TickPhase.End, (ClientLevelTickHandler handler) -> { + NeoForge.EVENT_BUS.addListener((TickEvent.ClientTickEvent orig) -> { + if (orig.phase == TickEvent.Phase.END) { + handler.handle(Minecraft.getInstance().level); + } + }); + }); + + events.registerEvent(ClientStartedEvent.class, priority -> { + FMLJavaModLoadingContext.get().getModEventBus().addListener(NeoForgeBalmEvents.toForge(priority), (FMLLoadCompleteEvent orig) -> { + orig.enqueueWork(() -> { + final ClientStartedEvent event = new ClientStartedEvent(Minecraft.getInstance()); + events.fireEventHandlers(priority, event); + }); + }); + }); + + events.registerEvent(ConnectedToServerEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ClientPlayerNetworkEvent.LoggingIn orig) -> { + final ConnectedToServerEvent event = new ConnectedToServerEvent(Minecraft.getInstance()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(DisconnectedFromServerEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ClientPlayerNetworkEvent.LoggingOut orig) -> { + final DisconnectedFromServerEvent event = new DisconnectedFromServerEvent(Minecraft.getInstance()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(ScreenInitEvent.Pre.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.Init.Pre orig) -> { + final ScreenInitEvent.Pre event = new ScreenInitEvent.Pre(orig.getScreen()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(ScreenInitEvent.Post.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.Init.Post orig) -> { + final ScreenInitEvent.Post event = new ScreenInitEvent.Post(orig.getScreen()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(ScreenDrawEvent.Pre.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.Render.Pre orig) -> { + final ScreenDrawEvent.Pre event = new ScreenDrawEvent.Pre(orig.getScreen(), + orig.getGuiGraphics(), + orig.getMouseX(), + orig.getMouseY(), + orig.getPartialTick()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(ContainerScreenDrawEvent.Background.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ContainerScreenEvent.Render.Background orig) -> { + final ContainerScreenDrawEvent.Background event = new ContainerScreenDrawEvent.Background(orig.getContainerScreen(), + orig.getGuiGraphics(), + orig.getMouseX(), + orig.getMouseY()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(ContainerScreenDrawEvent.Foreground.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ContainerScreenEvent.Render.Foreground orig) -> { + final ContainerScreenDrawEvent.Foreground event = new ContainerScreenDrawEvent.Foreground(orig.getContainerScreen(), + orig.getGuiGraphics(), + orig.getMouseX(), + orig.getMouseY()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(ScreenDrawEvent.Post.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.Render.Post orig) -> { + final ScreenDrawEvent.Post event = new ScreenDrawEvent.Post(orig.getScreen(), + orig.getGuiGraphics(), + orig.getMouseX(), + orig.getMouseY(), + orig.getPartialTick()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(ScreenMouseEvent.Click.Pre.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.MouseButtonPressed.Pre orig) -> { + final ScreenMouseEvent.Click.Pre event = new ScreenMouseEvent.Click.Pre(orig.getScreen(), orig.getMouseX(), orig.getMouseY(), orig.getButton()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(ScreenMouseEvent.Click.Post.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.MouseButtonPressed.Post orig) -> { + final ScreenMouseEvent.Click.Post event = new ScreenMouseEvent.Click.Post(orig.getScreen(), + orig.getMouseX(), + orig.getMouseY(), + orig.getButton()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + event.setResult(Event.Result.ALLOW); + } + }); + }); + + events.registerEvent(ScreenMouseEvent.Drag.Pre.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.MouseDragged.Pre orig) -> { + final ScreenMouseEvent.Drag.Pre event = new ScreenMouseEvent.Drag.Pre(orig.getScreen(), + orig.getMouseX(), + orig.getMouseY(), + orig.getMouseButton(), + orig.getDragX(), + orig.getDragY()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(ScreenMouseEvent.Drag.Post.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.MouseDragged.Post orig) -> { + final ScreenMouseEvent.Drag.Post event = new ScreenMouseEvent.Drag.Post(orig.getScreen(), + orig.getMouseX(), + orig.getMouseY(), + orig.getMouseButton(), + orig.getDragX(), + orig.getDragY()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(ScreenMouseEvent.Release.Pre.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.MouseButtonReleased.Pre orig) -> { + final ScreenMouseEvent.Release.Pre event = new ScreenMouseEvent.Release.Pre(orig.getScreen(), + orig.getMouseX(), + orig.getMouseY(), + orig.getButton()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(ScreenMouseEvent.Release.Post.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.MouseButtonReleased.Post orig) -> { + final ScreenMouseEvent.Release.Post event = new ScreenMouseEvent.Release.Post(orig.getScreen(), + orig.getMouseX(), + orig.getMouseY(), + orig.getButton()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + event.setResult(Event.Result.ALLOW); + } + }); + }); + + events.registerEvent(ScreenKeyEvent.Press.Pre.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.KeyPressed.Pre orig) -> { + final ScreenKeyEvent.Press.Pre event = new ScreenKeyEvent.Press.Pre(orig.getScreen(), + orig.getKeyCode(), + orig.getScanCode(), + orig.getModifiers()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(ScreenKeyEvent.Press.Post.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.KeyPressed.Post orig) -> { + final ScreenKeyEvent.Press.Post event = new ScreenKeyEvent.Press.Post(orig.getScreen(), + orig.getKeyCode(), + orig.getScanCode(), + orig.getModifiers()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(ScreenKeyEvent.Release.Pre.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.KeyReleased.Pre orig) -> { + final ScreenKeyEvent.Release.Pre event = new ScreenKeyEvent.Release.Pre(orig.getScreen(), + orig.getKeyCode(), + orig.getScanCode(), + orig.getModifiers()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(ScreenKeyEvent.Release.Post.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.KeyReleased.Post orig) -> { + final ScreenKeyEvent.Release.Post event = new ScreenKeyEvent.Release.Post(orig.getScreen(), + orig.getKeyCode(), + orig.getScanCode(), + orig.getModifiers()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(FovUpdateEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ComputeFovModifierEvent orig) -> { + final FovUpdateEvent event = new FovUpdateEvent(orig.getPlayer()); + events.fireEventHandlers(priority, event); + if (event.getFov() != null) { + orig.setNewFovModifier(event.getFov()); + } + }); + }); + + events.registerEvent(RecipesUpdatedEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (net.neoforged.neoforge.client.event.RecipesUpdatedEvent orig) -> { + RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess(); // same way that Minecraft does it in the packet handler + final RecipesUpdatedEvent event = new RecipesUpdatedEvent(orig.getRecipeManager(), registryAccess); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(ItemTooltipEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (net.neoforged.neoforge.event.entity.player.ItemTooltipEvent orig) -> { + final ItemTooltipEvent event = new ItemTooltipEvent(orig.getItemStack(), orig.getEntity(), orig.getToolTip(), orig.getFlags()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(UseItemInputEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (InputEvent.InteractionKeyMappingTriggered orig) -> { + if (orig.isUseItem()) { + final UseItemInputEvent event = new UseItemInputEvent(orig.getHand()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setSwingHand(false); + orig.setCanceled(true); + } + } + }); + }); + + events.registerEvent(RenderHandEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (net.neoforged.neoforge.client.event.RenderHandEvent orig) -> { + final RenderHandEvent event = new RenderHandEvent(orig.getHand(), orig.getItemStack(), orig.getSwingProgress()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(KeyInputEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (InputEvent.Key orig) -> { + final KeyInputEvent event = new KeyInputEvent(orig.getKey(), orig.getScanCode(), orig.getAction(), orig.getModifiers()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(BlockHighlightDrawEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (RenderHighlightEvent.Block orig) -> { + final BlockHighlightDrawEvent event = new BlockHighlightDrawEvent(orig.getTarget(), + orig.getPoseStack(), + orig.getMultiBufferSource(), + orig.getCamera()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(OpenScreenEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ScreenEvent.Opening orig) -> { + final OpenScreenEvent event = new OpenScreenEvent(orig.getScreen()); + events.fireEventHandlers(priority, event); + if (event.getNewScreen() != null) { + orig.setNewScreen(event.getNewScreen()); + } + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(GuiDrawEvent.Pre.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (RenderGuiEvent.Pre orig) -> { + final GuiDrawEvent.Pre event = new GuiDrawEvent.Pre(orig.getWindow(), orig.getGuiGraphics(), GuiDrawEvent.Element.ALL); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (RenderGuiOverlayEvent.Pre orig) -> { + GuiDrawEvent.Element type = getGuiDrawEventElement(orig); + if (type != null) { + final GuiDrawEvent.Pre event = new GuiDrawEvent.Pre(orig.getWindow(), orig.getGuiGraphics(), type); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + } + }); + }); + + events.registerEvent(GuiDrawEvent.Post.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (RenderGuiEvent.Post orig) -> { + final GuiDrawEvent.Post event = new GuiDrawEvent.Post(orig.getWindow(), orig.getGuiGraphics(), GuiDrawEvent.Element.ALL); + events.fireEventHandlers(priority, event); + }); + + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (RenderGuiOverlayEvent.Post orig) -> { + GuiDrawEvent.Element type = getGuiDrawEventElement(orig); + if (type != null) { + final GuiDrawEvent.Post event = new GuiDrawEvent.Post(orig.getWindow(), orig.getGuiGraphics(), type); + events.fireEventHandlers(priority, event); + } + }); + }); + } + + @Nullable + private static GuiDrawEvent.Element getGuiDrawEventElement(RenderGuiOverlayEvent orig) { + GuiDrawEvent.Element type = null; + NamedGuiOverlay overlay = orig.getOverlay(); + if (overlay.id().equals(VanillaGuiOverlay.PLAYER_HEALTH.id())) { + type = GuiDrawEvent.Element.HEALTH; + } else if (overlay.id().equals(VanillaGuiOverlay.CHAT_PANEL.id())) { + type = GuiDrawEvent.Element.CHAT; + } else if (overlay.id().equals(VanillaGuiOverlay.DEBUG_SCREEN.id())) { + type = GuiDrawEvent.Element.DEBUG; + } else if (overlay.id().equals(VanillaGuiOverlay.BOSS_EVENT_PROGRESS.id())) { + type = GuiDrawEvent.Element.BOSS_INFO; + } else if (overlay.id().equals(VanillaGuiOverlay.PLAYER_LIST.id())) { + type = GuiDrawEvent.Element.PLAYER_LIST; + } + return type; + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/event/NeoForgeBalmCommonEvents.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/event/NeoForgeBalmCommonEvents.java new file mode 100644 index 00000000..fd44280f --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/event/NeoForgeBalmCommonEvents.java @@ -0,0 +1,258 @@ +package net.blay09.mods.balm.neoforge.event; + + +import net.blay09.mods.balm.api.event.*; +import net.blay09.mods.balm.api.event.server.ServerStartedEvent; +import net.blay09.mods.balm.api.event.server.ServerStoppedEvent; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.neoforged.bus.api.Event; +import net.neoforged.fml.LogicalSide; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.TickEvent; +import net.neoforged.neoforge.event.entity.item.ItemTossEvent; +import net.neoforged.neoforge.event.entity.player.AttackEntityEvent; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; +import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; +import net.neoforged.neoforge.event.level.BlockEvent; +import net.neoforged.neoforge.event.level.ChunkWatchEvent; +import net.neoforged.neoforge.server.ServerLifecycleHooks; + +public class NeoForgeBalmCommonEvents { + + public static void registerEvents(NeoForgeBalmEvents events) { + events.registerTickEvent(TickType.Server, TickPhase.Start, (ServerTickHandler handler) -> { + NeoForge.EVENT_BUS.addListener((TickEvent.ServerTickEvent orig) -> { + if (orig.phase == TickEvent.Phase.START) { + handler.handle(ServerLifecycleHooks.getCurrentServer()); + } + }); + }); + events.registerTickEvent(TickType.Server, TickPhase.End, (ServerTickHandler handler) -> { + NeoForge.EVENT_BUS.addListener((TickEvent.ServerTickEvent orig) -> { + if (orig.phase == TickEvent.Phase.END) { + handler.handle(ServerLifecycleHooks.getCurrentServer()); + } + }); + }); + events.registerTickEvent(TickType.ServerLevel, TickPhase.Start, (ServerLevelTickHandler handler) -> { + NeoForge.EVENT_BUS.addListener((TickEvent.LevelTickEvent orig) -> { + if (orig.phase == TickEvent.Phase.START && orig.side == LogicalSide.SERVER) { + handler.handle(orig.level); + } + }); + }); + events.registerTickEvent(TickType.ServerLevel, TickPhase.End, (ServerLevelTickHandler handler) -> { + NeoForge.EVENT_BUS.addListener((TickEvent.LevelTickEvent orig) -> { + if (orig.phase == TickEvent.Phase.END && orig.side == LogicalSide.SERVER) { + handler.handle(orig.level); + } + }); + }); + + events.registerTickEvent(TickType.ServerPlayer, TickPhase.Start, (ServerPlayerTickHandler handler) -> { + NeoForge.EVENT_BUS.addListener((TickEvent.PlayerTickEvent orig) -> { + if (orig.phase == TickEvent.Phase.START && orig.side == LogicalSide.SERVER) { + handler.handle(((ServerPlayer) orig.player)); + } + }); + }); + + events.registerTickEvent(TickType.ServerPlayer, TickPhase.End, (ServerPlayerTickHandler handler) -> { + NeoForge.EVENT_BUS.addListener((TickEvent.PlayerTickEvent orig) -> { + if (orig.phase == TickEvent.Phase.END && orig.side == LogicalSide.SERVER) { + handler.handle(((ServerPlayer) orig.player)); + } + }); + }); + + events.registerEvent(ServerStartedEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (net.neoforged.neoforge.event.server.ServerStartedEvent orig) -> { + final ServerStartedEvent event = new ServerStartedEvent(orig.getServer()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(ServerStoppedEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (net.neoforged.neoforge.event.server.ServerStoppedEvent orig) -> { + final ServerStoppedEvent event = new ServerStoppedEvent(orig.getServer()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(UseBlockEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (PlayerInteractEvent.RightClickBlock orig) -> { + final UseBlockEvent event = new UseBlockEvent(orig.getEntity(), orig.getLevel(), orig.getHand(), orig.getHitVec()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCancellationResult(event.getInteractionResult()); + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(UseItemEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (PlayerInteractEvent.RightClickItem orig) -> { + final UseItemEvent event = new UseItemEvent(orig.getEntity(), orig.getLevel(), orig.getHand()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCancellationResult(event.getInteractionResult()); + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(PlayerLoginEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (PlayerEvent.PlayerLoggedInEvent orig) -> { + final PlayerLoginEvent event = new PlayerLoginEvent((ServerPlayer) orig.getEntity()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(PlayerLogoutEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (PlayerEvent.PlayerLoggedOutEvent orig) -> { + final PlayerLogoutEvent event = new PlayerLogoutEvent((ServerPlayer) orig.getEntity()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(BreakBlockEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (BlockEvent.BreakEvent orig) -> { + BlockEntity blockEntity = orig.getLevel().getBlockEntity(orig.getPos()); + final BreakBlockEvent event = new BreakBlockEvent((Level) orig.getLevel(), orig.getPlayer(), orig.getPos(), orig.getState(), blockEntity); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(PlayerRespawnEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (PlayerEvent.PlayerRespawnEvent orig) -> { + final PlayerRespawnEvent event = new PlayerRespawnEvent(((ServerPlayer) orig.getEntity()), (ServerPlayer) orig.getEntity()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(LivingFallEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (net.neoforged.neoforge.event.entity.living.LivingFallEvent orig) -> { + final LivingFallEvent event = new LivingFallEvent(orig.getEntity()); + events.fireEventHandlers(priority, event); + + if (event.getFallDamageOverride() != null) { + orig.setDamageMultiplier(0f); + event.getEntity().hurt(event.getEntity().level().damageSources().fall(), event.getFallDamageOverride()); + } + + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(LivingDamageEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (net.neoforged.neoforge.event.entity.living.LivingDamageEvent orig) -> { + final LivingDamageEvent event = new LivingDamageEvent(orig.getEntity(), orig.getSource(), orig.getAmount()); + events.fireEventHandlers(priority, event); + orig.setAmount(event.getDamageAmount()); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(CropGrowEvent.Pre.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (BlockEvent.CropGrowEvent.Pre orig) -> { + if (orig.getLevel() instanceof Level level) { + final CropGrowEvent.Pre event = new CropGrowEvent.Pre(level, orig.getPos(), orig.getState()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setResult(Event.Result.DENY); + } + } + }); + }); + + events.registerEvent(CropGrowEvent.Post.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (BlockEvent.CropGrowEvent.Post orig) -> { + if (orig.getLevel() instanceof Level level) { + final CropGrowEvent.Post event = new CropGrowEvent.Post(level, orig.getPos(), orig.getState()); + events.fireEventHandlers(priority, event); + } + }); + }); + + events.registerEvent(ChunkTrackingEvent.Start.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ChunkWatchEvent.Watch orig) -> { + final ChunkTrackingEvent.Start event = new ChunkTrackingEvent.Start(orig.getLevel(), orig.getPlayer(), orig.getPos()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(ChunkTrackingEvent.Stop.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ChunkWatchEvent.UnWatch orig) -> { + final ChunkTrackingEvent.Stop event = new ChunkTrackingEvent.Stop(orig.getLevel(), orig.getPlayer(), orig.getPos()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(TossItemEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (ItemTossEvent orig) -> { + final TossItemEvent event = new TossItemEvent(orig.getPlayer(), orig.getEntity().getItem()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(PlayerAttackEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (AttackEntityEvent orig) -> { + final PlayerAttackEvent event = new PlayerAttackEvent(orig.getEntity(), orig.getTarget()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(LivingHealEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (net.neoforged.neoforge.event.entity.living.LivingHealEvent orig) -> { + final LivingHealEvent event = new LivingHealEvent(orig.getEntity(), orig.getAmount()); + events.fireEventHandlers(priority, event); + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(DigSpeedEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (PlayerEvent.BreakSpeed orig) -> { + final DigSpeedEvent event = new DigSpeedEvent(orig.getEntity(), orig.getState(), orig.getOriginalSpeed()); + events.fireEventHandlers(priority, event); + if (event.getSpeedOverride() != null) { + orig.setNewSpeed(event.getSpeedOverride()); + } + if (event.isCanceled()) { + orig.setCanceled(true); + } + }); + }); + + events.registerEvent(PlayerChangedDimensionEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (PlayerEvent.PlayerChangedDimensionEvent orig) -> { + final PlayerChangedDimensionEvent event = new PlayerChangedDimensionEvent((ServerPlayer) orig.getEntity(), orig.getFrom(), orig.getTo()); + events.fireEventHandlers(priority, event); + }); + }); + + events.registerEvent(ItemCraftedEvent.class, priority -> { + NeoForge.EVENT_BUS.addListener(NeoForgeBalmEvents.toForge(priority), (PlayerEvent.ItemCraftedEvent orig) -> { + final ItemCraftedEvent event = new ItemCraftedEvent(orig.getEntity(), orig.getCrafting(), orig.getInventory()); + events.fireEventHandlers(priority, event); + }); + }); + } + +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/event/NeoForgeBalmEvents.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/event/NeoForgeBalmEvents.java new file mode 100644 index 00000000..848c679c --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/event/NeoForgeBalmEvents.java @@ -0,0 +1,105 @@ +package net.blay09.mods.balm.neoforge.event; + + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import net.blay09.mods.balm.api.event.BalmEvents; +import net.blay09.mods.balm.api.event.EventPriority; +import net.blay09.mods.balm.api.event.TickPhase; +import net.blay09.mods.balm.api.event.TickType; +import net.neoforged.bus.api.Event; +import net.neoforged.neoforge.common.NeoForge; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +public class NeoForgeBalmEvents implements BalmEvents { + + private final Table, EventPriority, Consumer> eventInitializers = HashBasedTable.create(); + private final Map, Consumer> eventDispatchers = new HashMap<>(); + private final Table, EventPriority, List>> eventHandlers = HashBasedTable.create(); + private final Table, TickPhase, Consumer> tickEventInitializers = HashBasedTable.create(); + + public void registerEvent(Class eventClass, Consumer initializer) { + registerEvent(eventClass, initializer, null); + } + + public void registerEvent(Class eventClass, Consumer initializer, @Nullable Consumer dispatcher) { + for (EventPriority priority : EventPriority.values()) { + eventInitializers.put(eventClass, priority, initializer); + } + + if (dispatcher != null) { + eventDispatchers.put(eventClass, dispatcher); + } + } + + public void fireEventHandlers(EventPriority priority, T event) { + List> handlers = eventHandlers.get(event.getClass(), priority); + if (handlers != null) { + handlers.forEach(handler -> fireEventHandler(handler, event)); + } + } + + @SuppressWarnings("unchecked") + private void fireEventHandler(Consumer handler, Object event) { + handler.accept((T) event); + } + + @Override + public void onEvent(Class eventClass, Consumer handler, EventPriority priority) { + Consumer initializer = eventInitializers.remove(eventClass, priority); + if (initializer != null) { + initializer.accept(priority); + } + + List> consumers = eventHandlers.get(eventClass, priority); + if (consumers == null) { + consumers = new ArrayList<>(); + eventHandlers.put(eventClass, priority, consumers); + } + consumers.add(handler); + } + + @Override + @SuppressWarnings("unchecked") + public void fireEvent(T event) { + Consumer handler = (Consumer) eventDispatchers.get(event.getClass()); + if (handler != null) { + handler.accept(event); + } else { + for (EventPriority priority : EventPriority.values) { + fireEventHandlers(priority, event); + } + } + + if (event instanceof Event forgeEvent) { + NeoForge.EVENT_BUS.post(forgeEvent); + } + } + + @Override + @SuppressWarnings("unchecked") + public void onTickEvent(TickType type, TickPhase phase, T handler) { + Consumer initializer = (Consumer) tickEventInitializers.get(type, phase); + initializer.accept(handler); + } + + public void registerTickEvent(TickType type, TickPhase phase, Consumer initializer) { + tickEventInitializers.put(type, phase, initializer); + } + + public static net.neoforged.bus.api.EventPriority toForge(EventPriority priority) { + return switch (priority) { + case Lowest -> net.neoforged.bus.api.EventPriority.LOWEST; + case Low -> net.neoforged.bus.api.EventPriority.LOW; + case Normal -> net.neoforged.bus.api.EventPriority.NORMAL; + case High -> net.neoforged.bus.api.EventPriority.HIGH; + case Highest -> net.neoforged.bus.api.EventPriority.HIGHEST; + }; + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/fluid/NeoForgeFluidTank.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/fluid/NeoForgeFluidTank.java new file mode 100644 index 00000000..76155b27 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/fluid/NeoForgeFluidTank.java @@ -0,0 +1,55 @@ +package net.blay09.mods.balm.neoforge.fluid; + +import net.blay09.mods.balm.api.fluid.FluidTank; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; +import org.jetbrains.annotations.NotNull; + +public class NeoForgeFluidTank implements IFluidHandler { + + private final FluidTank fluidTank; + + public NeoForgeFluidTank(FluidTank fluidTank) { + this.fluidTank = fluidTank; + } + + @Override + public int getTanks() { + return 1; + } + + @NotNull + @Override + public FluidStack getFluidInTank(int tank) { + return new FluidStack(fluidTank.getFluid(), fluidTank.getAmount()); + } + + @Override + public int getTankCapacity(int tank) { + return fluidTank.getCapacity(); + } + + @Override + public boolean isFluidValid(int tank, @NotNull FluidStack stack) { + return fluidTank.canFill(stack.getFluid()); + } + + @Override + public int fill(FluidStack resource, FluidAction action) { + return fluidTank.fill(resource.getFluid(), resource.getAmount(), action.simulate()); + } + + @NotNull + @Override + public FluidStack drain(FluidStack resource, FluidAction action) { + int drained = fluidTank.drain(resource.getFluid(), resource.getAmount(), action.simulate()); + return new FluidStack(fluidTank.getFluid(), drained); + } + + @NotNull + @Override + public FluidStack drain(int maxDrain, FluidAction action) { + int drained = fluidTank.drain(fluidTank.getFluid(), maxDrain, action.simulate()); + return new FluidStack(fluidTank.getFluid(), drained); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/item/NeoForgeBalmItems.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/item/NeoForgeBalmItems.java new file mode 100644 index 00000000..3072aa37 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/item/NeoForgeBalmItems.java @@ -0,0 +1,90 @@ +package net.blay09.mods.balm.neoforge.item; + +import com.google.common.collect.*; +import net.blay09.mods.balm.api.DeferredObject; +import net.blay09.mods.balm.api.item.BalmItems; +import net.blay09.mods.balm.neoforge.DeferredRegisters; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.neoforged.neoforge.registries.ForgeRegistries; +import net.neoforged.neoforge.registries.RegistryObject; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +public class NeoForgeBalmItems implements BalmItems { + + private static class Registrations { + public final Multimap> creativeTabContents = ArrayListMultimap.create(); + + public void buildCreativeTabContents(ResourceLocation tabIdentifier, CreativeModeTab.Output entries) { + Collection> itemStacks = creativeTabContents.get(tabIdentifier); + if (!itemStacks.isEmpty()) { + itemStacks.forEach(it -> { + for (ItemLike itemStack : it.get()) { + entries.accept(itemStack); + } + }); + } + } + } + + private final Map registrations = new ConcurrentHashMap<>(); + + @Override + public Item.Properties itemProperties() { + return new Item.Properties(); + } + + @Override + public DeferredObject registerItem(Supplier supplier, ResourceLocation identifier, @Nullable ResourceLocation creativeTab) { + DeferredRegister register = DeferredRegisters.get(ForgeRegistries.ITEMS, identifier.getNamespace()); + RegistryObject registryObject = register.register(identifier.getPath(), supplier); + if (creativeTab != null) { + getActiveRegistrations().creativeTabContents.put(creativeTab, () -> new ItemLike[]{registryObject.get()}); + } + return new DeferredObject<>(identifier, registryObject, registryObject::isPresent); + } + + @Override + public DeferredObject registerCreativeModeTab(Supplier iconSupplier, ResourceLocation identifier) { + DeferredRegister register = DeferredRegisters.get(Registries.CREATIVE_MODE_TAB, identifier.getNamespace()); + RegistryObject registryObject = register.register(identifier.getPath(), () -> { + Component displayName = Component.translatable("itemGroup." + identifier.toString().replace(':', '.')); + final var registrations = getActiveRegistrations(); + CreativeModeTab creativeModeTab = CreativeModeTab.builder() + .title(displayName) + .icon(iconSupplier) + .displayItems((enabledFeatures, entries) -> registrations.buildCreativeTabContents(identifier, entries)) + .build(); + creativeModeTab = Registry.register(BuiltInRegistries.CREATIVE_MODE_TAB, identifier, creativeModeTab); + return creativeModeTab; + }); + return new DeferredObject<>(identifier, registryObject, registryObject::isPresent); + } + + @Override + public void addToCreativeModeTab(ResourceLocation tabIdentifier, Supplier itemsSupplier) { + getActiveRegistrations().creativeTabContents.put(tabIdentifier, itemsSupplier); + } + + public void register() { + FMLJavaModLoadingContext.get().getModEventBus().register(getActiveRegistrations()); + } + + private Registrations getActiveRegistrations() { + return registrations.computeIfAbsent(ModLoadingContext.get().getActiveNamespace(), it -> new Registrations()); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/menu/NeoForgeBalmMenus.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/menu/NeoForgeBalmMenus.java new file mode 100644 index 00000000..7aecd3c9 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/menu/NeoForgeBalmMenus.java @@ -0,0 +1,27 @@ +package net.blay09.mods.balm.neoforge.menu; + +import net.blay09.mods.balm.api.DeferredObject; +import net.blay09.mods.balm.api.menu.BalmMenuFactory; +import net.blay09.mods.balm.api.menu.BalmMenus; +import net.blay09.mods.balm.neoforge.DeferredRegisters; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.flag.FeatureFlags; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.neoforged.neoforge.network.IContainerFactory; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.neoforged.neoforge.registries.ForgeRegistries; +import net.neoforged.neoforge.registries.RegistryObject; + +public class NeoForgeBalmMenus implements BalmMenus { + + @Override + public DeferredObject> registerMenu(ResourceLocation identifier, BalmMenuFactory factory) { + DeferredRegister> register = DeferredRegisters.get(ForgeRegistries.MENU_TYPES, identifier.getNamespace()); + RegistryObject> registryObject = register.register(identifier.getPath(), + () -> new MenuType<>((IContainerFactory) factory::create, FeatureFlagSet.of(FeatureFlags.VANILLA))); + return new DeferredObject<>(identifier, registryObject, registryObject::isPresent); + } + +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/network/NeoForgeBalmNetworking.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/network/NeoForgeBalmNetworking.java new file mode 100644 index 00000000..5ab5b847 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/network/NeoForgeBalmNetworking.java @@ -0,0 +1,189 @@ +package net.blay09.mods.balm.neoforge.network; + +import net.blay09.mods.balm.api.Balm; +import net.blay09.mods.balm.api.client.BalmClient; +import net.blay09.mods.balm.api.menu.BalmMenuProvider; +import net.blay09.mods.balm.api.network.BalmNetworking; +import net.blay09.mods.balm.api.network.ClientboundMessageRegistration; +import net.blay09.mods.balm.api.network.MessageRegistration; +import net.blay09.mods.balm.api.network.ServerboundMessageRegistration; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.neoforged.neoforge.network.NetworkEvent; +import net.neoforged.neoforge.network.NetworkHooks; +import net.neoforged.neoforge.network.PacketDistributor; +import net.neoforged.neoforge.network.PlayNetworkDirection; +import net.neoforged.neoforge.network.simple.SimpleChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; +import java.util.function.Function; + +public class NeoForgeBalmNetworking implements BalmNetworking { + + private static final Logger logger = LoggerFactory.getLogger(NeoForgeBalmNetworking.class); + + private static final Map, MessageRegistration> messagesByClass = new ConcurrentHashMap<>(); + private static final Map> messagesByIdentifier = new ConcurrentHashMap<>(); + private static final Map discriminatorCounter = new ConcurrentHashMap<>(); + + private static NetworkEvent.Context replyContext; + + @Override + public void allowClientOnly(String modId) { + NetworkChannels.allowClientOnly(modId); + } + + @Override + public void allowServerOnly(String modId) { + NetworkChannels.allowServerOnly(modId); + } + + @Override + public void openGui(Player player, MenuProvider menuProvider) { + if (player instanceof ServerPlayer) { + if (menuProvider instanceof BalmMenuProvider balmMenuProvider) { + NetworkHooks.openScreen((ServerPlayer) player, menuProvider, buf -> balmMenuProvider.writeScreenOpeningData((ServerPlayer) player, buf)); + } else { + NetworkHooks.openScreen((ServerPlayer) player, menuProvider); + } + } + } + + @Override + public void reply(T message) { + if (replyContext == null) { + throw new IllegalStateException("No context to reply to"); + } + + MessageRegistration messageRegistration = getMessageRegistrationOrThrow(message); + ResourceLocation identifier = messageRegistration.getIdentifier(); + + SimpleChannel channel = NetworkChannels.get(identifier.getNamespace()); + channel.reply(message, replyContext); + } + + @Override + public void sendTo(Player player, T message) { + MessageRegistration messageRegistration = getMessageRegistrationOrThrow(message); + + ResourceLocation identifier = messageRegistration.getIdentifier(); + + SimpleChannel channel = NetworkChannels.get(identifier.getNamespace()); + channel.send(PacketDistributor.PLAYER.with(() -> ((ServerPlayer) player)), message); + } + + @Override + public void sendToTracking(ServerLevel world, BlockPos pos, T message) { + MessageRegistration messageRegistration = getMessageRegistrationOrThrow(message); + + ResourceLocation identifier = messageRegistration.getIdentifier(); + + SimpleChannel channel = NetworkChannels.get(identifier.getNamespace()); + channel.send(PacketDistributor.TRACKING_CHUNK.with(() -> world.getChunkAt(pos)), message); + } + + @Override + public void sendToTracking(Entity entity, T message) { + MessageRegistration messageRegistration = getMessageRegistrationOrThrow(message); + + ResourceLocation identifier = messageRegistration.getIdentifier(); + + SimpleChannel channel = NetworkChannels.get(identifier.getNamespace()); + channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> entity), message); + } + + @Override + public void sendToAll(MinecraftServer server, T message) { + MessageRegistration messageRegistration = getMessageRegistrationOrThrow(message); + + ResourceLocation identifier = messageRegistration.getIdentifier(); + + SimpleChannel channel = NetworkChannels.get(identifier.getNamespace()); + channel.send(PacketDistributor.ALL.noArg(), message); + } + + @Override + public void sendToServer(T message) { + if (!Balm.getProxy().isConnectedToServer()) { + logger.debug("Skipping message {} because we're not connected to a server", message); + return; + } + + MessageRegistration messageRegistration = getMessageRegistrationOrThrow(message); + + ResourceLocation identifier = messageRegistration.getIdentifier(); + + SimpleChannel channel = NetworkChannels.get(identifier.getNamespace()); + channel.sendToServer(message); + } + + @SuppressWarnings("unchecked") + private MessageRegistration getMessageRegistrationOrThrow(T message) { + MessageRegistration messageRegistration = (MessageRegistration) messagesByClass.get(message.getClass()); + if (messageRegistration == null) { + throw new IllegalArgumentException("Cannot send message " + message.getClass() + " as it is not registered"); + } + return messageRegistration; + } + + @Override + public void registerClientboundPacket(ResourceLocation identifier, Class clazz, BiConsumer encodeFunc, Function decodeFunc, BiConsumer handler) { + ClientboundMessageRegistration messageRegistration = new ClientboundMessageRegistration<>(identifier, clazz, encodeFunc, decodeFunc, handler); + + messagesByClass.put(clazz, messageRegistration); + messagesByIdentifier.put(identifier, messageRegistration); + + SimpleChannel channel = NetworkChannels.get(identifier.getNamespace()); + channel.registerMessage(nextDiscriminator(identifier.getNamespace()), clazz, encodeFunc::accept, decodeFunc::apply, (message, context) -> { + if (context.getDirection() != PlayNetworkDirection.PLAY_TO_CLIENT) { + logger.warn("Received {} on incorrect side {}", identifier, context.getDirection()); + return; + } + + context.enqueueWork(() -> { + handler.accept(BalmClient.getClientPlayer(), message); + }); + context.setPacketHandled(true); + }); + } + + @Override + public void registerServerboundPacket(ResourceLocation identifier, Class clazz, BiConsumer encodeFunc, Function decodeFunc, BiConsumer handler) { + MessageRegistration messageRegistration = new ServerboundMessageRegistration<>(identifier, clazz, encodeFunc, decodeFunc, handler); + + messagesByClass.put(clazz, messageRegistration); + messagesByIdentifier.put(identifier, messageRegistration); + + SimpleChannel channel = NetworkChannels.get(identifier.getNamespace()); + + channel.registerMessage(nextDiscriminator(identifier.getNamespace()), clazz, encodeFunc::accept, decodeFunc::apply, (message, context) -> { + if (context.getDirection() != PlayNetworkDirection.PLAY_TO_SERVER) { + logger.warn("Received {} on incorrect side {}", identifier, context.getDirection()); + return; + } + + context.enqueueWork(() -> { + replyContext = context; + ServerPlayer player = context.getSender(); + handler.accept(player, message); + replyContext = null; + }); + context.setPacketHandled(true); + }); + } + + private static int nextDiscriminator(String modId) { + return discriminatorCounter.compute(modId, (key, prev) -> prev != null ? prev + 1 : 0); + } +} \ No newline at end of file diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/network/NetworkChannels.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/network/NetworkChannels.java new file mode 100644 index 00000000..ec372595 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/network/NetworkChannels.java @@ -0,0 +1,38 @@ +package net.blay09.mods.balm.neoforge.network; + +import net.minecraft.resources.ResourceLocation; +import net.neoforged.neoforge.network.NetworkRegistry; +import net.neoforged.neoforge.network.simple.SimpleChannel; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; + +public class NetworkChannels { + private static final String version = "1.0"; // all mods will just have same version for now until I clean this up + private static final Map channels = new ConcurrentHashMap<>(); + private static final Map> acceptedClientVersions = new ConcurrentHashMap<>(); + private static final Map> acceptedServerVersions = new ConcurrentHashMap<>(); + + public static SimpleChannel get(String modId) { + return channels.computeIfAbsent(modId, key -> { + ResourceLocation channelName = new ResourceLocation(key, "network"); + return NetworkRegistry.newSimpleChannel(channelName, + () -> version, + it -> acceptedClientVersions.getOrDefault(modId, NetworkChannels::defaultVersionCheck).test(it), + it -> acceptedServerVersions.getOrDefault(modId, NetworkChannels::defaultVersionCheck).test(it)); + }); + } + + public static void allowClientOnly(String modId) { + acceptedClientVersions.put(modId, it -> true); + } + + public static void allowServerOnly(String modId) { + acceptedServerVersions.put(modId, it -> true); + } + + private static boolean defaultVersionCheck(String it) { + return it.equals(version); + } +} \ No newline at end of file diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/provider/NeoForgeBalmProviders.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/provider/NeoForgeBalmProviders.java new file mode 100644 index 00000000..ba422f18 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/provider/NeoForgeBalmProviders.java @@ -0,0 +1,44 @@ +package net.blay09.mods.balm.neoforge.provider; + +import net.blay09.mods.balm.api.provider.BalmProviders; +import net.minecraft.core.Direction; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.neoforged.neoforge.common.capabilities.Capability; +import net.neoforged.neoforge.common.capabilities.CapabilityManager; +import net.neoforged.neoforge.common.capabilities.CapabilityToken; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +public class NeoForgeBalmProviders implements BalmProviders { + + private final Map, Capability> capabilities = new HashMap<>(); + + @Override + public T getProvider(BlockEntity blockEntity, Class clazz) { + return getProvider(blockEntity, null, clazz); + } + + @Override + public T getProvider(BlockEntity blockEntity, @Nullable Direction direction, Class clazz) { + Capability capability = getCapability(clazz); + return blockEntity.getCapability(capability, direction).resolve().orElse(null); + } + + @Override + public T getProvider(Entity entity, Class clazz) { + Capability capability = getCapability(clazz); + return entity.getCapability(capability).resolve().orElse(null); + } + + public void register(Class clazz, CapabilityToken token) { + capabilities.put(clazz, CapabilityManager.get(token)); + } + + @SuppressWarnings("unchecked") + public Capability getCapability(Class clazz) { + return (Capability) capabilities.get(clazz); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/sound/NeoForgeBalmSounds.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/sound/NeoForgeBalmSounds.java new file mode 100644 index 00000000..ac33c101 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/sound/NeoForgeBalmSounds.java @@ -0,0 +1,19 @@ +package net.blay09.mods.balm.neoforge.sound; + +import net.blay09.mods.balm.api.DeferredObject; +import net.blay09.mods.balm.api.sound.BalmSounds; +import net.blay09.mods.balm.neoforge.DeferredRegisters; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.neoforged.neoforge.registries.ForgeRegistries; +import net.neoforged.neoforge.registries.RegistryObject; + +public class NeoForgeBalmSounds implements BalmSounds { + @Override + public DeferredObject register(ResourceLocation identifier) { + DeferredRegister register = DeferredRegisters.get(ForgeRegistries.SOUND_EVENTS, identifier.getNamespace()); + RegistryObject registryObject = register.register(identifier.getPath(), () -> SoundEvent.createVariableRangeEvent(identifier)); + return new DeferredObject<>(identifier, registryObject, registryObject::isPresent); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/stats/NeoForgeBalmStats.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/stats/NeoForgeBalmStats.java new file mode 100644 index 00000000..c47199e5 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/stats/NeoForgeBalmStats.java @@ -0,0 +1,47 @@ +package net.blay09.mods.balm.neoforge.stats; + +import net.blay09.mods.balm.api.stats.BalmStats; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.stats.StatFormatter; +import net.minecraft.stats.Stats; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; +import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class NeoForgeBalmStats implements BalmStats { + + private static class Registrations { + public final List customStats = new ArrayList<>(); + + @SubscribeEvent + public void commonSetup(FMLCommonSetupEvent event) { + event.enqueueWork(() -> customStats.forEach(it -> { + Registry.register(BuiltInRegistries.CUSTOM_STAT, it.getPath(), it); + Stats.CUSTOM.get(it, StatFormatter.DEFAULT); + })); + } + } + + private final Map registrations = new ConcurrentHashMap<>(); + + @Override + public void registerCustomStat(ResourceLocation identifier) { + getActiveRegistrations().customStats.add(identifier); + } + + public void register() { + FMLJavaModLoadingContext.get().getModEventBus().register(getActiveRegistrations()); + } + + private Registrations getActiveRegistrations() { + return registrations.computeIfAbsent(ModLoadingContext.get().getActiveNamespace(), it -> new Registrations()); + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/world/BalmBiomeModifier.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/world/BalmBiomeModifier.java new file mode 100644 index 00000000..8a322312 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/world/BalmBiomeModifier.java @@ -0,0 +1,25 @@ +package net.blay09.mods.balm.neoforge.world; + +import com.mojang.serialization.Codec; +import net.blay09.mods.balm.api.Balm; +import net.minecraft.core.Holder; +import net.minecraft.world.level.biome.Biome; +import net.neoforged.neoforge.common.world.BiomeModifier; +import net.neoforged.neoforge.common.world.ModifiableBiomeInfo; + +public class BalmBiomeModifier implements BiomeModifier { + + public static final BalmBiomeModifier INSTANCE = new BalmBiomeModifier(); + + @Override + public void modify(Holder biome, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) { + NeoForgeBalmWorldGen worldGen = (NeoForgeBalmWorldGen) Balm.getWorldGen(); + worldGen.modifyBiome(biome, phase, builder); + } + + @Override + public Codec codec() { + return NeoForgeBalmWorldGen.BALM_BIOME_MODIFIER_CODEC; + } + +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/world/BiomeModification.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/world/BiomeModification.java new file mode 100644 index 00000000..d43737ad --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/world/BiomeModification.java @@ -0,0 +1,30 @@ +package net.blay09.mods.balm.neoforge.world; + +import net.blay09.mods.balm.api.world.BiomePredicate; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; + +class BiomeModification { + private final BiomePredicate biomePredicate; + private final GenerationStep.Decoration step; + private final ResourceKey placedFeatureKey; + + BiomeModification(BiomePredicate biomePredicate, GenerationStep.Decoration step, ResourceKey placedFeatureKey) { + this.biomePredicate = biomePredicate; + this.step = step; + this.placedFeatureKey = placedFeatureKey; + } + + public BiomePredicate getBiomePredicate() { + return biomePredicate; + } + + public GenerationStep.Decoration getStep() { + return step; + } + + public ResourceKey getConfiguredFeatureKey() { + return placedFeatureKey; + } +} diff --git a/neoforge/src/main/java/net/blay09/mods/balm/neoforge/world/NeoForgeBalmWorldGen.java b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/world/NeoForgeBalmWorldGen.java new file mode 100644 index 00000000..faf78424 --- /dev/null +++ b/neoforge/src/main/java/net/blay09/mods/balm/neoforge/world/NeoForgeBalmWorldGen.java @@ -0,0 +1,115 @@ +package net.blay09.mods.balm.neoforge.world; + +import com.mojang.serialization.Codec; +import net.blay09.mods.balm.api.DeferredObject; +import net.blay09.mods.balm.api.world.BalmWorldGen; +import net.blay09.mods.balm.api.world.BiomePredicate; +import net.blay09.mods.balm.neoforge.DeferredRegisters; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import net.minecraft.world.level.levelgen.placement.PlacementModifierType; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; +import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.common.world.BiomeModifier; +import net.neoforged.neoforge.common.world.ModifiableBiomeInfo; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.neoforged.neoforge.registries.ForgeRegistries; +import net.neoforged.neoforge.registries.RegistryObject; +import net.neoforged.neoforge.server.ServerLifecycleHooks; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +public class NeoForgeBalmWorldGen implements BalmWorldGen { + + private static class Registrations { + public final List> configuredFeatures = new ArrayList<>(); + public final List> placedFeatures = new ArrayList<>(); + public final List> placementModifiers = new ArrayList<>(); + + @SubscribeEvent + public void commonSetup(FMLCommonSetupEvent event) { + event.enqueueWork(() -> { + placementModifiers.forEach(DeferredObject::resolve); + configuredFeatures.forEach(DeferredObject::resolve); + placedFeatures.forEach(DeferredObject::resolve); + }); + } + } + + public static final Codec BALM_BIOME_MODIFIER_CODEC = Codec.unit(BalmBiomeModifier.INSTANCE); + private final Map registrations = new ConcurrentHashMap<>(); + + public NeoForgeBalmWorldGen() { + NeoForge.EVENT_BUS.register(this); + } + + @Override + public > DeferredObject registerFeature(ResourceLocation identifier, Supplier supplier) { + DeferredRegister> register = DeferredRegisters.get(ForgeRegistries.FEATURES, identifier.getNamespace()); + RegistryObject registryObject = register.register(identifier.getPath(), supplier); + return new DeferredObject<>(identifier, registryObject, registryObject::isPresent); + } + + @Override + public > DeferredObject registerPlacementModifier(ResourceLocation identifier, Supplier supplier) { + DeferredObject deferredObject = new DeferredObject<>(identifier, () -> { + T placementModifierType = supplier.get(); + Registry.register(BuiltInRegistries.PLACEMENT_MODIFIER_TYPE, identifier, placementModifierType); + return placementModifierType; + }); + getActiveRegistrations().placementModifiers.add(deferredObject); + return deferredObject; + } + + private static final List biomeModifications = new ArrayList<>(); + + @Override + public void addFeatureToBiomes(BiomePredicate biomePredicate, GenerationStep.Decoration step, ResourceLocation placedFeatureIdentifier) { + ResourceKey resourceKey = ResourceKey.create(Registries.PLACED_FEATURE, placedFeatureIdentifier); + biomeModifications.add(new BiomeModification(biomePredicate, step, resourceKey)); + } + + public void modifyBiome(Holder biome, BiomeModifier.Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) { + if (phase == BiomeModifier.Phase.ADD) { + for (var biomeModification : biomeModifications) { + ResourceLocation location = biome.unwrapKey().map(ResourceKey::location).orElse(null); + if (location != null && biomeModification.getBiomePredicate().test(location, biome)) { + Registry placedFeatures = ServerLifecycleHooks.getCurrentServer() + .registryAccess() + .registryOrThrow(Registries.PLACED_FEATURE); + placedFeatures.getHolder(biomeModification.getConfiguredFeatureKey()) + .ifPresent(placedFeature -> builder.getGenerationSettings().addFeature(biomeModification.getStep(), placedFeature)); + } + } + } + } + + public void register() { + FMLJavaModLoadingContext.get().getModEventBus().register(getActiveRegistrations()); + } + + private Registrations getActiveRegistrations() { + return registrations.computeIfAbsent(ModLoadingContext.get().getActiveNamespace(), it -> new Registrations()); + } + + public static void initializeBalmBiomeModifiers() { + var registry = DeferredRegister.create(ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, "balm"); + registry.register("balm", () -> BALM_BIOME_MODIFIER_CODEC); + registry.register(FMLJavaModLoadingContext.get().getModEventBus()); + } +} diff --git a/shared/src/main/java/net/blay09/mods/balm/api/BalmProxy.java b/shared/src/main/java/net/blay09/mods/balm/api/BalmProxy.java index 6e6f31ac..fc2e9dd2 100644 --- a/shared/src/main/java/net/blay09/mods/balm/api/BalmProxy.java +++ b/shared/src/main/java/net/blay09/mods/balm/api/BalmProxy.java @@ -12,4 +12,8 @@ public Player getClientPlayer() { public boolean isConnectedToServer() { return false; } + + public boolean isIngame() { + return false; + } } diff --git a/shared/src/main/java/net/blay09/mods/balm/api/client/BalmClientProxy.java b/shared/src/main/java/net/blay09/mods/balm/api/client/BalmClientProxy.java index b1f4c7a9..2e26ea3c 100644 --- a/shared/src/main/java/net/blay09/mods/balm/api/client/BalmClientProxy.java +++ b/shared/src/main/java/net/blay09/mods/balm/api/client/BalmClientProxy.java @@ -15,4 +15,9 @@ public class BalmClientProxy extends BalmProxy { public boolean isConnectedToServer() { return Minecraft.getInstance().getConnection() != null; } + + @Override + public boolean isIngame() { + return Minecraft.getInstance().gameMode != null; + } } diff --git a/shared/src/main/resources/balm.mixins.json b/shared/src/main/resources/balm.mixins.json new file mode 100644 index 00000000..3493c530 --- /dev/null +++ b/shared/src/main/resources/balm.mixins.json @@ -0,0 +1,16 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "net.blay09.mods.mixin", + "refmap": "${mod_id}.refmap.json", + "compatibilityLevel": "JAVA_17", + "mixins": [ + ], + "client": [ + ], + "server": [ + ], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file