Skip to content

Commit 8f2b904

Browse files
authored
Use Forge configs instead of JSON (#10)
1 parent 7acf0cb commit 8f2b904

File tree

6 files changed

+228
-89
lines changed

6 files changed

+228
-89
lines changed

gradle.properties

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ enableGenericInjection = true
5050
# Generate a class with a String field for the mod version named as defined below.
5151
# If generateGradleTokenClass is empty or not missing, no such class will be generated.
5252
# If gradleTokenVersion is empty or missing, the field will not be present in the class.
53-
generateGradleTokenClass =
53+
generateGradleTokenClass = glowredman.defaultserverlist.Tags
5454

5555
# Name of the token containing the project's current version to generate/replace.
56-
gradleTokenVersion =
56+
gradleTokenVersion = VERSION
5757

5858
# [DEPRECATED] Mod ID replacement token.
5959
gradleTokenModId =
@@ -106,7 +106,7 @@ coreModClass = LoadingPlugin
106106

107107
# If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class
108108
# that is annotated with @Mod) you want this to be true. When in doubt: leave it on false!
109-
containsMixinsAndOrCoreModOnly = false
109+
containsMixinsAndOrCoreModOnly = true
110110

111111
# Enables Mixins even if this mod doesn't use them, useful if one of the dependencies uses mixins.
112112
forceEnableMixins = false
Lines changed: 140 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package glowredman.defaultserverlist;
22

3+
import static net.minecraftforge.common.config.Configuration.CATEGORY_GENERAL;
4+
35
import java.io.File;
4-
import java.io.IOException;
56
import java.io.Reader;
67
import java.net.URL;
78
import java.nio.charset.StandardCharsets;
@@ -15,92 +16,169 @@
1516
import java.util.Map;
1617

1718
import net.minecraft.client.multiplayer.ServerData;
19+
import net.minecraftforge.common.config.Configuration;
1820

1921
import org.apache.commons.io.IOUtils;
2022

2123
import com.google.common.reflect.TypeToken;
2224
import com.google.gson.Gson;
23-
import com.google.gson.GsonBuilder;
2425
import com.google.gson.annotations.SerializedName;
2526

26-
import cpw.mods.fml.common.FMLLog;
27-
2827
public class Config {
2928

30-
public static ConfigObj config = new ConfigObj();
3129
public static final List<ServerData> SERVERS = new ArrayList<>();
32-
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
33-
private static Path configPath;
34-
35-
public static void preInit(File configDir) {
36-
Reader fileReader = null;
37-
try {
38-
configPath = configDir.toPath().resolve("defaultserverlist.json");
39-
40-
if (!Files.exists(configPath)) {
41-
saveConfig(config);
42-
} else {
43-
fileReader = Files.newBufferedReader(configPath, StandardCharsets.UTF_8);
44-
config = GSON.fromJson(fileReader, ConfigObj.class);
30+
public static boolean allowModifications;
31+
private static Configuration config;
32+
33+
/*
34+
* spotless:off
35+
*
36+
* NOTE: There are three different representations for server list entries:
37+
* - String array: The entries are in the format ip|name. This is used for the new configs.
38+
* - Map<String, String>: The key is the name, the value is the ip. This is used for remote server lists.
39+
* - List<ServerData>: This is used by Minecraft.
40+
*
41+
* spotless:on
42+
*/
43+
44+
public static void init(File configDir) {
45+
46+
// Setup
47+
File configFile = new File(configDir, "defaultserverlist.cfg");
48+
Path legacyConfigPath = configDir.toPath().resolve("defaultserverlist.json");
49+
boolean migrate = !configFile.exists() && Files.exists(legacyConfigPath);
50+
Gson gson = new Gson();
51+
config = new Configuration(configFile);
52+
53+
// Migrate to new config file if needed.
54+
if (migrate) {
55+
LoadingPlugin.LOGGER.info("Found legacy config, attempting to migrate...");
56+
try (Reader fileReader = Files.newBufferedReader(legacyConfigPath)) {
57+
ConfigObj legacyConfig = gson.fromJson(fileReader, ConfigObj.class);
58+
config.get(CATEGORY_GENERAL, "useURL", false).set(legacyConfig.useURL);
59+
config.get(CATEGORY_GENERAL, "allowModifications", true).set(legacyConfig.allowModifications);
60+
config.get(CATEGORY_GENERAL, "url", "").set(legacyConfig.url);
61+
config.get(CATEGORY_GENERAL, "servers", new String[0]).set(toArray(legacyConfig.servers));
62+
config.get(CATEGORY_GENERAL, "prevDefaultServers", new String[0])
63+
.set(legacyConfig.prevDefaultServers.toArray(new String[0]));
64+
Files.delete(legacyConfigPath);
65+
LoadingPlugin.LOGGER.info("Migration successful!");
66+
} catch (Exception e) {
67+
LoadingPlugin.LOGGER.error("Migration failed!", e);
4568
}
69+
}
4670

47-
if (config.useURL) {
48-
try {
49-
// servers that are currently at the remote location
50-
Map<String, String> remoteDefaultServers = GSON.fromJson(
51-
IOUtils.toString(new URL(config.url), StandardCharsets.UTF_8),
52-
new TypeToken<LinkedHashMap<String, String>>() {
53-
54-
private static final long serialVersionUID = -1786059589535074931L;
55-
}.getType());
56-
57-
if (config.allowModifications) {
58-
// servers that were added to the remote location since the last time the list was fetched
59-
Map<String, String> diff = new LinkedHashMap<>();
60-
61-
// calculate diff
62-
remoteDefaultServers.forEach((name, ip) -> {
63-
if (!config.prevDefaultServers.contains(ip)) {
64-
diff.put(name, ip);
65-
}
66-
});
67-
68-
// save if the remote location was updated
69-
if (!diff.isEmpty()) {
70-
config.servers.putAll(diff);
71-
config.prevDefaultServers = remoteDefaultServers.values();
72-
saveConfig(config);
71+
// get config values and convert them to a usable format. This also adds comments to the properties.
72+
boolean useURL = config.getBoolean(
73+
"useURL",
74+
CATEGORY_GENERAL,
75+
false,
76+
"Whether or not the default servers should be fetched from a remote location.");
77+
allowModifications = config.getBoolean(
78+
"allowModifications",
79+
CATEGORY_GENERAL,
80+
true,
81+
"Whether or not the user should be able to delete, modify or change the order of the default servers.");
82+
String url = config.getString(
83+
"url",
84+
CATEGORY_GENERAL,
85+
"",
86+
"The remote location to fetch the default servers from. The returned content must be in JSON format (formatted as a map where the keys are the server names and the values the corresponding ip-adresses).");
87+
Map<String, String> servers = toMap(
88+
config.getStringList(
89+
"servers",
90+
CATEGORY_GENERAL,
91+
new String[0],
92+
"The default servers. Format: ip|name"));
93+
String[] prevDefaultServersArray = config
94+
.getStringList("prevDefaultServers", CATEGORY_GENERAL, new String[0], "DO NOT EDIT!");
95+
Collection<String> prevDefaultServers = new ArrayList<>(prevDefaultServersArray.length);
96+
Arrays.stream(prevDefaultServersArray).forEachOrdered(prevDefaultServers::add);
97+
98+
// Fetch servers from the specified remote location.
99+
if (useURL) {
100+
try {
101+
// servers that are currently at the remote location
102+
Map<String, String> remoteDefaultServers = gson.fromJson(
103+
IOUtils.toString(new URL(url), StandardCharsets.UTF_8),
104+
new TypeToken<LinkedHashMap<String, String>>() {
105+
106+
private static final long serialVersionUID = -1786059589535074931L;
107+
}.getType());
108+
109+
if (allowModifications) {
110+
// servers that were added to the remote location since the last time the list was fetched
111+
Map<String, String> diff = new LinkedHashMap<>();
112+
113+
// calculate diff
114+
for (Map.Entry<String, String> entry : remoteDefaultServers.entrySet()) {
115+
String ip = entry.getValue();
116+
if (!prevDefaultServers.contains(ip)) {
117+
diff.put(entry.getKey(), ip);
73118
}
119+
}
74120

75-
} else {
76-
config.servers = remoteDefaultServers;
77-
saveConfig(config);
121+
// save if the remote location was updated
122+
if (!diff.isEmpty()) {
123+
servers.putAll(diff);
124+
prevDefaultServers = remoteDefaultServers.values();
125+
setStringList("servers", toArray(servers));
126+
setStringList("prevDefaultServers", prevDefaultServers.toArray(new String[0]));
78127
}
79-
} catch (Exception e) {
80-
FMLLog.warning(
81-
"Could not get default server list from default location! Are you connected to the internet?");
82-
e.printStackTrace();
128+
129+
} else {
130+
servers = remoteDefaultServers;
131+
setStringList("servers", toArray(servers));
83132
}
133+
} catch (Exception e) {
134+
LoadingPlugin.LOGGER
135+
.error("Could not get default server list from {}! Are you connected to the internet?", url, e);
84136
}
137+
}
138+
139+
// save the config if it changed.
140+
if (config.hasChanged()) {
141+
config.save();
142+
}
85143

86-
config.servers.forEach((name, ip) -> SERVERS.add(new ServerData(name, ip)));
144+
// Convert from Map<String, String> to List<ServerData>
145+
servers.forEach((name, ip) -> SERVERS.add(new ServerData(name, ip)));
146+
}
87147

88-
} catch (Exception e) {
89-
FMLLog.severe("Could not parse default server list!");
90-
e.printStackTrace();
91-
} finally {
92-
IOUtils.closeQuietly(fileReader);
148+
private static String[] toArray(Map<String, String> map) {
149+
String[] array = new String[map.size()];
150+
int i = 0;
151+
for (Map.Entry<String, String> entry : map.entrySet()) {
152+
array[i] = entry.getValue() + '|' + entry.getKey();
153+
i++;
93154
}
155+
return array;
94156
}
95157

96-
public static void saveConfig(ConfigObj config) throws IOException {
97-
File f = configPath.toFile();
98-
if (f.exists()) {
99-
f.delete();
158+
private static Map<String, String> toMap(String[] array) {
159+
Map<String, String> map = new LinkedHashMap<>(array.length);
160+
for (String entry : array) {
161+
String[] parts = entry.split("\\|", 2);
162+
if (parts.length < 2) {
163+
LoadingPlugin.LOGGER.warn("Could not parse entry {} because not '|' was found!", entry);
164+
continue;
165+
}
166+
map.put(parts[1], parts[0]);
100167
}
101-
Files.write(configPath, Arrays.asList(GSON.toJson(config)), StandardCharsets.UTF_8);
168+
return map;
169+
}
170+
171+
public static void saveServers(String[] servers) {
172+
setStringList("servers", servers);
173+
config.save();
174+
}
175+
176+
private static void setStringList(String key, String[] values) {
177+
// config.get(CATEGORY_GENERAL, key, new String[0]).set(values); resets the comment so we can't use that here
178+
config.getCategory(CATEGORY_GENERAL).get(key).set(values);
102179
}
103180

181+
@Deprecated
104182
public static final class ConfigObj {
105183

106184
public boolean useURL = false;
@@ -110,11 +188,5 @@ public static final class ConfigObj {
110188

111189
@SerializedName("DO_NOT_EDIT_prevDefaultServers")
112190
public Collection<String> prevDefaultServers = new ArrayList<>();
113-
114-
public ConfigObj() {}
115-
116-
public ConfigObj(Map<String, String> servers) {
117-
this.servers = servers;
118-
}
119191
}
120192
}

src/main/java/glowredman/defaultserverlist/LoadingPlugin.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import java.util.Map;
77
import java.util.Set;
88

9+
import org.apache.logging.log4j.LogManager;
10+
import org.apache.logging.log4j.Logger;
11+
912
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
1013

1114
import cpw.mods.fml.relauncher.FMLLaunchHandler;
@@ -16,10 +19,14 @@
1619
import cpw.mods.fml.relauncher.Side;
1720

1821
@MCVersion("1.7.10")
19-
@Name("DefaultServerList")
22+
@Name("DefaultServerList Core")
2023
@TransformerExclusions("glowredman.defaultserverlist.LoadingPlugin")
2124
public class LoadingPlugin implements IFMLLoadingPlugin, IEarlyMixinLoader {
2225

26+
public static final Logger LOGGER = LogManager.getLogger("DefaultServerList");
27+
28+
static File location;
29+
2330
@Override
2431
public String getMixinConfig() {
2532
return "mixins.defaultserverlist.early.json";
@@ -40,6 +47,9 @@ public String[] getASMTransformerClass() {
4047

4148
@Override
4249
public String getModContainerClass() {
50+
if (FMLLaunchHandler.side() == Side.CLIENT) {
51+
return ModContainer.class.getName();
52+
}
4353
return null;
4454
}
4555

@@ -50,8 +60,9 @@ public String getSetupClass() {
5060

5161
@Override
5262
public void injectData(Map<String, Object> data) {
53-
if (FMLLaunchHandler.side() == Side.CLIENT) {
54-
Config.preInit(new File((File) data.get("mcLocation"), "config"));
63+
location = (File) data.get("coremodLocation");
64+
if (location == null) {
65+
location = new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath());
5566
}
5667
}
5768

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package glowredman.defaultserverlist;
2+
3+
import java.io.File;
4+
import java.util.Collections;
5+
import java.util.Set;
6+
7+
import com.google.common.eventbus.EventBus;
8+
import com.google.common.eventbus.Subscribe;
9+
10+
import cpw.mods.fml.common.DummyModContainer;
11+
import cpw.mods.fml.common.LoadController;
12+
import cpw.mods.fml.common.ModMetadata;
13+
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
14+
import cpw.mods.fml.common.versioning.ArtifactVersion;
15+
import cpw.mods.fml.common.versioning.VersionParser;
16+
import cpw.mods.fml.common.versioning.VersionRange;
17+
18+
public class ModContainer extends DummyModContainer {
19+
20+
public ModContainer() {
21+
super(new ModMetadata());
22+
ModMetadata md = this.getMetadata();
23+
24+
// NOTE: If you change this, change mcmod.info too!
25+
md.authorList = Collections.singletonList("glowredman");
26+
md.description = "Add default servers to the multiplayer screen.";
27+
md.modId = "defaultserverlist";
28+
md.name = "DefaultServerList";
29+
md.updateUrl = "https://files.data-hole.de/mods/defaultserverlist/updates.json";
30+
md.url = "https://github.com/glowredman/DefaultServerList";
31+
md.version = Tags.VERSION;
32+
}
33+
34+
@Override
35+
public VersionRange acceptableMinecraftVersionRange() {
36+
return VersionParser.parseRange("[1.7.10]");
37+
}
38+
39+
@Override
40+
public Set<ArtifactVersion> getRequirements() {
41+
return Collections.singleton(VersionParser.parseVersionReference("gtnhmixins"));
42+
}
43+
44+
@Override
45+
public File getSource() {
46+
return LoadingPlugin.location;
47+
}
48+
49+
@Override
50+
public boolean registerBus(EventBus bus, LoadController controller) {
51+
bus.register(this);
52+
return true;
53+
}
54+
55+
@Subscribe
56+
public void preInit(FMLPreInitializationEvent event) {
57+
Config.init(event.getModConfigurationDirectory());
58+
}
59+
}

0 commit comments

Comments
 (0)