Merge branch 'main' into 'new_buildsystem'

Put main into buildscript for updated testing

See merge request jeseibel/minecraft-lod-mod!26
This commit is contained in:
coolGi
2023-01-29 05:12:51 +00:00
29 changed files with 850 additions and 288 deletions
+49 -43
View File
@@ -5,8 +5,8 @@
# What is Distant Horizons?
This mod adds a Level Of Detail (LOD) system to Minecraft.\
This implementation renders simplified chunks outside the normal render distance\
Distant Horizons is a Minecraft mod that adds a Level Of Detail (LOD) system to\
render simplified chunks outside the normal render distance\
allowing for an increased view distance without harming performance.
In other words: this mod lets you see farther without turning your game into a slide show.\
@@ -16,47 +16,42 @@ If you want to see a quick demo, check out a video covering the mod here:
<br>
## Mod and Library Versions
## Minecraft and Library Versions
This branch supports the following versions of Minecraft:
#### 1.19 (WIP)
Supported MC versions: [1.19]\
Forge version: 41.0.19\
Fabric version: 0.14.7\
Fabric API version: 0.55.3+1.19\
Modmenu version: 4.0.0
Forge: 41.0.19\
Fabric: 0.14.7\
Fabric API: 0.55.3+1.19\
Modmenu: 4.0.0
#### 1.18.2
Supported MC versions: [1.18.2]\
Forge version: 40.0.18\
Fabric version: 0.13.3\
Fabric API version: 0.48.0+1.18.2\
Modmenu version: 3.1.0
Forge: 40.0.18\
Fabric: 0.13.3\
Fabric API: 0.48.0+1.18.2\
Modmenu: 3.1.0
#### 1.18.1
Supported MC versions: [1.18.1, 1.18]\
Forge version: 39.1.2\
Fabric version: 0.13.3\
Fabric API version: 0.42.6+1.18\
Modmenu version: 3.0.1
#### 1.18.1, 1.18
Forge: 39.1.2\
Fabric: 0.13.3\
Fabric API: 0.42.6+1.18\
Modmenu: 3.0.1
#### 1.17.1
Supported MC versions: [1.17.1, 1.17]\
Forge version: 37.1.1\
Fabric version: 0.13.2\
Fabric API version: 0.46.1+1.17\
Modmenu version: 2.0.14
#### 1.17.1, 1.17
Forge: 37.1.1\
Fabric: 0.13.2\
Fabric API: 0.46.1+1.17\
Modmenu: 2.0.14
#### 1.16.5
Supported MC versions: [1.16.5, 1.16.4]\
Forge version: 36.2.28\
Fabric vetsion: 0.13.2\
Fabric API version: 0.42.0+1.16\
Modmenu version: 1.16.22
#### 1.16.5, 1.16.5
Forge: 36.2.28\
Fabric: 0.13.2\
Fabric API: 0.42.0+1.16\
Modmenu: 1.16.22
<br><br>
#### Plugin and Library versions
### Plugin and Library versions
Architectury version: 3.4-SNAPSHOT\
Architectury loom version: 0.12.0-SNAPSHOT\
@@ -85,31 +80,42 @@ Java Compiler plugin: Manifold Preprocessor
3. Make sure eclipse has the JDK 17 installed. (This is needed so that eclipse can run minecraft)
4. Import the project into eclipse
## Switching Versions
To switch between active versions, change `mcVer=1.?` in `gradle.properties` file.
To switch between different Minecraft versions, change `mcVer=1.?` in the `gradle.properties` file.
If running in an IDE, to ensure the IDE noticed the version change, run a gradle command to prompt gradle into updating the libs. (In IntellJ you will also need to do a gradle sync if it didn't happen automatically.)
>Note: There may be a `java.nio.file.FileSystemException` thrown when running the command after switching versions. To fix it, either restart your IDE (as your IDE is locking up a file) or use a tool like LockHunter to unlock the linked file(s). (Generally it is a lib file under `common\build\lib`, `forge\build\lib`, or `fabric\build\lib`). If anyone knows how to solve this issue please write a comment on this issue: https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/233
If running in an IDE, to ensure the IDE noticed the version change, run any gradle command to refresh gradle. (In IntellJ you will also need to do a gradle sync if it didn't happen automatically.)
>Note: There may be a `java.nio.file.FileSystemException` thrown when running the command after switching versions. To fix it, either restart your IDE (as your IDE is probably locking a file) or use a tool like LockHunter to unlock the linked file(s). (Generally it is a lib file under `common\build\lib`, `forge\build\lib`, or `fabric\build\lib`). \
> If anyone knows how to solve this issue please let us know here: \
> https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/233
<br>
## Compiling
**From the File Explorer:**
1. Download and extract the zip of the project
Prerequisites:
- JDK 17 or newer
From the File Explorer:
1. Download and extract the project zip
2. Download the core from https://gitlab.com/jeseibel/distant-horizons-core and extract into a folder called `core`
3. Open a terminal emulator in the project folder (On Windows you can type `cmd` in the title bar)
4. Run the commands: `./gradlew assemble`
6. The compiled jar file will be in the folder `Merged`
5. The compiled jar file will be in the folder `Merged`
**If in terminal:**
From the command line:
1. `git clone --recurse-submodules https://gitlab.com/jeseibel/minecraft-lod-mod.git`
2. `cd minecraft-lod-mod`
3. `./gradlew assemble`
4. The compiled jar file will be in the folder `Merged`
>Note: You can add the arg: `-PmcVer=1.?` to tell gradle to build a selected MC version instead of having to manually modify the `gradle.properties` file.
Run tests with: `./gradlew test`
>Note: You can add the arg: `-PmcVer=?` to tell gradle to build a selected MC version instead of having to modify the `gradle.properties` file. \
> Example: `./gradlew assemble -PmcVer=1.18.2`
<Br>
@@ -117,7 +123,7 @@ If running in an IDE, to ensure the IDE noticed the version change, run a gradle
`./gradlew --refresh-dependencies` to refresh local dependencies.
`./gradlew clean` to reset everything (this does not affect your code) and then start the process again.
`./gradlew clean` to delete any compiled code.
## Note to self
@@ -132,7 +138,7 @@ If your IDE fails to auto-detect the source jars when browsing Minecraft classes
<br>
## Useful commands
## Other Useful commands
Run the standalone jar: `./gradlew run`\
Build the standalone jar: `./gradlew core:build`\
@@ -159,5 +165,5 @@ https://github.com/TheElectronWill/night-config
SVG Salamander for SVG's\
https://github.com/blackears/svgSalamander
FlatLaf for theming (Tempory to test stuff)\
FlatLaf for theming (for development testing)\
https://www.formdev.com/flatlaf/
+78
View File
@@ -0,0 +1,78 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the team lead James Seibel through Discord at `@BackSun#4157`. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
@@ -19,9 +19,9 @@
package com.seibel.lod.common.wrappers;
import com.seibel.lod.common.wrappers.gui.ConfigWrapper;
import com.seibel.lod.common.wrappers.gui.LangWrapper;
import com.seibel.lod.common.wrappers.minecraft.MinecraftDedicatedServerWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.IConfigWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILangWrapper;
import com.seibel.lod.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.lod.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.lod.core.IReflectionHandler;
@@ -49,7 +49,7 @@ public class DependencySetup {
public static void createSharedBindings()
{
SingletonInjector.INSTANCE.bind(ILodConfigWrapperSingleton.class, LodConfigWrapperSingleton.INSTANCE); // TODO: Remove
SingletonInjector.INSTANCE.bind(IConfigWrapper.class, ConfigWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(ILangWrapper.class, LangWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IVersionConstants.class, VersionConstants.INSTANCE);
SingletonInjector.INSTANCE.bind(IWrapperFactory.class, WrapperFactory.INSTANCE);
DependencySetupDoneCheck.isDone = true;
@@ -65,6 +65,6 @@ public class DependencySetup {
SingletonInjector.INSTANCE.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IReflectionHandler.class, ReflectionHandler.createSingleton());
SingletonInjector.INSTANCE.bind(IReflectionHandler.class, ReflectionHandler.INSTANCE);
}
}
@@ -19,30 +19,38 @@
package com.seibel.lod.common.wrappers;
import com.seibel.lod.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
import com.seibel.lod.common.wrappers.block.BlockStateWrapper;
import com.seibel.lod.common.wrappers.block.BiomeWrapper;
import com.seibel.lod.common.wrappers.chunk.ChunkWrapper;
import com.seibel.lod.core.level.IDhLevel;
import com.seibel.lod.core.level.IDhServerLevel;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.chunk.ChunkAccess;
import java.io.IOException;
/**
* This handles creating abstract wrapper objects.
*
* @author James Seibel
* @version 11-20-2021
* @version 2022-12-5
*/
public class WrapperFactory implements IWrapperFactory
{
public static final WrapperFactory INSTANCE = new WrapperFactory();
@Override
public AbstractBatchGenerationEnvionmentWrapper createBatchGenerator(IDhLevel targetLevel) {
public AbstractBatchGenerationEnvionmentWrapper createBatchGenerator(IDhLevel targetLevel)
{
if (targetLevel instanceof IDhServerLevel)
{
return new BatchGenerationEnvironment((IDhServerLevel) targetLevel);
@@ -52,19 +60,87 @@ public class WrapperFactory implements IWrapperFactory
throw new IllegalArgumentException("The target level must be a server-side level.");
}
}
@Override
public IBiomeWrapper deserializeBiomeWrapper(String str) throws IOException {
return BiomeWrapper.deserialize(str);
}
public IBiomeWrapper deserializeBiomeWrapper(String str) throws IOException { return BiomeWrapper.deserialize(str); }
@Override
public IBlockStateWrapper deserializeBlockStateWrapper(String str) throws IOException {
return BlockStateWrapper.deserialize(str);
}
public IBlockStateWrapper deserializeBlockStateWrapper(String str) throws IOException { return BlockStateWrapper.deserialize(str); }
@Override
public IBlockStateWrapper getAirBlockStateWrapper() {
return BlockStateWrapper.AIR;
public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; }
/**
* Note: when this is updated for different MC versions, make sure you also update the documentation in
* {@link IDhApiWorldGenerator#generateChunks} and the type list in {@link WrapperFactory#createChunkWrapperErrorMessage}. <br><br>
*
* For full method documentation please see: {@link IWrapperFactory#createChunkWrapper}
* @see IWrapperFactory#createChunkWrapper
*/
public IChunkWrapper createChunkWrapper(Object[] objectArray) throws ClassCastException
{
if (objectArray.length == 1 && objectArray[0] instanceof IChunkWrapper) // alternate option if "instanceof" can't be compiled down to older JRE versions "IChunkWrapper.class.isInstance(objectArray[0])" Feel free to delete this comment if we've compiled the mod for 1.16 or earlier - James
{
try
{
// this path should only happen when called by Distant Horizons code
// API implementors should never hit this path
return (IChunkWrapper) objectArray[0];
}
catch (Exception e)
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
}
// correct number of parameters from the API
else if (objectArray.length == 2)
{
// chunk
if (!(objectArray[0] instanceof ChunkAccess chunk))
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
// light source
if (!(objectArray[1] instanceof LevelReader lightSource))
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
return new ChunkWrapper(chunk, lightSource, /*A DH wrapped level isn't necessary*/null);
}
// incorrect number of parameters from the API
else
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
}
/** Note: when this is updated for different MC versions, make sure you also update the documentation in {@link IDhApiWorldGenerator#generateChunks}. */
private static String createChunkWrapperErrorMessage(Object[] objectArray)
{
StringBuilder message = new StringBuilder(
"Chunk wrapper creation failed. \n" +
"Expected parameters: " +
"[" + ChunkAccess.class.getName() + "], " +
"[" + LevelReader.class.getName() + "]. \n");
if (objectArray.length != 0)
{
message.append("Given parameters: ");
for (Object obj : objectArray)
{
message.append("[").append(obj.getClass().getName()).append("], ");
}
}
else
{
message.append(" No parameters given.");
}
return message.toString();
}
}
@@ -43,7 +43,7 @@ import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
//This class wraps the minecraft BlockPos.Mutable (and BlockPos) class
/** This class wraps the minecraft BlockPos.Mutable (and BlockPos) class */
public class BiomeWrapper implements IBiomeWrapper
{
#if PRE_MC_1_18_2
@@ -106,4 +106,9 @@ public class BiomeWrapper implements IBiomeWrapper
throw new IOException("Failed to deserialize biome wrapper", e);
}
}
@Override
public Object getWrappedMcObject_UNSAFE() { return this.biome; }
}
@@ -12,14 +12,18 @@ import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
public class BlockStateWrapper implements IBlockStateWrapper {
public class BlockStateWrapper implements IBlockStateWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static final BlockStateWrapper AIR = new BlockStateWrapper(null);
public static ConcurrentHashMap<BlockState, BlockStateWrapper> cache = new ConcurrentHashMap<>();
public static BlockStateWrapper fromBlockState(BlockState blockState) {
if (blockState == null || blockState.isAir()) return AIR;
public static BlockStateWrapper fromBlockState(BlockState blockState)
{
if (blockState == null || blockState.isAir())
return AIR;
if (blockState.getFluidState().isEmpty())
return cache.computeIfAbsent(blockState, BlockStateWrapper::new);
else
@@ -65,12 +69,19 @@ public class BlockStateWrapper implements IBlockStateWrapper {
public int hashCode() {
return Objects.hash(blockState);
}
@Override
public Object getWrappedMcObject_UNSAFE() { return this.blockState; }
@Override
public boolean isAir() { return this.isAir(this.blockState); }
public boolean isAir(BlockState blockState) { return blockState == null || blockState.isAir(); }
}
@@ -20,10 +20,7 @@
package com.seibel.lod.common.wrappers.chunk;
import com.seibel.lod.common.wrappers.block.BlockStateWrapper;
import com.seibel.lod.core.pos.DhBlockPos;
import com.seibel.lod.core.pos.DhChunkPos;
import com.seibel.lod.core.util.LevelPosUtil;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
@@ -41,7 +38,6 @@ import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.Heightmap;
import javax.annotation.Nullable;
@@ -126,26 +122,6 @@ public class ChunkWrapper implements IChunkWrapper
return chunk;
}
@Override
public int getChunkPosX(){
return chunk.getPos().x;
}
@Override
public int getChunkPosZ(){
return chunk.getPos().z;
}
@Override
public int getRegionPosX(){
return LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().x, LodUtil.REGION_DETAIL_LEVEL);
}
@Override
public int getRegionPosZ(){
return LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().z, LodUtil.REGION_DETAIL_LEVEL);
}
@Override
public int getMaxY(int x, int z) {
return chunk.getHeight(Heightmap.Types.WORLD_SURFACE, Math.floorMod(x, 16), Math.floorMod(z, 16));
@@ -113,7 +113,7 @@ public abstract class ClassicConfigGUI {
#if PRE_MC_1_19
((EntryInfo) info.guiValue).error = ((ConfigEntry) info).isValid(value) == 0 ? null : new AbstractMap.SimpleEntry<>(editBox, new TextComponent(((ConfigEntry) info).isValid(value) == -1 ?
#else
((EntryInfo) info.guiValue).error = ((ConfigEntry) info).isValid(value) == 0 ? null : new AbstractMap.SimpleEntry<>(editBox, Component.translatable(((ConfigEntry) info).isValid(value) == -1 ?
((EntryInfo) info.guiValue).error = ((ConfigEntry) info).isValidMemoryAddress(value) == 0 ? null : new AbstractMap.SimpleEntry<>(editBox, Component.translatable(((ConfigEntry) info).isValidMemoryAddress(value) == -1 ?
#endif
"§cMinimum " + "length" + (cast ? " is " + (int) ((ConfigEntry) info).getMin() : " is " + ((ConfigEntry) info).getMin()) :
"§cMaximum " + "length" + (cast ? " is " + (int) ((ConfigEntry) info).getMax() : " is " + ((ConfigEntry) info).getMax())));
@@ -130,7 +130,7 @@ public abstract class ClassicConfigGUI {
else
((ConfigEntry) info).setWithoutSaving(value.intValue());
}
// else if (((ConfigEntry) info).isValid() == 0)
// else if (((ConfigEntry) info).isValidMemoryAddress() == 0)
// {
// if (((List<String>) info.get()).size() == ((EntryInfo) info.guiValue).index)
// info.set(((List<String>) info.get()).add(""));
@@ -281,7 +281,7 @@ public abstract class ClassicConfigGUI {
drawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title
// Render the tooltip only if it can find a tooltip in the language file
for (AbstractConfigType info : ConfigBase.INSTANCE.entries) { // idk why this is using the normal entries but as long as it works, it works
for (AbstractConfigType info : ConfigBase.INSTANCE.entries) {
if (info.getCategory().matches(category) && info.getAppearance().showInGui) {
if (list.getHoveredButton(mouseX, mouseY).isPresent()) {
AbstractWidget buttonWidget = list.getHoveredButton(mouseX, mouseY).get();
@@ -1,10 +1,10 @@
package com.seibel.lod.common.wrappers.gui;
import com.seibel.lod.core.wrapperInterfaces.config.IConfigWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILangWrapper;
import net.minecraft.client.resources.language.I18n;
public class ConfigWrapper implements IConfigWrapper {
public static final ConfigWrapper INSTANCE = new ConfigWrapper();
public class LangWrapper implements ILangWrapper {
public static final LangWrapper INSTANCE = new LangWrapper();
@Override
public boolean langExists(String str) {
return I18n.exists(str);
@@ -1,103 +0,0 @@
package com.seibel.lod.common.wrappers.gui;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.jar.updater.SelfUpdater;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.narration.NarratableEntry;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import java.util.*;
public class UpdateModScreen extends Screen {
private Screen parent;
private String newVersion;
#if PRE_MC_1_19
public static net.minecraft.network.chat.TranslatableComponent translate (String str, Object... args) {
return new net.minecraft.network.chat.TranslatableComponent(str, args);
}
#else
public static net.minecraft.network.chat.MutableComponent translate (String str, Object... args) {
return net.minecraft.network.chat.Component.translatable(str, args);
}
#endif
public UpdateModScreen(Screen parent, String newVersion) {
super(translate(ModInfo.ID + ".updater.title"));
this.parent = parent;
this.newVersion = newVersion;
}
@Override
protected void init() {
super.init();
this.addBtn(
new Button(this.width / 2 - 155, this.height / 2, 150, 20, translate(ModInfo.ID + ".updater.update"), (btn) -> {
SelfUpdater.deleteOldOnClose = true;
SelfUpdater.updateMod();
this.onClose();
})
);
this.addBtn(
new Button(this.width / 2 + 5, this.height / 2, 150, 20, translate(ModInfo.ID + ".updater.later"), (btn) -> {
this.onClose();
})
);
this.addBtn(
new Button(this.width / 2 - 155, this.height / 2 + 25, 150, 20, translate(ModInfo.ID + ".updater.never"), (btn) -> {
Config.Client.AutoUpdater.enableAutoUpdater.set(false);
this.onClose();
})
);
this.addBtn(
new Button(this.width / 2 + 5, this.height / 2 + 25, 150, 20, translate(ModInfo.ID + ".updater.silentUpdate"), (btn) -> {
Config.Client.AutoUpdater.promptForUpdate.set(false);
SelfUpdater.updateMod();
this.onClose();
})
);
}
@Override
public void render(PoseStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices); // Render background
// Render the text's
drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text1"), this.width / 2, this.height / 2 - 40, 0xFFFFFF);
drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text2", ModInfo.VERSION, this.newVersion), this.width / 2, this.height / 2 - 25, 0xFFFFFF);
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
// TODO: Add tooltips
}
@Override
public void onClose() {
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
}
// addRenderableWidget in 1.17 and over
// addButton in 1.16 and below
private void addBtn(Button button) {
#if PRE_MC_1_17_1
this.addButton(button);
#else
this.addRenderableWidget(button);
#endif
}
}
@@ -0,0 +1,188 @@
package com.seibel.lod.common.wrappers.gui.updater;
import com.google.common.collect.Lists;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.lod.common.wrappers.gui.ClassicConfigGUI;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.jar.JarUtils;
import com.seibel.lod.core.jar.installer.MarkdownFormatter;
import com.seibel.lod.core.jar.installer.ModrinthGetter;
import com.seibel.lod.core.jar.updater.SelfUpdater;
import net.minecraft.client.Minecraft;
import net.minecraft.client.StringSplitter;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.narration.NarratableEntry;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.FormattedCharSequence;
import java.util.*;
/**
* The screen that pops up if the mod has an update.
*
* @author coolGi
*/
// TODO: After finishing the config, rewrite this in openGL as well
// TODO: Make this
public class ChangelogScreen extends Screen {
private Screen parent;
private String versionID;
private List<String> changelog;
private TextArea changelogArea;
public ChangelogScreen(Screen parent, String versionID) {
super(translate(ModInfo.ID + ".updater.title"));
this.parent = parent;
this.versionID = versionID;
this.changelog = new ArrayList<>();
// Get the release changelog and split it by the new lines
List<String> unwrappedChangelog =
List.of(new MarkdownFormatter.MinecraftFormat().convertTo( // This formats markdown to minecraft's "§" characters
ModrinthGetter.changeLogs.get(versionID)
).split("\\n"));
// Makes the words wrap around to not go off the screen
for (String str: unwrappedChangelog) {
this.changelog.addAll(
MarkdownFormatter.splitString(str, 75)
);
}
// Debugging
// System.out.println(this.changelog);
}
@Override
protected void init() {
super.init();
this.addBtn( // Close
new Button(5, this.height - 25, 100, 20, translate(ModInfo.ID + ".general.back"), (btn) -> {
this.onClose();
})
);
this.changelogArea = new TextArea(this.minecraft, this.width*2, this.height, 32, this.height - 32, 10);
for (int i = 0; i < changelog.size(); i++) {
this.changelogArea.addButton(new TextComponent(changelog.get(i)));
// drawString(matrices, this.font, changelog.get(i), this.width / 2 - 175, this.height / 2 - 100 + i*10, 0xFFFFFF);
}
}
@Override
public void render(PoseStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices); // Render background
// Set the scroll position to the mouse height relative to the screen
this.changelogArea.setScrollAmount(
((double) mouseY)/((double) this.height) * this.changelogArea.getMaxScroll()
);
this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
drawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title
}
@Override
public void onClose() {
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
}
// addRenderableWidget in 1.17 and over
// addButton in 1.16 and below
private void addBtn(Button button) {
#if PRE_MC_1_17_1
this.addButton(button);
#else
this.addRenderableWidget(button);
#endif
}
#if PRE_MC_1_19
public static net.minecraft.network.chat.TranslatableComponent translate (String str, Object... args) {
return new net.minecraft.network.chat.TranslatableComponent(str, args);
}
#else
public static net.minecraft.network.chat.MutableComponent translate (String str, Object... args) {
return net.minecraft.network.chat.Component.translatable(str, args);
}
#endif
public static class TextArea extends ContainerObjectSelectionList<ButtonEntry> {
Font textRenderer;
public TextArea(Minecraft minecraftClient, int i, int j, int k, int l, int m) {
super(minecraftClient, i, j, k, l, m);
this.centerListVertically = false;
textRenderer = minecraftClient.font;
}
public void addButton(Component text) {
this.addEntry(ButtonEntry.create(text));
}
@Override
public int getRowWidth() {
return 10000;
}
}
public static class ButtonEntry extends ContainerObjectSelectionList.Entry<ButtonEntry> {
private static final Font textRenderer = Minecraft.getInstance().font;
private final Component text;
private final List<AbstractWidget> children = new ArrayList<>();
private ButtonEntry(Component text) {
this.text = text;
}
public static ButtonEntry create(Component text) {
return new ButtonEntry(text);
}
@Override
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF);
}
@Override
public List<? extends GuiEventListener> children() {
return children;
}
#if POST_MC_1_17_1
@Override
public List<? extends NarratableEntry> narratables() {
return children;
}
#endif
}
}
@@ -0,0 +1,159 @@
package com.seibel.lod.common.wrappers.gui.updater;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.lod.common.wrappers.gui.TexturedButtonWidget;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.jar.JarUtils;
import com.seibel.lod.core.jar.installer.ModrinthGetter;
import com.seibel.lod.core.jar.updater.SelfUpdater;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.resources.ResourceLocation;
import java.util.*;
/**
* The screen that pops up if the mod has an update.
*
* @author coolGi
*/
// TODO: After finishing the config, rewrite this in openGL as well
// and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192
public class UpdateModScreen extends Screen {
private Screen parent;
private String newVersionID;
public UpdateModScreen(Screen parent, String newVersionID) {
super(translate(ModInfo.ID + ".updater.title"));
this.parent = parent;
this.newVersionID = newVersionID;
}
@Override
protected void init() {
super.init();
try {
// We cannot get assets from the root of the mod so we use this hack
// TODO: Load the icon.png and logo.png in the mod initialise rather than here
ResourceLocation logoLocation = new ResourceLocation(ModInfo.ID, "logo.png");
Minecraft.getInstance().getTextureManager().register(
logoLocation,
new DynamicTexture(NativeImage.read(JarUtils.accessFile("logo.png")))
);
// Logo image
this.addBtn(new ImageButton(
// Where the button is on the screen
this.width / 2 - 65, this.height / 2 - 110,
// Width and height of the button
130, 65,
// Offset
0, 0,
// Some textuary stuff
0, logoLocation, 130, 65,
// Create the button and tell it where to go
// For now it goes to the client option by default
(buttonWidget) -> System.out.println("Nice, you found an easter egg :)"), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
// Add a title to the button
translate(ModInfo.ID + ".updater.title")
));
} catch (Exception e) { e.printStackTrace(); }
this.addBtn(new TexturedButtonWidget(
// Where the button is on the screen
this.width / 2 - 97, this.height / 2 + 8,
// Width and height of the button
20, 20,
// Offset
0, 0,
// Some textuary stuff
0, new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), 20, 20,
// Create the button and tell it where to go
// For now it goes to the client option by default
(buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(new ChangelogScreen(this, this.newVersionID)), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
// Add a title to the button
translate(ModInfo.ID + ".updater.title")
));
this.addBtn( // Update
new Button(this.width / 2 - 75, this.height / 2 + 8, 150, 20, translate(ModInfo.ID + ".updater.update"), (btn) -> {
SelfUpdater.deleteOldOnClose = true;
SelfUpdater.updateMod();
this.onClose();
})
);
this.addBtn( // Silent update
new Button(this.width / 2 - 75, this.height / 2 + 30, 150, 20, translate(ModInfo.ID + ".updater.silent"), (btn) -> {
Config.Client.AutoUpdater.promptForUpdate.set(false);
SelfUpdater.updateMod();
this.onClose();
})
);
this.addBtn( // Later (not now)
new Button(this.width / 2 + 2, this.height / 2 + 70, 100, 20, translate(ModInfo.ID + ".updater.later"), (btn) -> {
this.onClose();
})
);
this.addBtn( // Never
new Button(this.width / 2 - 102, this.height / 2 + 70, 100, 20, translate(ModInfo.ID + ".updater.never"), (btn) -> {
Config.Client.AutoUpdater.enableAutoUpdater.set(false);
this.onClose();
})
);
}
@Override
public void render(PoseStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices); // Render background
// Render the text's
drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text1"), this.width / 2, this.height / 2 - 35, 0xFFFFFF);
drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text2", ModInfo.VERSION, ModrinthGetter.releaseNames.get(this.newVersionID)), this.width / 2, this.height / 2 -20, 0x52FD52);
// TODO: add the tooltips for the buttons
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
// TODO: Add tooltips
}
@Override
public void onClose() {
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
}
// addRenderableWidget in 1.17 and over
// addButton in 1.16 and below
private void addBtn(Button button) {
#if PRE_MC_1_17_1
this.addButton(button);
#else
this.addRenderableWidget(button);
#endif
}
#if PRE_MC_1_19
public static net.minecraft.network.chat.TranslatableComponent translate (String str, Object... args) {
return new net.minecraft.network.chat.TranslatableComponent(str, args);
}
#else
public static net.minecraft.network.chat.MutableComponent translate (String str, Object... args) {
return net.minecraft.network.chat.Component.translatable(str, args);
}
#endif
}
@@ -22,8 +22,10 @@ package com.seibel.lod.common.wrappers.world;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.lod.api.interfaces.world.IDhApiDimensionTypeWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.dimension.DimensionType;
/**
@@ -84,6 +86,22 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
{
return this.dimensionType;
}
@Override
public boolean equals(Object obj)
{
if (obj.getClass() != DimensionTypeWrapper.class)
{
return false;
}
else
{
DimensionTypeWrapper other = (DimensionTypeWrapper) obj;
return other.getDimensionName().equals(this.getDimensionName());
}
}
}
@@ -21,13 +21,13 @@
package com.seibel.lod.common.wrappers.worldGeneration;
import com.seibel.lod.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.lod.common.wrappers.world.ServerLevelWrapper;
import com.seibel.lod.core.level.IDhServerLevel;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.api.enums.config.ELightGenerationMode;
import com.seibel.lod.core.logging.ConfigBasedLogger;
import com.seibel.lod.core.logging.ConfigBasedSpamLogger;
import com.seibel.lod.api.enums.config.EDistanceGenerationMode;
import com.seibel.lod.core.pos.DhChunkPos;
import com.seibel.lod.core.util.objects.EventTimer;
import com.seibel.lod.core.util.LodUtil;
@@ -321,7 +321,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
EVENT_LOGGER.debug("Lod Generate Event: " + e.minPos);
ArrayGridList<ChunkAccess> referencedChunks;
ArrayGridList<ChunkAccess> genChunks;
EDistanceGenerationMode generationMode;
EDhApiDistantGeneratorMode generatorDetail;
LightedWorldGenRegion region;
WorldGenLevelLightEngine lightEngine;
LightGetterAdaptor adaptor;
@@ -362,7 +362,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
region = new LightedWorldGenRegion(params.level, lightEngine, referencedChunks,
ChunkStatus.STRUCTURE_STARTS, refSize/2, e.lightMode, generator);
adaptor.setRegion(region);
e.tParam.makeStructFeat(region, params);
e.threadedParam.makeStructFeat(region, params);
genChunks = new ArrayGridList<>(referencedChunks, RANGE_TO_RANGE_EMPTY_EXTENSION,
referencedChunks.gridSize - RANGE_TO_RANGE_EMPTY_EXTENSION);
generateDirect(e, genChunks, e.target, region);
@@ -370,7 +370,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
}
catch (StepStructureStart.StructStartCorruptedException f)
{
e.tParam.markAsInvalid();
e.threadedParam.markAsInvalid();
throw (RuntimeException)f.getCause();
}
@@ -418,7 +418,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
e.refreshTimeout();
if (PREF_LOGGER.canMaybeLog())
{
e.tParam.perf.recordEvent(e.timer);
e.threadedParam.perf.recordEvent(e.timer);
PREF_LOGGER.infoInc("{}", e.timer);
}
}
@@ -439,27 +439,27 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (step == Steps.Empty)
return;
e.timer.nextEvent("structStart");
stepStructureStart.generateGroup(e.tParam, region, subRange);
stepStructureStart.generateGroup(e.threadedParam, region, subRange);
e.refreshTimeout();
if (step == Steps.StructureStart)
return;
e.timer.nextEvent("structRef");
stepStructureReference.generateGroup(e.tParam, region, subRange);
stepStructureReference.generateGroup(e.threadedParam, region, subRange);
e.refreshTimeout();
if (step == Steps.StructureReference)
return;
e.timer.nextEvent("biome");
stepBiomes.generateGroup(e.tParam, region, subRange);
stepBiomes.generateGroup(e.threadedParam, region, subRange);
e.refreshTimeout();
if (step == Steps.Biomes)
return;
e.timer.nextEvent("noise");
stepNoise.generateGroup(e.tParam, region, subRange);
stepNoise.generateGroup(e.threadedParam, region, subRange);
e.refreshTimeout();
if (step == Steps.Noise)
return;
e.timer.nextEvent("surface");
stepSurface.generateGroup(e.tParam, region, subRange);
stepSurface.generateGroup(e.threadedParam, region, subRange);
e.refreshTimeout();
if (step == Steps.Surface)
return;
@@ -467,7 +467,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (step == Steps.Carvers)
return;
e.timer.nextEvent("feature");
stepFeatures.generateGroup(e.tParam, region, subRange);
stepFeatures.generateGroup(e.threadedParam, region, subRange);
e.refreshTimeout();
}
finally
@@ -527,7 +527,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
}
@Override
public CompletableFuture<Void> generateChunks(int minX, int minZ, int genSize, Steps targetStep, double runTimeRatio, Consumer<IChunkWrapper> resultConsumer) {
public CompletableFuture<Void> generateChunks(int minX, int minZ, int genSize, Steps targetStep, double runTimeRatio, Consumer<IChunkWrapper> resultConsumer)
{
// TODO: Check event overlap via e.tooClose()
GenerationEvent e = GenerationEvent.startEvent(new DhChunkPos(minX, minZ), genSize, this, targetStep, runTimeRatio, resultConsumer);
events.add(e);
@@ -16,7 +16,7 @@
* 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.lod.common.wrappers.worldGeneration;
import java.lang.invoke.MethodHandles;
@@ -41,7 +41,7 @@ public final class GenerationEvent
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
private static int generationFutureDebugIDs = 0;
final int id;
final ThreadedParameters tParam;
final ThreadedParameters threadedParam;
final DhChunkPos minPos;
final int size;
final Steps target;
@@ -52,93 +52,113 @@ public final class GenerationEvent
long timeoutTime = -1;
public CompletableFuture<Void> future = null;
final Consumer<IChunkWrapper> resultConsumer;
public GenerationEvent(DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup,
Steps target, double runTimeRatio, Consumer<IChunkWrapper> resultConsumer) {
inQueueTime = System.nanoTime();
Steps target, double runTimeRatio, Consumer<IChunkWrapper> resultConsumer)
{
this.inQueueTime = System.nanoTime();
this.id = generationFutureDebugIDs++;
this.minPos = minPos;
this.size = size;
this.target = target;
this.tParam = ThreadedParameters.getOrMake(generationGroup.params);
this.threadedParam = ThreadedParameters.getOrMake(generationGroup.params);
this.lightMode = Config.Client.WorldGenerator.lightGenerationMode.get();
this.runTimeRatio = runTimeRatio;
this.resultConsumer = resultConsumer;
}
public static GenerationEvent startEvent(DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup,
Steps target, double runTimeRatio, Consumer<IChunkWrapper> resultConsumer)
Steps target, double runTimeRatio, Consumer<IChunkWrapper> resultConsumer)
{
if (size % 2 == 0) size += 1; // size must be odd for vanilla world gen region to work
if (size % 2 == 0)
{
size += 1; // size must be odd for vanilla world gen regions to work
}
GenerationEvent event = new GenerationEvent(minPos, size, generationGroup, target, runTimeRatio, resultConsumer);
event.future = CompletableFuture.runAsync(() ->
{
long runStartTime = System.nanoTime();
event.timeoutTime = runStartTime;
event.inQueueTime = runStartTime - event.inQueueTime;
event.timer = new EventTimer("setup");
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
try
{
LOGGER.info("generating [{}]", event.minPos);
generationGroup.generateLodFromList(event);
}
finally
{
BatchGenerationEnvironment.isDistantGeneratorThread.remove();
if (!Thread.interrupted() && runTimeRatio < 1.0)
{
long runStartTime = System.nanoTime();
event.timeoutTime = runStartTime;
event.inQueueTime = runStartTime - event.inQueueTime;
event.timer = new EventTimer("setup");
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
try {
generationGroup.generateLodFromList(event);
} finally {
BatchGenerationEnvironment.isDistantGeneratorThread.remove();
if (!Thread.interrupted() && runTimeRatio < 1.0) {
long endTime = System.nanoTime();
try {
long deltaMs = TimeUnit.NANOSECONDS.toMillis(endTime - runStartTime);
Thread.sleep((long) (deltaMs/runTimeRatio - deltaMs));
} catch (InterruptedException ignored) {}
}
long endTime = System.nanoTime();
try
{
long deltaMs = TimeUnit.NANOSECONDS.toMillis(endTime - runStartTime);
Thread.sleep((long) (deltaMs / runTimeRatio - deltaMs));
}
}, generationGroup.executors);
catch (InterruptedException ignored)
{
}
}
}
}, generationGroup.executors);
return event;
}
public boolean isComplete()
{
return future.isDone();
}
public boolean isComplete() { return this.future.isDone(); }
public boolean hasTimeout(int duration, TimeUnit unit)
{
if (timeoutTime == -1) return false;
if (this.timeoutTime == -1)
{
return false;
}
long currentTime = System.nanoTime();
long delta = currentTime - timeoutTime;
long delta = currentTime - this.timeoutTime;
return (delta > TimeUnit.NANOSECONDS.convert(duration, unit));
}
public boolean terminate()
{
LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN=======================");
BatchGenerationEnvironment.threadFactory.dumpAllThreadStacks();
future.cancel(true);
return future.isCancelled();
this.future.cancel(true);
return this.future.isCancelled();
}
public boolean tooClose(int minX, int minZ, int w)
public boolean tooClose(int minX, int minZ, int width)
{
int aMinX = minPos.x;
int aMinZ = minPos.z;
int aSize = size;
int aMinX = this.minPos.x;
int aMinZ = this.minPos.z;
int aSize = this.size;
// Account for required empty chunks in the border
aSize += 1;
w+= 1;
width += 1;
// Do a AABB to AABB intersection test
return (aMinX + aSize >= minX &&
aMinX <= minX + w &&
aMinX <= minX + width &&
aMinZ + aSize >= minZ &&
aMinZ <= minZ + w);
aMinZ <= minZ + width);
}
public void refreshTimeout()
{
timeoutTime = System.nanoTime();
this.timeoutTime = System.nanoTime();
UncheckedInterruptedException.throwIfInterrupted();
}
@Override
public String toString()
{
return id + ":" + size + "@" + minPos + "(" + target + ")";
return this.id + ":" + this.size + "@" + this.minPos + "(" + this.target + ")";
}
}
+52
View File
@@ -0,0 +1,52 @@
## Contributing
Thanks for your interest in contributing to Distant Horizons!
Check out the [Core Wiki](https://gitlab.com/jeseibel/distant-horizons-core/-/wikis/home) for a rough overview of Distant Horizon's project structure.
## Submitting a merge request
We love merge requests from everyone.
By sending a merge request, you agree to abide by the Distant Horizons [Contributor Code of Conduct](code_of_conduct.md). \
Contributions to this project are under the [lesser GPL v3 license](LICENSE.txt) Copyright James Seibel, so please include the [license header](license_header.txt) at the top of any new code files.
1. Fork, then clone the repo: \
`git clone --recurse-submodules https://gitlab.com/jeseibel/minecraft-lod-mod.git`
2. Set up your dev environment: \
`./gradlew build`
3. (Optional) Confirm the tests pass: \
`./gradlew test`
4. (Optional) Confirm the game runs with either Forge or Fabric: \
`./gradlew forge:runClient` \
`./gradlew fabric:runClient`
5. Make your change(s).
6. Add tests (if appropriate).
7. Confirm the tests pass \
`./gradlew test`
8. Confirm the game runs with both Forge **and** Fabric: \
`./gradlew forge:runClient` \
`./gradlew fabric:runClient` \
When running the game, load or generate a world to confirm Distant Horizons initializes correctly.
9. Push to your fork, make sure to include the Core submodule, and submit a [new merge request](https://gitlab.com/jeseibel/minecraft-lod-mod/-/merge_requests/new).
## General Guidelines
* Check the existing issue list to verify that a given [bug](https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/?sort=created_date&state=opened&label_name%5B%5D=Bug&first_page_size=100), [feature](https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/?sort=created_date&state=opened&label_name%5B%5D=Feature&first_page_size=100), or [improvement](https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/?sort=created_date&state=opened&label_name%5B%5D=Improvement&first_page_size=100) hasn't already been submitted.
* Please open an issue if things aren't working as expected.
* Open a merge request to: fix bugs, fix documentations, improve an existing system, or complete a feature.
* When contributing:
* Put any Minecraft independent code in the [Core](https://gitlab.com/jeseibel/distant-horizons-core) repo when possible.
* Comment and format your code so other people can easily understand it.
@@ -24,7 +24,7 @@ import com.seibel.lod.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent;
import com.seibel.lod.common.LodCommonMain;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.DependencyInjection.DhApiEventInjector;
import com.seibel.lod.core.DependencyInjection.ApiEventInjector;
import com.seibel.lod.core.dependencyInjection.ModAccessorInjector;
import com.seibel.lod.core.dependencyInjection.SingletonInjector;
import com.seibel.lod.core.logging.DhLoggerBuilder;
@@ -64,7 +64,7 @@ public class FabricMain
// This loads the mod after minecraft loads which doesn't causes a lot of issues
public static void init()
{
DhApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
LOGGER.info("Initializing Mod");
LodCommonMain.startup(null);
@@ -85,7 +85,7 @@ public class FabricMain
}
LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
DhApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
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
@@ -1,7 +1,8 @@
package com.seibel.lod;
package com.seibel.lod.mixins;
import com.seibel.lod.core.dependencyInjection.ModAccessorInjector;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import net.fabricmc.loader.api.FabricLoader;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
@@ -9,12 +10,22 @@ import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import java.util.List;
import java.util.Set;
/**
* @author coolGi
* @author cortex
*/
public class FabricMixinPlugin implements IMixinConfigPlugin {
@Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
if (mixinClassName.startsWith("mods.sodium")) {
return ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class) != null;
if (mixinClassName.contains(".mods.")) { // If the mixin wants to go into a mod then we check if that mod is loaded or not
return FabricLoader.getInstance().isModLoaded(
mixinClassName
// What these 2 regex's do is get the mod name that we are checking out of the mixinClassName
// Eg. "com.seibel.lod.mixins.mods.sodium.MixinSodiumChunkRenderer" turns into "sodium"
.replaceAll("^.*.mods.", "") // Replaces everything before the mods
.replaceAll("\\..*$", "") // Replaces everything after the mod name
);
}
return true;
}
@@ -1,6 +1,6 @@
package com.seibel.lod.mixins.client;
import com.seibel.lod.common.wrappers.gui.UpdateModScreen;
import com.seibel.lod.common.wrappers.gui.updater.UpdateModScreen;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.dependencyInjection.SingletonInjector;
import com.seibel.lod.core.jar.installer.ModrinthGetter;
@@ -34,7 +34,7 @@ public class MixinMinecraft
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
ModrinthGetter.getLatestNameForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion())
ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion())
));
} else {
instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
@@ -67,7 +67,7 @@ public class MixinOptionsScreen extends Screen {
// 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 screen
// Add a title to the utton
#if PRE_MC_1_19
new TranslatableComponent(ModInfo.ID + ".title")));
#else
@@ -22,9 +22,10 @@ package com.seibel.lod.wrappers.modAccessor;
import java.util.HashSet;
import com.seibel.lod.core.pos.DhChunkPos;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
public class OptifineAccessor implements IOptifineAccessor
public class OptifineAccessor extends AbstractOptifineAccessor
{
@Override
@@ -23,5 +23,5 @@
"injectors": {
"defaultRequire": 1
},
"plugin": "com.seibel.lod.FabricMixinPlugin"
"plugin": "com.seibel.lod.mixins.FabricMixinPlugin"
}
@@ -26,7 +26,7 @@ import com.seibel.lod.common.forge.LodForgeMethodCaller;
import com.seibel.lod.common.wrappers.DependencySetup;
import com.seibel.lod.common.wrappers.gui.GetConfigScreen;
import com.seibel.lod.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.lod.core.DependencyInjection.DhApiEventInjector;
import com.seibel.lod.core.DependencyInjection.ApiEventInjector;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.ReflectionHandler;
import com.seibel.lod.core.dependencyInjection.ModAccessorInjector;
@@ -115,13 +115,13 @@ public class ForgeMain implements LodForgeMethodCaller
private void initCommon()
{
DhApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
LodCommonMain.startup(this);
ForgeDependencySetup.createInitialBindings();
LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
if (ReflectionHandler.instance.optifinePresent()) {
if (ReflectionHandler.INSTANCE.optifinePresent()) {
ModAccessorInjector.INSTANCE.bind(IOptifineAccessor.class, new OptifineAccessor());
}
#if PRE_MC_1_17_1
@@ -140,7 +140,7 @@ public class ForgeMain implements LodForgeMethodCaller
LodCommonMain.initConfig();
LOGGER.info("Mod Post-Initialized");
DhApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
}
private final ModelDataMap dataMap = new ModelDataMap.Builder().build();
@@ -0,0 +1,61 @@
package com.seibel.lod.mixins;
import net.minecraftforge.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.lod.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) {
}
}
@@ -1,6 +1,6 @@
package com.seibel.lod.mixins.client;
import com.seibel.lod.common.wrappers.gui.UpdateModScreen;
import com.seibel.lod.common.wrappers.gui.updater.UpdateModScreen;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.dependencyInjection.SingletonInjector;
import com.seibel.lod.core.jar.installer.ModrinthGetter;
@@ -34,7 +34,7 @@ public class MixinMinecraft
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
ModrinthGetter.getLatestNameForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion())
ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion())
));
} else {
instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
@@ -67,7 +67,7 @@ public class MixinOptionsScreen extends Screen {
// 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 screen
// Add a title to the button
#if PRE_MC_1_19
new TranslatableComponent(ModInfo.ID + ".title")));
#else
@@ -22,9 +22,10 @@ package com.seibel.lod.wrappers.modAccessor;
import java.util.HashSet;
import com.seibel.lod.core.pos.DhChunkPos;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
public class OptifineAccessor implements IOptifineAccessor
public class OptifineAccessor extends AbstractOptifineAccessor
{
@Override
@@ -16,5 +16,6 @@
"client.MixinLightmap",
"client.MixinOptionsScreen"
],
"server": []
"server": [],
"plugin": "com.seibel.lod.mixins.ForgeMixinPlugin"
}