diff --git a/SubServers.Bungee/common/pom.xml b/SubServers.Bungee/common/pom.xml
index d73e371e..efe0b115 100644
--- a/SubServers.Bungee/common/pom.xml
+++ b/SubServers.Bungee/common/pom.xml
@@ -28,13 +28,13 @@
net.ME1312.Galaxi
GalaxiUtil
- 21w15b
+ 21w23c
provided
net.ME1312.Galaxi
GalaxiEngine
- 21w15b
+ 21w23c
provided
diff --git a/SubServers.Bungee/pom.xml b/SubServers.Bungee/pom.xml
index 6c575f95..3c636724 100644
--- a/SubServers.Bungee/pom.xml
+++ b/SubServers.Bungee/pom.xml
@@ -28,14 +28,14 @@
net.ME1312.Galaxi
GalaxiUtil
- 21w15b
+ 21w23c
compile
true
net.ME1312.Galaxi
GalaxiEngine
- 21w15b
+ 21w23c
provided
diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/FileScanner.java b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/FileScanner.java
new file mode 100644
index 00000000..ac3e8684
--- /dev/null
+++ b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/FileScanner.java
@@ -0,0 +1,138 @@
+package net.ME1312.SubServers.Bungee.Library;
+
+import net.ME1312.Galaxi.Library.Util;
+
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * File Scanner Base Class
+ */
+public abstract class FileScanner {
+
+ /**
+ * Scan a Directory
+ *
+ * @param dir Directory
+ * @param whitelist File Whitelist
+ */
+ protected void scan(File dir, String... whitelist) throws IOException {
+ List files;
+ try {
+ files = Util.reflect(Util.class.getDeclaredMethod("zipsearch", File.class, File.class), null, dir, dir);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ throw new IllegalStateException("Cannot access zipsearch()", e);
+ }
+ if (files.size() <= 0 || whitelist.length <= 0)
+ return;
+
+ boolean csfs = false;
+ {
+ long stamp = Math.round(Math.random() * 100000);
+ File test1 = new File(dir, '.' + stamp + ".ss_fsc");
+ File test2 = new File(dir, '.' + stamp + ".SS_FSC");
+
+ test1.createNewFile();
+ if (test2.createNewFile()) {
+ csfs = true;
+ test2.delete();
+ }
+ test1.delete();
+ }
+
+ LinkedHashMap rules = new LinkedHashMap();
+ for (String entry : whitelist) {
+ boolean mode = !entry.startsWith("!");
+ if (!mode) entry = entry.substring(1);
+
+ String pattern;
+ if (!entry.startsWith("%")) {
+ if (entry.startsWith("./"))
+ entry = entry.substring(1);
+
+ StringBuilder rule = new StringBuilder();
+ if (entry.startsWith("**")) {
+ entry = entry.substring(2);
+ rule.append("^.*");
+ } else if (entry.startsWith("/")) {
+ rule.append("^");
+ }
+
+ boolean greedyEnding = false;
+ if (entry.endsWith("**")) {
+ entry = entry.substring(0, entry.length() - 2);
+ greedyEnding = true;
+ } else if (entry.endsWith("/")) {
+ greedyEnding = true;
+ }
+
+ StringBuilder literal = new StringBuilder();
+ for (PrimitiveIterator.OfInt i = entry.codePoints().iterator(); i.hasNext(); ) {
+ int c = i.next();
+ if ((c == '*' || c == '?' || c == '[') && literal.length() > 0) {
+ rule.append(Pattern.quote(literal.toString()));
+ literal = new StringBuilder();
+ }
+ switch (c) {
+ case '\\':
+ if (i.hasNext()) c = i.next();
+ literal.appendCodePoint(c);
+ case '[':
+ for (boolean escaped = false; i.hasNext() && (c != ']' || escaped); c = i.next()) {
+ if (c == '\\') escaped = !escaped;
+ else escaped = false;
+ literal.appendCodePoint(c);
+ }
+ if (c == ']' && literal.length() > 1) {
+ literal.appendCodePoint(c);
+ rule.append(literal.toString());
+ }
+ literal = new StringBuilder();
+ break;
+ case '*':
+ rule.append("[^/]+");
+ break;
+ case '?':
+ rule.append("[^/]");
+ break;
+ default:
+ literal.appendCodePoint(c);
+ break;
+ }
+ }
+ if (literal.length() > 0)
+ rule.append(Pattern.quote(literal.toString()));
+
+ if (greedyEnding)
+ rule.append(".*");
+ rule.append("$");
+ pattern = rule.toString();
+ } else {
+ pattern = entry.substring(1);
+ }
+
+ if (csfs) rules.put(Pattern.compile(pattern), mode);
+ else rules.put(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE), mode);
+ }
+
+ for (String file : files) {
+ boolean act = false;
+
+ for (Map.Entry rule : rules.entrySet()) {
+ if (rule.getKey().matcher('/' + file.replace(File.separatorChar, '/')).find()) act = rule.getValue();
+ }
+
+ if (act) act(dir, file);
+ }
+ }
+
+ /**
+ * Perform an action on an included file
+ *
+ * @param dir Parent Directory
+ * @param name File Name
+ */
+ protected abstract void act(File dir, String name) throws IOException;
+}
diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/ReplacementScanner.java b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/ReplacementScanner.java
index 2783b17f..839cb0ca 100644
--- a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/ReplacementScanner.java
+++ b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/ReplacementScanner.java
@@ -10,7 +10,7 @@ import java.util.regex.Pattern;
/**
* File Replacement Scanner
*/
-public class ReplacementScanner {
+public class ReplacementScanner extends FileScanner {
private final Map replacements = new LinkedHashMap<>();
public ReplacementScanner(Map replacements) {
@@ -44,114 +44,11 @@ public class ReplacementScanner {
* @param whitelist File Whitelist
*/
public void replace(File dir, String... whitelist) throws IOException {
- List files;
- try {
- files = Util.reflect(Util.class.getDeclaredMethod("zipsearch", File.class, File.class), null, dir, dir);
- } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
- throw new IllegalStateException("Cannot access zipsearch()", e);
- }
- if (files.size() <= 0 || whitelist.length <= 0)
- return;
+ super.scan(dir, whitelist);
+ }
- boolean csfs = false;
- {
- long stamp = Math.round(Math.random() * 100000);
- File test1 = new File(dir, '.' + stamp + ".ss_fsc");
- File test2 = new File(dir, '.' + stamp + ".SS_FSC");
-
- test1.createNewFile();
- if (test2.createNewFile()) {
- csfs = true;
- test2.delete();
- }
- test1.delete();
- }
-
- LinkedHashMap rules = new LinkedHashMap();
- for (String entry : whitelist) {
- boolean mode = !entry.startsWith("!");
- if (!mode) entry = entry.substring(1);
-
- String pattern;
- if (!entry.startsWith("%")) {
- if (entry.startsWith("./"))
- entry = entry.substring(1);
-
- StringBuilder rule = new StringBuilder();
- if (entry.startsWith("**")) {
- entry = entry.substring(2);
- rule.append("^.*");
- } else if (entry.startsWith("/")) {
- rule.append("^");
- }
-
- boolean greedyEnding = false;
- if (entry.endsWith("**")) {
- entry = entry.substring(0, entry.length() - 2);
- greedyEnding = true;
- } else if (entry.endsWith("/")) {
- greedyEnding = true;
- }
-
- StringBuilder literal = new StringBuilder();
- for (PrimitiveIterator.OfInt i = entry.codePoints().iterator(); i.hasNext(); ) {
- int c = i.next();
- if ((c == '*' || c == '?' || c == '[') && literal.length() > 0) {
- rule.append(Pattern.quote(literal.toString()));
- literal = new StringBuilder();
- }
- switch (c) {
- case '\\':
- if (i.hasNext()) c = i.next();
- literal.appendCodePoint(c);
- case '[':
- for (boolean escaped = false; i.hasNext() && (c != ']' || escaped); c = i.next()) {
- if (c == '\\') escaped = !escaped;
- else escaped = false;
- literal.appendCodePoint(c);
- }
- if (c == ']' && literal.length() > 1) {
- literal.appendCodePoint(c);
- rule.append(literal.toString());
- }
- literal = new StringBuilder();
- break;
- case '*':
- rule.append("[^/]+");
- break;
- case '?':
- rule.append("[^/]");
- break;
- default:
- literal.appendCodePoint(c);
- break;
- }
- }
- if (literal.length() > 0)
- rule.append(Pattern.quote(literal.toString()));
-
- if (greedyEnding)
- rule.append(".*");
- rule.append("$");
- pattern = rule.toString();
- } else {
- pattern = entry.substring(1);
- }
-
- if (csfs) rules.put(Pattern.compile(pattern), mode);
- else rules.put(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE), mode);
- }
-
- for (String file : files) {
- boolean act = false;
-
- for (Map.Entry rule : rules.entrySet()) {
- if (rule.getKey().matcher('/' + file.replace(File.separatorChar, '/')).find()) act = rule.getValue();
- }
-
- if (act) replaceFile(new File(dir, file));
- }
- } private void replaceFile(File file) throws IOException {
+ protected void act(File dir, String name) throws IOException {
+ File file = new File(dir, name);
FileInputStream stream = new FileInputStream(file);
String string = Util.readAll(new InputStreamReader(stream));
stream.close();
diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/SubCommand.java b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/SubCommand.java
index 75da4a48..aa98be16 100644
--- a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/SubCommand.java
+++ b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/SubCommand.java
@@ -115,8 +115,6 @@ public final class SubCommand extends Command implements TabExecutor {
if (reload == null || !reload.isAlive()) (reload = new Thread(() -> {
if (args.length > 1) {
switch (args[1].toLowerCase()) {
- case "*":
- case "all":
case "hard":
case "system":
case "subdata":
@@ -127,6 +125,8 @@ public final class SubCommand extends Command implements TabExecutor {
Util.isException(() -> player.disconnect(plugin.getTranslation("restart")));
}
plugin.shutdown();
+ case "*":
+ case "all":
case "soft":
case "bungee":
case "bungeecord":
diff --git a/SubServers.Client/Bukkit/pom.xml b/SubServers.Client/Bukkit/pom.xml
index 4bf1c31a..c844d24a 100644
--- a/SubServers.Client/Bukkit/pom.xml
+++ b/SubServers.Client/Bukkit/pom.xml
@@ -46,7 +46,7 @@
net.ME1312.Galaxi
GalaxiUtil
- 21w15b
+ 21w23c
compile
true
diff --git a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/Library/ObjectPermission.java b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/Library/ObjectPermission.java
index fc671d3f..ff022792 100644
--- a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/Library/ObjectPermission.java
+++ b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/Library/ObjectPermission.java
@@ -55,7 +55,7 @@ public class ObjectPermission {
for (int p = 0; !permitted && p < permissions.length; ++p) {
String perm = permissions[p];
if (perm != null) {
- // Check all proxies & individual proxies permission
+ // Check all objects & individual objects permission
permitted = object.hasPermission(perm.replace("%", "*"))
|| object.hasPermission(perm.replace("%", string.toLowerCase()));
}
diff --git a/SubServers.Client/Common/pom.xml b/SubServers.Client/Common/pom.xml
index 4d0311ba..0b3f1eab 100644
--- a/SubServers.Client/Common/pom.xml
+++ b/SubServers.Client/Common/pom.xml
@@ -18,7 +18,7 @@
net.ME1312.Galaxi
GalaxiUtil
- 21w15b
+ 21w23c
provided
diff --git a/SubServers.Client/Sponge/pom.xml b/SubServers.Client/Sponge/pom.xml
index 4849a85c..de8ecb8e 100644
--- a/SubServers.Client/Sponge/pom.xml
+++ b/SubServers.Client/Sponge/pom.xml
@@ -28,7 +28,7 @@
net.ME1312.Galaxi
GalaxiUtil
- 21w15b
+ 21w23c
compile
true
diff --git a/SubServers.Host/pom.xml b/SubServers.Host/pom.xml
index 1cba1be8..d0482c86 100644
--- a/SubServers.Host/pom.xml
+++ b/SubServers.Host/pom.xml
@@ -31,14 +31,14 @@
net.ME1312.Galaxi
GalaxiEngine
- 21w15b
+ 21w23c
compile
true
net.ME1312.Galaxi
GalaxiUI
- 21w15b
+ 21w23c
runtime
true
diff --git a/SubServers.Host/src/net/ME1312/SubServers/Host/Library/FileScanner.java b/SubServers.Host/src/net/ME1312/SubServers/Host/Library/FileScanner.java
new file mode 100644
index 00000000..370917c2
--- /dev/null
+++ b/SubServers.Host/src/net/ME1312/SubServers/Host/Library/FileScanner.java
@@ -0,0 +1,142 @@
+package net.ME1312.SubServers.Host.Library;
+
+import net.ME1312.Galaxi.Library.Util;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.PrimitiveIterator;
+import java.util.regex.Pattern;
+
+/**
+ * File Scanner Base Class
+ */
+public abstract class FileScanner {
+
+ /**
+ * Scan a Directory
+ *
+ * @param dir Directory
+ * @param whitelist File Whitelist
+ */
+ protected void scan(File dir, String... whitelist) throws IOException {
+ List files;
+ try {
+ files = Util.reflect(Util.class.getDeclaredMethod("zipsearch", File.class, File.class), null, dir, dir);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ throw new IllegalStateException("Cannot access zipsearch()", e);
+ }
+ if (files.size() <= 0 || whitelist.length <= 0)
+ return;
+
+ boolean csfs = false;
+ {
+ long stamp = Math.round(Math.random() * 100000);
+ File test1 = new File(dir, '.' + stamp + ".ss_fsc");
+ File test2 = new File(dir, '.' + stamp + ".SS_FSC");
+
+ test1.createNewFile();
+ if (test2.createNewFile()) {
+ csfs = true;
+ test2.delete();
+ }
+ test1.delete();
+ }
+
+ LinkedHashMap rules = new LinkedHashMap();
+ for (String entry : whitelist) {
+ boolean mode = !entry.startsWith("!");
+ if (!mode) entry = entry.substring(1);
+
+ String pattern;
+ if (!entry.startsWith("%")) {
+ if (entry.startsWith("./"))
+ entry = entry.substring(1);
+
+ StringBuilder rule = new StringBuilder();
+ if (entry.startsWith("**")) {
+ entry = entry.substring(2);
+ rule.append("^.*");
+ } else if (entry.startsWith("/")) {
+ rule.append("^");
+ }
+
+ boolean greedyEnding = false;
+ if (entry.endsWith("**")) {
+ entry = entry.substring(0, entry.length() - 2);
+ greedyEnding = true;
+ } else if (entry.endsWith("/")) {
+ greedyEnding = true;
+ }
+
+ StringBuilder literal = new StringBuilder();
+ for (PrimitiveIterator.OfInt i = entry.codePoints().iterator(); i.hasNext(); ) {
+ int c = i.next();
+ if ((c == '*' || c == '?' || c == '[') && literal.length() > 0) {
+ rule.append(Pattern.quote(literal.toString()));
+ literal = new StringBuilder();
+ }
+ switch (c) {
+ case '\\':
+ if (i.hasNext()) c = i.next();
+ literal.appendCodePoint(c);
+ case '[':
+ for (boolean escaped = false; i.hasNext() && (c != ']' || escaped); c = i.next()) {
+ if (c == '\\') escaped = !escaped;
+ else escaped = false;
+ literal.appendCodePoint(c);
+ }
+ if (c == ']' && literal.length() > 1) {
+ literal.appendCodePoint(c);
+ rule.append(literal.toString());
+ }
+ literal = new StringBuilder();
+ break;
+ case '*':
+ rule.append("[^/]+");
+ break;
+ case '?':
+ rule.append("[^/]");
+ break;
+ default:
+ literal.appendCodePoint(c);
+ break;
+ }
+ }
+ if (literal.length() > 0)
+ rule.append(Pattern.quote(literal.toString()));
+
+ if (greedyEnding)
+ rule.append(".*");
+ rule.append("$");
+ pattern = rule.toString();
+ } else {
+ pattern = entry.substring(1);
+ }
+
+ if (csfs) rules.put(Pattern.compile(pattern), mode);
+ else rules.put(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE), mode);
+ }
+
+ for (String file : files) {
+ boolean act = false;
+
+ for (Map.Entry rule : rules.entrySet()) {
+ if (rule.getKey().matcher('/' + file.replace(File.separatorChar, '/')).find()) act = rule.getValue();
+ }
+
+ if (act) act(dir, file);
+ }
+ }
+
+ /**
+ * Perform an action on an included file
+ *
+ * @param dir Parent Directory
+ * @param name File Name
+ */
+ protected abstract void act(File dir, String name) throws IOException;
+}
diff --git a/SubServers.Host/src/net/ME1312/SubServers/Host/Library/ReplacementScanner.java b/SubServers.Host/src/net/ME1312/SubServers/Host/Library/ReplacementScanner.java
index 364c2f25..86215e59 100644
--- a/SubServers.Host/src/net/ME1312/SubServers/Host/Library/ReplacementScanner.java
+++ b/SubServers.Host/src/net/ME1312/SubServers/Host/Library/ReplacementScanner.java
@@ -3,14 +3,12 @@ package net.ME1312.SubServers.Host.Library;
import net.ME1312.Galaxi.Library.Util;
import java.io.*;
-import java.lang.reflect.InvocationTargetException;
import java.util.*;
-import java.util.regex.Pattern;
/**
* File Replacement Scanner
*/
-public class ReplacementScanner {
+public class ReplacementScanner extends FileScanner {
private final Map replacements = new LinkedHashMap<>();
public ReplacementScanner(Map replacements) {
@@ -44,114 +42,11 @@ public class ReplacementScanner {
* @param whitelist File Whitelist
*/
public void replace(File dir, String... whitelist) throws IOException {
- List files;
- try {
- files = Util.reflect(Util.class.getDeclaredMethod("zipsearch", File.class, File.class), null, dir, dir);
- } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
- throw new IllegalStateException("Cannot access zipsearch()", e);
- }
- if (files.size() <= 0 || whitelist.length <= 0)
- return;
+ super.scan(dir, whitelist);
+ }
- boolean csfs = false;
- {
- long stamp = Math.round(Math.random() * 100000);
- File test1 = new File(dir, '.' + stamp + ".ss_fsc");
- File test2 = new File(dir, '.' + stamp + ".SS_FSC");
-
- test1.createNewFile();
- if (test2.createNewFile()) {
- csfs = true;
- test2.delete();
- }
- test1.delete();
- }
-
- LinkedHashMap rules = new LinkedHashMap();
- for (String entry : whitelist) {
- boolean mode = !entry.startsWith("!");
- if (!mode) entry = entry.substring(1);
-
- String pattern;
- if (!entry.startsWith("%")) {
- if (entry.startsWith("./"))
- entry = entry.substring(1);
-
- StringBuilder rule = new StringBuilder();
- if (entry.startsWith("**")) {
- entry = entry.substring(2);
- rule.append("^.*");
- } else if (entry.startsWith("/")) {
- rule.append("^");
- }
-
- boolean greedyEnding = false;
- if (entry.endsWith("**")) {
- entry = entry.substring(0, entry.length() - 2);
- greedyEnding = true;
- } else if (entry.endsWith("/")) {
- greedyEnding = true;
- }
-
- StringBuilder literal = new StringBuilder();
- for (PrimitiveIterator.OfInt i = entry.codePoints().iterator(); i.hasNext(); ) {
- int c = i.next();
- if ((c == '*' || c == '?' || c == '[') && literal.length() > 0) {
- rule.append(Pattern.quote(literal.toString()));
- literal = new StringBuilder();
- }
- switch (c) {
- case '\\':
- if (i.hasNext()) c = i.next();
- literal.appendCodePoint(c);
- case '[':
- for (boolean escaped = false; i.hasNext() && (c != ']' || escaped); c = i.next()) {
- if (c == '\\') escaped = !escaped;
- else escaped = false;
- literal.appendCodePoint(c);
- }
- if (c == ']' && literal.length() > 1) {
- literal.appendCodePoint(c);
- rule.append(literal.toString());
- }
- literal = new StringBuilder();
- break;
- case '*':
- rule.append("[^/]+");
- break;
- case '?':
- rule.append("[^/]");
- break;
- default:
- literal.appendCodePoint(c);
- break;
- }
- }
- if (literal.length() > 0)
- rule.append(Pattern.quote(literal.toString()));
-
- if (greedyEnding)
- rule.append(".*");
- rule.append("$");
- pattern = rule.toString();
- } else {
- pattern = entry.substring(1);
- }
-
- if (csfs) rules.put(Pattern.compile(pattern), mode);
- else rules.put(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE), mode);
- }
-
- for (String file : files) {
- boolean act = false;
-
- for (Map.Entry rule : rules.entrySet()) {
- if (rule.getKey().matcher('/' + file.replace(File.separatorChar, '/')).find()) act = rule.getValue();
- }
-
- if (act) replaceFile(new File(dir, file));
- }
- } private void replaceFile(File file) throws IOException {
+ protected void act(File dir, String name) throws IOException {
+ File file = new File(dir, name);
FileInputStream stream = new FileInputStream(file);
String string = Util.readAll(new InputStreamReader(stream));
stream.close();
diff --git a/SubServers.Host/src/net/ME1312/SubServers/Host/SubCommand.java b/SubServers.Host/src/net/ME1312/SubServers/Host/SubCommand.java
index 1124e309..532e07c1 100644
--- a/SubServers.Host/src/net/ME1312/SubServers/Host/SubCommand.java
+++ b/SubServers.Host/src/net/ME1312/SubServers/Host/SubCommand.java
@@ -1,5 +1,6 @@
package net.ME1312.SubServers.Host;
+import net.ME1312.Galaxi.Command.CommandProcessor.Status;
import net.ME1312.Galaxi.Engine.CommandParser;
import net.ME1312.Galaxi.Engine.GalaxiEngine;
import net.ME1312.Galaxi.Library.AsyncConsolidator;
@@ -59,7 +60,10 @@ public class SubCommand {
args.removeFirst();
CommandParser console = GalaxiEngine.getInstance().getCommandProcessor();
- console.runCommand(sender, console.escapeCommand(rargs[0], args.toArray(new String[0])));
+ String command = console.escapeCommand(rargs[0], args.toArray(new String[0]));
+ if (console.runCommand(sender, command) == Status.UNKNOWN) {
+ sender.sendMessage("Unknown Command: " + command);
+ }
} else {
sender.sendMessage("Usage: /" + label + " [Args...]");
}
diff --git a/SubServers.Sync/pom.xml b/SubServers.Sync/pom.xml
index 4cd56702..00b1b056 100644
--- a/SubServers.Sync/pom.xml
+++ b/SubServers.Sync/pom.xml
@@ -28,14 +28,14 @@
net.ME1312.Galaxi
GalaxiUtil
- 21w15b
+ 21w23c
compile
true
net.ME1312.Galaxi
GalaxiEngine
- 21w15b
+ 21w23c
provided