Skip to content

Commit cae7867

Browse files
committed
Fix terminal input when ran with gui, improve non-overwritable file handling
1 parent 4678fa9 commit cae7867

9 files changed

Lines changed: 105 additions & 28 deletions

File tree

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import kotlin.jvm.functions.Function1
22

33
plugins {
44
id 'com.gradleup.shadow' version '8.3.5'
5-
id 'xyz.wagyourtail.jvmdowngrader' version '1.3.0-SNAPSHOT'
5+
id 'xyz.wagyourtail.jvmdowngrader' version '1.2.2'
66
id 'java'
77
id 'maven-publish'
88
}

changelog-next

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
- Added simple UI visible when running the jar directly on systems with graphics.
22
- It will be closed once Minecraft opens it's own server window.
33
- Can be disabled by providing `--noGui` or `noGui` option, just like vanilla game.
4+
- Modpack info files can now define non-overwritable files (prevents' modification if they already exist).
5+
- You can now add custom non-overwritable files/folders as a new-line separated list in `lockedpaths.txt` file in main server directory.
46
- When running standalone and selecting a project, it now validates if it's actually a modpack.
57
- When providing modpack slug/link, you can force it to always search by prefixing the input with question mark.
68
- Removed `mrpack4server` text from log prefix.

src/main/java/eu/pb4/forgefx/S.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ public static void gc() {
120120
}
121121

122122
public static void runFinalization() {
123-
System.runFinalization();
123+
//noinspection removal
124+
System.runFinalization();
124125
}
125126

126127
public static void load(String filename) {

src/main/java/eu/pb4/mrpackserver/Main.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ public static void main(String[] args) throws Throwable {
3232
var modpackInfo = Utils.resolveModpackInfo(runPath);
3333

3434
if (!noGui) {
35-
InstallerGui.setup(modpackInfo);
35+
try {
36+
InstallerGui.setup(modpackInfo);
37+
} catch (Throwable e) {
38+
Logger.error("Tried to open terminal ui, but it failed!", e);
39+
}
3640
}
3741

3842
if (modpackInfo == null) {

src/main/java/eu/pb4/mrpackserver/format/ModpackInfo.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public class ModpackInfo {
2222
public String versionId = "";
2323
@SerializedName("whitelisted_domains")
2424
public List<String> whitelistedDomains = new ArrayList<>();
25+
@SerializedName("non_overwritable_paths")
26+
public List<String> nonOverwritablePaths = new ArrayList<>();
2527
@Nullable
2628
public String sha512 = null;
2729
@Nullable
@@ -37,6 +39,9 @@ public class ModpackInfo {
3739
@Nullable
3840
public String internalFlavor = null;
3941

42+
@SerializedName("skip_java_version_check")
43+
public Boolean skipJavaVersionCheck = null;
44+
4045
public boolean isValid() {
4146
return !this.projectId.isBlank() && !this.versionId.isBlank();
4247
}

src/main/java/eu/pb4/mrpackserver/installer/MrPackInstaller.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ public class MrPackInstaller {
2020
private final Path destinationOldModified;
2121
private final InstanceInfo currentInstanceData;
2222
private final Set<String> whitelistedDomains;
23+
private final HashSet<String> nonOverwritablePaths;
2324
@Nullable
2425
private String newLauncher;
2526
@Nullable
2627
private Installer installer;
2728
private boolean forceSystemClasspath = false;
2829

29-
public MrPackInstaller(Path source, ModpackIndex index, Path destination, InstanceInfo data, HashMap<String, HashData> hashes, Set<String> whitelistedDomains) throws IOException {
30+
public MrPackInstaller(Path source, ModpackIndex index, Path destination, InstanceInfo data, HashMap<String, HashData> hashes, Set<String> whitelistedDomains, HashSet<String> nonOverwritablePaths) throws IOException {
3031
this.source = source;
3132
this.index = index;
3233
this.currentInstanceData = data;
@@ -38,6 +39,7 @@ public MrPackInstaller(Path source, ModpackIndex index, Path destination, Instan
3839
this.newHashes = new HashMap<>();
3940

4041
this.whitelistedDomains = whitelistedDomains;
42+
this.nonOverwritablePaths = nonOverwritablePaths;
4143
}
4244

4345
public void prepareFolders() throws IOException {
@@ -52,13 +54,14 @@ public void extractIncluded(Map<String, HashData> existingHashes) throws IOExcep
5254
Files.walkFileTree(path, new FileVisitor<Path>() {
5355
@Override
5456
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
55-
var p = destination.resolve(path.relativize(dir).toString()).normalize();
57+
var local = path.relativize(dir).toString();
58+
var p = destination.resolve(local).normalize();
5659
if (!p.startsWith(destination.toAbsolutePath())) {
57-
Logger.error("Modpack contains files, that are placed outside of server's root! Found '%s'", path.relativize(dir).toString());
60+
Logger.error("Modpack contains files, that are placed outside of server's root! Found '%s'", local);
5861
return FileVisitResult.TERMINATE;
5962
}
6063

61-
if (Constants.NON_OVERWRITABLE.contains(p.toString()) && Files.exists(p)) {
64+
if (nonOverwritablePaths.contains(local) && Files.exists(p)) {
6265
Logger.warn("Skipping non-overwritable path: %s", path);
6366
return FileVisitResult.SKIP_SUBTREE;
6467
}
@@ -75,7 +78,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
7578
return FileVisitResult.TERMINATE;
7679
}
7780

78-
if (Constants.NON_OVERWRITABLE.contains(outPath.toString()) && Files.exists(file)) {
81+
if (nonOverwritablePaths.contains(local) && Files.exists(file)) {
7982
Logger.warn("Skipping non-overwritable file: %s", path);
8083
return FileVisitResult.CONTINUE;
8184
}
@@ -241,6 +244,8 @@ public void requestDownloads(FileDownloader downloader, Map<String, HashData> ha
241244

242245
for (var file : this.index.files) {
243246
var currentHash = hashExisting.remove(file.path);
247+
var path = this.destination.resolve(file.path);
248+
244249
if (currentHash != null) {
245250
var newHash = HashData.read(Constants.MODRINTH_HASH, file.hashes);
246251

@@ -250,6 +255,10 @@ public void requestDownloads(FileDownloader downloader, Map<String, HashData> ha
250255
} else if (currentHash.equals(newHash)) {
251256
this.newHashes.put(file.path, newHash);
252257
continue;
258+
} else if (nonOverwritablePaths.contains(file.path) && Files.exists(path)) {
259+
Logger.warn("Skipping non-overwritable file: %s", file.path);
260+
this.newHashes.put(file.path, this.oldHashes.get(file.path));
261+
continue;
253262
} else if (currentHash.equals(this.oldHashes.get(file.path))) {
254263
Files.deleteIfExists(this.destination.resolve(file.path));
255264
} else {
@@ -258,6 +267,9 @@ public void requestDownloads(FileDownloader downloader, Map<String, HashData> ha
258267
Logger.info("File '%s' was modified, but modpack required it to be updated! Moving it to '%s'", file.path, "old_modified_files/" + file.path);
259268
Files.deleteIfExists(this.destination.resolve(file.path));
260269
}
270+
} else if (nonOverwritablePaths.contains(file.path) && Files.exists(path)) {
271+
Logger.warn("Skipping non-overwritable file: %s", file.path);
272+
continue;
261273
}
262274

263275
for (var url : file.downloads) {
@@ -266,7 +278,6 @@ public void requestDownloads(FileDownloader downloader, Map<String, HashData> ha
266278
}
267279
}
268280

269-
var path = this.destination.resolve(file.path);
270281
if (Files.exists(path)) {
271282
Files.createDirectories(this.destinationOldModified.resolve(file.path).getParent());
272283
Files.deleteIfExists(this.destinationOldModified.resolve(file.path));

src/main/java/eu/pb4/mrpackserver/util/Constants.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ public class Constants {
1818
public static final int DOWNLOAD_UPDATE_TIME = 1500;
1919
public static final int DOWNLOAD_CHUNK_SIZE = 512;
2020
public static final String DATA_FOLDER = ".mrpack4server";
21+
public static final String CUSTOM_NON_OVERWRITABLE_LIST = "lockedpaths.txt";
2122

2223
public static final String FABRIC_INSTALLER_VERSIONS = "https://meta.fabricmc.net/v2/versions/installer";
2324
public static final String MODRINTH_API = "https://api.modrinth.com/v2";
2425
public static final String MODRINTH_API_VERSIONS = MODRINTH_API + "/project/{PROJECT_ID}/version";
2526
public static final List<String> OVERWRITES = List.of("/overrides", "/server_overrides");
26-
public static final List<String> NON_OVERWRITABLE = List.of("server.properties", "world", DATA_FOLDER);
27+
public static final List<String> DEFAULT_NON_OVERWRITABLE = List.of("server.properties", "world", DATA_FOLDER, CUSTOM_NON_OVERWRITABLE_LIST);
2728

28-
public static final Set<String> WHITELISTED_URLS = Set.of(
29+
public static final Set<String> DEFAULT_WHITELISTED_URLS = Set.of(
2930
"cdn.modrinth.com",
3031
"github.com",
3132
"raw.githubusercontent.com",

src/main/java/eu/pb4/mrpackserver/util/InstallerGui.java

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,18 @@
1414
import java.awt.event.WindowEvent;
1515
import java.io.*;
1616
import java.nio.charset.Charset;
17-
import java.nio.charset.StandardCharsets;
1817
import java.util.ArrayList;
1918

2019
public class InstallerGui extends JComponent {
2120
private static final Font FONT_MONOSPACE = new Font("Monospaced", Font.PLAIN, 12);
2221
private final OutputStream logger;
2322
private final Charset charset;
24-
private PrintStream oldOut;
25-
private PrintStream oldErr;
26-
private InputStream oldIn;
23+
private final PrintStream oldOut;
24+
private final PrintStream oldErr;
25+
private final InputStream oldIn;
2726
private final AppendableInputStream in = new AppendableInputStream();
2827
private final JFrame frame;
2928
private JTextArea logBox;
30-
private JScrollPane scrollPane;
3129
private boolean closed;
3230

3331
public static InstallerGui instance = null;
@@ -58,7 +56,7 @@ public void write(int b) {
5856

5957
System.setOut(new PrintStream(new DoubleOutputStream(System.out, logger), false, this.charset));
6058
System.setErr(new PrintStream(new DoubleOutputStream(System.err, logger), false, this.charset));
61-
System.setIn(System.console() != null ? new DoubleInputStream(System.in, this.in) : this.in);
59+
System.setIn(System.console() != null ? new DoubleWaitingInputStream(System.in, this.in) : this.in);
6260

6361
instance = this;
6462
}
@@ -128,13 +126,13 @@ private Component createConsole() {
128126
var panel = new JPanel(new BorderLayout());
129127
this.logBox = new JTextArea();
130128
this.logBox.setFocusable(false);
129+
this.logBox.setLineWrap(true);
131130
var caret = new DefaultCaret();
132131
caret.setVisible(false);
133132
//caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
134133
this.logBox.setCaret(caret);
135134

136135
var scroll = new JScrollPane(logBox, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
137-
this.scrollPane = scroll;
138136

139137
logBox.setEditable(false);
140138
logBox.setFont(FONT_MONOSPACE);
@@ -217,18 +215,56 @@ public void append(byte[] arr) {
217215
}
218216
}
219217

220-
private static class DoubleInputStream extends InputStream {
218+
private static class DoubleWaitingInputStream extends InputStream {
221219
private final InputStream out;
222220
private final InputStream out2;
223221

224-
public DoubleInputStream(InputStream out, InputStream out2) {
222+
public DoubleWaitingInputStream(InputStream out, InputStream out2) {
225223
this.out = out;
226224
this.out2 = out2;
227225
}
228226

229227
@Override
230228
public int read() throws IOException {
231-
return this.out.available() > this.out2.available() ? this.out.read() : this.out2.read();
229+
var stream = getStream();
230+
if (stream != null) {
231+
return stream.read();
232+
}
233+
return -1;
234+
}
235+
236+
@Override
237+
public int read(@NotNull byte[] b) throws IOException {
238+
var stream = getStream();
239+
if (stream != null) {
240+
return stream.read(b);
241+
}
242+
return 0;
243+
}
244+
245+
@Override
246+
public int read(@NotNull byte[] b, int off, int len) throws IOException {
247+
var stream = getStream();
248+
if (stream != null) {
249+
return stream.read(b, off, len);
250+
}
251+
return 0;
252+
}
253+
254+
private InputStream getStream() throws IOException {
255+
while (true) {
256+
if (this.out.available() > 0) {
257+
return this.out;
258+
}
259+
if (this.out2.available() > 0) {
260+
return this.out2;
261+
}
262+
try {
263+
Thread.sleep(1);
264+
} catch (InterruptedException e) {
265+
return null;
266+
}
267+
}
232268
}
233269

234270
@Override
@@ -277,7 +313,7 @@ public void close() throws IOException {
277313
}
278314
}
279315

280-
public void handleForgeFix() {
316+
/*public void handleForgeFix() {
281317
Logger.warn("Forge on 1.16.5 and older breaks mrpack4server's initial log screen.");
282318
Logger.warn("You can safely close it without stopping the server.");
283319
var currOut = System.out;
@@ -313,5 +349,5 @@ public void handleForgeFix() {
313349
} catch (Throwable e) {
314350
Logger.error("Failed to apply forge logging fix!", e);
315351
}
316-
}
352+
}*/
317353
}

src/main/java/eu/pb4/mrpackserver/util/Utils.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,14 +139,26 @@ static InstallResult checkAndSetupModpack(ModpackInfo modpackInfo, InstanceInfo
139139
hashes = Utils.GSON_MAIN.fromJson(Files.readString(hashPath), new TypeToken<HashMap<String, HashData>>() {}.getType());
140140
}
141141
var whitelistedDomains = new HashSet<String>();
142-
whitelistedDomains.addAll(Constants.WHITELISTED_URLS);
142+
whitelistedDomains.addAll(Constants.DEFAULT_WHITELISTED_URLS);
143143
whitelistedDomains.addAll(modpackInfo.whitelistedDomains);
144+
var nonOverwritablePaths = new HashSet<String>();
145+
nonOverwritablePaths.addAll(Constants.DEFAULT_NON_OVERWRITABLE);
146+
nonOverwritablePaths.addAll(modpackInfo.nonOverwritablePaths);
147+
148+
var customNonOverwritable = currentDir.resolve(Constants.CUSTOM_NON_OVERWRITABLE_LIST);
149+
try {
150+
if (Files.exists(customNonOverwritable)) {
151+
nonOverwritablePaths.addAll(Files.readAllLines(customNonOverwritable));
152+
}
153+
} catch (Throwable ignored) {
154+
155+
}
144156

145157
Logger.info("Starting %s of %s (%s)", hashes.isEmpty() ? "installation" : "update", modpackInfo.getDisplayName(), modpackInfo.getDisplayVersion());
146158

147-
var handler = new MrPackInstaller(zip.getPath(""), index, currentDir, instance, hashes, whitelistedDomains);
159+
var handler = new MrPackInstaller(zip.getPath(""), index, currentDir, instance, hashes, whitelistedDomains, nonOverwritablePaths);
148160

149-
if (!handler.checkJavaVersion()) {
161+
if (modpackInfo.skipJavaVersionCheck != Boolean.TRUE && !handler.checkJavaVersion()) {
150162
return null;
151163
}
152164

@@ -394,8 +406,13 @@ static void configureModpack(Path runPath) throws IOException, InterruptedExcept
394406
}
395407
} else {
396408
var client = Utils.createHttpClient();
397-
var res = data.charAt(0) == '?' ? null : client.send(Utils.createGetRequest(URI.create(Constants.MODRINTH_API + "/project/" + URLEncoder.encode(data, StandardCharsets.UTF_8))), HttpResponse.BodyHandlers.ofString());
398-
409+
HttpResponse<String> res;
410+
if (data.charAt(0) == '?') {
411+
data = data.substring(1);
412+
res = null;
413+
} else {
414+
res = client.send(Utils.createGetRequest(URI.create(Constants.MODRINTH_API + "/project/" + URLEncoder.encode(data, StandardCharsets.UTF_8))), HttpResponse.BodyHandlers.ofString());
415+
}
399416
boolean foundProject = false;
400417
if (res != null && res.statusCode() == 200) {
401418
var project = ModrinthProjectData.read(res.body());

0 commit comments

Comments
 (0)