This commit is contained in:
s809
2023-12-24 19:16:16 +05:00
38 changed files with 2187 additions and 23 deletions
+6 -3
View File
@@ -42,17 +42,20 @@ build:
name: "NightlyBuild_${MC_VER}-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
- Merged/*.jar
- quilt/build/libs/*.jar
- fabric/build/libs/*.jar
- forge/build/libs/*.jar
- quilt/build/libs/*.jar
- neoforged/build/libs/*.jar
exclude:
# TODO: There is a lot of duplicate stuff here, try to maybe make it smaller
- fabric/build/libs/*-all.jar
- fabric/build/libs/*-sources.jar
- forge/build/libs/*-all.jar
- forge/build/libs/*-sources.jar
- quilt/build/libs/*-all.jar
- quilt/build/libs/*-sources.jar
- forge/build/libs/*-all.jar
- forge/build/libs/*-sources.jar
- neoforged/build/libs/*-all.jar
- neoforged/build/libs/*-sources.jar
expire_in: 14 days
when: always
extends: .build_java
+29 -6
View File
@@ -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"
@@ -318,7 +327,7 @@ subprojects { p ->
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
// Fix forge version numbering system as it is weird
// For whatever reason forge uses [1.18, 1.18.1, 1.18.2) instead of the standard ["1.18", "1.18.1", "1.18.2"] which make more sense
// For whatever reason forge uses [1.18, 1.18.1, 1.18.2) instead of the standard ["1.18", "1.18.1", "1.18.2"]
def compatible_forgemc_versions = "${compatible_minecraft_versions}".replaceAll("\"", "").replaceAll("]", ",)")
// println compatible_forgemc_versions
@@ -400,7 +409,7 @@ subprojects { p ->
//// include "${accessWidenerVersion}.distanthorizons.accesswidener"
// Jank solution to remove all unused accesswideners
// The line above would work..., except forge requires the original accesswidener file, meaning we require this jank solution to keep it
// The line above would work..., except that (neo)forge (well, mainly architectury) requires the original accesswidener file, meaning we require this jank solution to keep it
exclude { file ->
if (file.name.contains(".distanthorizons.accesswidener") && file.name != "${accessWidenerVersion}.distanthorizons.accesswidener") {
return true
@@ -446,7 +455,7 @@ subprojects { p ->
}
allprojects { p ->
// Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge")"
// Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")"
// Useful later on so we dont have duplicated code
def isMinecraftSubProject = p != project(":core") && p != project(":api")
@@ -461,8 +470,21 @@ allprojects { p ->
// this is the text that appears at the top of the overview (home) page
// and is used when bookmarking a page
javadoc.title = rootProject.mod_name + "-" + project.name
// Some annotations arent "technically" part of the official java standard,
// so we define it ourself here
javadoc {
configure( options ) {
tags(
'todo:X"',
'apiNote:a:API Note:',
'implSpec:a:Implementation Requirements:',
'implNote:a:Implementation Note:'
)
}
}
repositories {
// The central repo
mavenCentral()
@@ -527,6 +549,7 @@ allprojects { p ->
includeGroup "forge-mod"
}
}
// TODO: If neoforged is ever needed, should we use that, or call it a forge mod?
}
// Adds some dependencies that are in vanilla but not in core
@@ -39,7 +39,9 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper;
import net.minecraft.client.multiplayer.ClientLevel;
#if MC_VER > MC_1_17_1
import net.minecraft.core.Holder;
#endif
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
@@ -288,11 +290,13 @@ public class WrapperFactory implements IWrapperFactory
{
String[] expectedClassNames;
#if MC_VER <= MC_1_20_4
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
expectedClassNames = new String[] { Biome.class.getName() };
#elif MC_VER <= MC_1_20_4
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
#else
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
#endif
return createWrapperErrorMessage("BlockState wrapper", expectedClassNames, objectArray);
+1 -1
View File
@@ -28,7 +28,7 @@ loom {
extraAccessWideners.add loom.accessWidenerPath.get().asFile.name
mixinConfigs = [
"DistantHorizons.mixins.json"
"DistantHorizons.forge.mixins.json"
]
}
@@ -1,5 +1,6 @@
package com.seibel.distanthorizons.forge.mixins;
import net.minecraft.client.ClientBrandRetriever;
import net.minecraftforge.fml.ModList;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
@@ -18,6 +19,9 @@ public class ForgeMixinPlugin implements IMixinConfigPlugin
@Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName)
{
if (!ClientBrandRetriever.getClientModName().equals("forge"))
return false;
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(
@@ -38,8 +38,10 @@ 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 class MixinDynamicTexture implements ILightTextureMarker
public abstract class MixinDynamicTexture implements ILightTextureMarker
{
/** Used to prevent accidentally using other dynamic textures as a lightmap */
@Unique
+2 -1
View File
@@ -26,7 +26,8 @@ issueTrackerURL = "${issues}"
[[dependencies.distanthorizons]]
modId = "minecraft"
mandatory = true
mandatory = true # Forge syntax
type = "required" # Neoforged syntax
versionRange = "${compatible_forgemc_versions}" # Where we set what version of mc it is avalible for
ordering = "NONE"
side = "BOTH"
+149
View File
@@ -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()
// }
//}
+1
View File
@@ -0,0 +1 @@
loom.platform=neoForge
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged;
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);
}
}
}
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged;
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.neoforged.wrappers.ForgeDependencySetup;
import com.seibel.distanthorizons.neoforged.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. <br>
* 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<BakedQuad> 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);
}
}
@@ -0,0 +1,125 @@
package com.seibel.distanthorizons.neoforged;
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<Boolean> 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); }
}
@@ -0,0 +1,75 @@
package com.seibel.distanthorizons.neoforged.mixins;
import net.minecraft.client.ClientBrandRetriever;
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 NeoforgedMixinPlugin implements IMixinConfigPlugin
{
@Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName)
{
if (!ClientBrandRetriever.getClientModName().equals("neoforge"))
return false;
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<String> myTargets, Set<String> otherTargets)
{
}
@Override
public List<String> 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)
{
}
}
@@ -0,0 +1,29 @@
package com.seibel.distanthorizons.neoforged.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();
}
}
@@ -0,0 +1,23 @@
package com.seibel.distanthorizons.neoforged.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<List<String>> cir)
{
List<String> messages = cir.getReturnValue();
F3Screen.addStringToDisplay(messages);
}
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged.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; }
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged.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
}
}
}
@@ -0,0 +1,58 @@
package com.seibel.distanthorizons.neoforged.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
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged.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. <br><br>
*
* 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();
}
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged.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 = "<init>", at = @At("RETURN"))
public void markLightTexture(CallbackInfo ci) { ((ILightTextureMarker) this.lightTexture).markLightTexture(); }
}
@@ -0,0 +1,95 @@
package com.seibel.distanthorizons.neoforged.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 = "<init>(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();
}
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged.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
}
}
@@ -0,0 +1,31 @@
package com.seibel.distanthorizons.neoforged.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);
}
}
}
@@ -0,0 +1,156 @@
package com.seibel.distanthorizons.neoforged.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<Level> 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<Level> key = resourceKey;
fakeLevelWrapper = new FakeLevelWrapper(levelStorage, worldGenSettings, key);
dhServerLevel = new DhServerLevel(saveStructure, fakeLevelWrapper);
}
}
#endif
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged.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
@@ -0,0 +1,61 @@
package com.seibel.distanthorizons.neoforged.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
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged.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<ExecutorService> 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<Runnable> 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<Supplier<?>> ci)
{
if (shouldApplyOverride())
{
//ApiShared.LOGGER.info("util wrapThreadWithTaskName(Supplier) triggered");
ci.setReturnValue(r);
}
}
#endif
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged.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 = "<init>", 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
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged.wrappers;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.neoforged.wrappers.modAccessor.ModChecker;
/**
* Binds all necessary dependencies so we
* can access them in Core. <br>
* 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();
}
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged.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);
}
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforged.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<DhChunkPos> getNormalRenderedChunks()
{
// TODO: Impl proper methods here
return null;
}
}
@@ -0,0 +1,24 @@
{
"required": true,
"minVersion": "0.8",
"package": "com.seibel.distanthorizons.neoforged.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.neoforged.mixins.NeoforgedMixinPlugin"
}
@@ -0,0 +1,37 @@
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 = "*"
# TODO: Once there is a way to move this to the `META-INF/MANIFEST.MF` with architectury, DO SO!
# (currently, this only works cus neoforge's mods.toml is added to the jar after forge's mods.toml, so this can work
[[mixins]]
config = "DistantHorizons.neoforged.mixins.json"
[[dependencies.distanthorizons]]
modId = "minecraft"
mandatory = true # Forge syntax
type = "required" # Neoforged syntax
versionRange = "${compatible_forgemc_versions}" # Where we set what version of mc it is avalible for
ordering = "NONE"
side = "BOTH"
+10
View File
@@ -0,0 +1,10 @@
{
"pack": {
"pack_format": 7,
"supported_formats": {
"min_inclusive": 16,
"max_inclusive": 90000
},
"description": "Distant Horizons"
}
}
+8 -2
View File
@@ -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/"
@@ -84,7 +88,9 @@ project(":api").projectDir = file('coreSubProjects/api')
include("common")
// Enables or disables the subprojects depending on whats in the versionProperties/mcVer.properties
for (loader in ((String) gradle.builds_for).split(",")) {
include(loader.strip()) // Strip it in case a space is added before or after the comma
def l = loader.strip() // Strip it in case a space is added before or after the comma
println "Adding loader " + l
include(l)
}
//if (gradle.builds_for.contains("fabric") || gradle.builds_for.contains("quilt"))
// include("fabricLike")
+6 -5
View File
@@ -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,forge,neoforged
# Fabric loader
fabric_loader_version=0.15.1
@@ -36,13 +36,14 @@ fabric_api_version=0.91.2+1.20.4
enable_immersive_portals=0
enable_canvas=0
# Forge loader
# (Neo)Forge loader
forge_version=49.0.3
# Forge mod versions
neoforged_version=20.4.49-beta
# (Neo)Forge mod versions
starlight_version_forge=
terraforged_version=
# Forge mod run
# (Neo)Forge 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