Skip to content

Commit 05befb8

Browse files
committed
Merge branch '1.21.1/dev' into 1.21.1/stable
2 parents 7ec294e + 8f5e130 commit 05befb8

9 files changed

Lines changed: 256 additions & 46 deletions

File tree

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ val PARCHMENT_VERSION by extra { null }
1414
// https://semver.org/
1515
val MAVEN_GROUP by extra { "me.flashyreese.mods" }
1616
val ARCHIVE_NAME by extra { "sodium-extra" }
17-
val MOD_VERSION by extra { "0.9.0" }
17+
val MOD_VERSION by extra { "0.9.1" }
1818
val SODIUM_VERSION by extra { "0.8.12-alpha.3+mc1.21.1" }
1919

2020
allprojects {

common/src/main/java/me/flashyreese/mods/sodiumextra/client/SodiumExtraClientMod.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public static CaffeineConfig mixinConfig() {
8383
}
8484

8585
private static SodiumExtraGameOptions loadConfig() {
86-
return SodiumExtraGameOptions.load(PlatformRuntimeInformation.getInstance().getConfigDirectory().resolve(SodiumExtraConfigKeys.FILE_NAME).toFile());
86+
return SodiumExtraGameOptions.load(PlatformRuntimeInformation.getInstance().getConfigDirectory().resolve(SodiumExtraConfigKeys.FILE_NAME));
8787
}
8888

8989
public static void armWaylandFullscreenResolutionRecovery() {
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package me.flashyreese.mods.sodiumextra.client.config;
2+
3+
import java.io.IOException;
4+
import java.nio.ByteBuffer;
5+
import java.nio.channels.FileChannel;
6+
import java.nio.charset.StandardCharsets;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import java.nio.file.StandardCopyOption;
10+
import java.nio.file.StandardOpenOption;
11+
12+
public final class ConfigFileIO {
13+
private ConfigFileIO() {
14+
}
15+
16+
public static void writeStringAtomically(Path path, String contents) throws IOException {
17+
writeBytesAtomically(path, contents.getBytes(StandardCharsets.UTF_8));
18+
}
19+
20+
public static void writeLinesAtomically(Path path, Iterable<String> lines) throws IOException {
21+
StringBuilder builder = new StringBuilder();
22+
for (String line : lines) {
23+
builder.append(line).append(System.lineSeparator());
24+
}
25+
26+
writeStringAtomically(path, builder.toString());
27+
}
28+
29+
public static void writeBytesAtomically(Path path, byte[] bytes) throws IOException {
30+
Path targetPath = path.toAbsolutePath();
31+
Path directory = targetPath.getParent();
32+
Path fileName = targetPath.getFileName();
33+
34+
if (directory == null || fileName == null) {
35+
throw new IOException("Path has no parent directory: " + path);
36+
}
37+
38+
Path tempPath = null;
39+
IOException failure = null;
40+
41+
try {
42+
Files.createDirectories(directory);
43+
tempPath = Files.createTempFile(directory, fileName.toString(), ".tmp");
44+
45+
writeBytesToTempFile(tempPath, bytes);
46+
Files.move(tempPath, targetPath, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
47+
tempPath = null;
48+
49+
forceDirectory(directory);
50+
} catch (IOException e) {
51+
failure = e;
52+
throw e;
53+
} finally {
54+
if (tempPath != null) {
55+
try {
56+
Files.deleteIfExists(tempPath);
57+
} catch (IOException e) {
58+
if (failure != null) {
59+
failure.addSuppressed(e);
60+
} else {
61+
throw e;
62+
}
63+
}
64+
}
65+
}
66+
}
67+
68+
public static Path moveCorruptFile(Path path) throws IOException {
69+
Path corruptPath = nextCorruptFilePath(path);
70+
Files.move(path, corruptPath);
71+
return corruptPath;
72+
}
73+
74+
private static void writeBytesToTempFile(Path tempPath, byte[] bytes) throws IOException {
75+
try (FileChannel channel = FileChannel.open(tempPath, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
76+
ByteBuffer buffer = ByteBuffer.wrap(bytes);
77+
while (buffer.hasRemaining()) {
78+
channel.write(buffer);
79+
}
80+
81+
channel.force(true);
82+
}
83+
}
84+
85+
private static Path nextCorruptFilePath(Path path) {
86+
Path basePath = path.resolveSibling(path.getFileName() + ".corrupt");
87+
if (!Files.exists(basePath)) {
88+
return basePath;
89+
}
90+
91+
for (int i = 1; ; i++) {
92+
Path candidate = path.resolveSibling(path.getFileName() + ".corrupt." + i);
93+
if (!Files.exists(candidate)) {
94+
return candidate;
95+
}
96+
}
97+
}
98+
99+
private static void forceDirectory(Path directory) {
100+
try (FileChannel channel = FileChannel.open(directory, StandardOpenOption.READ)) {
101+
channel.force(true);
102+
} catch (IOException ignored) {
103+
}
104+
}
105+
}

common/src/main/java/me/flashyreese/mods/sodiumextra/client/config/SodiumExtraConfig.java

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,36 @@ private static SodiumExtraGameOptions.AtmosphericFogSettings createDimensionFogS
217217
return settings;
218218
}
219219

220+
private static List<ResourceLocation> getDimensionFogEffectIds(SodiumExtraGameOptions.FogSettings fogSettings) {
221+
Set<ResourceLocation> identifiers = new LinkedHashSet<>();
222+
WorldDimensions.keysInOrder(Stream.empty())
223+
.map(dim -> dim.location())
224+
.forEach(identifiers::add);
225+
addKnownWorldDimensionEffectIds(identifiers);
226+
identifiers.addAll(fogSettings.dimensionOverrides.keySet());
227+
228+
identifiers.forEach(identifier -> fogSettings.dimensionOverrides.computeIfAbsent(identifier, ignored -> createDimensionFogSettings()));
229+
230+
return identifiers.stream()
231+
.sorted(Comparator.comparing(ResourceLocation::toString))
232+
.toList();
233+
}
234+
235+
private static void addKnownWorldDimensionEffectIds(Set<ResourceLocation> identifiers) {
236+
Minecraft minecraft = Minecraft.getInstance();
237+
if (minecraft == null) {
238+
return;
239+
}
240+
241+
if (minecraft.level != null) {
242+
identifiers.add(minecraft.level.dimensionType().effectsLocation());
243+
}
244+
245+
if (minecraft.getSingleplayerServer() != null) {
246+
minecraft.getSingleplayerServer().getAllLevels().forEach(level -> identifiers.add(level.dimensionType().effectsLocation()));
247+
}
248+
}
249+
220250
private static void setAtmosphericFogStart(int value) {
221251
SodiumExtraGameOptions.FogSettings fogSettings = fogSettings();
222252
int clampedValue = Math.max(0, Math.min(value, 100));
@@ -515,10 +545,7 @@ private OptionPageBuilder createRenderPage(ConfigBuilder builder) {
515545
.setName(Component.translatable("sodium-extra.option.render"));
516546

517547
SodiumExtraGameOptions.FogSettings fogSettings = fogSettings();
518-
WorldDimensions.keysInOrder(Stream.empty())
519-
.map(dim -> dim.location())
520-
.filter(identifier -> !fogSettings.dimensionOverrides.containsKey(identifier))
521-
.forEach(identifier -> fogSettings.dimensionOverrides.put(identifier, createDimensionFogSettings()));
548+
List<ResourceLocation> dimensionFogEffectIds = getDimensionFogEffectIds(fogSettings);
522549

523550
page.addOptionGroup(builder.createOptionGroup()
524551
.addOption(builder.createBooleanOption(ADVANCED_FOG_OPTION_ID)
@@ -606,9 +633,7 @@ private OptionPageBuilder createRenderPage(ConfigBuilder builder) {
606633
);
607634

608635
OptionGroupBuilder dimensionFogGroup = builder.createOptionGroup();
609-
fogSettings.dimensionOverrides.keySet().stream()
610-
.sorted(Comparator.comparing(ResourceLocation::toString))
611-
.forEach(identifier -> dimensionFogGroup.addOption(builder.createIntegerOption(id("fog." + identifier.toLanguageKey("options.dimensions")))
636+
dimensionFogEffectIds.forEach(identifier -> dimensionFogGroup.addOption(builder.createIntegerOption(id("fog." + identifier.toLanguageKey("options.dimensions")))
612637
.setEnabledProvider(
613638
SodiumExtraConfig::isDimensionFogOptionEnabled,
614639
ADVANCED_FOG_OPTION_ID,
@@ -626,7 +651,9 @@ private OptionPageBuilder createRenderPage(ConfigBuilder builder) {
626651
)
627652
.setDefaultValue(0)
628653
));
629-
page.addOptionGroup(dimensionFogGroup);
654+
if (!dimensionFogEffectIds.isEmpty()) {
655+
page.addOptionGroup(dimensionFogGroup);
656+
}
630657

631658
page.addOptionGroup(builder.createOptionGroup()
632659
.addOption(builder.createBooleanOption(PROTECTED_GAMEPLAY_FOG_OPTION_ID)

common/src/main/java/me/flashyreese/mods/sodiumextra/client/config/SodiumExtraGameOptions.java

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.google.gson.FieldNamingPolicy;
44
import com.google.gson.Gson;
55
import com.google.gson.GsonBuilder;
6+
import com.google.gson.JsonParseException;
67
import com.google.gson.annotations.SerializedName;
78
import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
89
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
@@ -17,10 +18,11 @@
1718
import org.lwjgl.glfw.GLFW;
1819

1920
import java.io.File;
20-
import java.io.FileReader;
21-
import java.io.FileWriter;
2221
import java.io.IOException;
2322
import java.lang.reflect.Modifier;
23+
import java.nio.charset.StandardCharsets;
24+
import java.nio.file.Files;
25+
import java.nio.file.Path;
2426
import java.util.Arrays;
2527
import java.util.EnumSet;
2628
import java.util.Map;
@@ -38,30 +40,37 @@ public class SodiumExtraGameOptions implements StorageEventHandler {
3840
public RenderSettings renderSettings = new RenderSettings();
3941
@SerializedName(SodiumExtraConfigKeys.EXTRA_SETTINGS)
4042
public ExtraSettings extraSettings = new ExtraSettings();
41-
private File file;
43+
private Path path;
4244

4345
public static SodiumExtraGameOptions load(File file) {
46+
return load(file.toPath());
47+
}
48+
49+
public static SodiumExtraGameOptions load(Path path) {
4450
SodiumExtraGameOptions config;
51+
boolean shouldWriteChanges = true;
4552

46-
if (file.exists()) {
47-
try (FileReader reader = new FileReader(file)) {
53+
if (Files.exists(path)) {
54+
try (var reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
4855
config = gson.fromJson(reader, SodiumExtraGameOptions.class);
49-
} catch (Exception e) {
50-
SodiumExtraClientMod.logger().error("Could not parse config, falling back to defaults!", e);
56+
if (config == null) {
57+
throw new JsonParseException("Root element must be a JSON object");
58+
}
59+
} catch (IOException | JsonParseException | IllegalStateException e) {
60+
SodiumExtraClientMod.logger().warn("Could not read config, falling back to defaults", e);
5161
config = new SodiumExtraGameOptions();
62+
shouldWriteChanges = moveCorruptConfig(path);
5263
}
5364
} else {
5465
config = new SodiumExtraGameOptions();
5566
}
5667

57-
if (config == null) {
58-
SodiumExtraClientMod.logger().error("Could not parse config, falling back to defaults!");
59-
config = new SodiumExtraGameOptions();
60-
}
61-
6268
config.sanitize();
63-
config.file = file;
64-
config.writeChanges();
69+
config.path = path;
70+
71+
if (shouldWriteChanges) {
72+
config.writeChanges();
73+
}
6574

6675
return config;
6776
}
@@ -92,20 +101,27 @@ private void sanitize() {
92101
}
93102

94103
public void writeChanges() {
95-
File dir = this.file.getParentFile();
104+
if (this.path == null) {
105+
SodiumExtraClientMod.logger().warn("Could not save configuration file because no path was set");
106+
return;
107+
}
96108

97-
if (!dir.exists()) {
98-
if (!dir.mkdirs()) {
99-
throw new RuntimeException("Could not create parent directories");
100-
}
101-
} else if (!dir.isDirectory()) {
102-
throw new RuntimeException("The parent file is not a directory");
109+
try {
110+
this.sanitize();
111+
ConfigFileIO.writeStringAtomically(this.path, gson.toJson(this) + System.lineSeparator());
112+
} catch (IOException e) {
113+
SodiumExtraClientMod.logger().warn("Could not save configuration file", e);
103114
}
115+
}
104116

105-
try (FileWriter writer = new FileWriter(this.file)) {
106-
gson.toJson(this, writer);
117+
private static boolean moveCorruptConfig(Path path) {
118+
try {
119+
Path corruptPath = ConfigFileIO.moveCorruptFile(path);
120+
SodiumExtraClientMod.logger().warn("Moved corrupt configuration file to {}", corruptPath);
121+
return true;
107122
} catch (IOException e) {
108-
throw new RuntimeException("Could not save configuration file", e);
123+
SodiumExtraClientMod.logger().warn("Could not move corrupt configuration file", e);
124+
return false;
109125
}
110126
}
111127

@@ -335,14 +351,23 @@ public void sanitize() {
335351
if (this.dimensionOverrides == null) {
336352
this.dimensionOverrides = new Object2ObjectArrayMap<>();
337353
}
338-
this.dimensionOverrides.replaceAll((identifier, settings) -> {
354+
355+
Map<ResourceLocation, AtmosphericFogSettings> sanitizedDimensionOverrides = new Object2ObjectArrayMap<>(this.dimensionOverrides.size());
356+
for (Map.Entry<ResourceLocation, AtmosphericFogSettings> entry : this.dimensionOverrides.entrySet()) {
357+
ResourceLocation identifier = entry.getKey();
358+
if (identifier == null) {
359+
continue;
360+
}
361+
362+
AtmosphericFogSettings settings = entry.getValue();
339363
if (settings == null) {
340364
settings = new AtmosphericFogSettings();
341365
}
342366

343367
settings.sanitize();
344-
return settings;
345-
});
368+
sanitizedDimensionOverrides.put(identifier, settings);
369+
}
370+
this.dimensionOverrides = sanitizedDimensionOverrides;
346371

347372
if (this.protectedGameplay == null) {
348373
this.protectedGameplay = new ProtectedFogSettings();

common/src/main/java/me/flashyreese/mods/sodiumextra/client/fog/FogDistanceHelper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ private FogDistanceHelper() {
4343

4444
public static SodiumExtraGameOptions.AtmosphericFogSettings getAtmosphericSettings(ClientLevel level) {
4545
SodiumExtraGameOptions.FogSettings fogSettings = getFogSettings();
46-
ResourceLocation dimensionId = level.dimension().location();
47-
return fogSettings.getAtmospheric(dimensionId);
46+
ResourceLocation dimensionEffectsId = level.dimensionType().effectsLocation();
47+
return fogSettings.getAtmospheric(dimensionEffectsId);
4848
}
4949

5050
public static int normalizeFogDistance(int fogDistance) {

common/src/main/java/me/flashyreese/mods/sodiumextra/client/recovery/WaylandFullscreenResolutionRecovery.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.google.gson.JsonElement;
66
import com.google.gson.JsonObject;
77
import com.google.gson.JsonParser;
8+
import me.flashyreese.mods.sodiumextra.client.config.ConfigFileIO;
89
import me.flashyreese.mods.sodiumextra.client.config.SodiumExtraConfigKeys;
910
import org.slf4j.Logger;
1011
import org.slf4j.LoggerFactory;
@@ -83,7 +84,7 @@ private static boolean recoverMinecraftOptions(Path optionsFile) {
8384
}
8485

8586
if (changed) {
86-
Files.write(optionsFile, output, StandardCharsets.UTF_8);
87+
ConfigFileIO.writeLinesAtomically(optionsFile, output);
8788
}
8889

8990
return changed;
@@ -109,8 +110,7 @@ private static JsonObject readJsonObject(Path path) {
109110

110111
private static void writeJson(Path path, JsonObject object) {
111112
try {
112-
Files.createDirectories(path.getParent());
113-
Files.writeString(path, GSON.toJson(object) + System.lineSeparator(), StandardCharsets.UTF_8);
113+
ConfigFileIO.writeStringAtomically(path, GSON.toJson(object) + System.lineSeparator());
114114
} catch (IOException e) {
115115
LOGGER.error("Failed to write Sodium Extra fullscreen recovery state", e);
116116
}

0 commit comments

Comments
 (0)