mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-25 23:07:50 +00:00
Move Data, Plugin, Script, Packet access from Strings to Paths (#1839)
* Move Data, Plugin, Script, Packet access from Strings to Paths - No longer dump default Data files to folder on launch - Allow Scripts to be loaded from Resources zip - Lay groundwork for Plugins to be loaded from zip
This commit is contained in:
parent
f6ce7e349d
commit
dd6e1bb8a3
@ -84,7 +84,7 @@ public class ConfigContainer {
|
||||
public String resources = "./resources/";
|
||||
public String data = "./data/";
|
||||
public String packets = "./packets/";
|
||||
public String scripts = "./resources/Scripts/";
|
||||
public String scripts = "resources:Scripts/";
|
||||
public String plugins = "./plugins/";
|
||||
|
||||
// UNUSED (potentially added later?)
|
||||
|
@ -1,16 +1,9 @@
|
||||
package emu.grasscutter.config;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Stream;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Locale;
|
||||
|
||||
import static emu.grasscutter.Grasscutter.config;
|
||||
|
||||
@ -34,46 +27,9 @@ public final class Configuration extends ConfigContainer {
|
||||
public static final Locale FALLBACK_LANGUAGE = config.language.fallback;
|
||||
public static final String DOCUMENT_LANGUAGE = config.language.document;
|
||||
private static final String DATA_FOLDER = config.folderStructure.data;
|
||||
private static final String RESOURCES_FOLDER = config.folderStructure.resources;
|
||||
private static final String PLUGINS_FOLDER = config.folderStructure.plugins;
|
||||
private static final String SCRIPTS_FOLDER = config.folderStructure.scripts;
|
||||
private static final String PACKETS_FOLDER = config.folderStructure.packets;
|
||||
private static final FileSystem RESOURCES_FILE_SYSTEM; // Not sure about lifetime rules on this one, might be safe to remove
|
||||
private static final Path RESOURCES_PATH;
|
||||
static {
|
||||
FileSystem fs = null;
|
||||
Path path = Path.of(RESOURCES_FOLDER);
|
||||
if (RESOURCES_FOLDER.endsWith(".zip")) { // Would be nice to support .tar.gz too at some point, but it doesn't come for free in Java
|
||||
try {
|
||||
fs = FileSystems.newFileSystem(path);
|
||||
} catch (IOException e) {
|
||||
Grasscutter.getLogger().error("Failed to load resources zip \"" + RESOURCES_FOLDER + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (fs != null) {
|
||||
var root = fs.getPath("");
|
||||
try (Stream<Path> pathStream = java.nio.file.Files.find(root, 3, (p, a) -> {
|
||||
var filename = p.getFileName();
|
||||
if (filename == null) return false;
|
||||
return filename.toString().equals("ExcelBinOutput");
|
||||
})) {
|
||||
var excelBinOutput = pathStream.findFirst();
|
||||
if (excelBinOutput.isPresent()) {
|
||||
path = excelBinOutput.get().getParent();
|
||||
if (path == null)
|
||||
path = root;
|
||||
Grasscutter.getLogger().debug("Resources will be loaded from \"" + RESOURCES_FOLDER + "/" + path.toString() + "\"");
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Failed to find ExcelBinOutput in resources zip \"" + RESOURCES_FOLDER + "\"");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Grasscutter.getLogger().error("Failed to scan resources zip \"" + RESOURCES_FOLDER + "\"");
|
||||
}
|
||||
}
|
||||
RESOURCES_FILE_SYSTEM = fs;
|
||||
RESOURCES_PATH = path;
|
||||
};
|
||||
|
||||
public static final Server SERVER = config.server;
|
||||
public static final Database DATABASE = config.databaseInfo;
|
||||
@ -93,22 +49,27 @@ public final class Configuration extends ConfigContainer {
|
||||
/*
|
||||
* Utilities
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static String DATA() {
|
||||
return DATA_FOLDER;
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public static String DATA(String path) {
|
||||
return Path.of(DATA_FOLDER, path).toString();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public static Path getResourcePath(String path) {
|
||||
return RESOURCES_PATH.resolve(path);
|
||||
return FileUtils.getResourcePath(path);
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public static String RESOURCE(String path) {
|
||||
return getResourcePath(path).toString();
|
||||
return FileUtils.getResourcePath(path).toString();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public static String PLUGIN() {
|
||||
return PLUGINS_FOLDER;
|
||||
}
|
||||
@ -117,10 +78,12 @@ public final class Configuration extends ConfigContainer {
|
||||
return Path.of(PLUGINS_FOLDER, path).toString();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public static String SCRIPT(String path) {
|
||||
return Path.of(SCRIPTS_FOLDER, path).toString();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public static String PACKET(String path) {
|
||||
return Path.of(PACKETS_FOLDER, path).toString();
|
||||
}
|
||||
|
@ -5,15 +5,12 @@ import emu.grasscutter.server.http.handlers.GachaHandler;
|
||||
import emu.grasscutter.tools.Tools;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import emu.grasscutter.utils.JsonUtils;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.DATA;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -59,15 +56,17 @@ public class DataLoader {
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public static InputStream load(String resourcePath, boolean useFallback) throws FileNotFoundException {
|
||||
if (Utils.fileExists(DATA(resourcePath))) {
|
||||
Path path = useFallback
|
||||
? FileUtils.getDataPath(resourcePath)
|
||||
: FileUtils.getDataUserPath(resourcePath);
|
||||
if (Files.exists(path)) {
|
||||
// Data is in the resource directory
|
||||
return new FileInputStream(DATA(resourcePath));
|
||||
} else {
|
||||
if (useFallback) {
|
||||
return FileUtils.readResourceAsStream("/defaults/data/" + resourcePath);
|
||||
try {
|
||||
return Files.newInputStream(path);
|
||||
} catch (IOException e) {
|
||||
throw new FileNotFoundException(e.getMessage()); // This is evil but so is changing the function signature at this point
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -95,11 +94,11 @@ public class DataLoader {
|
||||
|
||||
if (filenames == null) {
|
||||
Grasscutter.getLogger().error("We were unable to locate your default data files.");
|
||||
} else for (Path file : filenames) {
|
||||
String relativePath = String.valueOf(file).split("defaults[\\\\\\/]data[\\\\\\/]")[1];
|
||||
} //else for (Path file : filenames) {
|
||||
// String relativePath = String.valueOf(file).split("defaults[\\\\\\/]data[\\\\\\/]")[1];
|
||||
|
||||
checkAndCopyData(relativePath);
|
||||
}
|
||||
// checkAndCopyData(relativePath);
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("An error occurred while trying to check the data folder.", e);
|
||||
}
|
||||
@ -108,36 +107,25 @@ public class DataLoader {
|
||||
}
|
||||
|
||||
private static void checkAndCopyData(String name) {
|
||||
String filePath = Utils.toFilePath(DATA(name));
|
||||
// TODO: Revisit this if default dumping is ever reintroduced
|
||||
Path filePath = FileUtils.getDataPath(name);
|
||||
|
||||
if (!Utils.fileExists(filePath)) {
|
||||
// Check if file is in subdirectory
|
||||
if (name.contains("/")) {
|
||||
String[] path = name.split("/");
|
||||
|
||||
String folder = "";
|
||||
for (int i = 0; i < (path.length - 1); i++) {
|
||||
folder += path[i] + "/";
|
||||
|
||||
// Make sure the current folder exists
|
||||
String folderToCreate = Utils.toFilePath(DATA(folder));
|
||||
if (!Utils.fileExists(folderToCreate)) {
|
||||
Grasscutter.getLogger().info("Creating data folder '" + folder + "'");
|
||||
Utils.createFolder(folderToCreate);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Files.exists(filePath)) {
|
||||
var root = filePath.getParent();
|
||||
if (root.toFile().mkdirs())
|
||||
Grasscutter.getLogger().info("Created data folder '" + root + "'");
|
||||
|
||||
Grasscutter.getLogger().info("Creating default '" + name + "' data");
|
||||
FileUtils.copyResource("/defaults/data/" + name, filePath);
|
||||
FileUtils.copyResource("/defaults/data/" + name, filePath.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateGachaMappings() {
|
||||
if (!Utils.fileExists(GachaHandler.gachaMappings)) {
|
||||
var path = GachaHandler.getGachaMappingsPath();
|
||||
if (!Files.exists(path)) {
|
||||
try {
|
||||
Grasscutter.getLogger().info("Creating default '" + GachaHandler.gachaMappings + "' data");
|
||||
Tools.createGachaMapping(GachaHandler.gachaMappings);
|
||||
Grasscutter.getLogger().info("Creating default '" + path.toString() + "' data");
|
||||
Tools.createGachaMappings(path);
|
||||
} catch (Exception exception) {
|
||||
Grasscutter.getLogger().warn("Failed to create gacha mappings. \n" + exception);
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.DATA;
|
||||
import static emu.grasscutter.config.Configuration.getResourcePath;
|
||||
import static emu.grasscutter.utils.FileUtils.getDataPath;
|
||||
import static emu.grasscutter.utils.FileUtils.getResourcePath;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
public class ResourceLoader {
|
||||
@ -174,7 +174,7 @@ public class ResourceLoader {
|
||||
|
||||
// Read from cached file if exists
|
||||
try {
|
||||
embryoList = JsonUtils.loadToList(DATA("AbilityEmbryos.json"), AbilityEmbryoEntry.class);
|
||||
embryoList = JsonUtils.loadToList(getDataPath("AbilityEmbryos.json"), AbilityEmbryoEntry.class);
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
if (embryoList == null) {
|
||||
@ -318,7 +318,7 @@ public class ResourceLoader {
|
||||
List<OpenConfigEntry> list = null;
|
||||
|
||||
try {
|
||||
list = JsonUtils.loadToList(DATA("OpenConfig.json"), OpenConfigEntry.class);
|
||||
list = JsonUtils.loadToList(getDataPath("OpenConfig.json"), OpenConfigEntry.class);
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
if (list == null) {
|
||||
|
@ -33,6 +33,7 @@ import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.game.GameServerTickEvent;
|
||||
import emu.grasscutter.server.packet.send.PacketDoGachaRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketGachaWishRsp;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
@ -383,8 +384,7 @@ public class GachaSystem extends BaseGameSystem {
|
||||
if (this.watchService == null) {
|
||||
try {
|
||||
this.watchService = FileSystems.getDefault().newWatchService();
|
||||
Path path = new File(DATA()).toPath();
|
||||
path.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}, SensitivityWatchEventModifier.HIGH);
|
||||
FileUtils.getDataUserPath("").register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}, SensitivityWatchEventModifier.HIGH);
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load the Gacha Manager Watch Service. If ServerOptions.watchGacha is true it will not auto-reload");
|
||||
e.printStackTrace();
|
||||
|
@ -190,13 +190,12 @@ public class GameMainQuest {
|
||||
}
|
||||
public void addRewindPoints() {
|
||||
Bindings bindings = ScriptLoader.getEngine().createBindings();
|
||||
|
||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||
SCRIPT("Quest/Share/Q" + getParentQuestId() + "ShareConfig." + ScriptLoader.getScriptType()));
|
||||
String script = "Quest/Share/Q" + getParentQuestId() + "ShareConfig.lua";
|
||||
CompiledScript cs = ScriptLoader.getScript(script);
|
||||
|
||||
//mainQuest 303 doesn't have a ShareConfig
|
||||
if (cs == null) {
|
||||
Grasscutter.getLogger().debug("Couldn't find Q" + getParentQuestId() + "ShareConfig." + ScriptLoader.getScriptType());
|
||||
Grasscutter.getLogger().debug("Couldn't find " + script);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,11 @@ package emu.grasscutter.plugin;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.plugin.api.ServerHook;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLClassLoader;
|
||||
@ -37,7 +37,7 @@ public abstract class Plugin {
|
||||
|
||||
this.identifier = identifier;
|
||||
this.classLoader = classLoader;
|
||||
this.dataFolder = new File(PLUGIN(), identifier.name);
|
||||
this.dataFolder = FileUtils.getPluginPath(identifier.name).toFile();
|
||||
this.logger = LoggerFactory.getLogger(identifier.name);
|
||||
|
||||
if (!this.dataFolder.exists() && !this.dataFolder.mkdirs()) {
|
||||
|
@ -2,13 +2,12 @@ package emu.grasscutter.plugin;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.server.event.*;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import emu.grasscutter.utils.JsonUtils;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import lombok.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.PLUGIN;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import java.io.*;
|
||||
@ -43,7 +42,7 @@ public final class PluginManager {
|
||||
* Loads plugins from the config-specified directory.
|
||||
*/
|
||||
private void loadPlugins() {
|
||||
File pluginsDir = new File(Utils.toFilePath(PLUGIN()));
|
||||
File pluginsDir = FileUtils.getPluginPath("").toFile();
|
||||
if (!pluginsDir.exists() && !pluginsDir.mkdirs()) {
|
||||
Grasscutter.getLogger().error(translate("plugin.directory_failed", pluginsDir.getAbsolutePath()));
|
||||
return;
|
||||
|
@ -9,6 +9,9 @@ import emu.grasscutter.scripts.constants.ScriptRegionShape;
|
||||
import emu.grasscutter.scripts.data.SceneMeta;
|
||||
import emu.grasscutter.scripts.serializer.LuaSerializer;
|
||||
import emu.grasscutter.scripts.serializer.Serializer;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import lombok.Getter;
|
||||
|
||||
import org.luaj.vm2.LuaTable;
|
||||
import org.luaj.vm2.LuaValue;
|
||||
import org.luaj.vm2.lib.OneArgFunction;
|
||||
@ -19,6 +22,8 @@ import javax.script.*;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@ -26,12 +31,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ScriptLoader {
|
||||
private static ScriptEngineManager sm;
|
||||
private static ScriptEngine engine;
|
||||
@Getter private static ScriptEngine engine;
|
||||
private static ScriptEngineFactory factory;
|
||||
private static String fileType;
|
||||
private static Serializer serializer;
|
||||
private static ScriptLib scriptLib;
|
||||
private static LuaValue scriptLibLua;
|
||||
@Getter private static Serializer serializer;
|
||||
@Getter private static ScriptLib scriptLib;
|
||||
@Getter private static LuaValue scriptLibLua;
|
||||
/**
|
||||
* suggest GC to remove it if the memory is less
|
||||
*/
|
||||
@ -52,7 +56,6 @@ public class ScriptLoader {
|
||||
factory = getEngine().getFactory();
|
||||
|
||||
// Lua stuff
|
||||
fileType = "lua";
|
||||
serializer = new LuaSerializer();
|
||||
|
||||
// Set engine to replace require as a temporary fix to missing scripts
|
||||
@ -81,26 +84,6 @@ public class ScriptLoader {
|
||||
ctx.globals.set("ScriptLib", scriptLibLua);
|
||||
}
|
||||
|
||||
public static ScriptEngine getEngine() {
|
||||
return engine;
|
||||
}
|
||||
|
||||
public static String getScriptType() {
|
||||
return fileType;
|
||||
}
|
||||
|
||||
public static Serializer getSerializer() {
|
||||
return serializer;
|
||||
}
|
||||
|
||||
public static ScriptLib getScriptLib() {
|
||||
return scriptLib;
|
||||
}
|
||||
|
||||
public static LuaValue getScriptLibLua() {
|
||||
return scriptLibLua;
|
||||
}
|
||||
|
||||
public static <T> Optional<T> tryGet(SoftReference<T> softReference){
|
||||
try{
|
||||
return Optional.ofNullable(softReference.get());
|
||||
@ -108,6 +91,8 @@ public class ScriptLoader {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public static CompiledScript getScriptByPath(String path) {
|
||||
var sc = tryGet(scriptsCache.get(path));
|
||||
if (sc.isPresent()) {
|
||||
@ -131,6 +116,26 @@ public class ScriptLoader {
|
||||
|
||||
}
|
||||
|
||||
public static CompiledScript getScript(String path) {
|
||||
var sc = tryGet(scriptsCache.get(path));
|
||||
if (sc.isPresent()) {
|
||||
return sc.get();
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().debug("Loading script " + path);
|
||||
final Path scriptPath = FileUtils.getScriptPath(path);
|
||||
if (!Files.exists(scriptPath)) return null;
|
||||
|
||||
try {
|
||||
var script = ((Compilable) getEngine()).compile(Files.newBufferedReader(scriptPath));
|
||||
scriptsCache.put(path, new SoftReference<>(script));
|
||||
return script;
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Loading script {} failed!", path, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static SceneMeta getSceneMeta(int sceneId) {
|
||||
return tryGet(sceneMetaCache.get(sceneId)).orElseGet(() -> {
|
||||
var instance = SceneMeta.of(sceneId);
|
||||
|
@ -14,8 +14,6 @@ import javax.script.Bindings;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.SCRIPT;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -52,8 +50,7 @@ public class SceneBlock {
|
||||
this.sceneId = sceneId;
|
||||
this.setLoaded(true);
|
||||
|
||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_block" + this.id + "." + ScriptLoader.getScriptType()));
|
||||
CompiledScript cs = ScriptLoader.getScript("Scene/" + sceneId + "/scene" + sceneId + "_block" + this.id + ".lua");
|
||||
|
||||
if (cs == null) {
|
||||
return null;
|
||||
|
@ -11,8 +11,6 @@ import javax.script.Bindings;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.SCRIPT;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@ -84,8 +82,7 @@ public class SceneGroup {
|
||||
|
||||
this.bindings = ScriptLoader.getEngine().createBindings();
|
||||
|
||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_group" + this.id + "." + ScriptLoader.getScriptType()));
|
||||
CompiledScript cs = ScriptLoader.getScript("Scene/" + sceneId + "/scene" + sceneId + "_group" + this.id + ".lua");
|
||||
|
||||
if (cs == null) {
|
||||
return this;
|
||||
|
@ -12,8 +12,6 @@ import javax.script.Bindings;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.SCRIPT;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
@ -35,8 +33,7 @@ public class SceneMeta {
|
||||
|
||||
public SceneMeta load(int sceneId) {
|
||||
// Get compiled script if cached
|
||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "." + ScriptLoader.getScriptType()));
|
||||
CompiledScript cs = ScriptLoader.getScript("Scene/" + sceneId + "/scene" + sceneId + ".lua");
|
||||
|
||||
if (cs == null) {
|
||||
Grasscutter.getLogger().warn("No script found for scene " + sceneId);
|
||||
|
@ -2,7 +2,8 @@ package emu.grasscutter.server.game;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Set;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
||||
import emu.grasscutter.game.Account;
|
||||
@ -16,6 +17,8 @@ import emu.grasscutter.utils.FileUtils;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.*;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
@ -24,14 +27,14 @@ public class GameSession implements GameSessionManager.KcpChannel {
|
||||
private final GameServer server;
|
||||
private GameSessionManager.KcpTunnel tunnel;
|
||||
|
||||
private Account account;
|
||||
private Player player;
|
||||
@Getter @Setter private Account account;
|
||||
@Getter private Player player;
|
||||
|
||||
private boolean useSecretKey;
|
||||
private SessionState state;
|
||||
@Setter private boolean useSecretKey;
|
||||
@Getter @Setter private SessionState state;
|
||||
|
||||
private int clientTime;
|
||||
private long lastPingTime;
|
||||
@Getter private int clientTime;
|
||||
@Getter private long lastPingTime;
|
||||
private int lastClientSeq = 10;
|
||||
|
||||
public GameSession(GameServer server) {
|
||||
@ -56,52 +59,20 @@ public class GameSession implements GameSessionManager.KcpChannel {
|
||||
return useSecretKey;
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setAccount(Account account) {
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public String getAccountId() {
|
||||
return this.getAccount().getId();
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public synchronized void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
this.player.setSession(this);
|
||||
this.player.setAccount(this.getAccount());
|
||||
}
|
||||
|
||||
public SessionState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(SessionState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public boolean isLoggedIn() {
|
||||
return this.getPlayer() != null;
|
||||
}
|
||||
|
||||
public void setUseSecretKey(boolean useSecretKey) {
|
||||
this.useSecretKey = useSecretKey;
|
||||
}
|
||||
|
||||
public int getClientTime() {
|
||||
return this.clientTime;
|
||||
}
|
||||
|
||||
public long getLastPingTime() {
|
||||
return lastPingTime;
|
||||
}
|
||||
|
||||
public void updateLastPingTime(int clientTime) {
|
||||
this.clientTime = clientTime;
|
||||
this.lastPingTime = System.currentTimeMillis();
|
||||
@ -112,8 +83,8 @@ public class GameSession implements GameSessionManager.KcpChannel {
|
||||
}
|
||||
|
||||
public void replayPacket(int opcode, String name) {
|
||||
String filePath = PACKET(name);
|
||||
File p = new File(filePath);
|
||||
Path filePath = FileUtils.getPluginPath(name);
|
||||
File p = filePath.toFile();
|
||||
|
||||
if (!p.exists()) return;
|
||||
|
||||
|
@ -11,42 +11,38 @@ import emu.grasscutter.data.excels.MonsterData;
|
||||
import emu.grasscutter.data.excels.SceneData;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import emu.grasscutter.utils.Language;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.Context;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class HandbookRequestHandler implements DocumentationHandler {
|
||||
private List<String> handbookHtmls;
|
||||
private final String template;
|
||||
|
||||
public HandbookRequestHandler() {
|
||||
final File templateFile = new File(Utils.toFilePath(DATA("documentation/handbook.html")));
|
||||
if (templateFile.exists()) {
|
||||
this.template = new String(FileUtils.read(templateFile), StandardCharsets.UTF_8);
|
||||
this.handbookHtmls = generateHandbookHtmls();
|
||||
} else {
|
||||
Grasscutter.getLogger().warn("File does not exist: " + templateFile);
|
||||
this.template = null;
|
||||
var templatePath = FileUtils.getDataPath("documentation/handbook.html");
|
||||
try {
|
||||
this.handbookHtmls = generateHandbookHtmls(Files.readString(templatePath));
|
||||
} catch (IOException ignored) {
|
||||
Grasscutter.getLogger().warn("File does not exist: " + templatePath);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Context ctx) {
|
||||
final int langIdx = Language.TextStrings.MAP_LANGUAGES.getOrDefault(DOCUMENT_LANGUAGE, 0); // TODO: This should really be based off the client language somehow
|
||||
if (template == null) {
|
||||
if (this.handbookHtmls == null) {
|
||||
ctx.status(500);
|
||||
} else {
|
||||
ctx.contentType(ContentType.TEXT_HTML);
|
||||
ctx.result(handbookHtmls.get(langIdx));
|
||||
ctx.result(this.handbookHtmls.get(langIdx));
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> generateHandbookHtmls() {
|
||||
private List<String> generateHandbookHtmls(String template) {
|
||||
final int NUM_LANGUAGES = Language.TextStrings.NUM_LANGUAGES;
|
||||
final List<String> output = new ArrayList<>(NUM_LANGUAGES);
|
||||
final List<Language> languages = Language.TextStrings.getLanguages();
|
||||
|
@ -1,29 +1,28 @@
|
||||
package emu.grasscutter.server.http.documentation;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.DATA;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.Context;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
final class RootRequestHandler implements DocumentationHandler {
|
||||
|
||||
private final String template;
|
||||
|
||||
public RootRequestHandler() {
|
||||
final File templateFile = new File(Utils.toFilePath(DATA("documentation/index.html")));
|
||||
if (templateFile.exists()) {
|
||||
template = new String(FileUtils.read(templateFile), StandardCharsets.UTF_8);
|
||||
} else {
|
||||
Grasscutter.getLogger().warn("File does not exist: " + templateFile);
|
||||
template = null;
|
||||
var templatePath = FileUtils.getDataPath("documentation/index.html");
|
||||
String t = null;
|
||||
try {
|
||||
t = Files.readString(templatePath);
|
||||
} catch (IOException ignored) {
|
||||
Grasscutter.getLogger().warn("File does not exist: " + templatePath);
|
||||
}
|
||||
this.template = t;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -5,7 +5,6 @@ import emu.grasscutter.data.DataLoader;
|
||||
import emu.grasscutter.server.http.objects.HttpJsonResponse;
|
||||
import emu.grasscutter.server.http.Router;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import io.javalin.Javalin;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.Context;
|
||||
@ -74,7 +73,7 @@ public final class AnnouncementsHandler implements Router {
|
||||
|
||||
private static void getPageResources(Context ctx) {
|
||||
try (InputStream filestream = DataLoader.load(ctx.path())) {
|
||||
String possibleFilename = Utils.toFilePath(DATA(ctx.path()));
|
||||
String possibleFilename = ctx.path();
|
||||
|
||||
ContentType fromExtension = ContentType.getContentTypeByExtension(possibleFilename.substring(possibleFilename.lastIndexOf(".") + 1));
|
||||
ctx.contentType(fromExtension != null ? fromExtension : ContentType.APPLICATION_OCTET_STREAM);
|
||||
|
@ -13,31 +13,36 @@ import io.javalin.Javalin;
|
||||
import io.javalin.http.ContentType;
|
||||
import io.javalin.http.Context;
|
||||
import io.javalin.http.staticfiles.Location;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.DATA;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
/**
|
||||
* Handles all gacha-related HTTP requests.
|
||||
*/
|
||||
public final class GachaHandler implements Router {
|
||||
public static final String gachaMappings = DATA(Utils.toFilePath("gacha/mappings.js"));
|
||||
@Getter private static final Path gachaMappingsPath = FileUtils.getDataUserPath("gacha/mappings.js");
|
||||
@Deprecated(forRemoval = true)
|
||||
public static final String gachaMappings = gachaMappingsPath.toString();
|
||||
|
||||
@Override public void applyRoutes(Javalin javalin) {
|
||||
javalin.get("/gacha", GachaHandler::gachaRecords);
|
||||
javalin.get("/gacha/details", GachaHandler::gachaDetails);
|
||||
|
||||
javalin._conf.addSinglePageRoot("/gacha/mappings", gachaMappings, Location.EXTERNAL);
|
||||
javalin._conf.addSinglePageRoot("/gacha/mappings", gachaMappingsPath.toString(), Location.EXTERNAL); // TODO: This ***must*** be changed to take the Path not a String. This might involve upgrading Javalin.
|
||||
}
|
||||
|
||||
private static void gachaRecords(Context ctx) {
|
||||
File recordsTemplate = new File(Utils.toFilePath(DATA("gacha/records.html")));
|
||||
File recordsTemplate = FileUtils.getDataPath("gacha/records.html").toFile();
|
||||
if (!recordsTemplate.exists()) {
|
||||
Grasscutter.getLogger().warn("File does not exist: " + recordsTemplate);
|
||||
ctx.status(500);
|
||||
@ -77,13 +82,7 @@ public final class GachaHandler implements Router {
|
||||
}
|
||||
|
||||
private static void gachaDetails(Context ctx) {
|
||||
File detailsTemplate = new File(Utils.toFilePath(DATA("gacha/details.html")));
|
||||
if (!detailsTemplate.exists()) {
|
||||
Grasscutter.getLogger().warn("File does not exist: " + detailsTemplate);
|
||||
ctx.status(500);
|
||||
return;
|
||||
}
|
||||
|
||||
Path detailsTemplate = FileUtils.getDataPath("gacha/details.html");
|
||||
String sessionKey = ctx.queryParam("s");
|
||||
Account account = DatabaseHelper.getAccountBySessionKey(sessionKey);
|
||||
if (account == null) {
|
||||
@ -96,7 +95,14 @@ public final class GachaHandler implements Router {
|
||||
return;
|
||||
}
|
||||
|
||||
String template = new String(FileUtils.read(detailsTemplate), StandardCharsets.UTF_8);
|
||||
String template;
|
||||
try {
|
||||
template = Files.readString(detailsTemplate);
|
||||
} catch (IOException e) {
|
||||
Grasscutter.getLogger().warn("Failed to read data/gacha/details.html");
|
||||
ctx.status(500);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add translated title etc. to the page.
|
||||
template = template.replace("{{TITLE}}", translate(player, "gacha.details.title"))
|
||||
|
@ -7,6 +7,7 @@ import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
@ -28,6 +29,7 @@ import it.unimi.dsi.fastutil.ints.Int2IntRBTreeMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.*;
|
||||
import static emu.grasscutter.utils.FileUtils.getResourcePath;
|
||||
|
||||
public final class Tools {
|
||||
public static void createGmHandbooks() throws Exception {
|
||||
@ -109,10 +111,6 @@ public final class Tools {
|
||||
Grasscutter.getLogger().info("GM Handbooks generated!");
|
||||
}
|
||||
|
||||
public static void createGachaMapping(String location) throws Exception {
|
||||
createGachaMappings(location);
|
||||
}
|
||||
|
||||
public static List<String> createGachaMappingJsons() {
|
||||
final int NUM_LANGUAGES = Language.TextStrings.NUM_LANGUAGES;
|
||||
final Language.TextStrings CHARACTER = Language.getTextMapKey(4233146695L); // "Character" in EN
|
||||
@ -196,7 +194,7 @@ public final class Tools {
|
||||
return sbs.stream().map(StringBuilder::toString).toList();
|
||||
}
|
||||
|
||||
public static void createGachaMappings(String location) throws Exception {
|
||||
public static void createGachaMappings(Path location) throws IOException {
|
||||
ResourceLoader.loadResources();
|
||||
List<String> jsons = createGachaMappingJsons();
|
||||
StringBuilder sb = new StringBuilder("mappings = {\n");
|
||||
@ -207,13 +205,9 @@ public final class Tools {
|
||||
sb.setLength(sb.length() - 2); // Delete trailing ",\n"
|
||||
sb.append("\n}");
|
||||
|
||||
try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(location), StandardCharsets.UTF_8), false)) {
|
||||
// if the user made choices for language, I assume it's okay to assign his/her selected language to "en-us"
|
||||
// since it's the fallback language and there will be no difference in the gacha record page.
|
||||
// The end-user can still modify the `gacha/mappings.js` directly to enable multilingual for the gacha record system.
|
||||
writer.println(sb);
|
||||
Grasscutter.getLogger().info("Mappings generated to " + location + " !");
|
||||
}
|
||||
Files.createDirectories(location.getParent());
|
||||
Files.writeString(location, sb);
|
||||
Grasscutter.getLogger().info("Mappings generated to " + location);
|
||||
}
|
||||
|
||||
public static List<String> getAvailableLanguage() {
|
||||
|
@ -10,11 +10,107 @@ import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class FileUtils {
|
||||
private static final FileSystem JAR_FILE_SYSTEM;
|
||||
private static final Path DATA_DEFAULT_PATH;
|
||||
private static final Path DATA_USER_PATH = Path.of(Grasscutter.config.folderStructure.data);
|
||||
private static final Path PACKETS_PATH = Path.of(Grasscutter.config.folderStructure.packets);
|
||||
private static final Path PLUGINS_PATH = Path.of(Grasscutter.config.folderStructure.plugins);
|
||||
private static final Path RESOURCES_PATH;
|
||||
private static final Path SCRIPTS_PATH;
|
||||
static {
|
||||
FileSystem fs = null;
|
||||
Path path = DATA_USER_PATH;
|
||||
// Setup Data paths
|
||||
// Get pathUri of the current running JAR
|
||||
try {
|
||||
URI jarUri = Grasscutter.class.getProtectionDomain()
|
||||
.getCodeSource()
|
||||
.getLocation()
|
||||
.toURI();
|
||||
fs = FileSystems.newFileSystem(Path.of(jarUri));
|
||||
path = fs.getPath("/defaults/data");
|
||||
} catch (URISyntaxException | IOException e) {
|
||||
// Failed to load this jar. How?
|
||||
System.err.println("Failed to load jar?????????????????????");
|
||||
} finally {
|
||||
JAR_FILE_SYSTEM = fs;
|
||||
DATA_DEFAULT_PATH = path;
|
||||
}
|
||||
|
||||
// Setup Resources path
|
||||
final String resources = Grasscutter.config.folderStructure.resources;
|
||||
fs = null;
|
||||
path = Path.of(resources);
|
||||
if (resources.endsWith(".zip")) { // Would be nice to support .tar.gz too at some point, but it doesn't come for free in Java
|
||||
try {
|
||||
fs = FileSystems.newFileSystem(path);
|
||||
} catch (IOException e) {
|
||||
Grasscutter.getLogger().error("Failed to load resources zip \"" + resources + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (fs != null) {
|
||||
var root = fs.getPath("");
|
||||
try (Stream<Path> pathStream = Files.find(root, 3, (p, a) -> {
|
||||
var filename = p.getFileName();
|
||||
if (filename == null) return false;
|
||||
return filename.toString().equals("ExcelBinOutput");
|
||||
})) {
|
||||
var excelBinOutput = pathStream.findFirst();
|
||||
if (excelBinOutput.isPresent()) {
|
||||
path = excelBinOutput.get().getParent();
|
||||
if (path == null)
|
||||
path = root;
|
||||
Grasscutter.getLogger().debug("Resources will be loaded from \"" + resources + "/" + path.toString() + "\"");
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Failed to find ExcelBinOutput in resources zip \"" + resources + "\"");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Grasscutter.getLogger().error("Failed to scan resources zip \"" + resources + "\"");
|
||||
}
|
||||
}
|
||||
RESOURCES_PATH = path;
|
||||
|
||||
// Setup Scripts path
|
||||
final String scripts = Grasscutter.config.folderStructure.scripts;
|
||||
SCRIPTS_PATH = (scripts.startsWith("resources:"))
|
||||
? RESOURCES_PATH.resolve(scripts.substring("resources:".length()))
|
||||
: Path.of(scripts);
|
||||
};
|
||||
|
||||
public static Path getDataPath(String path) {
|
||||
Path userPath = DATA_USER_PATH.resolve(path);
|
||||
if (Files.exists(userPath)) return userPath;
|
||||
Path defaultPath = DATA_DEFAULT_PATH.resolve(path);
|
||||
if (Files.exists(defaultPath)) return defaultPath;
|
||||
return userPath; // Maybe they want to write to a new file
|
||||
}
|
||||
|
||||
public static Path getDataUserPath(String path) {
|
||||
return DATA_USER_PATH.resolve(path);
|
||||
}
|
||||
|
||||
public static Path getPacketPath(String path) {
|
||||
return PACKETS_PATH.resolve(path);
|
||||
}
|
||||
|
||||
public static Path getPluginPath(String path) {
|
||||
return PLUGINS_PATH.resolve(path);
|
||||
}
|
||||
|
||||
public static Path getResourcePath(String path) {
|
||||
return RESOURCES_PATH.resolve(path);
|
||||
}
|
||||
|
||||
public static Path getScriptPath(String path) {
|
||||
return SCRIPTS_PATH.resolve(path);
|
||||
}
|
||||
|
||||
public static void write(String dest, byte[] bytes) {
|
||||
Path path = Path.of(dest);
|
||||
|
||||
@ -79,20 +175,11 @@ public final class FileUtils {
|
||||
public static List<Path> getPathsFromResource(String folder) throws URISyntaxException {
|
||||
List<Path> result = null;
|
||||
|
||||
// Get pathUri of the current running JAR
|
||||
URI pathUri = Grasscutter.class.getProtectionDomain()
|
||||
.getCodeSource()
|
||||
.getLocation()
|
||||
.toURI();
|
||||
|
||||
try {
|
||||
// file walks JAR
|
||||
URI uri = URI.create("jar:file:" + pathUri.getRawPath());
|
||||
try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) {
|
||||
result = Files.walk(fs.getPath(folder))
|
||||
.filter(Files::isRegularFile)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
result = Files.walk(JAR_FILE_SYSTEM.getPath(folder))
|
||||
.filter(Files::isRegularFile)
|
||||
.collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
// Eclipse puts resources in its bin folder
|
||||
File f = new File(System.getProperty("user.dir") + folder);
|
||||
|
@ -17,6 +17,7 @@ import lombok.EqualsAndHashCode;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.*;
|
||||
import static emu.grasscutter.utils.FileUtils.getResourcePath;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
|
@ -22,7 +22,7 @@ import org.slf4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.getResourcePath;
|
||||
import static emu.grasscutter.utils.FileUtils.getResourcePath;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@SuppressWarnings({"UnusedReturnValue", "BooleanMethodIsAlwaysInverted"})
|
||||
|
Loading…
Reference in New Issue
Block a user