aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergConfigPlugin.java
blob: 7f230de55b58c86a7eef93dca711ac256824e11a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package com.anthonyhilyard.iceberg.compat.configured;

import com.anthonyhilyard.iceberg.config.IcebergConfigSpec;
import com.electronwill.nightconfig.core.AbstractConfig;
import com.electronwill.nightconfig.core.CommentedConfig;
import com.electronwill.nightconfig.core.UnmodifiableConfig;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.google.common.collect.ImmutableList;
import com.mojang.datafixers.util.Pair;
import com.mrcrayfish.configured.Configured;
import com.mrcrayfish.configured.api.ConfigType;
import com.mrcrayfish.configured.api.IConfigEntry;
import com.mrcrayfish.configured.api.IConfigValue;
import com.mrcrayfish.configured.api.IModConfig;
import com.mrcrayfish.configured.impl.forge.ForgeListValue;
import com.mrcrayfish.configured.impl.forge.ForgeValue;
import com.mrcrayfish.configured.util.ConfigHelper;
import net.minecraft.Util;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.config.ModConfigEvent;

import java.nio.file.Path;
import java.util.EnumMap;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.compress.utils.Lists;

public class IcebergConfigPlugin implements IModConfig
{
	protected static final EnumMap<ModConfig.Type, ConfigType> TYPE_RESOLVER = Util.make(new EnumMap<>(ModConfig.Type.class), (map) -> {
		map.put(ModConfig.Type.CLIENT, ConfigType.CLIENT);
		map.put(ModConfig.Type.COMMON, ConfigType.UNIVERSAL);
		map.put(ModConfig.Type.SERVER, ConfigType.WORLD_SYNC);
	});

	protected final ModConfig config;
	protected final List<IcebergValueEntry> allConfigValues;

	public IcebergConfigPlugin(ModConfig config)
	{
		this.config = config;
		this.allConfigValues = getAllConfigValues(config);
	}

	@Override
	public void update(IConfigEntry entry)
	{
		Set<IConfigValue<?>> changedValues = ConfigHelper.getChangedValues(entry);
		if (!changedValues.isEmpty())
		{
			CommentedConfig newConfig = CommentedConfig.copy(this.config.getConfigData());
			changedValues.forEach(value ->
			{
				if (value instanceof ForgeValue<?> forge)
				{
					if (forge instanceof ForgeListValue forgeList)
					{
						Function<List<?>, List<?>> converter = forgeList.getConverter();
						if (converter != null)
						{
							newConfig.set(forge.configValue.getPath(), converter.apply(forgeList.get()));
							return;
						}
					}
					newConfig.set(forge.configValue.getPath(), value.get());
				}
			});
			this.config.getConfigData().putAll(newConfig);
		}

		if (this.getType() == ConfigType.WORLD_SYNC)
		{
			if (!ConfigHelper.isPlayingGame())
			{
				// Unload server configs since still in main menu
				this.config.getHandler().unload(this.config.getFullPath().getParent(), this.config);
				ConfigHelper.setForgeConfigData(this.config, null);
			}
		}
		else if (!changedValues.isEmpty())
		{
			Configured.LOGGER.info("Sending config reloading event for {}", this.config.getFileName());
			this.config.getSpec().afterReload();
			ConfigHelper.fireForgeConfigEvent(this.config, new ModConfigEvent.Reloading(this.config));
		}
	}

	@Override
	public IConfigEntry getRoot()
	{
		return new IcebergFolderEntry(((IcebergConfigSpec) this.config.getSpec()).getValues(), (IcebergConfigSpec) this.config.getSpec());
	}

	@Override
	public ConfigType getType()
	{
		return TYPE_RESOLVER.get(this.config.getType());
	}

	@Override
	public String getFileName()
	{
		return this.config.getFileName();
	}

	@Override
	public String getModId()
	{
		return this.config.getModId();
	}

	@Override
	public void loadWorldConfig(Path path, Consumer<IModConfig> result)
	{
		final CommentedFileConfig data = this.config.getHandler().reader(path).apply(this.config);
		ConfigHelper.setForgeConfigData(this.config, data);
		result.accept(this);
	}

	@Override
	public void stopEditing()
	{
		// Attempts to unload the server config if player simply just went back
		if (this.config != null && this.getType() == ConfigType.WORLD)
		{
			if (!ConfigHelper.isPlayingGame())
			{
				// Unload server configs since still in main menu
				this.config.getHandler().unload(this.config.getFullPath().getParent(), this.config);
				ConfigHelper.setForgeConfigData(this.config, null);
			}
		}
	}

	@Override
	public boolean isChanged()
	{
		// Block world configs since the path is dynamic
		if (ConfigHelper.isWorldConfig(this) && this.config.getConfigData() == null)
		{
			return false;
		}

		// Check if any config value doesn't equal it's default
		return this.allConfigValues.stream().anyMatch(entry -> {
			return !Objects.equals(entry.value.get(), entry.spec.getDefault());
		});
	}

	@Override
	public void restoreDefaults()
	{
		// Block world configs since the path is dynamic
		if (ConfigHelper.isWorldConfig(this) && this.config.getConfigData() == null)
		{
			return;
		}

		// Creates a copy of the config data then pushes all at once to avoid multiple IO ops
		CommentedConfig newConfig = CommentedConfig.copy(this.config.getConfigData());
		this.allConfigValues.forEach(entry -> newConfig.set(entry.value.getPath(), entry.spec.getDefault()));
		this.config.getConfigData().putAll(newConfig);

		// Finally clear cache of all config values
		this.allConfigValues.forEach(pair -> pair.value.clearCache());
	}

	private static List<IcebergValueEntry> getAllConfigValues(ModConfig config)
	{
		IcebergConfigSpec icebergConfigSpec = (IcebergConfigSpec) config.getSpec();
		List<Pair<ForgeConfigSpec.ConfigValue<?>, ForgeConfigSpec.ValueSpec>> values = Lists.newArrayList();
		gatherValuesFromIcebergConfig(icebergConfigSpec.getValues(), icebergConfigSpec, values);
		return ImmutableList.copyOf(values).stream().map(pair -> new IcebergValueEntry(pair.getFirst(), pair.getSecond())).collect(Collectors.toList());
	}

	private static void gatherValuesFromIcebergConfig(UnmodifiableConfig config, IcebergConfigSpec spec, List<Pair<ForgeConfigSpec.ConfigValue<?>, ForgeConfigSpec.ValueSpec>> values)
	{
		config.valueMap().forEach((key, value) ->
		{
			if (value instanceof AbstractConfig)
			{
				gatherValuesFromIcebergConfig((UnmodifiableConfig) value, spec, values);
			}
			else if (value instanceof ForgeConfigSpec.ConfigValue<?> configValue)
			{
				ForgeConfigSpec.ValueSpec valueSpec = spec.getRaw(configValue.getPath());
				values.add(Pair.of(configValue, valueSpec));
			}
		});
	}

	private record IcebergValueEntry(ForgeConfigSpec.ConfigValue<?> value, ForgeConfigSpec.ValueSpec spec) {}
}