diff --git a/build.gradle b/build.gradle
index 2656dd32f..9a8144508 100644
--- a/build.gradle
+++ b/build.gradle
@@ -75,6 +75,12 @@ forgix {
forge {
jarLocation = "build/libs/DistantHorizons-forge-${rootProject.versionStr}.jar"
}
+
+ if (findProject(":neoforged"))
+ custom {
+ projectName = "neoforged"
+ jarLocation = "build/libs/DistantHorizons-neoforged-${rootProject.versionStr}.jar"
+ }
if (findProject(":fabric"))
fabric {
@@ -105,7 +111,10 @@ subprojects { p ->
// apply plugin: "org.spongepowered.gradle.vanilla" // Provides minecraft libraries
// Apply forge's loom
- if (findProject(":forge") && p == project(":forge"))
+ if (
+ (findProject(":forge") && p == project(":forge")) ||
+ (findProject(":neoforged") && p == project(":neoforged"))
+ )
apply plugin: "dev.architectury.loom"
diff --git a/coreSubProjects b/coreSubProjects
index fa12443cb..1b8ee5cd4 160000
--- a/coreSubProjects
+++ b/coreSubProjects
@@ -1 +1 @@
-Subproject commit fa12443cb11a096ab0cb11ba25cbd297ed5deaa1
+Subproject commit 1b8ee5cd48f8abc18bbdfe23fe160d17dc5d6cef
diff --git a/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinDynamicTexture.java b/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinDynamicTexture.java
index 4efbcd350..7117e7063 100644
--- a/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinDynamicTexture.java
+++ b/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinDynamicTexture.java
@@ -48,9 +48,6 @@ public abstract class MixinDynamicTexture implements ILightTextureMarker
private boolean isLightTexture = false;
@Shadow
- #if MC_VER >= MC_1_20_4
- (remap = false)
- #endif
@Final
private NativeImage pixels;
diff --git a/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLevelRenderer.java b/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLevelRenderer.java
index 0add4e5de..f6223c099 100644
--- a/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLevelRenderer.java
+++ b/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLevelRenderer.java
@@ -74,9 +74,6 @@ import org.lwjgl.opengl.GL15;
public class MixinLevelRenderer
{
@Shadow
- #if MC_VER >= MC_1_20_4
- (remap = false)
- #endif
private ClientLevel level;
@Unique
private static float previousPartialTicks = 0;
diff --git a/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLightTexture.java b/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLightTexture.java
index 9bafb8a8f..f350a1754 100644
--- a/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLightTexture.java
+++ b/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLightTexture.java
@@ -36,9 +36,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public class MixinLightTexture
{
@Shadow
- #if MC_VER >= MC_1_20_4
- (remap = false)
- #endif
@Final
private DynamicTexture lightTexture;
diff --git a/neoforged/build.gradle b/neoforged/build.gradle
new file mode 100644
index 000000000..eb49e83bb
--- /dev/null
+++ b/neoforged/build.gradle
@@ -0,0 +1,149 @@
+plugins {
+ // Note: This is only needed for multi-loader projects
+ // The main architectury loom version is set at the start of the root build.gradle
+ id "architectury-plugin" version "3.4-SNAPSHOT"
+}
+
+sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17
+
+architectury {
+ platformSetupLoomIde()
+ forge()
+}
+
+repositories {
+ maven {
+ name "Neoforged"
+ url "https://maven.neoforged.net/releases/"
+ }
+}
+
+//loom {
+// forge {
+// convertAccessWideners.set(true)
+// extraAccessWideners.add("lod.accesswidener")
+// mixinConfigs("DistantHorizons.mixins.json")
+// }
+//}
+
+loom {
+ silentMojangMappingsLicense() // Shut the licencing warning
+ accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener")
+
+ neoForge {
+ // Access wideners are now defined in the `remapJar.atAccessWideners`
+// convertAccessWideners = true
+// extraAccessWideners.add loom.accessWidenerPath.get().asFile.name
+
+ // Mixins are now defined in the `mods.toml`
+// mixinConfigs = [
+// "DistantHorizons.mixins.json"
+// ]
+ }
+ mixin {
+ useLegacyMixinAp = true
+
+ // Mixins are now defined in the `mods.toml`
+// mixinConfigs = [
+// "DistantHorizons.mixins.json"
+// ]
+ }
+
+ // "runs" isn't required, but when we do need it then it can be useful
+ runs {
+ client {
+ client()
+ setConfigName("Neoforged Client")
+ ideConfigGenerated(true)
+ runDir("../run")
+// vmArgs("-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg)
+ }
+ server {
+ server()
+ setConfigName("Neoforged Server")
+ ideConfigGenerated(true)
+ runDir("../run")
+ }
+ }
+}
+
+remapJar {
+ inputFile = shadowJar.archiveFile
+ dependsOn shadowJar
+// classifier null
+
+ atAccessWideners.add("distanthorizons.accesswidener")
+}
+
+
+def addMod(path, enabled) {
+ if (enabled == "2")
+ dependencies { implementation(path) }
+ else if (enabled == "1")
+ dependencies { modCompileOnly(path) }
+}
+dependencies {
+ minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
+ mappings loom.layered() {
+ // Mojmap mappings
+ officialMojangMappings()
+ // Parchment mappings (it adds parameter mappings & javadoc)
+ parchment("org.parchmentmc.data:parchment-${rootProject.parchment_version}@zip")
+
+ // Architectury hackishness
+// it.mappings "dev.architectury:yarn-mappings-patch-forge:${rootProject.mappings_patch}"
+ }
+
+ // Neoforged
+ neoForge "net.neoforged:neoforge:${rootProject.neoforged_version}"
+
+ // Architectury API
+// if (minecraft_version == "1.16.5") {
+// implementation("me.shedaniel:architectury-forge:${rootProject.architectury_version}")
+// } else {
+// implementation("dev.architectury:architectury-forge:${rootProject.architectury_version}")
+// }
+
+ // Starlight
+ addMod("curse.maven:starlight-forge-526854:${rootProject.starlight_version_forge}", rootProject.enable_starlight_forge)
+// annotationProcessor "org.spongepowered:mixin:0.8.4:processor"
+
+ addMod("curse.maven:TerraForged-363820:${rootProject.terraforged_version}", rootProject.enable_terraforged)
+
+ addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft)
+
+// if (System.getProperty("idea.sync.active") != "true") {
+// annotationProcessor "org.spongepowered:mixin:0.8.4:processor"
+// }
+}
+
+task deleteResources(type: Delete) {
+ delete file("build/resources/main")
+}
+
+tasks.register('copyAllResources') {
+ dependsOn(copyCoreResources)
+ dependsOn(copyCommonLoaderResources)
+}
+
+processResources {
+ dependsOn(tasks.named('copyAllResources'))
+}
+
+tasks.named('runClient') {
+ dependsOn(tasks.named('copyAllResources'))
+ finalizedBy(deleteResources)
+}
+
+
+sourcesJar {
+ def commonSources = project(":common").sourcesJar
+ dependsOn commonSources
+ from commonSources.archiveFile.map { zipTree(it) }
+}
+
+//components.java {
+// withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
+// skip()
+// }
+//}
\ No newline at end of file
diff --git a/neoforged/gradle.properties b/neoforged/gradle.properties
new file mode 100644
index 000000000..85e1db4b7
--- /dev/null
+++ b/neoforged/gradle.properties
@@ -0,0 +1 @@
+loom.platform=neoForge
\ No newline at end of file
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java
new file mode 100644
index 000000000..f03766596
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java
@@ -0,0 +1,285 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge;
+
+import com.seibel.distanthorizons.common.util.ProxyUtil;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
+import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
+import com.seibel.distanthorizons.core.api.internal.ClientApi;
+import com.seibel.distanthorizons.core.api.internal.SharedApi;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
+
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
+import com.seibel.distanthorizons.coreapi.ModInfo;
+//import io.netty.buffer.ByteBuf;
+import net.minecraft.world.level.LevelAccessor;
+
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
+import net.neoforged.neoforge.event.level.ChunkEvent;
+import net.neoforged.neoforge.event.level.LevelEvent;
+
+import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent;
+import net.minecraft.world.level.chunk.ChunkAccess;
+
+import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
+//import net.neoforged.network.NetworkRegistry;
+//import net.neoforged.network.simple.SimpleChannel;
+import org.apache.logging.log4j.Logger;
+import org.lwjgl.glfw.GLFW;
+
+import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
+
+import net.minecraft.client.Minecraft;
+import net.neoforged.neoforge.client.event.InputEvent;
+import net.neoforged.neoforge.event.TickEvent;
+import net.neoforged.bus.api.SubscribeEvent;
+import org.lwjgl.opengl.GL32;
+
+/**
+ * This handles all events sent to the client,
+ * and is the starting point for most of the mod.
+ *
+ * @author James_Seibel
+ * @version 2023-7-27
+ */
+public class ForgeClientProxy
+{
+ private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
+ private static final Logger LOGGER = DhLoggerBuilder.getLogger();
+
+// private static SimpleChannel multiversePluginChannel;
+
+
+ private static LevelAccessor GetEventLevel(LevelEvent e) { return e.getLevel(); }
+
+
+
+ //=============//
+ // tick events //
+ //=============//
+
+ @SubscribeEvent
+ public void clientTickEvent(TickEvent.ClientTickEvent event)
+ {
+ if (event.phase == TickEvent.Phase.START)
+ {
+ ClientApi.INSTANCE.clientTickEvent();
+ }
+ }
+
+
+
+ //==============//
+ // world events //
+ //==============//
+
+ @SubscribeEvent
+ public void clientLevelLoadEvent(LevelEvent.Load event)
+ {
+ LOGGER.info("level load");
+
+ LevelAccessor level = event.getLevel();
+ if (!(level instanceof ClientLevel))
+ {
+ return;
+ }
+
+ ClientLevel clientLevel = (ClientLevel) level;
+ IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel);
+ // TODO this causes a crash due to level being set to null somewhere
+ ClientApi.INSTANCE.clientLevelLoadEvent(clientLevelWrapper);
+ }
+ @SubscribeEvent
+ public void clientLevelUnloadEvent(LevelEvent.Load event)
+ {
+ LOGGER.info("level unload");
+
+ LevelAccessor level = event.getLevel();
+ if (!(level instanceof ClientLevel))
+ {
+ return;
+ }
+
+ ClientLevel clientLevel = (ClientLevel) level;
+ IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel);
+ ClientApi.INSTANCE.clientLevelUnloadEvent(clientLevelWrapper);
+ }
+
+
+
+ //==============//
+ // chunk events //
+ //==============//
+
+ @SubscribeEvent
+ public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event)
+ {
+ LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
+
+ LevelAccessor level = event.getLevel();
+
+ ChunkAccess chunk = level.getChunk(event.getPos());
+ this.onBlockChangeEvent(level, chunk);
+ }
+ @SubscribeEvent
+ public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event)
+ {
+ LOGGER.trace("break or block attack at blockPos: " + event.getPos());
+
+ LevelAccessor level = event.getLevel();
+
+ ChunkAccess chunk = level.getChunk(event.getPos());
+ this.onBlockChangeEvent(level, chunk);
+ }
+ private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk)
+ {
+ ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(level);
+ SharedApi.INSTANCE.chunkBlockChangedEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
+ }
+
+
+ @SubscribeEvent
+ public void clientChunkLoadEvent(ChunkEvent.Load event)
+ {
+ ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
+ IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
+ SharedApi.INSTANCE.chunkLoadEvent(chunk, wrappedLevel);
+ }
+ @SubscribeEvent
+ public void clientChunkUnloadEvent(ChunkEvent.Unload event)
+ {
+ ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
+ IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
+ SharedApi.INSTANCE.chunkUnloadEvent(chunk, wrappedLevel);
+ }
+
+
+
+ //==============//
+ // key bindings //
+ //==============//
+
+ @SubscribeEvent
+ public void registerKeyBindings(InputEvent.Key event)
+ {
+ if (Minecraft.getInstance().player == null)
+ {
+ return;
+ }
+ if (event.getAction() != GLFW.GLFW_PRESS)
+ {
+ return;
+ }
+
+ ClientApi.INSTANCE.keyPressedEvent(event.getKey());
+ }
+
+
+
+ //============//
+ // networking //
+ //============//
+
+ /** @param event this is just to ensure the event is called at the right time, if it is called outside the {@link FMLClientSetupEvent} event, the binding may fail */
+ public static void setupNetworkingListeners(FMLClientSetupEvent event)
+ {
+// multiversePluginChannel = NetworkRegistry.newSimpleChannel(
+// new ResourceLocation(ModInfo.NETWORKING_RESOURCE_NAMESPACE, ModInfo.MULTIVERSE_PLUGIN_NAMESPACE),
+// // network protocol version
+// () -> ModInfo.MULTIVERSE_PLUGIN_PROTOCOL_VERSION +"",
+// // client accepted versions
+// ForgeClientProxy::isReceivedProtocolVersionAcceptable,
+// // server accepted versions
+// ForgeClientProxy::isReceivedProtocolVersionAcceptable
+// );
+//
+// multiversePluginChannel.registerMessage(0/*should be incremented for each simple channel we listen to*/, ByteBuf.class,
+// // encoder
+// (pack, friendlyByteBuf) -> { },
+// // decoder
+// (friendlyByteBuf) -> friendlyByteBuf.asByteBuf(),
+// // message consumer
+// (nettyByteBuf, contextRef) ->
+// {
+// ClientApi.INSTANCE.serverMessageReceived(nettyByteBuf);
+// contextRef.get().setPacketHandled(true);
+// }
+// );
+ }
+
+ public static boolean isReceivedProtocolVersionAcceptable(String versionString)
+ {
+ if (versionString.toLowerCase().contains("allowvanilla"))
+ {
+ // allow using networking on vanilla servers
+ return true;
+ }
+ else if (versionString.toLowerCase().contains("absent"))
+ {
+ // allow using networking even if DH isn't installed on the server
+ return true;
+ }
+ else
+ {
+ // DH is installed on the server, check if the version is valid to use
+ try
+ {
+ int version = Integer.parseInt(versionString);
+ return ModInfo.MULTIVERSE_PLUGIN_PROTOCOL_VERSION == version;
+ }
+ catch (NumberFormatException ignored)
+ {
+ return false;
+ }
+ }
+ }
+
+
+
+ //===========//
+ // rendering //
+ //===========//
+
+ @SubscribeEvent
+ public void afterLevelRenderEvent(RenderLevelStageEvent event)
+ {
+ if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_LEVEL)
+ {
+ try
+ {
+ // should generally only need to be set once per game session
+ // allows DH to render directly to Optifine's level frame buffer,
+ // allowing better shader support
+ MinecraftRenderWrapper.INSTANCE.finalLevelFrameBufferId = GL32.glGetInteger(GL32.GL_FRAMEBUFFER_BINDING);
+ }
+ catch (Exception | Error e)
+ {
+ LOGGER.error("Unexpected error in afterLevelRenderEvent: "+e.getMessage(), e);
+ }
+ }
+ }
+
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/ForgeMain.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/ForgeMain.java
new file mode 100644
index 000000000..49219e57c
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/ForgeMain.java
@@ -0,0 +1,161 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge;
+
+import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
+import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent;
+import com.seibel.distanthorizons.common.LodCommonMain;
+import com.seibel.distanthorizons.common.forge.LodForgeMethodCaller;
+import com.seibel.distanthorizons.common.wrappers.DependencySetup;
+import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
+import com.seibel.distanthorizons.core.jar.ModJarInfo;
+import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
+import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
+import com.seibel.distanthorizons.coreapi.ModInfo;
+import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
+import com.seibel.distanthorizons.forge.wrappers.ForgeDependencySetup;
+
+import com.seibel.distanthorizons.forge.wrappers.modAccessor.OptifineAccessor;
+
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.core.Direction;
+import net.minecraft.util.RandomSource;
+import net.minecraft.world.level.ColorResolver;
+import net.minecraft.world.level.biome.Biome;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.fml.ModLoadingContext;
+import net.neoforged.fml.common.Mod;
+import net.neoforged.fml.event.lifecycle.*;
+import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext;
+import net.neoforged.neoforge.client.ConfigScreenHandler;
+
+import net.neoforged.neoforge.common.NeoForge;
+import org.apache.logging.log4j.Logger;
+
+import net.minecraft.client.renderer.RenderType;
+import net.neoforged.neoforge.client.model.data.ModelData;
+
+import java.lang.invoke.MethodHandles;
+import java.util.List;
+
+/**
+ * Initialize and setup the Mod.
+ * If you are looking for the real start of the mod
+ * check out the ClientProxy.
+ *
+ * @author coolGi
+ * @author Ran
+ * @author James Seibel
+ * @version 8-15-2022
+ */
+@Mod(ModInfo.ID)
+public class ForgeMain implements LodForgeMethodCaller
+{
+ private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
+ public static ForgeClientProxy client_proxy = null;
+ public static ForgeServerProxy server_proxy = null;
+
+ public ForgeMain()
+ {
+ DependencySetup.createClientBindings();
+
+// initDedicated(null);
+// initDedicated(null);
+ // Register the mod initializer (Actual event registration is done in the different proxies)
+ FMLJavaModLoadingContext.get().getModEventBus().addListener(this::initClient);
+ FMLJavaModLoadingContext.get().getModEventBus().addListener(this::initDedicated);
+ }
+
+ private void initClient(final FMLClientSetupEvent event)
+ {
+ ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
+
+ LOGGER.info("Initializing Mod");
+ LodCommonMain.startup(this);
+ ForgeDependencySetup.createInitialBindings();
+ LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
+
+ // Print git info (Useful for dev builds)
+ LOGGER.info("DH Branch: " + ModJarInfo.Git_Branch);
+ LOGGER.info("DH Commit: " + ModJarInfo.Git_Commit);
+ LOGGER.info("DH Jar Build Source: " + ModJarInfo.Build_Source);
+
+ client_proxy = new ForgeClientProxy();
+ NeoForge.EVENT_BUS.register(client_proxy);
+ server_proxy = new ForgeServerProxy(false);
+ NeoForge.EVENT_BUS.register(server_proxy);
+
+ if (AbstractOptifineAccessor.optifinePresent())
+ {
+ ModAccessorInjector.INSTANCE.bind(IOptifineAccessor.class, new OptifineAccessor());
+ }
+
+ ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class,
+ () -> new ConfigScreenHandler.ConfigScreenFactory((client, parent) -> GetConfigScreen.getScreen(parent)));
+
+ ForgeClientProxy.setupNetworkingListeners(event);
+
+ LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
+
+ ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
+
+ // Init config
+ // The reason im initialising in this rather than the post init process is cus im using this for the auto updater
+ LodCommonMain.initConfig();
+ }
+
+ private void initDedicated(final FMLDedicatedServerSetupEvent event)
+ {
+// DependencySetup.createServerBindings();
+// initCommon();
+
+// server_proxy = new ForgeServerProxy(true);
+// MinecraftForge.EVENT_BUS.register(server_proxy);
+//
+ postInitCommon();
+ }
+
+ private void postInitCommon()
+ {
+ LOGGER.info("Post-Initializing Mod");
+ ForgeDependencySetup.runDelayedSetup();
+
+ LOGGER.info("Mod Post-Initialized");
+ }
+
+ private final ModelData modelData = ModelData.EMPTY;
+
+ @Override
+ public List getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, RandomSource random)
+ {
+ return mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, modelData, RenderType.solid() );
+ }
+
+ @Override //TODO: Check this if its still needed
+ public int colorResolverGetColor(ColorResolver resolver, Biome biome, double x, double z)
+ {
+ return resolver.getColor(biome, x, z);
+ }
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/ForgeServerProxy.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/ForgeServerProxy.java
new file mode 100644
index 000000000..a55530613
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/ForgeServerProxy.java
@@ -0,0 +1,125 @@
+package com.seibel.distanthorizons.forge;
+
+import com.seibel.distanthorizons.common.util.ProxyUtil;
+import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
+import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
+import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
+import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
+import com.seibel.distanthorizons.core.api.internal.ServerApi;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.LevelAccessor;
+import net.neoforged.neoforge.event.TickEvent;
+import net.neoforged.neoforge.event.level.ChunkEvent;
+import net.neoforged.neoforge.event.level.LevelEvent;
+import net.neoforged.bus.api.SubscribeEvent;
+
+import net.neoforged.neoforge.event.server.ServerAboutToStartEvent;
+import net.neoforged.neoforge.event.server.ServerStoppingEvent;
+
+
+import org.apache.logging.log4j.Logger;
+
+import java.util.function.Supplier;
+
+public class ForgeServerProxy
+{
+ private static LevelAccessor GetEventLevel(LevelEvent e) { return e.getLevel(); }
+
+ private final ServerApi serverApi = ServerApi.INSTANCE;
+ private static final Logger LOGGER = DhLoggerBuilder.getLogger();
+ private final boolean isDedicated;
+ public static Supplier isGenerationThreadChecker = null;
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public ForgeServerProxy(boolean isDedicated)
+ {
+ this.isDedicated = isDedicated;
+ isGenerationThreadChecker = BatchGenerationEnvironment::isCurrentThreadDistantGeneratorThread;
+ }
+
+
+
+ //========//
+ // events //
+ //========//
+
+ // ServerTickEvent (at end)
+ @SubscribeEvent
+ public void serverTickEvent(TickEvent.ServerTickEvent event)
+ {
+ if (event.phase == TickEvent.Phase.END)
+ {
+ this.serverApi.serverTickEvent();
+ }
+ }
+
+ // ServerWorldLoadEvent
+ @SubscribeEvent
+ public void dedicatedWorldLoadEvent(ServerAboutToStartEvent event)
+ {
+ this.serverApi.serverLoadEvent(this.isDedicated);
+ }
+
+ // ServerWorldUnloadEvent
+ @SubscribeEvent
+ public void serverWorldUnloadEvent(ServerStoppingEvent event)
+ {
+ this.serverApi.serverUnloadEvent();
+ }
+
+ // ServerLevelLoadEvent
+ @SubscribeEvent
+ public void serverLevelLoadEvent(LevelEvent.Load event)
+ {
+ if (GetEventLevel(event) instanceof ServerLevel)
+ {
+ this.serverApi.serverLevelLoadEvent(this.getServerLevelWrapper((ServerLevel) GetEventLevel(event)));
+ }
+ }
+
+ // ServerLevelUnloadEvent
+ @SubscribeEvent
+ public void serverLevelUnloadEvent(LevelEvent.Unload event)
+ {
+ if (GetEventLevel(event) instanceof ServerLevel)
+ {
+ this.serverApi.serverLevelUnloadEvent(this.getServerLevelWrapper((ServerLevel) GetEventLevel(event)));
+ }
+ }
+
+ @SubscribeEvent
+ public void serverChunkLoadEvent(ChunkEvent.Load event)
+ {
+ ILevelWrapper levelWrapper = ProxyUtil.getLevelWrapper(GetEventLevel(event));
+
+ IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
+ this.serverApi.serverChunkLoadEvent(chunk, levelWrapper);
+ }
+ @SubscribeEvent
+ public void serverChunkSaveEvent(ChunkEvent.Unload event)
+ {
+ ILevelWrapper levelWrapper = ProxyUtil.getLevelWrapper(GetEventLevel(event));
+
+ IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
+ this.serverApi.serverChunkSaveEvent(chunk, levelWrapper);
+ }
+
+
+
+ //================//
+ // helper methods //
+ //================//
+
+ private static ServerLevelWrapper getServerLevelWrapper(ServerLevel level) { return ServerLevelWrapper.getWrapper(level); }
+
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/ForgeMixinPlugin.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/ForgeMixinPlugin.java
new file mode 100644
index 000000000..734adf4ac
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/ForgeMixinPlugin.java
@@ -0,0 +1,71 @@
+package com.seibel.distanthorizons.forge.mixins;
+
+import net.neoforged.fml.ModList;
+import org.objectweb.asm.tree.ClassNode;
+import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
+import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author coolGi
+ * @author cortex
+ */
+public class ForgeMixinPlugin implements IMixinConfigPlugin
+{
+
+ @Override
+ public boolean shouldApplyMixin(String targetClassName, String mixinClassName)
+ {
+ if (mixinClassName.contains(".mods."))
+ { // If the mixin wants to go into a mod then we check if that mod is loaded or not
+ return ModList.get().isLoaded(
+ mixinClassName
+ // What these 2 regex's do is get the mod name that we are checking out of the mixinClassName
+ // Eg. "com.seibel.distanthorizons.mixins.mods.sodium.MixinSodiumChunkRenderer" turns into "sodium"
+ .replaceAll("^.*mods.", "") // Replaces everything before the mods
+ .replaceAll("\\..*$", "") // Replaces everything after the mod name
+ );
+ }
+ return true;
+ }
+
+
+ @Override
+ public void onLoad(String mixinPackage)
+ {
+
+ }
+
+ @Override
+ public String getRefMapperConfig()
+ {
+ return null;
+ }
+
+ @Override
+ public void acceptTargets(Set myTargets, Set otherTargets)
+ {
+
+ }
+
+ @Override
+ public List getMixins()
+ {
+ return null;
+ }
+
+ @Override
+ public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo)
+ {
+
+ }
+
+ @Override
+ public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo)
+ {
+
+ }
+
+}
\ No newline at end of file
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinClientPacketListener.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinClientPacketListener.java
new file mode 100644
index 000000000..2726de266
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinClientPacketListener.java
@@ -0,0 +1,29 @@
+package com.seibel.distanthorizons.forge.mixins.client;
+
+import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
+import com.seibel.distanthorizons.core.api.internal.ClientApi;
+import net.minecraft.client.multiplayer.ClientPacketListener;
+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(ClientPacketListener.class)
+public class MixinClientPacketListener
+{
+ // TODO update fabric version as well
+
+ @Inject(method = "handleLogin", at = @At("RETURN"))
+ void onHandleLoginEnd(CallbackInfo ci) { ClientApi.INSTANCE.onClientOnlyConnected(); }
+
+ #if MC_VER < MC_1_19_4
+ @Inject(method = "cleanup", at = @At("HEAD"))
+ #else
+ @Inject(method = "close", at = @At("HEAD"))
+ #endif
+ void onCleanupStart(CallbackInfo ci)
+ {
+ ClientApi.INSTANCE.onClientOnlyDisconnected();
+ }
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinDebugScreenOverlay.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinDebugScreenOverlay.java
new file mode 100644
index 000000000..b2ea4d17a
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinDebugScreenOverlay.java
@@ -0,0 +1,23 @@
+package com.seibel.distanthorizons.forge.mixins.client;
+
+import com.seibel.distanthorizons.core.logging.f3.F3Screen;
+import net.minecraft.client.gui.components.DebugScreenOverlay;
+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.CallbackInfoReturnable;
+
+import java.util.List;
+
+@Mixin(DebugScreenOverlay.class)
+public class MixinDebugScreenOverlay
+{
+
+ @Inject(method = "getSystemInformation", at = @At("RETURN"))
+ private void addCustomF3(CallbackInfoReturnable> cir)
+ {
+ List messages = cir.getReturnValue();
+ F3Screen.addStringToDisplay(messages);
+ }
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinDynamicTexture.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinDynamicTexture.java
new file mode 100644
index 000000000..4efbcd350
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinDynamicTexture.java
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge.mixins.client;
+
+
+import com.mojang.blaze3d.platform.NativeImage;
+
+import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
+import com.seibel.distanthorizons.common.util.ILightTextureMarker;
+
+import net.minecraft.client.renderer.texture.DynamicTexture;
+
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import javax.annotation.Nullable;
+
+@Mixin(DynamicTexture.class)
+public abstract class MixinDynamicTexture implements ILightTextureMarker
+{
+ /** Used to prevent accidentally using other dynamic textures as a lightmap */
+ @Unique
+ private boolean isLightTexture = false;
+
+ @Shadow
+ #if MC_VER >= MC_1_20_4
+ (remap = false)
+ #endif
+ @Final
+ private NativeImage pixels;
+
+ @Inject(method = "upload()V", at = @At("HEAD"))
+ public void updateLightTexture(CallbackInfo ci)
+ {
+ // since the light map is always updated on the client render thread we should be able to access the client level at the same time
+ IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
+ if (!this.isLightTexture
+ || mc == null
+ || mc.getWrappedClientLevel() == null
+ )
+ {
+ return;
+ }
+
+ //ApiShared.LOGGER.info("Lightmap update");
+ IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
+ MinecraftRenderWrapper.INSTANCE.updateLightmap(this.pixels, clientLevel);
+ }
+
+ public void markLightTexture() { this.isLightTexture = true; }
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinFogRenderer.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinFogRenderer.java
new file mode 100644
index 000000000..db0ed1a9f
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinFogRenderer.java
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge.mixins.client;
+
+import com.seibel.distanthorizons.core.config.Config;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+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 com.mojang.blaze3d.systems.RenderSystem;
+
+import net.minecraft.client.Camera;
+import net.minecraft.client.renderer.FogRenderer;
+import net.minecraft.client.renderer.FogRenderer.FogMode;
+import net.minecraft.world.effect.MobEffects;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.entity.LivingEntity;
+#if MC_VER < MC_1_17_1
+import net.minecraft.world.level.material.FluidState;
+#else
+import net.minecraft.world.level.material.FogType;
+#endif
+
+
+
+@Mixin(FogRenderer.class)
+public class MixinFogRenderer
+{
+
+ // Using this instead of Float.MAX_VALUE because Sodium don't like it.
+ private static final float A_REALLY_REALLY_BIG_VALUE = 420694206942069.F;
+ private static final float A_EVEN_LARGER_VALUE = 42069420694206942069.F;
+
+ @Inject(at = @At("RETURN"),
+ method = "setupFog(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/FogRenderer$FogMode;FZF)V",
+ remap = #if MC_VER == MC_1_17_1 || MC_VER == MC_1_18_2 false #else true #endif ) // Remap messiness due to this being weird in forge
+ private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, float partTick, CallbackInfo callback)
+ {
+ #if MC_VER < MC_1_17_1
+ FluidState fluidState = camera.getFluidInCamera();
+ boolean cameraNotInFluid = fluidState.isEmpty();
+ #else
+ FogType fogTypes = camera.getFluidInCamera();
+ boolean cameraNotInFluid = fogTypes == FogType.NONE;
+ #endif
+
+
+ Entity entity = camera.getEntity();
+ boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS);
+ if (!isSpecialFog && cameraNotInFluid && fogMode == FogMode.FOG_TERRAIN
+ && !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial()
+ && Config.Client.Advanced.Graphics.Fog.disableVanillaFog.get())
+ {
+ #if MC_VER < MC_1_17_1
+ RenderSystem.fogStart(A_REALLY_REALLY_BIG_VALUE);
+ RenderSystem.fogEnd(A_EVEN_LARGER_VALUE);
+ #else
+ RenderSystem.setShaderFogStart(A_REALLY_REALLY_BIG_VALUE);
+ RenderSystem.setShaderFogEnd(A_EVEN_LARGER_VALUE);
+ #endif
+ }
+ }
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinGameRenderer.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinGameRenderer.java
new file mode 100644
index 000000000..614054ee2
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinGameRenderer.java
@@ -0,0 +1,58 @@
+package com.seibel.distanthorizons.forge.mixins.client;
+
+import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck;
+import com.seibel.distanthorizons.core.api.internal.ClientApi;
+import net.minecraft.client.renderer.GameRenderer;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+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;
+
+// TODO: Check if this port from fabric works
+@Mixin(GameRenderer.class)
+public class MixinGameRenderer
+{
+ private static final Logger LOGGER = LogManager.getLogger(MixinGameRenderer.class.getSimpleName());
+
+ #if MC_VER >= MC_1_17_1
+ // FIXME: This I think will dup multiple renderStartupEvent calls...
+ @Inject(method = {"reloadShaders", "preloadUiShader"}, at = @At("TAIL"))
+ public void onStartupShaders(CallbackInfo ci)
+ {
+ LOGGER.info("Starting up renderer (forge)");
+ if (!DependencySetupDoneCheck.isDone)
+ {
+ LOGGER.warn("Dependency setup is not done yet, skipping renderer this startup event!");
+ return;
+ }
+ ClientApi.INSTANCE.rendererStartupEvent();
+ }
+
+ @Inject(method = "shutdownShaders", at = @At("HEAD"))
+ public void onShutdownShaders(CallbackInfo ci)
+ {
+ LOGGER.info("Shutting down renderer (forge)");
+ if (!DependencySetupDoneCheck.isDone)
+ {
+ LOGGER.warn("Dependency setup is not done yet, skipping renderer this shutdown event!");
+ return;
+ }
+ ClientApi.INSTANCE.rendererShutdownEvent();
+ }
+ #else
+
+
+ @Inject(method = {"loadEffect"}, at = @At("TAIL"))
+ public void onStartupShaders(CallbackInfo ci) {
+ ClientApi.INSTANCE.rendererStartupEvent();
+ }
+
+ @Inject(method = "shutdownEffect", at = @At("HEAD"))
+ public void onShutdownShaders(CallbackInfo ci) {
+ ClientApi.INSTANCE.rendererShutdownEvent();
+ }
+ #endif
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLevelRenderer.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLevelRenderer.java
new file mode 100644
index 000000000..0add4e5de
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLevelRenderer.java
@@ -0,0 +1,185 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge.mixins.client;
+
+import com.mojang.blaze3d.vertex.PoseStack;
+#if MC_VER < MC_1_19_4
+import com.mojang.math.Matrix4f;
+#else
+import net.minecraft.client.Camera;
+import net.minecraft.client.renderer.GameRenderer;
+import net.minecraft.client.renderer.LightTexture;
+import org.joml.Matrix4f;
+#endif
+import com.seibel.distanthorizons.common.rendering.SeamlessOverdraw;
+import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
+import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
+import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
+import com.seibel.distanthorizons.core.config.Config;
+import com.seibel.distanthorizons.core.api.internal.ClientApi;
+import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
+import net.minecraft.client.Camera;
+import net.minecraft.client.multiplayer.ClientLevel;
+import net.minecraft.client.renderer.GameRenderer;
+import net.minecraft.client.renderer.LevelRenderer;
+import net.minecraft.client.renderer.LightTexture;
+import net.minecraft.client.renderer.RenderType;
+import net.minecraft.world.level.lighting.LevelLightEngine;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.nio.FloatBuffer;
+
+#if MC_VER < MC_1_17_1
+import org.lwjgl.opengl.GL15;
+#endif
+
+
+/**
+ * This class is used to mix in my rendering code
+ * before Minecraft starts rendering blocks.
+ * If this wasn't done, and we used Forge's
+ * render last event, the LODs would render on top
+ * of the normal terrain.
+ *
+ * This is also the mixin for rendering the clouds
+ *
+ * @author coolGi
+ * @author James Seibel
+ * @version 12-31-2021
+ */
+@Mixin(LevelRenderer.class)
+public class MixinLevelRenderer
+{
+ @Shadow
+ #if MC_VER >= MC_1_20_4
+ (remap = false)
+ #endif
+ private ClientLevel level;
+ @Unique
+ private static float previousPartialTicks = 0;
+
+ // TODO: Is there any reason why this is here? Can it be deleted?
+ public MixinLevelRenderer()
+ {
+ throw new NullPointerException("Null cannot be cast to non-null type.");
+ }
+
+ #if MC_VER < MC_1_17_1
+ @Inject(at = @At("RETURN"), method = "renderSky(Lcom/mojang/blaze3d/vertex/PoseStack;F)V")
+ private void renderSky(PoseStack matrixStackIn, float partialTicks, CallbackInfo callback)
+ #else
+ @Inject(method = "renderClouds", at = @At("HEAD"), cancellable = true)
+ public void renderClouds(PoseStack poseStack, Matrix4f projectionMatrix, float partialTicks, double cameraX, double cameraY, double cameraZ, CallbackInfo ci)
+ #endif
+ {
+ // get the partial ticks since renderBlockLayer doesn't
+ // have access to them
+ previousPartialTicks = partialTicks;
+ }
+
+
+ // TODO: Can we move this to forge's client proxy similarly to how fabric does it
+ #if MC_VER < MC_1_17_1
+ @Inject(at = @At("HEAD"),
+ method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDD)V",
+ cancellable = true)
+ private void renderChunkLayer(RenderType renderType, PoseStack matrixStackIn, double xIn, double yIn, double zIn, CallbackInfo callback)
+ #elif MC_VER < MC_1_19_4
+ @Inject(at = @At("HEAD"),
+ method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLcom/mojang/math/Matrix4f;)V",
+ cancellable = true)
+ private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback)
+ #elif MC_VER < MC_1_20_2
+ @Inject(at = @At("HEAD"),
+ method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
+ cancellable = true)
+ private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback)
+ #else
+ @Inject(at = @At("HEAD"),
+ method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
+ cancellable = true)
+ private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double camX, double camY, double camZ, Matrix4f projectionMatrix, CallbackInfo callback)
+ #endif
+ {
+ // get MC's model view and projection matrices
+ #if MC_VER == MC_1_16_5
+ // get the matrices from the OpenGL fixed pipeline
+ float[] mcProjMatrixRaw = new float[16];
+ GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
+ Mat4f mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
+ mcProjectionMatrix.transpose();
+
+ Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose());
+
+ #else
+ // get the matrices directly from MC
+ Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
+ Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
+ #endif
+
+
+
+ // only render before solid blocks
+ if (renderType.equals(RenderType.solid()))
+ {
+ ClientApi.INSTANCE.renderLods(ClientLevelWrapper.getWrapper(level), mcModelViewMatrix, mcProjectionMatrix, previousPartialTicks);
+
+ // experimental proof-of-concept option
+ if (Config.Client.Advanced.Graphics.AdvancedGraphics.seamlessOverdraw.get())
+ {
+ float[] matrixFloatArray = SeamlessOverdraw.overwriteMinecraftNearFarClipPlanes(mcProjectionMatrix, previousPartialTicks);
+
+ #if MC_VER == MC_1_16_5
+ SeamlessOverdraw.applyLegacyProjectionMatrix(matrixFloatArray);
+ #elif MC_VER < MC_1_19_4
+ projectionMatrix.load(FloatBuffer.wrap(matrixFloatArray));
+ #else
+ projectionMatrix.set(matrixFloatArray);
+ #endif
+ }
+ }
+
+ if (Config.Client.Advanced.Debugging.lodOnlyMode.get())
+ {
+ callback.cancel();
+ }
+ }
+
+ #if MC_VER < MC_1_19_4
+ @Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runUpdates(IZZ)I"), method = "renderLevel")
+ public void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci)
+ #elif MC_VER < MC_1_20_1
+ @Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runUpdates(IZZ)I"), method = "renderLevel")
+ public void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci)
+ #else
+ @Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I"), method = "renderLevel")
+ private void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci)
+ #endif
+ {
+ ChunkWrapper.syncedUpdateClientLightStatus();
+ }
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLightTexture.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLightTexture.java
new file mode 100644
index 000000000..9bafb8a8f
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinLightTexture.java
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge.mixins.client;
+
+
+import com.seibel.distanthorizons.common.util.ILightTextureMarker;
+
+import net.minecraft.client.renderer.LightTexture;
+import net.minecraft.client.renderer.texture.DynamicTexture;
+
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(LightTexture.class)
+public class MixinLightTexture
+{
+ @Shadow
+ #if MC_VER >= MC_1_20_4
+ (remap = false)
+ #endif
+ @Final
+ private DynamicTexture lightTexture;
+
+ @Inject(method = "", at = @At("RETURN"))
+ public void markLightTexture(CallbackInfo ci) { ((ILightTextureMarker) this.lightTexture).markLightTexture(); }
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinMinecraft.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinMinecraft.java
new file mode 100644
index 000000000..b8c25e6e1
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinMinecraft.java
@@ -0,0 +1,95 @@
+package com.seibel.distanthorizons.forge.mixins.client;
+
+import com.seibel.distanthorizons.api.enums.config.EUpdateBranch;
+import com.seibel.distanthorizons.common.wrappers.gui.updater.UpdateModScreen;
+import com.seibel.distanthorizons.core.config.Config;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.jar.installer.GitlabGetter;
+import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
+import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
+import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.screens.Screen;
+import net.minecraft.client.gui.screens.TitleScreen;
+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.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+/**
+ * At the moment this is only used for the auto updater
+ *
+ * @author coolGi
+ */
+@Mixin(Minecraft.class)
+public class MixinMinecraft
+{
+ #if MC_VER < MC_1_20_2
+ #if MC_VER == MC_1_20_1
+ @Redirect(
+ method = "Lnet/minecraft/client/Minecraft;setInitialScreen(Lcom/mojang/realmsclient/client/RealmsClient;Lnet/minecraft/server/packs/resources/ReloadInstance;Lnet/minecraft/client/main/GameConfig$QuickPlayData;)V",
+ at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;setScreen(Lnet/minecraft/client/gui/screens/Screen;)V")
+ )
+ public void onOpenScreen(Minecraft instance, Screen guiScreen)
+ {
+ #else
+ @Redirect(
+ method = "(Lnet/minecraft/client/main/GameConfig;)V",
+ at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;setScreen(Lnet/minecraft/client/gui/screens/Screen;)V")
+ )
+ public void onOpenScreen(Minecraft instance, Screen guiScreen)
+ {
+ #endif
+ if (!Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get()) // Don't do anything if the user doesn't want it
+ {
+ instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
+ return;
+ }
+
+ if (SelfUpdater.onStart())
+ {
+ instance.setScreen(new UpdateModScreen(
+ new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
+ (Config.Client.Advanced.AutoUpdater.updateBranch.get() == EUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()): GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
+ ));
+ }
+ else
+ {
+ instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
+ }
+ }
+ #endif
+
+ #if MC_VER >= MC_1_20_2
+ @Redirect(
+ method = "Lnet/minecraft/client/Minecraft;onGameLoadFinished(Lnet/minecraft/client/Minecraft$GameLoadCookie;)V",
+ at = @At(value = "INVOKE", target = "Ljava/lang/Runnable;run()V")
+ )
+ private void buildInitialScreens(Runnable runnable)
+ {
+ if (
+ Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() // Don't do anything if the user doesn't want it
+ && SelfUpdater.onStart()
+ )
+ {
+ runnable = () -> {
+ Minecraft.getInstance().setScreen(new UpdateModScreen(
+ // TODO: Change to runnable, instead of tittle screen
+ new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
+ (Config.Client.Advanced.AutoUpdater.updateBranch.get() == EUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()) : GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
+ ));
+ };
+ }
+
+ runnable.run();
+ }
+ #endif
+
+ @Inject(at = @At("HEAD"), method = "close()V", remap = false)
+ public void close(CallbackInfo ci)
+ {
+ SelfUpdater.onClose();
+ }
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinOptionsScreen.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinOptionsScreen.java
new file mode 100644
index 000000000..e3af8c9e8
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinOptionsScreen.java
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge.mixins.client;
+
+import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
+import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
+import com.seibel.distanthorizons.coreapi.ModInfo;
+import com.seibel.distanthorizons.core.config.Config;
+import net.minecraft.client.gui.screens.OptionsScreen;
+import net.minecraft.client.gui.screens.Screen;
+import net.minecraft.network.chat.Component;
+#if MC_VER < MC_1_19_2
+import net.minecraft.network.chat.TranslatableComponent;
+#endif
+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.Objects;
+
+/**
+ * Adds a button to the menu to goto the config
+ *
+ * @author coolGi
+ * @version 12-02-2021
+ */
+@Mixin(OptionsScreen.class)
+public class MixinOptionsScreen extends Screen
+{
+ // Get the texture for the button
+ private static final ResourceLocation ICON_TEXTURE = new ResourceLocation(ModInfo.ID, "textures/gui/button.png");
+ protected MixinOptionsScreen(Component title)
+ {
+ super(title);
+ }
+
+ @Inject(at = @At("HEAD"), method = "init")
+ private void lodconfig$init(CallbackInfo ci)
+ {
+ if (Config.Client.optionsButton.get())
+ this. #if MC_VER < MC_1_17_1 addButton #else addRenderableWidget #endif
+ (new TexturedButtonWidget(
+ // Where the button is on the screen
+ this.width / 2 - 180, this.height / 6 - 12,
+ // Width and height of the button
+ 20, 20,
+ // Offset
+ 0, 0,
+ // Some textuary stuff
+ 20, ICON_TEXTURE, 20, 40,
+ // Create the button and tell it where to go
+ // For now it goes to the client option by default
+ (buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(GetConfigScreen.getScreen(this)),
+ // Add a title to the button
+ #if MC_VER < MC_1_19_2
+ new TranslatableComponent(ModInfo.ID + ".title")));
+ #else
+ Component.translatable(ModInfo.ID + ".title")));
+ #endif
+ }
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinTextureUtil.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinTextureUtil.java
new file mode 100644
index 000000000..a9da8632b
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinTextureUtil.java
@@ -0,0 +1,31 @@
+package com.seibel.distanthorizons.forge.mixins.client;
+
+import com.mojang.blaze3d.platform.GlStateManager;
+import com.mojang.blaze3d.platform.TextureUtil;
+import com.seibel.distanthorizons.core.config.Config;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+/**
+ * Sets Minecraft's LOD Bias (looks similar to mipmaps)
+ *
+ * @author coolGi
+ */
+@Mixin(TextureUtil.class)
+public class MixinTextureUtil
+{
+ @Redirect(method = "prepareImage(Lcom/mojang/blaze3d/platform/NativeImage$InternalGlFormat;IIII)V",
+ at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;_texParameter(IIF)V"), remap = false)
+ private static void setLodBias(int target, int pname, float param)
+ {
+ float biasValue = Config.Client.Advanced.Graphics.AdvancedGraphics.lodBias.get().floatValue();
+ if (biasValue != 0)
+ {
+ // The target is GL11.GL_TEXTURE_2D
+ // And the pname is GL14.GL_TEXTURE_LOD_BIAS
+ GlStateManager._texParameter(target, pname, biasValue);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinWorldUpgrader.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinWorldUpgrader.java
new file mode 100644
index 000000000..9e2f76cda
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinWorldUpgrader.java
@@ -0,0 +1,156 @@
+package com.seibel.distanthorizons.forge.mixins.client;
+
+import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
+import com.seibel.distanthorizons.api.interfaces.world.IDhApiDimensionTypeWrapper;
+import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
+import com.seibel.distanthorizons.common.wrappers.world.DimensionTypeWrapper;
+import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure;
+import com.seibel.distanthorizons.core.level.DhServerLevel;
+import com.seibel.distanthorizons.core.pos.DhBlockPos;
+import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.util.worldupdate.WorldUpgrader;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.dimension.DimensionType;
+import net.minecraft.world.level.dimension.LevelStem;
+import net.minecraft.world.level.levelgen.WorldGenSettings;
+import net.minecraft.world.level.storage.DimensionDataStorage;
+import net.minecraft.world.level.storage.LevelStorageSource;
+import org.jetbrains.annotations.Nullable;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+
+import java.io.File;
+import java.nio.file.Path;
+
+#if FALSE
+@Mixin(WorldUpgrader.class)
+public class MixinWorldUpgrader {
+ static class FakeLevelWrapper implements IServerLevelWrapper {
+ private Path saveFolder;
+ private LevelStem stem;
+ private DimensionType dimension;
+ private DimensionTypeWrapper dimensionTypeWrapper;
+
+ public FakeLevelWrapper(LevelStorageSource.LevelStorageAccess storage, WorldGenSettings gen, ResourceKey dim) {
+ saveFolder = storage.getDimensionPath(dim);
+ stem = gen.dimensions().getOrThrow(WorldGenSettings.levelToLevelStem(dim));
+ dimension = stem.typeHolder().value();
+ dimensionTypeWrapper = DimensionTypeWrapper.getDimensionTypeWrapper(dimension);
+ }
+
+ @Override
+ public EDhApiLevelType getLevelType() {
+ return EDhApiLevelType.SERVER_LEVEL;
+ }
+
+ @Override
+ public IDhApiDimensionTypeWrapper getDimensionType() {
+ return dimensionTypeWrapper;
+ }
+
+ @Override
+ public int getBlockLight(int x, int y, int z) {
+ return 0;
+ }
+
+ @Override
+ public int getSkyLight(int x, int y, int z) {
+ return 0;
+ }
+
+ @Override
+ public boolean hasCeiling() {
+ return dimension.hasCeiling();
+ }
+
+ @Override
+ public boolean hasSkyLight() {
+ return dimension.hasSkyLight();
+ }
+
+ @Override
+ public int getHeight() {
+ return dimension.height();
+ }
+
+ @Override
+ public int getMinHeight() {
+ return dimension.minY();
+ }
+
+ @Override
+ public boolean hasChunkLoaded(int chunkX, int chunkZ) {
+ return false;
+ }
+
+ @Override
+ public IBlockStateWrapper getBlockState(DhBlockPos pos) {
+ return BlockStateWrapper.AIR;
+ }
+
+ @Override
+ public IBiomeWrapper getBiome(DhBlockPos pos) {
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ @Override
+ public Object getWrappedMcObject() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public IClientLevelWrapper tryGetClientLevelWrapper() {
+ return null;
+ }
+
+ @Override
+ public File getSaveFolder() {
+ return saveFolder.toFile();
+ }
+ }
+
+ @Unique
+ private DhServerLevel dhServerLevel;
+ @Unique
+ private FakeLevelWrapper fakeLevelWrapper;
+ @Unique
+ public LocalSaveStructure saveStructure;
+
+ @Shadow @Final
+ private DimensionDataStorage overworldDataStorage;
+ @Shadow @Final
+ private LevelStorageSource.LevelStorageAccess levelStorage;
+ @Shadow @Final
+ private WorldGenSettings worldGenSettings;
+
+ @Inject(method = "Lnet/minecraft/util/worldupdate/WorldUpgrader;work()V",
+ at = @At(value = "INVOKE")
+ )
+ private void initWorldUpgrade() {
+ saveStructure = new LocalSaveStructure();
+ }
+
+ @Inject(method = "Lnet/minecraft/util/worldupdate/WorldUpgrader;work()V",
+ at = @At(value = "INVOKE", target = "Lnet/minecraft/util/worldupdate/WorldUpgrader;getAllChunkPos(Lnet/minecraft/resources/ResourceKey;)Ljava/util/List;", shift = At.Shift.AFTER),
+ locals = LocalCapture.CAPTURE_FAILSOFT
+ )
+ private void startWorldUpgrade(CallbackInfo info, ResourceKey resourceKey) {
+ ResourceKey key = resourceKey;
+ fakeLevelWrapper = new FakeLevelWrapper(levelStorage, worldGenSettings, key);
+ dhServerLevel = new DhServerLevel(saveStructure, fakeLevelWrapper);
+ }
+
+
+}
+#endif
\ No newline at end of file
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/server/MixinChunkGenerator.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/server/MixinChunkGenerator.java
new file mode 100644
index 000000000..3541cf2fb
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/server/MixinChunkGenerator.java
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge.mixins.server;
+
+import org.spongepowered.asm.mixin.Mixin;
+import net.minecraft.world.level.chunk.ChunkGenerator;
+
+#if MC_VER < MC_1_18_2
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.server.level.WorldGenRegion;
+import net.minecraft.world.level.StructureFeatureManager;
+import net.minecraft.world.level.biome.Biome;
+import net.minecraft.world.level.levelgen.WorldgenRandom;
+
+@Mixin(ChunkGenerator.class)
+public class MixinChunkGenerator
+{
+ @Redirect(method = "applyBiomeDecoration", at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/world/level/biome/Biome;generate(Lnet/minecraft/world/level/StructureFeatureManager;"
+ + "Lnet/minecraft/world/level/chunk/ChunkGenerator;Lnet/minecraft/server/level/WorldGenRegion;J"
+ + "Lnet/minecraft/world/level/levelgen/WorldgenRandom;Lnet/minecraft/core/BlockPos;)V"
+
+ ))
+ private void wrapBiomeGenerateCall(
+ Biome biome, StructureFeatureManager structFeatManager, ChunkGenerator generator,
+ WorldGenRegion genRegion, long l, WorldgenRandom random, BlockPos pos)
+ {
+ synchronized (ChunkGenerator.class)
+ {
+ //ApiShared.LOGGER.info("Generating Biome {} and acquired lock.", biome.getRegistryName());
+ biome.generate(structFeatManager, (ChunkGenerator) (Object) this, genRegion, l, random, pos);
+ }
+ //ApiShared.LOGGER.info("Released lock. Biome {} generated.", biome.getRegistryName());
+ }
+
+}
+
+#else
+@Mixin(ChunkGenerator.class)
+public class MixinChunkGenerator { }
+#endif
\ No newline at end of file
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/server/MixinTFChunkGenerator.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/server/MixinTFChunkGenerator.java
new file mode 100644
index 000000000..ab203d244
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/server/MixinTFChunkGenerator.java
@@ -0,0 +1,61 @@
+package com.seibel.distanthorizons.forge.mixins.server;
+
+import net.minecraft.world.level.chunk.ChunkGenerator;
+import org.spongepowered.asm.mixin.Mixin;
+
+#if MC_VER == MC_1_16_5
+@Mixin(ChunkGenerator.class)
+class MixinTFChunkGenerator
+{
+ // not currently implemented, attempting to run with the mod enabled in the IDE causes the game to lock up
+}
+#elif MC_VER < MC_1_17_1
+
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Redirect;
+
+import com.terraforged.mod.chunk.generator.FeatureGenerator;
+
+import java.util.Random;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.level.WorldGenLevel;
+import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
+
+@Mixin(FeatureGenerator.class)
+public class MixinTFChunkGenerator
+{
+
+ @Redirect(method = "decorate("
+ + "Lnet/minecraft/world/level/StructureFeatureManager;"
+ + "Lnet/minecraft/world/level/WorldGenLevel;"
+ + "Lnet/minecraft/world/level/chunk/ChunkAccess;"
+ + "Lnet/minecraft/world/level/biome/Biome;"
+ + "Lnet/minecraft/core/BlockPos;"
+ + "Lcom/terraforged/mod/profiler/watchdog/WatchdogContext;)V",
+ at = @At(
+ value = "INVOKE",
+ target = "Lnet/minecraft/world/level/levelgen/feature/ConfiguredFeature;place("
+ + "Lnet/minecraft/world/level/WorldGenLevel;"
+ + "Lnet/minecraft/world/level/chunk/ChunkGenerator;"
+ + "Ljava/util/Random;Lnet/minecraft/core/BlockPos;)Z"
+ ))
+ private boolean wrapDecorate$FeaturePlace(ConfiguredFeature, ?> feature, WorldGenLevel arg,
+ ChunkGenerator arg2, Random random, BlockPos arg3) {
+ synchronized(FeatureGenerator.class) {
+ //ClientApi.LOGGER.info("wrapDecorate FeaturePlace triggered");
+ return feature.place(arg, arg2, random, arg3);
+ }
+ }
+
+ //METHOD: com.terraforged.mod.chunk.generator.FeatureGenerator.decorate(StructureFeatureManager manager,
+ // WorldGenLevel region, ChunkAccess chunk, Biome biome, BlockPos pos, WatchdogContext context)
+
+ //TARGET: boolean net.minecraft.world.level.levelgen.feature.ConfiguredFeature.place
+ // (WorldGenLevel arg, ChunkGenerator arg2, Random random, BlockPos arg3)
+}
+
+#else
+@Mixin(ChunkGenerator.class)
+class MixinTFChunkGenerator { }
+#endif
\ No newline at end of file
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/server/MixinUtilBackgroundThread.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/server/MixinUtilBackgroundThread.java
new file mode 100644
index 000000000..dee12f792
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/server/MixinUtilBackgroundThread.java
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge.mixins.server;
+
+import java.util.concurrent.ExecutorService;
+import java.util.function.Supplier;
+
+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.CallbackInfoReturnable;
+
+import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck;
+import com.seibel.distanthorizons.core.util.objects.DummyRunExecutorService;
+
+import net.minecraft.Util;
+
+@Mixin(Util.class)
+public class MixinUtilBackgroundThread
+{
+ private static boolean shouldApplyOverride()
+ {
+ return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get();
+ }
+
+ @Inject(method = "backgroundExecutor", at = @At("HEAD"), cancellable = true)
+ private static void overrideUtil$backgroundExecutor(CallbackInfoReturnable ci)
+ {
+ if (shouldApplyOverride())
+ {
+ //ApiShared.LOGGER.info("util backgroundExecutor triggered");
+ ci.setReturnValue(new DummyRunExecutorService());
+ }
+ }
+
+ #if MC_VER >= MC_1_17_1
+ @Inject(method = "wrapThreadWithTaskName(Ljava/lang/String;Ljava/lang/Runnable;)Ljava/lang/Runnable;",
+ at = @At("HEAD"), cancellable = true)
+ private static void overrideUtil$wrapThreadWithTaskName(String string, Runnable r, CallbackInfoReturnable ci)
+ {
+ if (shouldApplyOverride())
+ {
+ //ApiShared.LOGGER.info("util wrapThreadWithTaskName(Runnable) triggered");
+ ci.setReturnValue(r);
+ }
+ }
+ #endif
+ #if MC_VER >= MC_1_18_2
+ @Inject(method = "wrapThreadWithTaskName(Ljava/lang/String;Ljava/util/function/Supplier;)Ljava/util/function/Supplier;",
+ at = @At("HEAD"), cancellable = true)
+ private static void overrideUtil$wrapThreadWithTaskNameForSupplier(String string, Supplier> r, CallbackInfoReturnable> ci)
+ {
+ if (shouldApplyOverride())
+ {
+ //ApiShared.LOGGER.info("util wrapThreadWithTaskName(Supplier) triggered");
+ ci.setReturnValue(r);
+ }
+ }
+ #endif
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/server/unsafe/MixinThreadingDetector.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/server/unsafe/MixinThreadingDetector.java
new file mode 100644
index 000000000..2cb73c02c
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/mixins/server/unsafe/MixinThreadingDetector.java
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge.mixins.server.unsafe;
+
+import org.spongepowered.asm.mixin.Mixin;
+#if MC_VER >= MC_1_18_2
+
+import net.minecraft.util.ThreadingDetector;
+import org.spongepowered.asm.mixin.Mutable;
+import org.spongepowered.asm.mixin.Shadow;
+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.concurrent.Semaphore;
+
+/**
+ * Why does this exist? But okay! (Will be probably removed when the experimental generator is done)
+ * FIXME: Recheck this // STILL check this
+ */
+@Mixin(ThreadingDetector.class)
+public class MixinThreadingDetector
+{
+ @Mutable
+ @Shadow
+ private Semaphore lock;
+
+ @Inject(method = "", at = @At("RETURN"))
+ private void setSemaphore(CallbackInfo ci)
+ {
+ this.lock = new Semaphore(2);
+ }
+
+}
+
+#else
+
+import net.minecraft.world.level.chunk.ChunkGenerator;
+
+@Mixin(ChunkGenerator.class)
+public class MixinThreadingDetector { }
+#endif
\ No newline at end of file
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/wrappers/ForgeDependencySetup.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/wrappers/ForgeDependencySetup.java
new file mode 100644
index 000000000..5008ee6c0
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/wrappers/ForgeDependencySetup.java
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge.wrappers;
+
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
+import com.seibel.distanthorizons.forge.wrappers.modAccessor.ModChecker;
+
+/**
+ * Binds all necessary dependencies so we
+ * can access them in Core.
+ * This needs to be called before any Core classes
+ * are loaded.
+ *
+ * @author James Seibel
+ * @author Ran
+ * @version 12-1-2021
+ */
+public class ForgeDependencySetup
+{
+ public static void createInitialBindings()
+ {
+ SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE);
+ }
+
+ public static void runDelayedSetup()
+ {
+ SingletonInjector.INSTANCE.runDelayedSetup();
+ }
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/wrappers/modAccessor/ModChecker.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/wrappers/modAccessor/ModChecker.java
new file mode 100644
index 000000000..4e8b11d69
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/wrappers/modAccessor/ModChecker.java
@@ -0,0 +1,35 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge.wrappers.modAccessor;
+
+import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
+import net.neoforged.fml.ModList;
+
+public class ModChecker implements IModChecker
+{
+ public static final ModChecker INSTANCE = new ModChecker();
+
+ @Override
+ public boolean isModLoaded(String modid)
+ {
+ return ModList.get().isLoaded(modid);
+ }
+
+}
diff --git a/neoforged/src/main/java/com/seibel/distanthorizons/forge/wrappers/modAccessor/OptifineAccessor.java b/neoforged/src/main/java/com/seibel/distanthorizons/forge/wrappers/modAccessor/OptifineAccessor.java
new file mode 100644
index 000000000..13b6688be
--- /dev/null
+++ b/neoforged/src/main/java/com/seibel/distanthorizons/forge/wrappers/modAccessor/OptifineAccessor.java
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the Distant Horizons mod
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2023 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.distanthorizons.forge.wrappers.modAccessor;
+
+import java.util.HashSet;
+
+import com.seibel.distanthorizons.core.pos.DhChunkPos;
+import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
+
+public class OptifineAccessor extends AbstractOptifineAccessor
+{
+
+ @Override
+ public String getModName()
+ {
+ return "Optifine-Forge-1.18.X";
+ }
+
+ @Override
+ public HashSet getNormalRenderedChunks()
+ {
+ // TODO: Impl proper methods here
+ return null;
+ }
+
+}
diff --git a/neoforged/src/main/resources/DistantHorizons.mixins.json b/neoforged/src/main/resources/DistantHorizons.mixins.json
new file mode 100644
index 000000000..b30ef91ed
--- /dev/null
+++ b/neoforged/src/main/resources/DistantHorizons.mixins.json
@@ -0,0 +1,24 @@
+{
+ "required": true,
+ "minVersion": "0.8",
+ "package": "com.seibel.distanthorizons.forge.mixins",
+ "mixins": [
+ "server.unsafe.MixinThreadingDetector",
+ "server.MixinUtilBackgroundThread",
+ "server.MixinChunkGenerator",
+ "server.MixinTFChunkGenerator"
+ ],
+ "client": [
+ "client.MixinClientPacketListener",
+ "client.MixinDebugScreenOverlay",
+ "client.MixinFogRenderer",
+ "client.MixinGameRenderer",
+ "client.MixinLevelRenderer",
+ "client.MixinDynamicTexture",
+ "client.MixinLightTexture",
+ "client.MixinOptionsScreen",
+ "client.MixinTextureUtil"
+ ],
+ "server": [],
+ "plugin": "com.seibel.distanthorizons.forge.mixins.ForgeMixinPlugin"
+}
diff --git a/neoforged/src/main/resources/META-INF/mods.toml b/neoforged/src/main/resources/META-INF/mods.toml
new file mode 100644
index 000000000..c7e5b0193
--- /dev/null
+++ b/neoforged/src/main/resources/META-INF/mods.toml
@@ -0,0 +1,34 @@
+modLoader = "javafml" #//mandatory
+loaderVersion = "*" # // mandatory. Allow all forge versions as we are definding what Minecraft versions we requre later on
+license = "LGPL"
+issueTrackerURL = "${issues}"
+
+
+[[mods]] #//mandatory
+ modId = "distanthorizons" #//mandatory
+ version = "${version}" #//mandatory, gets the version number from jar populated by the build.gradle script
+ displayName = "${mod_name}" #//mandatory
+ authors = ["James Seibel", "Leonardo Amato", "Cola", "coolGi", "Ran", "Leetom"] # Should be done with `$authors`, but architectury complains
+ #//updateJSONURL="https://change.me.example.invalid/updates.json" # A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/
+ displayURL = "${homepage}"
+ description = "${description}" #//mandatory. The description text for the mod
+ logoFile = "logo.png"
+ catalogueImageIcon = "icon.png"
+ credits = "Massive thanks to: Leonardo, Cola, Ran, CoolGi, and Leetom. For their hard work to bring Distant Horizons to where it is today. - James"
+ #// if not set defaults to "false"
+ clientSideOnly = "true"
+ #// if not set side defaults to "BOTH"
+ #// TODO change to "BOTH" when we add server support
+ side = "CLIENT"
+ #// Allow any version to be present (or not) on the server
+ acceptableRemoteVersions = "*"
+
+[[mixins]]
+config = "DistantHorizons.mixins.json"
+
+[[dependencies.distanthorizons]]
+ modId = "minecraft"
+ type = "required"
+ versionRange = "${compatible_forgemc_versions}" # Where we set what version of mc it is avalible for
+ ordering = "NONE"
+ side = "BOTH"
\ No newline at end of file
diff --git a/neoforged/src/main/resources/pack.mcmeta b/neoforged/src/main/resources/pack.mcmeta
new file mode 100644
index 000000000..37621f6f9
--- /dev/null
+++ b/neoforged/src/main/resources/pack.mcmeta
@@ -0,0 +1,10 @@
+{
+ "pack": {
+ "pack_format": 7,
+ "supported_formats": {
+ "min_inclusive": 16,
+ "max_inclusive": 90000
+ },
+ "description": "Distant Horizons"
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index 7a873a6bb..393a31af1 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -4,11 +4,15 @@ pluginManagement {
name "Fabric"
url "https://maven.fabricmc.net/"
}
- // TODO: Stop using Forge for versions with NeoForge
+ // TODO: Stop using Forge for versions with NeoForged
maven {
name "Forge"
url "https://maven.minecraftforge.net/"
}
+ maven {
+ name "Neoforged"
+ url "https://maven.neoforged.net/releases/"
+ }
maven {
name "Architectury (Better Forge because regular Forge is annoying)" // TODO: Once we switch to NeoForge, would it's gradle work better? or will it have Forge's problems in it
url "https://maven.architectury.dev/"
diff --git a/versionProperties/1.20.4.properties b/versionProperties/1.20.4.properties
index 5cec6abec..7c58a8fc8 100644
--- a/versionProperties/1.20.4.properties
+++ b/versionProperties/1.20.4.properties
@@ -1,10 +1,10 @@
# 1.20.4 version
java_version=17
minecraft_version=1.20.4
-parchment_version=1.20.1:2023.09.03
+parchment_version=1.20.2:2023.12.10
compatible_minecraft_versions=["1.20.3", "1.20.4"]
accessWidenerVersion=1_20_2
-builds_for=fabric,forge
+builds_for=fabric,neoforged
# Fabric loader
fabric_loader_version=0.15.1
@@ -36,13 +36,13 @@ fabric_api_version=0.91.2+1.20.4
enable_immersive_portals=0
enable_canvas=0
-# Forge loader
-forge_version=49.0.3
- # Forge mod versions
+# Neoforged loader
+neoforged_version=20.4.49-beta
+ # Neoforged mod versions
starlight_version_forge=
terraforged_version=
- # Forge mod run
+ # Neoforged mod run
# 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client