Updated everything to latest 1.18.X version
This commit is contained in:
+2
-4
@@ -29,8 +29,6 @@ publishing {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.moandjiezana.toml:toml4j:${rootProject.toml_version}")
|
||||
shadowMe("com.moandjiezana.toml:toml4j:${rootProject.toml_version}") {
|
||||
exclude(module: "gson")
|
||||
}
|
||||
implementation("com.electronwill.night-config:toml:${rootProject.toml_version}")
|
||||
shadowMe("com.electronwill.night-config:toml:${rootProject.toml_version}") {}
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
package com.seibel.lod.common;
|
||||
|
||||
import com.seibel.lod.common.wrappers.config.ConfigGui;
|
||||
import com.seibel.lod.core.config.*;
|
||||
import com.seibel.lod.core.enums.config.*;
|
||||
import com.seibel.lod.core.enums.rendering.*;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton.IClient.IAdvanced.*;
|
||||
@@ -51,82 +52,82 @@ public class Config extends ConfigGui
|
||||
|
||||
// Since the original config system uses forge stuff, that means we have to rewrite the whole config system
|
||||
|
||||
@ScreenEntry
|
||||
@ConfigAnnotations.ScreenEntry
|
||||
public static Client client;
|
||||
|
||||
@Entry
|
||||
@ConfigAnnotations.Entry
|
||||
public static boolean ShowButton = true;
|
||||
|
||||
public static class Client
|
||||
{
|
||||
@Category("client")
|
||||
@ScreenEntry
|
||||
@ConfigAnnotations.Category("client")
|
||||
@ConfigAnnotations.ScreenEntry
|
||||
public static Graphics graphics;
|
||||
|
||||
@Category("client")
|
||||
@ScreenEntry
|
||||
@ConfigAnnotations.Category("client")
|
||||
@ConfigAnnotations.ScreenEntry
|
||||
public static WorldGenerator worldGenerator;
|
||||
|
||||
@Category("client")
|
||||
@ScreenEntry
|
||||
@ConfigAnnotations.Category("client")
|
||||
@ConfigAnnotations.ScreenEntry
|
||||
public static Advanced advanced;
|
||||
|
||||
|
||||
public static class Graphics
|
||||
{
|
||||
@Category("client.graphics")
|
||||
@ScreenEntry
|
||||
@ConfigAnnotations.Category("client.graphics")
|
||||
@ConfigAnnotations.ScreenEntry
|
||||
public static Quality quality;
|
||||
|
||||
@Category("client.graphics")
|
||||
@ScreenEntry
|
||||
@ConfigAnnotations.Category("client.graphics")
|
||||
@ConfigAnnotations.ScreenEntry
|
||||
public static FogQuality fogQuality;
|
||||
|
||||
@Category("client.graphics")
|
||||
@ScreenEntry
|
||||
@ConfigAnnotations.Category("client.graphics")
|
||||
@ConfigAnnotations.ScreenEntry
|
||||
public static AdvancedGraphics advancedGraphics;
|
||||
|
||||
|
||||
public static class Quality
|
||||
{
|
||||
@Category("client.graphics.quality")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.graphics.quality")
|
||||
@ConfigAnnotations.Entry
|
||||
public static HorizontalResolution drawResolution = IQuality.DRAW_RESOLUTION_DEFAULT;
|
||||
|
||||
@Category("client.graphics.quality")
|
||||
@Entry(minValue = 16, maxValue = 1024)
|
||||
@ConfigAnnotations.Category("client.graphics.quality")
|
||||
@ConfigAnnotations.Entry(minValue = 16, maxValue = 1024)
|
||||
public static int lodChunkRenderDistance = IQuality.LOD_CHUNK_RENDER_DISTANCE_MIN_DEFAULT_MAX.defaultValue;
|
||||
|
||||
@Category("client.graphics.quality")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.graphics.quality")
|
||||
@ConfigAnnotations.Entry
|
||||
public static VerticalQuality verticalQuality = IQuality.VERTICAL_QUALITY_DEFAULT;
|
||||
|
||||
@Category("client.graphics.quality")
|
||||
@Entry(minValue = 2, maxValue = 32)
|
||||
@ConfigAnnotations.Category("client.graphics.quality")
|
||||
@ConfigAnnotations.Entry(minValue = 2, maxValue = 32)
|
||||
public static int horizontalScale = IQuality.HORIZONTAL_SCALE_MIN_DEFAULT_MAX.defaultValue;
|
||||
|
||||
@Category("client.graphics.quality")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.graphics.quality")
|
||||
@ConfigAnnotations.Entry
|
||||
public static HorizontalQuality horizontalQuality = IQuality.HORIZONTAL_QUALITY_DEFAULT;
|
||||
}
|
||||
|
||||
|
||||
public static class FogQuality
|
||||
{
|
||||
@Category("client.graphics.fogQuality")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.graphics.fogQuality")
|
||||
@ConfigAnnotations.Entry
|
||||
public static FogDistance fogDistance = IFogQuality.FOG_DISTANCE_DEFAULT;
|
||||
|
||||
@Category("client.graphics.fogQuality")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.graphics.fogQuality")
|
||||
@ConfigAnnotations.Entry
|
||||
public static FogDrawMode fogDrawMode = IFogQuality.FOG_DRAW_MODE_DEFAULT;
|
||||
|
||||
@Category("client.graphics.fogQuality")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.graphics.fogQuality")
|
||||
@ConfigAnnotations.Entry
|
||||
public static FogColorMode fogColorMode = IFogQuality.FOG_COLOR_MODE_DEFAULT;
|
||||
|
||||
@Category("client.graphics.fogQuality")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.graphics.fogQuality")
|
||||
@ConfigAnnotations.Entry
|
||||
public static boolean disableVanillaFog = IFogQuality.DISABLE_VANILLA_FOG_DEFAULT;
|
||||
}
|
||||
|
||||
@@ -134,20 +135,20 @@ public class Config extends ConfigGui
|
||||
public static class AdvancedGraphics
|
||||
{
|
||||
|
||||
@Category("client.graphics.advancedGraphics")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.graphics.advancedGraphics")
|
||||
@ConfigAnnotations.Entry
|
||||
public static boolean disableDirectionalCulling = IAdvancedGraphics.DISABLE_DIRECTIONAL_CULLING_DEFAULT;
|
||||
|
||||
@Category("client.graphics.advancedGraphics")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.graphics.advancedGraphics")
|
||||
@ConfigAnnotations.Entry
|
||||
public static boolean alwaysDrawAtMaxQuality = IAdvancedGraphics.ALWAYS_DRAW_AT_MAD_QUALITY_DEFAULT;
|
||||
|
||||
@Category("client.graphics.advancedGraphics")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.graphics.advancedGraphics")
|
||||
@ConfigAnnotations.Entry
|
||||
public static VanillaOverdraw vanillaOverdraw = IAdvancedGraphics.VANILLA_OVERDRAW_DEFAULT;
|
||||
|
||||
@Category("client.graphics.advancedGraphics")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.graphics.advancedGraphics")
|
||||
@ConfigAnnotations.Entry
|
||||
public static boolean useExtendedNearClipPlane = IAdvancedGraphics.USE_EXTENDED_NEAR_CLIP_PLANE_DEFAULT;
|
||||
}
|
||||
}
|
||||
@@ -155,79 +156,79 @@ public class Config extends ConfigGui
|
||||
|
||||
public static class WorldGenerator
|
||||
{
|
||||
@Category("client.worldGenerator")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.worldGenerator")
|
||||
@ConfigAnnotations.Entry
|
||||
public static GenerationPriority generationPriority = IWorldGenerator.GENERATION_PRIORITY_DEFAULT;
|
||||
|
||||
@Category("client.worldGenerator")
|
||||
@Entry
|
||||
public static DistanceGenerationMode distanceGenerationMode = IWorldGenerator.DISTANCE_GENERATION_MODE_DEFAULT;
|
||||
@ConfigAnnotations.Category("client.worldGenerator")
|
||||
@ConfigAnnotations.Entry
|
||||
public static DistanceGenerationMode distanceGenerationMode = DistanceGenerationMode.FEATURES; //IWorldGenerator.DISTANCE_GENERATION_MODE_DEFAULT;
|
||||
|
||||
// FIXME: Temperary override. In 1.18, the newer Unstable gnerator is more usable
|
||||
@Category("client.worldGenerator")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.worldGenerator")
|
||||
@ConfigAnnotations.Entry
|
||||
public static boolean allowUnstableFeatureGeneration = true;//IWorldGenerator.ALLOW_UNSTABLE_FEATURE_GENERATION_DEFAULT;
|
||||
|
||||
@Category("client.worldGenerator")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.worldGenerator")
|
||||
@ConfigAnnotations.Entry
|
||||
public static BlocksToAvoid blocksToAvoid = IWorldGenerator.BLOCKS_TO_AVOID_DEFAULT;
|
||||
}
|
||||
|
||||
public static class Advanced
|
||||
{
|
||||
@Category("client.advanced")
|
||||
@ScreenEntry
|
||||
@ConfigAnnotations.Category("client.advanced")
|
||||
@ConfigAnnotations.ScreenEntry
|
||||
public static Threading threading;
|
||||
|
||||
@Category("client.advanced")
|
||||
@ScreenEntry
|
||||
@ConfigAnnotations.Category("client.advanced")
|
||||
@ConfigAnnotations.ScreenEntry
|
||||
public static Debugging debugging;
|
||||
|
||||
@Category("client.advanced")
|
||||
@ScreenEntry
|
||||
@ConfigAnnotations.Category("client.advanced")
|
||||
@ConfigAnnotations.ScreenEntry
|
||||
public static Buffers buffers;
|
||||
|
||||
|
||||
public static class Threading
|
||||
{
|
||||
@Category("client.advanced.threading")
|
||||
@Entry(minValue = 1, maxValue = 50)
|
||||
@ConfigAnnotations.Category("client.advanced.threading")
|
||||
@ConfigAnnotations.Entry(minValue = 1, maxValue = 50)
|
||||
public static int numberOfWorldGenerationThreads = IThreading.NUMBER_OF_WORLD_GENERATION_THREADS_DEFAULT.defaultValue;
|
||||
|
||||
@Category("client.advanced.threading")
|
||||
@Entry(minValue = 1, maxValue = 50)
|
||||
@ConfigAnnotations.Category("client.advanced.threading")
|
||||
@ConfigAnnotations.Entry(minValue = 1, maxValue = 50)
|
||||
public static int numberOfBufferBuilderThreads = IThreading.NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX.defaultValue;
|
||||
}
|
||||
|
||||
|
||||
public static class Debugging
|
||||
{
|
||||
@Category("client.advanced.debugging")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.advanced.debugging")
|
||||
@ConfigAnnotations.Entry
|
||||
public static boolean drawLods = IDebugging.DRAW_LODS_DEFAULT;
|
||||
|
||||
@Category("client.advanced.debugging")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.advanced.debugging")
|
||||
@ConfigAnnotations.Entry
|
||||
public static DebugMode debugMode = IDebugging.DEBUG_MODE_DEFAULT;
|
||||
|
||||
@Category("client.advanced.debugging")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.advanced.debugging")
|
||||
@ConfigAnnotations.Entry
|
||||
public static boolean enableDebugKeybindings = IDebugging.DEBUG_KEYBINDINGS_ENABLED_DEFAULT;
|
||||
}
|
||||
|
||||
|
||||
public static class Buffers
|
||||
{
|
||||
@Category("client.advanced.buffers")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.advanced.buffers")
|
||||
@ConfigAnnotations.Entry
|
||||
public static GpuUploadMethod gpuUploadMethod = IBuffers.GPU_UPLOAD_METHOD_DEFAULT;
|
||||
|
||||
@Category("client.advanced.buffers")
|
||||
@Entry(minValue = 0, maxValue = 5000)
|
||||
public static int gpuUploadTimeoutInMilleseconds = IBuffers.GPU_UPLOAD_TIMEOUT_IN_MILLISECONDS_DEFAULT.defaultValue;
|
||||
@ConfigAnnotations.Category("client.advanced.buffers")
|
||||
@ConfigAnnotations.Entry(minValue = 0, maxValue = 5000)
|
||||
public static int gpuUploadPerMegabyteInMilliseconds = IBuffers.GPU_UPLOAD_PER_MEGABYTE_IN_MILLISECONDS_DEFAULT.defaultValue;
|
||||
|
||||
@Category("client.advanced.buffers")
|
||||
@Entry
|
||||
@ConfigAnnotations.Category("client.advanced.buffers")
|
||||
@ConfigAnnotations.Entry
|
||||
public static BufferRebuildTimes rebuildTimes = IBuffers.REBUILD_TIMES_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class BlockColorWrapper implements IBlockColorWrapper
|
||||
{
|
||||
//set of block which require tint
|
||||
public static final ConcurrentMap<Block, BlockColorWrapper> blockColorWrapperMap = new ConcurrentHashMap<>();
|
||||
// public static final ModelDataMap dataMap = new ModelDataMap.Builder().build();
|
||||
// public static final ModelDataMap dataMap = new ModelDataMap.Builder().build();
|
||||
public static final AbstractBlockPosWrapper blockPos = new BlockPosWrapper(0, 0, 0);
|
||||
public static final Random random = new Random(0);
|
||||
//public static BlockColourWrapper WATER_COLOR = getBlockColorWrapper(Blocks.WATER);
|
||||
@@ -100,28 +100,28 @@ public class BlockColorWrapper implements IBlockColorWrapper
|
||||
private void setupColorAndTint()
|
||||
{
|
||||
BlockState blockState = block.defaultBlockState();
|
||||
BlockPosWrapper blockPosWrapper = new BlockPosWrapper();
|
||||
//BlockPosWrapper blockPosWrapper = new BlockPosWrapper();
|
||||
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
|
||||
TextureAtlasSprite texture;
|
||||
List<BakedQuad> quads = null;
|
||||
|
||||
boolean isTinted = false;
|
||||
int listSize = 0;
|
||||
//boolean isTinted = false;
|
||||
//int listSize = 0;
|
||||
|
||||
// first step is to check if this block has a tinted face
|
||||
for (Direction direction : directions)
|
||||
{
|
||||
quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random);
|
||||
listSize = Math.max(listSize, quads.size());
|
||||
for (BakedQuad bakedQuad : quads)
|
||||
{
|
||||
isTinted |= bakedQuad.isTinted();
|
||||
}
|
||||
}
|
||||
//for (Direction direction : directions)
|
||||
//{
|
||||
// quads = mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random);
|
||||
// listSize = Math.max(listSize, quads.size());
|
||||
// for (BakedQuad bakedQuad : quads)
|
||||
// {
|
||||
// isTinted |= bakedQuad.isTinted();
|
||||
// }
|
||||
//}
|
||||
|
||||
//if it contains a tinted face then we store this block in the toTint set
|
||||
if (isTinted)
|
||||
this.toTint = true;
|
||||
//if (isTinted)
|
||||
// this.toTint = true;
|
||||
|
||||
//now we get the first non-empty face
|
||||
for (Direction direction : directions)
|
||||
@@ -154,6 +154,10 @@ public class BlockColorWrapper implements IBlockColorWrapper
|
||||
|
||||
// generate the block's color
|
||||
// for (int frameIndex = 0; frameIndex < texture.getFrameCount(); frameIndex++)
|
||||
boolean lookForTint = false;
|
||||
if (grassInstance() || leavesInstance() || waterIstance())
|
||||
lookForTint = true;
|
||||
|
||||
int frameIndex = 0; // TODO
|
||||
{
|
||||
// textures normally use u and v instead of x and y
|
||||
@@ -161,18 +165,20 @@ public class BlockColorWrapper implements IBlockColorWrapper
|
||||
{
|
||||
for (int v = 0; v < texture.getHeight(); v++)
|
||||
{
|
||||
|
||||
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, frameIndex, u, v);
|
||||
|
||||
if (ColorUtil.getAlpha(TextureAtlasSpriteWrapper.getPixelRGBA(texture, frameIndex, u, v)) == 0)
|
||||
continue;
|
||||
|
||||
// determine if this pixel is gray
|
||||
int colorMax = Math.max(Math.max(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
|
||||
int colorMin = 4 + Math.min(Math.min(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
|
||||
boolean isGray = colorMax < colorMin;
|
||||
if (isGray)
|
||||
numberOfGreyPixel++;
|
||||
if (lookForTint)
|
||||
{
|
||||
// determine if this pixel is gray
|
||||
int colorMax = Math.max(Math.max(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
|
||||
int colorMin = 4 + Math.min(Math.min(ColorUtil.getBlue(tempColor), ColorUtil.getGreen(tempColor)), ColorUtil.getRed(tempColor));
|
||||
boolean isGray = colorMax < colorMin;
|
||||
if (isGray)
|
||||
numberOfGreyPixel++;
|
||||
}
|
||||
|
||||
|
||||
// for flowers, weight their non-green color higher
|
||||
@@ -207,7 +213,7 @@ public class BlockColorWrapper implements IBlockColorWrapper
|
||||
}
|
||||
|
||||
// determine if this block should use the biome color tint
|
||||
if ((grassInstance() || leavesInstance() || waterIstance()) && (float) numberOfGreyPixel / count > 0.75f)
|
||||
if (lookForTint && (float) numberOfGreyPixel / count > 0.75f)
|
||||
this.toTint = true;
|
||||
|
||||
// we check which kind of tint we need to apply
|
||||
@@ -217,7 +223,13 @@ public class BlockColorWrapper implements IBlockColorWrapper
|
||||
|
||||
this.waterTint = waterIstance() && toTint;
|
||||
|
||||
color = tempColor;
|
||||
//hardcoded leaves
|
||||
if (block == Blocks.SPRUCE_LEAVES)
|
||||
color = ColorUtil.multiplyRGBcolors(tempColor, 0xFF619961);
|
||||
else if (block == Blocks.BIRCH_LEAVES)
|
||||
color = ColorUtil.multiplyRGBcolors(tempColor, 0xFF80A755);
|
||||
else
|
||||
color = tempColor;
|
||||
}
|
||||
|
||||
/** determine if the given block should use the biome's grass color */
|
||||
@@ -234,7 +246,7 @@ public class BlockColorWrapper implements IBlockColorWrapper
|
||||
/** determine if the given block should use the biome's foliage color */
|
||||
private boolean leavesInstance()
|
||||
{
|
||||
return block instanceof LeavesBlock
|
||||
return (block instanceof LeavesBlock && block != Blocks.SPRUCE_LEAVES && block != Blocks.BIRCH_LEAVES/* && block != Blocks.AZALEA_LEAVES && block != Blocks.FLOWERING_AZALEA_LEAVES*/)
|
||||
|| block == Blocks.VINE
|
||||
|| block == Blocks.SUGAR_CANE;
|
||||
}
|
||||
|
||||
@@ -155,8 +155,6 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
@Override
|
||||
public int getEmittedBrightness(int x, int y, int z)
|
||||
{
|
||||
BlockPos blockPos = new BlockPos(x,y,z);
|
||||
|
||||
return chunk.getLightEmission(blockPos);
|
||||
return chunk.getLightEmission(new BlockPos(x,y,z));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package com.seibel.lod.common.wrappers.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -21,19 +16,21 @@ import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
// Logger (for debug stuff)
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
// Uses https://github.com/mwanji/toml4j for toml
|
||||
// Uses https://github.com/TheElectronWill/night-config for toml (only for Fabric since Forge allready includes this)
|
||||
|
||||
import com.moandjiezana.toml.Toml;
|
||||
import com.moandjiezana.toml.TomlWriter;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||
|
||||
// Gets info from our own mod
|
||||
|
||||
import com.seibel.lod.common.LodCommonMain;
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.config.*;
|
||||
|
||||
// Minecraft imports
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -50,18 +47,19 @@ import net.minecraft.network.chat.CommonComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.client.resources.language.I18n;
|
||||
import net.minecraft.client.resources.language.I18n; // translation
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
/**
|
||||
* Based upon TinyConfig
|
||||
* https://github.com/Minenash/TinyConfig
|
||||
*
|
||||
* Everything required is packed into 1 class, so it is easier to copy
|
||||
* This config should work for both Fabric and Forge as long as you use Mojang mappings
|
||||
*
|
||||
* Credits to Motschen
|
||||
*
|
||||
* @author coolGi2007
|
||||
* @version 12-24-2021
|
||||
* @version 12-28-2021
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract class ConfigGui
|
||||
@@ -87,12 +85,11 @@ public abstract class ConfigGui
|
||||
|
||||
private static final List<EntryInfo> entries = new ArrayList<>();
|
||||
|
||||
// Chainge these to your own mod
|
||||
// Change these to your own mod
|
||||
private static final String MOD_NAME = ModInfo.NAME; // For file saving and identifying
|
||||
private static final String MOD_NAME_READABLE = ModInfo.READABLE_NAME; // For logs
|
||||
private static Logger LOGGER = ClientApi.LOGGER; // For logs
|
||||
|
||||
private static TomlWriter tomlWriter = new TomlWriter();
|
||||
// private static final Logger LOGGER = ClientApi.LOGGER; // For logs
|
||||
private static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME); // For logs (this inits before ClientAPI so this is a temp fix)
|
||||
|
||||
|
||||
|
||||
@@ -109,7 +106,7 @@ public abstract class ConfigGui
|
||||
public static final int ResetButtonWidth = 40;
|
||||
}
|
||||
|
||||
protected static class EntryInfo
|
||||
protected static class EntryInfo<T>
|
||||
{
|
||||
Field field;
|
||||
Object widget;
|
||||
@@ -129,9 +126,9 @@ public abstract class ConfigGui
|
||||
/** This is only called if button is true */
|
||||
String gotoScreen = "";
|
||||
String category;
|
||||
Class<T> varClass;
|
||||
}
|
||||
|
||||
public static final Map<String, Class<?>> configClass = new HashMap<>();
|
||||
private static Path configFilePath;
|
||||
|
||||
|
||||
@@ -141,68 +138,48 @@ public abstract class ConfigGui
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
configFilePath = mc.gameDirectory.toPath().resolve("config").resolve(MOD_NAME + ".toml");
|
||||
|
||||
initNestedClass(config, "");
|
||||
initNestedClass(config);
|
||||
|
||||
loadFromFile();
|
||||
|
||||
// Save and read the file
|
||||
try
|
||||
{
|
||||
new Toml().read(Files.newBufferedReader(configFilePath)).to(config);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
saveToFile();
|
||||
}
|
||||
|
||||
for (EntryInfo info : entries)
|
||||
{
|
||||
if (info.field.isAnnotationPresent(Entry.class))
|
||||
{
|
||||
try
|
||||
{
|
||||
for (EntryInfo info : entries) {
|
||||
if (info.field.isAnnotationPresent(ConfigAnnotations.Entry.class)) {
|
||||
try {
|
||||
info.value = info.field.get(null);
|
||||
info.tempValue = info.value.toString();
|
||||
}
|
||||
catch (IllegalAccessException ignored)
|
||||
{
|
||||
} catch (IllegalAccessException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadFromFile();
|
||||
}
|
||||
|
||||
private static void initNestedClass(Class<?> config, String category)
|
||||
private static void initNestedClass(Class<?> config)
|
||||
{
|
||||
String modCategory = MOD_NAME + (!category.isBlank() ? "." + category : "");
|
||||
configClass.put(modCategory, config);
|
||||
for (Field field : config.getFields())
|
||||
{
|
||||
EntryInfo info = new EntryInfo();
|
||||
if (field.isAnnotationPresent(Entry.class) || field.isAnnotationPresent(Comment.class) || field.isAnnotationPresent(ScreenEntry.class))
|
||||
if (field.isAnnotationPresent(ConfigAnnotations.Entry.class) || field.isAnnotationPresent(ConfigAnnotations.Comment.class) || field.isAnnotationPresent(ConfigAnnotations.ScreenEntry.class))
|
||||
{
|
||||
// If putting in your own mod then put your own check for server sided
|
||||
if (!LodCommonMain.serverSided)
|
||||
initClient(field, info);
|
||||
}
|
||||
|
||||
if (field.isAnnotationPresent(Entry.class))
|
||||
if (field.isAnnotationPresent(ConfigAnnotations.Entry.class))
|
||||
{
|
||||
info.varClass = field.getType();
|
||||
try
|
||||
{
|
||||
info.defaultValue = field.get(null);
|
||||
}
|
||||
catch (IllegalAccessException ignored)
|
||||
{
|
||||
}
|
||||
catch (IllegalAccessException ignored) {}
|
||||
}
|
||||
|
||||
if (field.isAnnotationPresent(ScreenEntry.class))
|
||||
{
|
||||
String className = field.getAnnotation(Category.class) != null ? field.getAnnotation(Category.class).value() : "";
|
||||
initNestedClass(field.getType(),
|
||||
(!className.isBlank() ? className + "." : "")
|
||||
+ field.getName());
|
||||
}
|
||||
if (field.isAnnotationPresent(ConfigAnnotations.ScreenEntry.class))
|
||||
initNestedClass(field.getType());
|
||||
|
||||
|
||||
info.field = field;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,16 +187,15 @@ public abstract class ConfigGui
|
||||
private static void initClient(Field field, EntryInfo info)
|
||||
{
|
||||
Class<?> fieldClass = field.getType();
|
||||
Category category = field.getAnnotation(Category.class);
|
||||
Entry entry = field.getAnnotation(Entry.class);
|
||||
ScreenEntry screenEntry = field.getAnnotation(ScreenEntry.class);
|
||||
ConfigAnnotations.Category category = field.getAnnotation(ConfigAnnotations.Category.class);
|
||||
ConfigAnnotations.Entry entry = field.getAnnotation(ConfigAnnotations.Entry.class);
|
||||
ConfigAnnotations.ScreenEntry screenEntry = field.getAnnotation(ConfigAnnotations.ScreenEntry.class);
|
||||
|
||||
if (entry != null)
|
||||
info.width = entry.width();
|
||||
else if (screenEntry != null)
|
||||
info.width = screenEntry.width();
|
||||
|
||||
info.field = field;
|
||||
info.category = category != null ? category.value() : "";
|
||||
|
||||
|
||||
@@ -229,7 +205,6 @@ public abstract class ConfigGui
|
||||
info.name = new TranslatableComponent(entry.name());
|
||||
|
||||
|
||||
|
||||
if (fieldClass == int.class)
|
||||
{
|
||||
// For int
|
||||
@@ -334,21 +309,29 @@ public abstract class ConfigGui
|
||||
/** Grabs what is in the config and puts it in modid.toml */
|
||||
public static void saveToFile()
|
||||
{
|
||||
// If this line fails then delete the modid.toml and start the mod again
|
||||
CommentedFileConfig config = CommentedFileConfig.builder(configFilePath.toFile()).build();
|
||||
|
||||
// First try to create a config file
|
||||
try
|
||||
{
|
||||
try {
|
||||
if (!Files.exists(configFilePath))
|
||||
Files.createFile(configFilePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
catch (Exception e) {
|
||||
LOGGER.info("Failed creating config file for " + MOD_NAME_READABLE + " at the path [" + configFilePath.toString() + "].");
|
||||
e.printStackTrace();
|
||||
}
|
||||
// If this line fails then delete the modid.toml and start the mod again
|
||||
Toml toml = new Toml().read(configFilePath.toFile());
|
||||
|
||||
LOGGER.info("TomlWriter stuff not made yet");
|
||||
config.load();
|
||||
|
||||
for (EntryInfo info : entries) {
|
||||
if (info.field.isAnnotationPresent(ConfigAnnotations.Entry.class)) {
|
||||
config.set((info.category.isBlank() ? "" : info.category + ".") + info.field.getName(), info.value);
|
||||
}
|
||||
}
|
||||
|
||||
config.save();
|
||||
config.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,38 +340,39 @@ public abstract class ConfigGui
|
||||
*/
|
||||
public static void loadFromFile()
|
||||
{
|
||||
Toml toml;
|
||||
try
|
||||
{
|
||||
toml = new Toml().read(configFilePath.toFile());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CommentedFileConfig config = CommentedFileConfig.builder(configFilePath.toFile()).autosave().build();
|
||||
|
||||
// First checks if the config file was already made
|
||||
if (!Files.exists(configFilePath)) {
|
||||
LOGGER.info("Config file not found for " + MOD_NAME_READABLE + ". Creating config...");
|
||||
saveToFile();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
config.load();
|
||||
|
||||
// Puts everything into its variable
|
||||
for (EntryInfo info : entries) {
|
||||
if (info.widget instanceof Map.Entry) { // For enum
|
||||
info.value = toml.getList((info.category != "" ? info.category + "." : "") + info.field.getName());
|
||||
} else if (info.field.getType() == String.class) {
|
||||
info.value = toml.getString((info.category != "" ? info.category + "." : "") + info.field.getName());
|
||||
} else if (info.field.getType() == Double.class) {
|
||||
info.value = toml.getDouble((info.category != "" ? info.category + "." : "") + info.field.getName());
|
||||
} else if (info.field.getType() == Long.class) {
|
||||
info.value = toml.getLong((info.category != "" ? info.category + "." : "") + info.field.getName());
|
||||
} else if (info.field.getType() == List.class) {
|
||||
info.value = toml.getList((info.category != "" ? info.category + "." : "") + info.field.getName());
|
||||
}
|
||||
}*/
|
||||
if (info.field.isAnnotationPresent(ConfigAnnotations.Entry.class)) {
|
||||
String itemPath = (info.category.isEmpty() ? "" : info.category + ".") + info.field.getName();
|
||||
if (config.contains(itemPath)) {
|
||||
if (info.field.getType().isEnum())
|
||||
info.value = config.getEnum(itemPath, info.varClass);
|
||||
else
|
||||
info.value = config.get(itemPath);
|
||||
} else
|
||||
config.set(itemPath, info.value);
|
||||
|
||||
try {
|
||||
info.field.set(null, info.value);
|
||||
} catch (IllegalAccessException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
config.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static Screen getScreen(Screen parent, String category)
|
||||
{
|
||||
return new ConfigScreen(parent, category);
|
||||
@@ -398,7 +382,11 @@ public abstract class ConfigGui
|
||||
{
|
||||
protected ConfigScreen(Screen parent, String category)
|
||||
{
|
||||
super(new TranslatableComponent(MOD_NAME + ".config.title"));
|
||||
super(new TranslatableComponent(
|
||||
I18n.exists(MOD_NAME + ".config" + (category.isBlank()? "." + category : "") + ".title") ?
|
||||
MOD_NAME + ".config.title" :
|
||||
MOD_NAME + ".config" + (category.isBlank() ? "" : "." + category) + ".title")
|
||||
);
|
||||
this.parent = parent;
|
||||
this.category = category;
|
||||
this.translationPrefix = MOD_NAME + ".config.";
|
||||
@@ -415,34 +403,6 @@ public abstract class ConfigGui
|
||||
public void tick()
|
||||
{
|
||||
super.tick();
|
||||
for (EntryInfo info : entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
info.field.set(null, info.value);
|
||||
}
|
||||
catch (IllegalAccessException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadValues()
|
||||
{
|
||||
loadFromFile();
|
||||
|
||||
for (EntryInfo info : entries)
|
||||
{
|
||||
if (info.field.isAnnotationPresent(Entry.class))
|
||||
try
|
||||
{
|
||||
info.value = info.field.get(null);
|
||||
info.tempValue = info.value.toString();
|
||||
}
|
||||
catch (IllegalAccessException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -450,25 +410,14 @@ public abstract class ConfigGui
|
||||
{
|
||||
super.init();
|
||||
if (!reload)
|
||||
loadValues();
|
||||
loadFromFile();
|
||||
|
||||
this.addRenderableWidget(new Button(this.width / 2 - 154, this.height - 28, 150, 20, CommonComponents.GUI_CANCEL, button -> {
|
||||
loadValues();
|
||||
loadFromFile();
|
||||
Objects.requireNonNull(minecraft).setScreen(parent);
|
||||
}));
|
||||
|
||||
Button done = this.addRenderableWidget(new Button(this.width / 2 + 4, this.height - 28, 150, 20, CommonComponents.GUI_DONE, (button) -> {
|
||||
for (EntryInfo info : entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
info.field.set(null, info.value);
|
||||
}
|
||||
catch (IllegalAccessException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
saveToFile();
|
||||
Objects.requireNonNull(minecraft).setScreen(parent);
|
||||
}));
|
||||
@@ -553,17 +502,16 @@ public abstract class ConfigGui
|
||||
this.list.render(matrices, mouseX, mouseY, delta); // Render buttons
|
||||
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 (EntryInfo info : entries) {
|
||||
if (info.category.matches(category) && !info.hideOption) {
|
||||
if (list.getHoveredButton(mouseX,mouseY).isPresent()) {
|
||||
AbstractWidget buttonWidget = list.getHoveredButton(mouseX,mouseY).get();
|
||||
Component text = ButtonEntry.buttonsWithText.get(buttonWidget);
|
||||
TranslatableComponent name = new TranslatableComponent(this.translationPrefix + (info.category != "" ? info.category + "." : "") + info.field.getName());
|
||||
String key = translationPrefix + (info.category != "" ? info.category + "." : "") + info.field.getName() + ".@tooltip";
|
||||
TranslatableComponent name = new TranslatableComponent(this.translationPrefix + (info.category.isBlank() ? "" : info.category + ".") + info.field.getName());
|
||||
String key = translationPrefix + (info.category.isBlank() ? "" : info.category + ".") + info.field.getName() + ".@tooltip";
|
||||
|
||||
if (info.error != null && text.equals(name)) renderTooltip(matrices, info.error.getValue(), mouseX, mouseY);
|
||||
if (info.error != null && text.equals(name)) renderTooltip(matrices, (Component) info.error.getValue(), mouseX, mouseY);
|
||||
else if (I18n.exists(key) && text.equals(name)) {
|
||||
List<Component> list = new ArrayList<>();
|
||||
for (String str : I18n.get(key).split("\n"))
|
||||
@@ -683,56 +631,4 @@ public abstract class ConfigGui
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// annotations //
|
||||
//=============//
|
||||
|
||||
// These could probably be moved into core since they don't rely on any Minecraft code. - James
|
||||
// Better not to since I want everything to be in 1 file. - coolGi
|
||||
|
||||
/** a textField, button, etc. that can be interacted with */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Entry
|
||||
{
|
||||
String name() default "";
|
||||
|
||||
int width() default 150;
|
||||
|
||||
double minValue() default Double.MIN_NORMAL;
|
||||
|
||||
double maxValue() default Double.MAX_VALUE;
|
||||
}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ScreenEntry
|
||||
{
|
||||
String name() default "";
|
||||
|
||||
int width() default 100;
|
||||
}
|
||||
|
||||
|
||||
/** Used when sorting the configs in the menu */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Category
|
||||
{
|
||||
String value();
|
||||
}
|
||||
|
||||
|
||||
/** Makes text (looks like @Entry but dosnt save and has no button */
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Comment
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -451,13 +451,13 @@ public class LodConfigWrapperSingleton implements ILodConfigWrapperSingleton
|
||||
|
||||
|
||||
@Override
|
||||
public int getGpuUploadTimeoutInMilliseconds()
|
||||
public int getGpuUploadPerMegabyteInMilliseconds()
|
||||
{
|
||||
return Config.Client.Advanced.Buffers.gpuUploadTimeoutInMilleseconds;
|
||||
return Config.Client.Advanced.Buffers.gpuUploadPerMegabyteInMilliseconds;
|
||||
}
|
||||
@Override
|
||||
public void setGpuUploadTimeoutInMilliseconds(int newTimeoutInMilliseconds) {
|
||||
Config.Client.Advanced.Buffers.gpuUploadTimeoutInMilleseconds = newTimeoutInMilliseconds;
|
||||
public void setGpuUploadPerMegabyteInMilliseconds(int newMilliseconds) {
|
||||
Config.Client.Advanced.Buffers.gpuUploadPerMegabyteInMilliseconds = newMilliseconds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
"DistantHorizons.config.client.advanced.buffers": "Buffers",
|
||||
"DistantHorizons.config.client.advanced.buffers.gpuUploadMethod": "GPU upload method",
|
||||
"DistantHorizons.config.client.advanced.buffers.gpuUploadMethod.@tooltip": "What method should be used to upload geometry to the GPU?",
|
||||
"DistantHorizons.config.client.advanced.buffers.gpuUploadTimeoutInMilleseconds": "GPU upload timeout (milliseconds)",
|
||||
"DistantHorizons.config.client.advanced.buffers.gpuUploadTimeoutInMilleseconds.@tooltip": "How long should we wait before uploading a buffer to the GPU? \nHelpful resource for frame times: https://fpstoms.com",
|
||||
"DistantHorizons.config.client.advanced.buffers.gpuUploadPerMegabyteInMilliseconds": "GPU upload speed (milliseconds)",
|
||||
"DistantHorizons.config.client.advanced.buffers.gpuUploadPerMegabyteInMilliseconds.@tooltip": "How long should a buffer wait per Megabyte of data uploaded? \nHelpful resource for frame times: https://fpstoms.com",
|
||||
"DistantHorizons.config.client.advanced.buffers.rebuildTimes": "Rebuild times",
|
||||
"DistantHorizons.config.client.advanced.buffers.rebuildTimes.@tooltip": "Rebuild times",
|
||||
"DistantHorizons.config.client.advanced.debugging": "Debug",
|
||||
|
||||
+1
-1
Submodule core updated: 01bfb65d9e...e052a0c96f
+4
-5
@@ -35,10 +35,8 @@ dependencies {
|
||||
exclude(group: "net.fabricmc.fabric-api")
|
||||
}
|
||||
|
||||
implementation("com.moandjiezana.toml:toml4j:${rootProject.toml_version}")
|
||||
shadowMe("com.moandjiezana.toml:toml4j:${rootProject.toml_version}") {
|
||||
exclude(module: "gson")
|
||||
}
|
||||
implementation("com.electronwill.night-config:toml:${rootProject.toml_version}")
|
||||
shadowMe("com.electronwill.night-config:toml:${rootProject.toml_version}") {}
|
||||
|
||||
common(project(path: ":common", configuration: "namedElements")) { transitive false }
|
||||
shadowMe(project(path: ":common", configuration: "transformProductionFabric")) { transitive false }
|
||||
@@ -72,6 +70,7 @@ task copyCommonResources(type: Copy) {
|
||||
runClient {
|
||||
dependsOn(copyCoreResources)
|
||||
dependsOn(copyCommonResources)
|
||||
jvmArgs "-XX:-OmitStackTraceInFastThrow"
|
||||
finalizedBy(deleteResources)
|
||||
}
|
||||
|
||||
@@ -88,7 +87,7 @@ shadowJar {
|
||||
configurations = [project.configurations.shadowMe]
|
||||
relocate 'org.tukaani', 'shaded.tukaani'
|
||||
relocate 'org.apache.commons.compress', 'shaded.apache.commons.compress'
|
||||
relocate 'com.moandjiezana.toml', 'shaded.moandjiezana.toml'
|
||||
relocate 'com.electronwill.nightconfig', 'shaded.electronwill.nightconfig'
|
||||
|
||||
classifier "dev-shadow"
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ public class ClientProxy
|
||||
|
||||
// The debug mode keybinding, which will be registered
|
||||
public static final KeyMapping DebugToggle = KeyBindingHelper.registerKeyBinding(
|
||||
new KeyMapping("key.lod.DebugToggle", GLFW.GLFW_KEY_F4, "key.lod.category"));
|
||||
new KeyMapping("key.lod.DebugToggle", GLFW.GLFW_KEY_F8, "key.lod.category"));
|
||||
|
||||
// The draw toggle keybinding, which will be registered
|
||||
public static final KeyMapping DrawToggle = KeyBindingHelper.registerKeyBinding(
|
||||
|
||||
@@ -54,9 +54,9 @@ public class Main implements ClientModInitializer
|
||||
|
||||
// This loads the mod after minecraft loads which doesn't causes a lot of issues (client sided)
|
||||
public static void init() {
|
||||
LodCommonMain.initConfig();
|
||||
LodCommonMain.startup(null, false);
|
||||
DependencySetup.createInitialBindings();
|
||||
LodCommonMain.initConfig();
|
||||
ClientApi.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
|
||||
|
||||
// Check if this works
|
||||
@@ -65,9 +65,9 @@ public class Main implements ClientModInitializer
|
||||
}
|
||||
|
||||
public static void initServer() {
|
||||
LodCommonMain.initConfig();
|
||||
LodCommonMain.startup(null, true);
|
||||
DependencySetup.createInitialBindings();
|
||||
LodCommonMain.initConfig();
|
||||
ClientApi.LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -29,10 +29,8 @@ dependencies {
|
||||
common(project(path: ":common", configuration: "namedElements")) { transitive false }
|
||||
shadowMe(project(path: ":common", configuration: "transformProductionForge")) { transitive = false }
|
||||
|
||||
implementation("com.moandjiezana.toml:toml4j:${rootProject.toml_version}")
|
||||
forgeDependencies(shadowMe("com.moandjiezana.toml:toml4j:${rootProject.toml_version}") {
|
||||
exclude(module: "gson")
|
||||
})
|
||||
// implementation("com.electronwill.night-config:toml:${rootProject.toml_version}")
|
||||
// forgeDependencies(shadowMe("com.electronwill.night-config:toml:${rootProject.toml_version}") {})
|
||||
|
||||
// forgeDependencies(project(":core")) { transitive false }
|
||||
|
||||
@@ -65,7 +63,7 @@ shadowJar {
|
||||
configurations = [project.configurations.shadowMe]
|
||||
relocate 'org.tukaani', 'shaded.tukaani'
|
||||
relocate 'org.apache.commons.compress', 'shaded.apache.commons.compress'
|
||||
relocate 'com.moandjiezana.toml', 'shaded.moandjiezana.toml'
|
||||
// relocate 'com.electronwill.nightconfig', 'shaded.electronwill.nightconfig'
|
||||
|
||||
classifier "dev-shadow"
|
||||
}
|
||||
|
||||
@@ -64,9 +64,9 @@ public class ForgeMain implements LodForgeMethodCaller
|
||||
private void init(final FMLCommonSetupEvent event)
|
||||
{
|
||||
// make sure the dependencies are set up before the mod needs them
|
||||
LodCommonMain.initConfig();
|
||||
LodCommonMain.startup(this, !FMLLoader.getDist().isClient());
|
||||
ForgeDependencySetup.createInitialBindings();
|
||||
LodCommonMain.initConfig();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ minecraft_version=1.17.1
|
||||
archives_base_name=DistantHorizons
|
||||
mod_version=1.5.4a
|
||||
maven_group=com.seibel.lod
|
||||
toml_version=0.7.2
|
||||
toml_version=3.6.0
|
||||
|
||||
fabric_loader_version=0.11.6
|
||||
fabric_api_version=0.37.1+1.17
|
||||
|
||||
Reference in New Issue
Block a user