Implement ConfigValue as a wrapper around ForgeConfigSpec results (#5361)

This commit is contained in:
tterrag 2019-01-15 23:28:40 -05:00 committed by GitHub
parent 755eff7415
commit cd21f7b927
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 459 additions and 333 deletions

View file

@ -5,7 +5,7 @@
this.func_110148_a(SharedMonsterAttributes.field_111264_e).func_111128_a(3.0D);
this.func_110148_a(SharedMonsterAttributes.field_188791_g).func_111128_a(2.0D);
- this.func_110140_aT().func_111150_b(field_110186_bp).func_111128_a(this.field_70146_Z.nextDouble() * (double)0.1F);
+ this.func_110140_aT().func_111150_b(field_110186_bp).func_111128_a(this.field_70146_Z.nextDouble() * net.minecraftforge.common.ForgeConfig.GENERAL.zombieBaseSummonChance());
+ this.func_110140_aT().func_111150_b(field_110186_bp).func_111128_a(this.field_70146_Z.nextDouble() * net.minecraftforge.common.ForgeConfig.GENERAL.zombieBaseSummonChance.get());
}
protected void func_70088_a() {
@ -52,7 +52,7 @@
this.func_98053_h(this.field_70146_Z.nextFloat() < 0.55F * f);
if (p_204210_2_ == null) {
- p_204210_2_ = new EntityZombie.GroupData(this.field_70170_p.field_73012_v.nextFloat() < 0.05F);
+ p_204210_2_ = new EntityZombie.GroupData(this.field_70170_p.field_73012_v.nextFloat() < net.minecraftforge.common.ForgeConfig.GENERAL.zombieBabyChance());
+ p_204210_2_ = new EntityZombie.GroupData(this.field_70170_p.field_73012_v.nextFloat() < net.minecraftforge.common.ForgeConfig.GENERAL.zombieBabyChance.get());
}
if (p_204210_2_ instanceof EntityZombie.GroupData) {

View file

@ -104,7 +104,7 @@
this.func_187273_a(this.field_187282_b.func_72688_a().func_175625_s(blockpos));
}
- } else if (this.field_187287_g == 64) {
+ } else if (this.field_187287_g >= net.minecraftforge.common.ForgeConfig.GENERAL.clumpingThreshold()) {
+ } else if (this.field_187287_g >= net.minecraftforge.common.ForgeConfig.GENERAL.clumpingThreshold.get()) {
this.func_187267_a(new SPacketChunkData(this.field_187286_f, this.field_187288_h));
+ //TODO: Fix Mojang's fuckup to modded by combining all TE data into the chunk data packet... seriously... packet size explosion!
} else {

View file

@ -403,7 +403,7 @@
entity.func_85029_a(crashreportcategory);
}
+ if (net.minecraftforge.common.ForgeConfig.GENERAL.removeErroringEntities()) {
+ if (net.minecraftforge.common.ForgeConfig.GENERAL.removeErroringEntities.get()) {
+ LogManager.getLogger().fatal("{}", crashreport.func_71502_e());
+ func_72900_e(entity);
+ } else
@ -421,7 +421,7 @@
CrashReport crashreport1 = CrashReport.func_85055_a(throwable1, "Ticking entity");
CrashReportCategory crashreportcategory1 = crashreport1.func_85058_a("Entity being ticked");
entity2.func_85029_a(crashreportcategory1);
+ if (net.minecraftforge.common.ForgeConfig.GENERAL.removeErroringEntities()) {
+ if (net.minecraftforge.common.ForgeConfig.GENERAL.removeErroringEntities.get()) {
+ LogManager.getLogger().fatal("{}", crashreport1.func_71502_e());
+ func_72900_e(entity2);
+ } else
@ -466,7 +466,7 @@
CrashReport crashreport2 = CrashReport.func_85055_a(throwable, "Ticking block entity");
CrashReportCategory crashreportcategory2 = crashreport2.func_85058_a("Block entity being ticked");
tileentity.func_145828_a(crashreportcategory2);
+ if (net.minecraftforge.common.ForgeConfig.GENERAL.removeErroringTileEntities()) {
+ if (net.minecraftforge.common.ForgeConfig.GENERAL.removeErroringTileEntities.get()) {
+ LogManager.getLogger().fatal("{}", crashreport2.func_71502_e());
+ tileentity.func_145843_s();
+ this.func_175713_t(tileentity.func_174877_v());

View file

@ -270,7 +270,7 @@ public class CloudRenderer implements ISelectiveResourceReloadListener
public void checkSettings()
{
boolean newEnabled = ForgeConfig.CLIENT.forgeCloudsEnabled()
boolean newEnabled = ForgeConfig.CLIENT.forgeCloudsEnabled.get()
&& mc.gameSettings.shouldRenderClouds() != 0
&& mc.world != null
&& mc.world.dimension.isSurfaceWorld();

View file

@ -395,7 +395,7 @@ public class DimensionManager
{
int id = queueIterator.nextInt();
DimensionData dimension = dimensions.get(id);
if (dimension.ticksWaited < ForgeConfig.GENERAL.dimensionUnloadQueueDelay())
if (dimension.ticksWaited < ForgeConfig.GENERAL.dimensionUnloadQueueDelay.get())
{
dimension.ticksWaited++;
continue;

View file

@ -448,9 +448,9 @@ public class ForgeChunkManager
return;
}
if (ForgeConfig.CHUNK.dormantChunkCacheSize() != 0)
if (ForgeConfig.CHUNK.dormantChunkCacheSize.get() != 0)
{ // only put into cache if we're using dormant chunk caching
dormantChunkCache.put(world, CacheBuilder.newBuilder().maximumSize(ForgeConfig.CHUNK.dormantChunkCacheSize()).build());
dormantChunkCache.put(world, CacheBuilder.newBuilder().maximumSize(ForgeConfig.CHUNK.dormantChunkCacheSize.get()).build());
}
WorldServer worldServer = (WorldServer) world;
File chunkDir = worldServer.getChunkSaveLocation();
@ -598,7 +598,7 @@ public class ForgeChunkManager
}
forcedChunks.remove(world);
if (ForgeConfig.CHUNK.dormantChunkCacheSize() != 0) // only if in use
if (ForgeConfig.CHUNK.dormantChunkCacheSize.get() != 0) // only if in use
{
dormantChunkCache.remove(world);
}
@ -658,7 +658,7 @@ public class ForgeChunkManager
public static int ticketCountAvailableFor(String username)
{
return ForgeConfig.CHUNK.playerTicketCount() - playerTickets.get(username).size();
return ForgeConfig.CHUNK.playerTicketCount.get() - playerTickets.get(username).size();
}
@Nullable
@ -670,7 +670,7 @@ public class ForgeChunkManager
LOGGER.error(CHUNK_MANAGER, "Failed to locate the container for mod instance {} ({} : {})", mod, mod.getClass().getName(), Integer.toHexString(System.identityHashCode(mod)));
return null;
}
if (playerTickets.get(player).size() > ForgeConfig.CHUNK.playerTicketCount())
if (playerTickets.get(player).size() > ForgeConfig.CHUNK.playerTicketCount.get())
{
LOGGER.warn(CHUNK_MANAGER, "Unable to assign further chunkloading tickets to player {} (on behalf of mod {})", player, mc.getModId());
return null;
@ -919,7 +919,7 @@ public class ForgeChunkManager
public static void putDormantChunk(long coords, Chunk chunk)
{
if (ForgeConfig.CHUNK.dormantChunkCacheSize() == 0) return; // Skip if we're not dormant caching chunks
if (ForgeConfig.CHUNK.dormantChunkCacheSize.get() == 0) return; // Skip if we're not dormant caching chunks
Cache<Long, ChunkEntry> cache = dormantChunkCache.get(chunk.getWorld());
if (cache != null)
{
@ -929,7 +929,7 @@ public class ForgeChunkManager
public static void storeChunkNBT(World world, IChunk ichunk, NBTTagCompound nbt)
{
if (ForgeConfig.CHUNK.dormantChunkCacheSize() == 0) return;
if (ForgeConfig.CHUNK.dormantChunkCacheSize.get() == 0) return;
Cache<Long, ChunkEntry> cache = dormantChunkCache.get(world);
if (cache == null) return;
@ -956,7 +956,7 @@ public class ForgeChunkManager
@Nullable
public static Chunk fetchDormantChunk(long coords, World world)
{
if (ForgeConfig.CHUNK.dormantChunkCacheSize() == 0) return null; // Don't bother with maps at all if its never gonna get a response
if (ForgeConfig.CHUNK.dormantChunkCacheSize.get() == 0) return null; // Don't bother with maps at all if its never gonna get a response
Cache<Long, ChunkEntry> cache = dormantChunkCache.get(world);
if (cache == null) return null;

View file

@ -23,274 +23,273 @@ import static net.minecraftforge.fml.Logging.CORE;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import org.apache.logging.log4j.LogManager;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.core.io.WritingMode;
import com.electronwill.nightconfig.core.CommentedConfig;
import com.google.common.collect.Lists;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.common.ForgeConfigSpec.BooleanValue;
import net.minecraftforge.common.ForgeConfigSpec.ConfigValue;
import net.minecraftforge.common.ForgeConfigSpec.DoubleValue;
import net.minecraftforge.common.ForgeConfigSpec.IntValue;
import net.minecraftforge.fml.loading.FMLPaths;
public class ForgeConfig
{
private static ForgeConfig INSTANCE = new ForgeConfig();
private static ForgeConfigSpec spec = new ForgeConfigSpec.Builder()
//General
.comment("General settings that effect both the client and server")
.push("general")
private static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
public static final General GENERAL = new General(BUILDER);
public static final Client CLIENT = new Client(BUILDER);
public static class General {
public final BooleanValue disableVersionCheck;
public final BooleanValue removeErroringEntities;
public final BooleanValue removeErroringTileEntities;
public final BooleanValue fullBoundingBoxLadders;
public final DoubleValue zombieBaseSummonChance;
public final DoubleValue zombieBabyChance ;
public final BooleanValue logCascadingWorldGeneration;
public final BooleanValue fixVanillaCascading;
public final IntValue dimensionUnloadQueueDelay;
public final IntValue clumpingThreshold;
General(ForgeConfigSpec.Builder builder) {
builder.comment("General settings that effect both the client and server")
.push("general");
disableVersionCheck = builder
.comment("Set to true to disable Forge's version check mechanics. Forge queries a small json file on our server for version information. For more details see the ForgeVersion class in our github.")
.translation("forge.configgui.disableVersionCheck")
.define("disableVersionCheck", false)
.define("disableVersionCheck", false);
removeErroringEntities = builder
.comment("Set this to true to remove any Entity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.")
.translation("forge.configgui.removeErroringEntities")
.worldRestart()
.define("removeErroringEntities", false)
.define("removeErroringEntities", false);
removeErroringTileEntities = builder
.comment("Set this to true to remove any TileEntity that throws an error in its update method instead of closing the server and reporting a crash log. BE WARNED THIS COULD SCREW UP EVERYTHING USE SPARINGLY WE ARE NOT RESPONSIBLE FOR DAMAGES.")
.translation("forge.configgui.removeErroringTileEntities")
.worldRestart()
.define("removeErroringTileEntities", false)
.define("removeErroringTileEntities", false);
fullBoundingBoxLadders = builder
.comment("Set this to true to check the entire entity's collision bounding box for ladders instead of just the block they are in. Causes noticeable differences in mechanics so default is vanilla behavior. Default: false")
.translation("forge.configgui.fullBoundingBoxLadders")
.worldRestart()
.define("fullBoundingBoxLadders", false)
.define("fullBoundingBoxLadders", false);
zombieBaseSummonChance = builder
.comment("Base zombie summoning spawn chance. Allows changing the bonus zombie summoning mechanic.")
.translation("forge.configgui.zombieBaseSummonChance")
.worldRestart()
.defineInRange("zombieBaseSummonChance", 0.1D, 0.0D, 1.0D)
.defineInRange("zombieBaseSummonChance", 0.1D, 0.0D, 1.0D);
zombieBabyChance = builder
.comment("Chance that a zombie (or subclass) is a baby. Allows changing the zombie spawning mechanic.")
.translation("forge.configgui.zombieBabyChance")
.worldRestart()
.defineInRange("zombieBabyChance", 0.05D, 0.0D, 1.0D)
.defineInRange("zombieBabyChance", 0.05D, 0.0D, 1.0D);
logCascadingWorldGeneration = builder
.comment("Log cascading chunk generation issues during terrain population.")
.translation("forge.configgui.logCascadingWorldGeneration")
.define("logCascadingWorldGeneration", true)
.define("logCascadingWorldGeneration", true);
fixVanillaCascading = builder
.comment("Fix vanilla issues that cause worldgen cascading. This DOES change vanilla worldgen so DO NOT report bugs related to world differences if this flag is on.")
.translation("forge.configgui.fixVanillaCascading")
.define("fixVanillaCascading", false)
.define("fixVanillaCascading", false);
dimensionUnloadQueueDelay = builder
.comment("The time in ticks the server will wait when a dimension was queued to unload. This can be useful when rapidly loading and unloading dimensions, like e.g. throwing items through a nether portal a few time per second.")
.translation("forge.configgui.dimensionUnloadQueueDelay")
.defineInRange("dimensionUnloadQueueDelay", 0, 0, Integer.MAX_VALUE)
.defineInRange("dimensionUnloadQueueDelay", 0, 0, Integer.MAX_VALUE);
clumpingThreshold = builder
.comment("Controls the number threshold at which Packet51 is preferred over Packet52, default and minimum 64, maximum 1024")
.translation("forge.configgui.clumpingThreshold")
.worldRestart()
.defineInRange("clumpingThreshold", 64, 64, 1024)
.pop()
.defineInRange("clumpingThreshold", 64, 64, 1024);
//Client
.comment("Client only settings, mostly things related to rendering")
.push("client")
builder.pop();
}
}
public static class Client {
public final BooleanValue zoomInMissingModelTextInGui;
public final BooleanValue forgeCloudsEnabled;
public final BooleanValue disableStairSlabCulling;
public final BooleanValue alwaysSetupTerrainOffThread;
public final BooleanValue forgeLightPipelineEnabled;
public final BooleanValue selectiveResourceReloadEnabled;
Client(ForgeConfigSpec.Builder builder) {
builder.comment("Client only settings, mostly things related to rendering")
.push("client");
zoomInMissingModelTextInGui = builder
.comment("Toggle off to make missing model text in the gui fit inside the slot.")
.translation("forge.configgui.zoomInMissingModelTextInGui")
.define("zoomInMissingModelTextInGui", false)
.define("zoomInMissingModelTextInGui", false);
forgeCloudsEnabled = builder
.comment("Enable uploading cloud geometry to the GPU for faster rendering.")
.translation("forge.configgui.forgeCloudsEnabled")
.define("forgeCloudsEnabled", true)
.define("forgeCloudsEnabled", true);
disableStairSlabCulling = builder
.comment("Disable culling of hidden faces next to stairs and slabs. Causes extra rendering, but may fix some resource packs that exploit this vanilla mechanic.")
.translation("forge.configgui.disableStairSlabCulling")
.define("disableStairSlabCulling", false)
.define("disableStairSlabCulling", false);
alwaysSetupTerrainOffThread = builder
.comment("Enable forge to queue all chunk updates to the Chunk Update thread.",
"May increase FPS significantly, but may also cause weird rendering lag.",
"Not recommended for computers without a significant number of cores available.")
.translation("forge.configgui.alwaysSetupTerrainOffThread")
.define("alwaysSetupTerrainOffThread", false)
.define("alwaysSetupTerrainOffThread", false);
forgeLightPipelineEnabled = builder
.comment("Enable the forge block rendering pipeline - fixes the lighting of custom models.")
.translation("forge.configgui.forgeLightPipelineEnabled")
.define("forgeLightPipelineEnabled", true)
.define("forgeLightPipelineEnabled", true);
selectiveResourceReloadEnabled = builder
.comment("When enabled, makes specific reload tasks such as language changing quicker to run.")
.translation("forge.configgui.selectiveResourceReloadEnabled")
.define("selectiveResourceReloadEnabled", true)
.pop()
.define("selectiveResourceReloadEnabled", true);
builder.pop();
}
}
private static final ForgeConfigSpec spec = BUILDER.build();
private static final ForgeConfigSpec.Builder CHUNK_BUILDER = new ForgeConfigSpec.Builder();
public static final Chunk CHUNK = new Chunk(CHUNK_BUILDER);
public static class Chunk {
public final BooleanValue enable;
public final IntValue chunksPerTicket;
public final IntValue maxTickets;
public final IntValue playerTicketCount;
public final IntValue dormantChunkCacheSize;
public final BooleanValue asyncChunkLoading;
private final ForgeConfigSpec chunkSpec = new ForgeConfigSpec.Builder()
.define("modid", "forge").next()
.defineInRange("maxTickets", 200, 0, Integer.MAX_VALUE).next()
.defineInRange("chunksPerTicket", 25, 0, Integer.MAX_VALUE).next()
.build();
private static ForgeConfigSpec chunk_spec = new ForgeConfigSpec.Builder()
.comment("Default configuration for Forge chunk loading control")
.push("defaults")
private final CommentedConfig modCfgDefault = CommentedConfig.inMemory();
Chunk(ForgeConfigSpec.Builder builder) {
builder.comment("Default configuration for Forge chunk loading control")
.push("defaults");
enable = builder
.comment("Allow mod overrides, false will use default for everything.")
.translation("forge.configgui.enableModOverrides")
.define("enable", true)
.define("enable", true);
chunksPerTicket = builder
.comment("The default maximum number of chunks a mod can force, per ticket,",
"for a mod without an override. This is the maximum number of chunks a single ticket can force.")
.translation("forge.configgui.maximumChunksPerTicket")
.defineInRange("chunksPerTicket", 25, 0, Integer.MAX_VALUE)
.defineInRange("chunksPerTicket", 25, 0, Integer.MAX_VALUE);
maxTickets = builder
.comment("The default maximum ticket count for a mod which does not have an override",
"in this file. This is the number of chunk loading requests a mod is allowed to make.")
.translation("forge.configgui.maximumTicketCount")
.defineInRange("maxTickets", 200, 0, Integer.MAX_VALUE)
.defineInRange("maxTickets", 200, 0, Integer.MAX_VALUE);
playerTicketCount = builder
.comment("The number of tickets a player can be assigned instead of a mod. This is shared across all mods and it is up to the mods to use it.")
.translation("forge.configgui.playerTicketCount")
.defineInRange("playerTicketCount", 500, 0, Integer.MAX_VALUE)
.defineInRange("playerTicketCount", 500, 0, Integer.MAX_VALUE);
dormantChunkCacheSize = builder
.comment("Unloaded chunks can first be kept in a dormant cache for quicker loading times. Specify the size (in chunks) of that cache here")
.translation("forge.configgui.dormantChunkCacheSize")
.defineInRange("dormantChunkCacheSize", 0, 0, Integer.MAX_VALUE)
.defineInRange("dormantChunkCacheSize", 0, 0, Integer.MAX_VALUE);
asyncChunkLoading = builder
.comment("Load chunks asynchronously for players, reducing load on the server thread.",
"Can be disabled to help troubleshoot chunk loading issues.")
.translation("forge.configgui.asyncChunkLoading")
.define("asyncChunkLoading", true)
.pop()
.define("asyncChunkLoading", true);
//Not sure how to do unlimited nested config objects, so doing this for now as it works.
.comment("Per Mod settings. If a mod is missing a entry, or value the default will be used.")
.defineList("mods", () -> {
Map<String, Object> ret = new HashMap<>();
ret.put("modid", ForgeVersion.MOD_ID);
ret.put("maxTickets", 200);
ret.put("chunksPerTicket", 25);
return Arrays.asList(ret);
}, (o) -> {
if (!(o instanceof Map)) return false;
@SuppressWarnings("unchecked")
Map<Object, Object> map = (Map<Object, Object>)o;
for (Entry<Object, Object> e : map.entrySet()) {
if ("modid".equals(e.getKey())) {
if (!(e.getValue() instanceof String))
return false;
} else if ("maxTickets".equals(e.getKey())) {
if (!(e.getValue() instanceof Integer))
return false;
} else if ("chunksPerTicket".equals(e.getKey())) {
if (!(e.getValue() instanceof Integer))
return false;
} else { //Unknown entry
return false;
chunkSpec.correct(modCfgDefault);
builder.pop();
}
private final ConfigValue<List<? extends CommentedConfig>> mods = CHUNK_BUILDER
.defineList("mods", Lists.newArrayList(modCfgDefault), o -> {
if (!(o instanceof CommentedConfig)) return false;
return chunkSpec.isCorrect((CommentedConfig) o);
});
private int getByMod(ConfigValue<Integer> def, String name, String modid) {
if (!enable.get() || modid == null)
return def.get();
return mods.get().stream().filter(c -> modid.equals(c.get("modid"))).findFirst()
.map(c -> c.<Integer>get(name))
.orElseGet(def::get);
}
public int maxTickets(@Nullable String modid) {
return getByMod(maxTickets, "maxTickets", modid);
}
public int chunksPerTicket(@Nullable String modid) {
return getByMod(chunksPerTicket, "chunksPerTicket", modid);
}
}
return true;
})
.build();
private CommentedFileConfig configData;
private CommentedFileConfig chunkData;
private void loadFrom(final Path configRoot) {
public static final ForgeConfigSpec chunk_spec = CHUNK_BUILDER.build();
private static void loadFrom(final Path configRoot) {
Path configFile = configRoot.resolve("forge.toml");
configData = CommentedFileConfig.builder(configFile).sync()
.autosave()
//.autoreload()
.writingMode(WritingMode.REPLACE)
.build();
configData.load();
if (!spec.isCorrect(configData)) {
LogManager.getLogger().warn(CORE, "Configuration file {} is not correct. Correcting", configRoot);
spec.correct(configData, (action, path, incorrectValue, correctedValue) ->
LogManager.getLogger().warn(CORE, "Incorrect key {} was corrected from {} to {}", path, incorrectValue, correctedValue));
configData.save();
}
spec.setConfigFile(configFile);
LogManager.getLogger().debug(CORE, "Loaded Forge config from {}", configFile);
configFile = configRoot.resolve("forge_chunks.toml");
chunkData = CommentedFileConfig.builder(configFile).sync()
.autosave()
//.autoreload()
.writingMode(WritingMode.REPLACE)
.build();
chunkData.load();
if (!chunk_spec.isCorrect(chunkData)) {
LogManager.getLogger().warn(CORE, "Configuration file {} is not correct. Correcting", configRoot);
chunk_spec.correct(chunkData, (action, path, incorrectValue, correctedValue) ->
LogManager.getLogger().warn(CORE, "Incorrect key {} was corrected from {} to {}", path, incorrectValue, correctedValue));
chunkData.save();
}
chunk_spec.setConfigFile(configFile);
LogManager.getLogger().debug(CORE, "Loaded Forge Chunk config from {}", configFile);
}
public static void load() {
INSTANCE.loadFrom(Paths.get("config"));
}
//TODO: Make this less duplciate? Maybe static CfgEntry<T> zombieBaseSummonChance = create((spec, name) -> spec.comment().translation().define(name), "zombieBaseSummonChance")
public static class GENERAL {
public static double zombieBaseSummonChance() {
return ForgeConfig.INSTANCE.configData.<Double>getOrElse("general.zombieBaseSummonChance", (double)0.01F);
}
public static double zombieBabyChance() {
return ForgeConfig.INSTANCE.configData.<Double>getOrElse("general.zombieBabyChance", 0.05D);
}
public static int clumpingThreshold() {
return ForgeConfig.INSTANCE.configData.<Integer>getOrElse("general.clumpingThreshold", 64);
}
public static boolean removeErroringEntities() {
return ForgeConfig.INSTANCE.configData.<Boolean>getOrElse("general.removeErroringEntities", false);
}
public static boolean removeErroringTileEntities() {
return ForgeConfig.INSTANCE.configData.<Boolean>getOrElse("general.removeErroringTileEntities", false);
}
public static boolean fullBoundingBoxLadders() {
return ForgeConfig.INSTANCE.configData.<Boolean>getOrElse("general.fullBoundingBoxLadders", false);
}
public static int dimensionUnloadQueueDelay() {
return ForgeConfig.INSTANCE.configData.<Integer>getOrElse("general.dimensionUnloadQueueDelay", 0);
}
}
public static class CLIENT {
public static boolean forgeCloudsEnabled() {
return ForgeConfig.INSTANCE.configData.<Boolean>getOrElse("general.forgeCloudsEnabled", true);
}
}
public static class CHUNK {
public static boolean enableModOverrides() {
return ForgeConfig.INSTANCE.chunkData.<Boolean>getOrElse("defaults.enable", true);
}
public static int playerTicketCount() {
return ForgeConfig.INSTANCE.chunkData.<Integer>getOrElse("defaults.playerTicketCount", 500);
}
public static int dormantChunkCacheSize() {
return ForgeConfig.INSTANCE.chunkData.<Integer>getOrElse("defaults.dormantChunkCacheSize", 0);
}
private static int maxTickets() {
return ForgeConfig.INSTANCE.chunkData.<Integer>getOrElse("defaults.maxTickets", 200);
}
public static int chunksPerTicket() {
return ForgeConfig.INSTANCE.chunkData.<Integer>getOrElse("defaults.chunksPerTicket", 0);
}
public static int maxTickets(@Nullable String modid) {
if (!enableModOverrides() || modid == null)
return maxTickets();
Map<Object,Object> data = ForgeConfig.INSTANCE.chunkData.<List<Map<Object, Object>>>getOrElse("mods", Collections.emptyList())
.stream().filter(e -> modid.equals(e.get("modid"))).findFirst().orElse(null);
Integer ret = data == null ? null : (Integer)data.get("maxTickets");
return ret == null ? maxTickets() : ret;
}
public static int chunksPerTicket(@Nullable String modid) {
if (!enableModOverrides() || modid == null)
return chunksPerTicket();
Map<Object,Object> data = ForgeConfig.INSTANCE.chunkData.<List<Map<Object, Object>>>getOrElse("mods", Collections.emptyList())
.stream().filter(e -> modid.equals(e.get("modid"))).findFirst().orElse(null);
Integer ret = data == null ? null : (Integer)data.get("chunksPerTicket");
return ret == null ? chunksPerTicket() : ret;
}
loadFrom(FMLPaths.CONFIGDIR.get());
}
//General

View file

@ -22,7 +22,9 @@ package net.minecraftforge.common;
import static com.electronwill.nightconfig.core.ConfigSpec.CorrectionAction.ADD;
import static com.electronwill.nightconfig.core.ConfigSpec.CorrectionAction.REMOVE;
import static com.electronwill.nightconfig.core.ConfigSpec.CorrectionAction.REPLACE;
import static net.minecraftforge.fml.Logging.CORE;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -31,21 +33,30 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.apache.logging.log4j.LogManager;
import com.electronwill.nightconfig.core.CommentedConfig;
import com.electronwill.nightconfig.core.Config;
import com.electronwill.nightconfig.core.InMemoryFormat;
import com.electronwill.nightconfig.core.utils.UnmodifiableConfigWrapper;
import com.electronwill.nightconfig.core.ConfigSpec.CorrectionAction;
import com.electronwill.nightconfig.core.ConfigSpec.CorrectionListener;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.core.file.FileConfig;
import com.electronwill.nightconfig.core.io.WritingMode;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import io.netty.util.BooleanSupplier;
/*
* Like {@link com.electronwill.nightconfig.core.ConfigSpec} except in builder format, and extended to acept comments, language keys,
* and other things Forge configs would find useful.
@ -54,11 +65,37 @@ import com.google.common.collect.Lists;
public class ForgeConfigSpec extends UnmodifiableConfigWrapper<Config>
{
private Map<List<String>, String> levelComments = new HashMap<>();
private Config childConfig;
private ForgeConfigSpec(Config storage, Map<List<String>, String> levelComments) {
super(storage);
this.levelComments = levelComments;
}
public void setConfigFile(Path path) {
setConfig(CommentedFileConfig.builder(path).sync()
.autosave()
//.autoreload()
.writingMode(WritingMode.REPLACE)
.build());
}
public void setConfig(CommentedConfig config) {
this.childConfig = config;
if (config instanceof FileConfig) {
((FileConfig) config).load();
}
if (!isCorrect(config)) {
LogManager.getLogger().warn(CORE, "Configuration file {} is not correct. Correcting", config);
correct(config, (action, path, incorrectValue, correctedValue) ->
LogManager.getLogger().warn(CORE, "Incorrect key {} was corrected from {} to {}", path, incorrectValue, correctedValue));
if (config instanceof FileConfig) {
((FileConfig) config).save();
}
}
}
public boolean isCorrect(CommentedConfig config) {
return correct(this.config, config, null, null, null, true) == 0;
}
@ -66,6 +103,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<Config>
public int correct(CommentedConfig config) {
return correct(config, (action, path, incorrectValue, correctedValue) -> {});
}
public int correct(CommentedConfig config, CorrectionListener listener) {
LinkedList<String> parentPath = new LinkedList<>(); //Linked list for fast add/removes
return correct(this.config, config, parentPath, Collections.unmodifiableList(parentPath), listener, false);
@ -128,7 +166,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<Config>
if (dryRun)
return 1;
Object newValue = valueSpec.getDefault();
Object newValue = valueSpec.correct(configValue);
configMap.put(key, newValue);
listener.onCorrect(action, parentPathUnmodifiable, configValue, newValue);
count++;
@ -166,180 +204,200 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<Config>
return count;
}
public static class Builder
{
private final Config storage = InMemoryFormat.withUniversalSupport().createConfig();
private BuilderContext context = new BuilderContext();
private Map<List<String>, String> levelComments = new HashMap<>();
private List<String> currentPath = new ArrayList<>();
private List<ConfigValue<?>> values = new ArrayList<>();
//Object
public Builder define(String path, Object defaultValue) {
public <T> ConfigValue<T> define(String path, T defaultValue) {
return define(split(path), defaultValue);
}
public Builder define(List<String> path, Object defaultValue) {
public <T> ConfigValue<T> define(List<String> path, T defaultValue) {
return define(path, defaultValue, o -> o != null && defaultValue.getClass().isAssignableFrom(o.getClass()));
}
public Builder define(String path, Object defaultValue, Predicate<Object> validator) {
public <T> ConfigValue<T> define(String path, T defaultValue, Predicate<Object> validator) {
return define(split(path), defaultValue, validator);
}
public Builder define(List<String> path, Object defaultValue, Predicate<Object> validator) {
public <T> ConfigValue<T> define(List<String> path, T defaultValue, Predicate<Object> validator) {
Objects.requireNonNull(defaultValue, "Default value can not be null");
return define(path, () -> defaultValue, validator);
}
public Builder define(String path, Supplier<?> defaultSupplier, Predicate<Object> validator) {
public <T> ConfigValue<T> define(String path, Supplier<T> defaultSupplier, Predicate<Object> validator) {
return define(split(path), defaultSupplier, validator);
}
public Builder define(List<String> path, Supplier<?> defaultSupplier, Predicate<Object> validator) {
public <T> ConfigValue<T> define(List<String> path, Supplier<T> defaultSupplier, Predicate<Object> validator) {
return define(path, defaultSupplier, validator, Object.class);
}
public Builder define(List<String> path, Supplier<?> defaultSupplier, Predicate<Object> validator, Class<?> clazz) { // This is the root where everything at the end of the day ends up.
public <T> ConfigValue<T> define(List<String> path, Supplier<T> defaultSupplier, Predicate<Object> validator, Class<?> clazz) {
context.setClazz(clazz);
return define(path, new ValueSpec(defaultSupplier, validator, context), defaultSupplier);
}
public <T> ConfigValue<T> define(List<String> path, ValueSpec value, Supplier<T> defaultSupplier) { // This is the root where everything at the end of the day ends up.
if (!currentPath.isEmpty()) {
List<String> tmp = new ArrayList<>(currentPath.size() + path.size());
tmp.addAll(currentPath);
tmp.addAll(path);
path = tmp;
}
context.setClazz(clazz);
storage.set(path, new ValueSpec(defaultSupplier, validator, context));
storage.set(path, value);
context = new BuilderContext();
return this;
return new ConfigValue<>(this, path, defaultSupplier);
}
public <V extends Comparable<? super V>> Builder defineInRange(String path, V defaultValue, V min, V max, Class<V> clazz) {
public <V extends Comparable<? super V>> ConfigValue<V> defineInRange(String path, V defaultValue, V min, V max, Class<V> clazz) {
return defineInRange(split(path), defaultValue, min, max, clazz);
}
public <V extends Comparable<? super V>> Builder defineInRange(List<String> path, V defaultValue, V min, V max, Class<V> clazz) {
public <V extends Comparable<? super V>> ConfigValue<V> defineInRange(List<String> path, V defaultValue, V min, V max, Class<V> clazz) {
return defineInRange(path, (Supplier<V>)() -> defaultValue, min, max, clazz);
}
public <V extends Comparable<? super V>> Builder defineInRange(String path, Supplier<V> defaultSupplier, V min, V max, Class<V> clazz) {
public <V extends Comparable<? super V>> ConfigValue<V> defineInRange(String path, Supplier<V> defaultSupplier, V min, V max, Class<V> clazz) {
return defineInRange(split(path), defaultSupplier, min, max, clazz);
}
public <V extends Comparable<? super V>> Builder defineInRange(List<String> path, Supplier<V> defaultSupplier, V min, V max, Class<V> clazz) {
public <V extends Comparable<? super V>> ConfigValue<V> defineInRange(List<String> path, Supplier<V> defaultSupplier, V min, V max, Class<V> clazz) {
Range<V> range = new Range<>(clazz, min, max);
context.setRange(range);
if (min.compareTo(max) > 0)
throw new IllegalArgumentException("Range min most be less then max.");
define(path, defaultSupplier, range);
return this;
return define(path, defaultSupplier, range);
}
public Builder defineInList(String path, Object defaultValue, Collection<?> acceptableValues) {
public <T> ConfigValue<T> defineInList(String path, T defaultValue, Collection<? extends T> acceptableValues) {
return defineInList(split(path), defaultValue, acceptableValues);
}
public Builder defineInList(String path, Supplier<?> defaultSupplier, Collection<?> acceptableValues) {
public <T> ConfigValue<T> defineInList(String path, Supplier<T> defaultSupplier, Collection<? extends T> acceptableValues) {
return defineInList(split(path), defaultSupplier, acceptableValues);
}
public Builder defineInList(List<String> path, Object defaultValue, Collection<?> acceptableValues) {
public <T> ConfigValue<T> defineInList(List<String> path, T defaultValue, Collection<? extends T> acceptableValues) {
return defineInList(path, () -> defaultValue, acceptableValues);
}
public Builder defineInList(List<String> path, Supplier<?> defaultSupplier, Collection<?> acceptableValues) {
public <T> ConfigValue<T> defineInList(List<String> path, Supplier<T> defaultSupplier, Collection<? extends T> acceptableValues) {
return define(path, defaultSupplier, acceptableValues::contains);
}
public Builder defineList(String path, List<?> defaultValue, Predicate<Object> elementValidator) {
public <T> ConfigValue<List<? extends T>> defineList(String path, List<? extends T> defaultValue, Predicate<Object> elementValidator) {
return defineList(split(path), defaultValue, elementValidator);
}
public Builder defineList(String path, Supplier<List<?>> defaultSupplier, Predicate<Object> elementValidator) {
public <T> ConfigValue<List<? extends T>> defineList(String path, Supplier<List<? extends T>> defaultSupplier, Predicate<Object> elementValidator) {
return defineList(split(path), defaultSupplier, elementValidator);
}
public Builder defineList(List<String> path, List<?> defaultValue, Predicate<Object> elementValidator) {
public <T> ConfigValue<List<? extends T>> defineList(List<String> path, List<? extends T> defaultValue, Predicate<Object> elementValidator) {
return defineList(path, () -> defaultValue, elementValidator);
}
public Builder defineList(List<String> path, Supplier<List<?>> defaultSupplier, Predicate<Object> elementValidator) {
return define(path, defaultSupplier, (Object o) -> {
if (!(o instanceof List)) return false;
return ((List<?>)o).stream().allMatch(elementValidator);
});
public <T> ConfigValue<List<? extends T>> defineList(List<String> path, Supplier<List<? extends T>> defaultSupplier, Predicate<Object> elementValidator) {
context.setClazz(List.class);
return define(path, new ValueSpec(defaultSupplier, elementValidator, context) {
@Override
public Object correct(Object value) {
if (value == null || !(value instanceof List) || ((List<?>)value).isEmpty()) {
return getDefault();
}
List<?> list = Lists.newArrayList((List<?>) value);
Iterator<?> iter = list.iterator();
while (iter.hasNext()) {
Object o = iter.next();
if (!elementValidator.test(o)) {
iter.remove();
}
}
if (list.isEmpty()) {
return getDefault();
}
return list;
}
}, defaultSupplier);
}
//Enum
public <V extends Enum<V>> Builder defineEnum(String path, V defaultValue) {
public <V extends Enum<V>> ConfigValue<V> defineEnum(String path, V defaultValue) {
return defineEnum(split(path), defaultValue);
}
public <V extends Enum<V>> Builder defineEnum(List<String> path, V defaultValue) {
public <V extends Enum<V>> ConfigValue<V> defineEnum(List<String> path, V defaultValue) {
return defineEnum(path, defaultValue, defaultValue.getDeclaringClass().getEnumConstants());
}
public <V extends Enum<V>> Builder defineEnum(String path, V defaultValue, @SuppressWarnings("unchecked") V... acceptableValues) {
public <V extends Enum<V>> ConfigValue<V> defineEnum(String path, V defaultValue, @SuppressWarnings("unchecked") V... acceptableValues) {
return defineEnum(split(path), defaultValue, acceptableValues);
}
public <V extends Enum<V>> Builder defineEnum(List<String> path, V defaultValue, @SuppressWarnings("unchecked") V... acceptableValues) {
public <V extends Enum<V>> ConfigValue<V> defineEnum(List<String> path, V defaultValue, @SuppressWarnings("unchecked") V... acceptableValues) {
return defineEnum(path, defaultValue, Arrays.asList(acceptableValues));
}
public <V extends Enum<V>> Builder defineEnum(String path, V defaultValue, Collection<V> acceptableValues) {
public <V extends Enum<V>> ConfigValue<V> defineEnum(String path, V defaultValue, Collection<V> acceptableValues) {
return defineEnum(split(path), defaultValue, acceptableValues);
}
public <V extends Enum<V>> Builder defineEnum(List<String> path, V defaultValue, Collection<V> acceptableValues) {
public <V extends Enum<V>> ConfigValue<V> defineEnum(List<String> path, V defaultValue, Collection<V> acceptableValues) {
return defineEnum(path, defaultValue, acceptableValues::contains);
}
public <V extends Enum<V>> Builder defineEnum(String path, V defaultValue, Predicate<Object> validator) {
public <V extends Enum<V>> ConfigValue<V> defineEnum(String path, V defaultValue, Predicate<Object> validator) {
return defineEnum(split(path), defaultValue, validator);
}
public <V extends Enum<V>> Builder defineEnum(List<String> path, V defaultValue, Predicate<Object> validator) {
public <V extends Enum<V>> ConfigValue<V> defineEnum(List<String> path, V defaultValue, Predicate<Object> validator) {
return defineEnum(path, () -> defaultValue, validator, defaultValue.getDeclaringClass());
}
public <V extends Enum<V>> Builder defineEnum(String path, Supplier<V> defaultSupplier, Predicate<Object> validator, Class<V> clazz) {
public <V extends Enum<V>> ConfigValue<V> defineEnum(String path, Supplier<V> defaultSupplier, Predicate<Object> validator, Class<V> clazz) {
return defineEnum(split(path), defaultSupplier, validator, clazz);
}
public <V extends Enum<V>> Builder defineEnum(List<String> path, Supplier<V> defaultSupplier, Predicate<Object> validator, Class<V> clazz) {
public <V extends Enum<V>> ConfigValue<V> defineEnum(List<String> path, Supplier<V> defaultSupplier, Predicate<Object> validator, Class<V> clazz) {
return define(path, defaultSupplier, validator, clazz);
}
//boolean
public Builder define(String path, boolean defaultValue) {
public BooleanValue define(String path, boolean defaultValue) {
return define(split(path), defaultValue);
}
public Builder define(List<String> path, boolean defaultValue) {
public BooleanValue define(List<String> path, boolean defaultValue) {
return define(path, (Supplier<Boolean>)() -> defaultValue);
}
public Builder define(String path, Supplier<Boolean> defaultSupplier) {
public BooleanValue define(String path, Supplier<Boolean> defaultSupplier) {
return define(split(path), defaultSupplier);
}
public Builder define(List<String> path, Supplier<Boolean> defaultSupplier) {
return define(path, defaultSupplier, o -> {
public BooleanValue define(List<String> path, Supplier<Boolean> defaultSupplier) {
return new BooleanValue(this, define(path, defaultSupplier, o -> {
if (o instanceof String) return ((String)o).equalsIgnoreCase("true") || ((String)o).equalsIgnoreCase("false");
return o instanceof Boolean;
}, Boolean.class);
}, Boolean.class).getPath(), defaultSupplier);
}
//Double
public Builder defineInRange(String path, double defaultValue, double min, double max) {
public DoubleValue defineInRange(String path, double defaultValue, double min, double max) {
return defineInRange(split(path), defaultValue, min, max);
}
public Builder defineInRange(List<String> path, double defaultValue, double min, double max) {
public DoubleValue defineInRange(List<String> path, double defaultValue, double min, double max) {
return defineInRange(path, (Supplier<Double>)() -> defaultValue, min, max);
}
public Builder defineInRange(String path, Supplier<Double> defaultSupplier, double min, double max) {
public DoubleValue defineInRange(String path, Supplier<Double> defaultSupplier, double min, double max) {
return defineInRange(split(path), defaultSupplier, min, max);
}
public Builder defineInRange(List<String> path, Supplier<Double> defaultSupplier, double min, double max) {
return defineInRange(path, defaultSupplier, min, max, Double.class);
public DoubleValue defineInRange(List<String> path, Supplier<Double> defaultSupplier, double min, double max) {
return new DoubleValue(this, defineInRange(path, defaultSupplier, min, max, Double.class).getPath(), defaultSupplier);
}
//Ints
public Builder defineInRange(String path, int defaultValue, int min, int max) {
public IntValue defineInRange(String path, int defaultValue, int min, int max) {
return defineInRange(split(path), defaultValue, min, max);
}
public Builder defineInRange(List<String> path, int defaultValue, int min, int max) {
public IntValue defineInRange(List<String> path, int defaultValue, int min, int max) {
return defineInRange(path, (Supplier<Integer>)() -> defaultValue, min, max);
}
public Builder defineInRange(String path, Supplier<Integer> defaultSupplier, int min, int max) {
public IntValue defineInRange(String path, Supplier<Integer> defaultSupplier, int min, int max) {
return defineInRange(split(path), defaultSupplier, min, max);
}
public Builder defineInRange(List<String> path, Supplier<Integer> defaultSupplier, int min, int max) {
return defineInRange(path, defaultSupplier, min, max, Integer.class);
public IntValue defineInRange(List<String> path, Supplier<Integer> defaultSupplier, int min, int max) {
return new IntValue(this, defineInRange(path, defaultSupplier, min, max, Integer.class).getPath(), defaultSupplier);
}
//Longs
public Builder defineInRange(String path, long defaultValue, long min, long max) {
public LongValue defineInRange(String path, long defaultValue, long min, long max) {
return defineInRange(split(path), defaultValue, min, max);
}
public Builder defineInRange(List<String> path, long defaultValue, long min, long max) {
public LongValue defineInRange(List<String> path, long defaultValue, long min, long max) {
return defineInRange(path, (Supplier<Long>)() -> defaultValue, min, max);
}
public Builder defineInRange(String path, Supplier<Long> defaultSupplier, long min, long max) {
public LongValue defineInRange(String path, Supplier<Long> defaultSupplier, long min, long max) {
return defineInRange(split(path), defaultSupplier, min, max);
}
public Builder defineInRange(List<String> path, Supplier<Long> defaultSupplier, long min, long max) {
return defineInRange(path, defaultSupplier, min, max, Long.class);
public LongValue defineInRange(List<String> path, Supplier<Long> defaultSupplier, long min, long max) {
return new LongValue(this, defineInRange(path, defaultSupplier, min, max, Long.class).getPath(), defaultSupplier);
}
public Builder comment(String comment)
@ -395,7 +453,9 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<Config>
public ForgeConfigSpec build()
{
context.ensureEmpty();
return new ForgeConfigSpec(storage, levelComments);
ForgeConfigSpec ret = new ForgeConfigSpec(storage, levelComments);
values.forEach(v -> v.spec = ret);
return ret;
}
}
@ -486,7 +546,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<Config>
Objects.requireNonNull(supplier, "Default supplier can not be null");
Objects.requireNonNull(validator, "Validator can not be null");
this.comment = LINE_JOINER.join(context.getComment());
this.comment = context.getComment() == null ? null : LINE_JOINER.join(context.getComment());
this.langKey = context.getTranslationKey();
this.range = context.getRange();
this.worldRestart = context.needsWorldRestart();
@ -502,6 +562,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<Config>
public boolean needsWorldRestart() { return this.worldRestart; }
public Class<?> getClazz(){ return this.clazz; }
public boolean test(Object value) { return validator.test(value); }
public Object correct(Object value) { return getDefault(); }
public Object getDefault()
{
@ -511,6 +572,72 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper<Config>
}
}
public static class ConfigValue<T>
{
private final Builder parent;
private final List<String> path;
private final Supplier<T> defaultSupplier;
private ForgeConfigSpec spec;
ConfigValue(Builder parent, List<String> path, Supplier<T> defaultSupplier)
{
this.parent = parent;
this.path = path;
this.defaultSupplier = defaultSupplier;
this.parent.values.add(this);
}
public List<String> getPath()
{
return Lists.newArrayList(path);
}
public T get()
{
Preconditions.checkNotNull(spec, "Cannot get config value before spec is built");
Preconditions.checkNotNull(spec.childConfig, "Cannot get config value without assigned Config object present");
return spec.childConfig.getOrElse(path, defaultSupplier);
}
public Builder next()
{
return parent;
}
}
public static class BooleanValue extends ConfigValue<Boolean>
{
BooleanValue(Builder parent, List<String> path, Supplier<Boolean> defaultSupplier)
{
super(parent, path, defaultSupplier);
}
}
public static class IntValue extends ConfigValue<Integer>
{
IntValue(Builder parent, List<String> path, Supplier<Integer> defaultSupplier)
{
super(parent, path, defaultSupplier);
}
}
public static class LongValue extends ConfigValue<Long>
{
LongValue(Builder parent, List<String> path, Supplier<Long> defaultSupplier)
{
super(parent, path, defaultSupplier);
}
}
public static class DoubleValue extends ConfigValue<Double>
{
DoubleValue(Builder parent, List<String> path, Supplier<Double> defaultSupplier)
{
super(parent, path, defaultSupplier);
}
}
private static final Joiner LINE_JOINER = Joiner.on("\n");
private static final Splitter DOT_SPLITTER = Splitter.on(".");
private static List<String> split(String path)

View file

@ -441,7 +441,7 @@ public class ForgeHooks
{
boolean isSpectator = (entity instanceof EntityPlayer && ((EntityPlayer)entity).isSpectator());
if (isSpectator) return false;
if (!ForgeConfig.GENERAL.fullBoundingBoxLadders())
if (!ForgeConfig.GENERAL.fullBoundingBoxLadders.get())
{
return state.getBlock().isLadder(state, world, pos, entity);
}