Commit fe4e5990 authored by Melledy's avatar Melledy
Browse files

Merge branch 'development' into dev-world-scripts

parents 3cffdd97 e3ed3968
...@@ -25,6 +25,16 @@ jobs: ...@@ -25,6 +25,16 @@ jobs:
with: with:
distribution: temurin distribution: temurin
java-version: '17' java-version: '17'
- name: Cache gradle files
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
./.gradle/loom-cache
key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle', 'gradle.properties', '**/*.accesswidener') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Run Gradle - name: Run Gradle
run: ./gradlew && ./gradlew jar run: ./gradlew && ./gradlew jar
- name: Upload build - name: Upload build
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
*.nar *.nar
*.ear *.ear
*.zip *.zip
*.tar.gz *.gz
*.rar *.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
...@@ -52,21 +52,23 @@ tmp/ ...@@ -52,21 +52,23 @@ tmp/
.vscode .vscode
# Grasscutter # Grasscutter
resources/ /resources
logs/ /logs
plugins/ /plugins
data/AbilityEmbryos.json /data
data/OpenConfig.json /keys
/language
/languages
/src/generated
/*.jar
/*.sh
GM Handbook.txt GM Handbook.txt
config.json config.json
mitmdump.exe mitmdump.exe
*.jar
!lib/*.jar
mongod.exe mongod.exe
/src/generated/
/*.sh
language/
languages/
gacha-mapping.js gacha-mapping.js
mappings.js mappings.js
BuildConfig.java BuildConfig.java
......
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400&display=swap">
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<style>
body {
background-color: #f0f0f0;
}
p {
font-weight: 300;
}
a, a:hover {
text-decoration: none !important;
color: #626976;
}
.content {
padding: 3rem 0;
}
.container {
color: #626976;
position: relative;
}
h2 {
font-size: 20px;
}
h3 {
font-size: 16px;
}
table {
border-collapse: collapse;
width: 70%;
margin: 0 auto;
}
table thead tr {
height: 60px;
background: #626976;
}
table thead tr th {
font-size: 18px;
color: white;
}
table tbody tr {
height: 50px;
background-color: #f5f5f5;
}
tbody tr:nth-child(even) {
background-color: #fdfdfd;
}
table th, table td {
text-align: left;
padding: 0 8px;
}
</style>
<title>GM Handbook</title>
</head>
<body>
<div class="content">
<div class="container">
<h2 class="mb-5">{{TITLE}}</h2>
<h3>{{TITLE_COMMANDS}}</h3>
<hr/>
<table>
<thead>
<tr>
<th>{{HEADER_COMMAND}}</th>
<th>{{HEADER_DESCRIPTION}}</th>
</tr>
</thead>
{{COMMANDS_TABLE}}
</table>
<h3>{{TITLE_AVATARS}}</h3>
<hr/>
<table>
<thead>
<tr>
<th>{{HEADER_ID}}</th>
<th>{{HEADER_AVATAR}}</th>
</tr>
</thead>
{{AVATARS_TABLE}}
</table>
<h3>{{TITLE_ITEMS}}</h3>
<hr/>
<table>
<thead>
<tr>
<th>{{HEADER_ID}}</th>
<th>{{HEADER_ITEM}}</th>
</tr>
</thead>
{{ITEMS_TABLE}}
</table>
<h3>{{TITLE_SCENES}}</h3>
<hr/>
<table>
<thead>
<tr>
<th>{{HEADER_ID}}</th>
<th>{{HEADER_SCENE}}</th>
</tr>
</thead>
{{SCENES_TABLE}}
</table>
<h3>{{TITLE_MONSTERS}}</h3>
<hr/>
<table>
<thead>
<tr>
<th>{{HEADER_ID}}</th>
<th>{{HEADER_MONSTER}}</th>
</tr>
</thead>
{{MONSTERS_TABLE}}
</table>
</div>
</div>
<footer>
<div class="copyright">
<div class="container">
<div class="row">
<div class="col-md-6">
<span>Template by BecodReyes. All rights reserved.</span>
</div>
<div class="col-md-6">
<ul style="float:right">
<li class="list-inline-item">
<a href="https://github.com/Grasscutters/Grasscutter">Github</a>
</li>
<li class="list-inline-item">·</li>
<li class="list-inline-item">
<a href="https://github.com/Grasscutters/Grasscutter/blob/stable/LICENSE">License</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</footer>
</body>
</html>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400&display=swap">
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<style>
body {
background-color: #f0f0f0;
}
p {
font-weight: 300;
}
a, a:hover {
text-decoration: none !important;
color: #626976;
}
.content {
padding: 3rem 0;
}
.container {
color: #626976;
position: relative;
}
h2 {
font-size: 20px;
}
h3 {
font-size: 16px;
}
table {
border-collapse: collapse;
width: 70%;
margin: 0 auto;
}
table thead tr {
height: 60px;
background: #626976;
}
table thead tr th {
font-size: 18px;
color: white;
}
table tbody tr {
height: 50px;
background-color: #f5f5f5;
}
tbody tr:nth-child(even) {
background-color: #fdfdfd;
}
table th, table td {
text-align: left;
padding: 0 8px;
}
</style>
<title>Documentation</title>
</head>
<body>
<div class="content">
<div class="container">
<h2 class="mb-5">{{TITLE}}</h2>
<ul>
<li><a href="/documentation/handbook">{{ITEM_HANDBOOK}}</a></li>
<li><a href="/documentation/gachamapping">{{ITEM_GACHA_MAPPING}}</a></li>
</ul>
</div>
</div>
<footer>
<div class="copyright">
<div class="container">
<div class="row">
<div class="col-md-6">
<span>Template by BecodReyes. All rights reserved.</span>
</div>
<div class="col-md-6">
<ul style="float:right">
<li class="list-inline-item">
<a href="https://github.com/Grasscutters/Grasscutter">Github</a>
</li>
<li class="list-inline-item">·</li>
<li class="list-inline-item">
<a href="https://github.com/Grasscutters/Grasscutter/blob/stable/LICENSE">License</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</footer>
</body>
</html>
...@@ -28,7 +28,6 @@ public final class Configuration extends ConfigContainer { ...@@ -28,7 +28,6 @@ public final class Configuration extends ConfigContainer {
public static final Locale FALLBACK_LANGUAGE = config.language.fallback; public static final Locale FALLBACK_LANGUAGE = config.language.fallback;
private static final String DATA_FOLDER = config.folderStructure.data; private static final String DATA_FOLDER = config.folderStructure.data;
private static final String RESOURCES_FOLDER = config.folderStructure.resources; private static final String RESOURCES_FOLDER = config.folderStructure.resources;
private static final String KEYS_FOLDER = config.folderStructure.keys;
private static final String PLUGINS_FOLDER = config.folderStructure.plugins; private static final String PLUGINS_FOLDER = config.folderStructure.plugins;
private static final String SCRIPTS_FOLDER = config.folderStructure.scripts; private static final String SCRIPTS_FOLDER = config.folderStructure.scripts;
private static final String PACKETS_FOLDER = config.folderStructure.packets; private static final String PACKETS_FOLDER = config.folderStructure.packets;
...@@ -62,10 +61,6 @@ public final class Configuration extends ConfigContainer { ...@@ -62,10 +61,6 @@ public final class Configuration extends ConfigContainer {
public static String RESOURCE(String path) { public static String RESOURCE(String path) {
return Paths.get(RESOURCES_FOLDER, path).toString(); return Paths.get(RESOURCES_FOLDER, path).toString();
} }
public static String KEY(String path) {
return Paths.get(KEYS_FOLDER, path).toString();
}
public static String PLUGIN() { public static String PLUGIN() {
return PLUGINS_FOLDER; return PLUGINS_FOLDER;
......
...@@ -14,6 +14,7 @@ import emu.grasscutter.server.http.HttpServer; ...@@ -14,6 +14,7 @@ import emu.grasscutter.server.http.HttpServer;
import emu.grasscutter.server.http.dispatch.DispatchHandler; import emu.grasscutter.server.http.dispatch.DispatchHandler;
import emu.grasscutter.server.http.handlers.*; import emu.grasscutter.server.http.handlers.*;
import emu.grasscutter.server.http.dispatch.RegionHandler; import emu.grasscutter.server.http.dispatch.RegionHandler;
import emu.grasscutter.server.http.documentation.DocumentationServerHandler;
import emu.grasscutter.utils.ConfigContainer; import emu.grasscutter.utils.ConfigContainer;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import org.jline.reader.EndOfFileException; import org.jline.reader.EndOfFileException;
...@@ -129,6 +130,7 @@ public final class Grasscutter { ...@@ -129,6 +130,7 @@ public final class Grasscutter {
httpServer.addRouter(AnnouncementsHandler.class); httpServer.addRouter(AnnouncementsHandler.class);
httpServer.addRouter(DispatchHandler.class); httpServer.addRouter(DispatchHandler.class);
httpServer.addRouter(GachaHandler.class); httpServer.addRouter(GachaHandler.class);
httpServer.addRouter(DocumentationServerHandler.class);
// TODO: find a better place? // TODO: find a better place?
StaminaManager.initialize(); StaminaManager.initialize();
......
package emu.grasscutter.auth; package emu.grasscutter.auth;
import emu.grasscutter.game.Account;
import emu.grasscutter.server.http.objects.*; import emu.grasscutter.server.http.objects.*;
import express.http.Request; import express.http.Request;
import express.http.Response; import express.http.Response;
...@@ -30,10 +31,10 @@ public interface AuthenticationSystem { ...@@ -30,10 +31,10 @@ public interface AuthenticationSystem {
/** /**
* Called by plugins to internally verify a user's identity. * Called by plugins to internally verify a user's identity.
* @param details A unique, one-time token to verify the user. * @param details A unique identifier to identify the user. (For example: a JWT token)
* @return True if the user is verified, False otherwise. * @return The user's account if the verification was successful, null if the user was unable to be verified.
*/ */
boolean verifyUser(String details); Account verifyUser(String details);
/** /**
* This is the authenticator used for password authentication. * This is the authenticator used for password authentication.
...@@ -59,6 +60,12 @@ public interface AuthenticationSystem { ...@@ -59,6 +60,12 @@ public interface AuthenticationSystem {
*/ */
ExternalAuthenticator getExternalAuthenticator(); ExternalAuthenticator getExternalAuthenticator();
/**
* This is the authenticator used for handling OAuth authentication requests.
* @return An authenticator.
*/
OAuthAuthenticator getOAuthAuthenticator();
/** /**
* A data container that holds relevant data for authenticating a client. * A data container that holds relevant data for authenticating a client.
*/ */
...@@ -124,4 +131,16 @@ public interface AuthenticationSystem { ...@@ -124,4 +131,16 @@ public interface AuthenticationSystem {
return AuthenticationRequest.builder().request(request) return AuthenticationRequest.builder().request(request)
.response(response).build(); .response(response).build();
} }
/**
* Generates an authentication request from a {@link Response} object.
* @param request The Express request.
* @param jsonData The JSON data.
* @return An authentication request.
*/
static AuthenticationRequest fromOAuthRequest(Request request, Response response) {
return AuthenticationRequest.builder().request(request)
.response(response).build();
}
} }
...@@ -2,6 +2,7 @@ package emu.grasscutter.auth; ...@@ -2,6 +2,7 @@ package emu.grasscutter.auth;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.auth.DefaultAuthenticators.*; import emu.grasscutter.auth.DefaultAuthenticators.*;
import emu.grasscutter.game.Account;
import emu.grasscutter.server.http.objects.ComboTokenResJson; import emu.grasscutter.server.http.objects.ComboTokenResJson;
import emu.grasscutter.server.http.objects.LoginResultJson; import emu.grasscutter.server.http.objects.LoginResultJson;
...@@ -16,6 +17,7 @@ public final class DefaultAuthentication implements AuthenticationSystem { ...@@ -16,6 +17,7 @@ public final class DefaultAuthentication implements AuthenticationSystem {
private final Authenticator<LoginResultJson> tokenAuthenticator = new TokenAuthenticator(); private final Authenticator<LoginResultJson> tokenAuthenticator = new TokenAuthenticator();
private final Authenticator<ComboTokenResJson> sessionKeyAuthenticator = new SessionKeyAuthenticator(); private final Authenticator<ComboTokenResJson> sessionKeyAuthenticator = new SessionKeyAuthenticator();
private final ExternalAuthenticator externalAuthenticator = new ExternalAuthentication(); private final ExternalAuthenticator externalAuthenticator = new ExternalAuthentication();
private final OAuthAuthenticator oAuthAuthenticator = new OAuthAuthentication();
@Override @Override
public void createAccount(String username, String password) { public void createAccount(String username, String password) {
...@@ -26,11 +28,11 @@ public final class DefaultAuthentication implements AuthenticationSystem { ...@@ -26,11 +28,11 @@ public final class DefaultAuthentication implements AuthenticationSystem {
public void resetPassword(String username) { public void resetPassword(String username) {
// Unhandled. The default authenticator doesn't store passwords. // Unhandled. The default authenticator doesn't store passwords.
} }
@Override @Override
public boolean verifyUser(String details) { public Account verifyUser(String details) {
Grasscutter.getLogger().info(translate("dispatch.authentication.default_unable_to_verify")); Grasscutter.getLogger().info(translate("dispatch.authentication.default_unable_to_verify"));
return false; return null;
} }
@Override @Override
...@@ -52,4 +54,9 @@ public final class DefaultAuthentication implements AuthenticationSystem { ...@@ -52,4 +54,9 @@ public final class DefaultAuthentication implements AuthenticationSystem {
public ExternalAuthenticator getExternalAuthenticator() { public ExternalAuthenticator getExternalAuthenticator() {
return this.externalAuthenticator; return this.externalAuthenticator;
} }
@Override
public OAuthAuthenticator getOAuthAuthenticator() {
return this.oAuthAuthenticator;
}
} }
...@@ -41,10 +41,6 @@ public final class DefaultAuthenticators { ...@@ -41,10 +41,6 @@ public final class DefaultAuthenticators {
responseMessage = translate("messages.dispatch.account.username_create_error"); responseMessage = translate("messages.dispatch.account.username_create_error");
Grasscutter.getLogger().info(translate("messages.dispatch.account.account_login_create_error", address)); Grasscutter.getLogger().info(translate("messages.dispatch.account.account_login_create_error", address));
} else { } else {
// Add default permissions.
for (var permission : ACCOUNT.defaultPermissions)
account.addPermission(permission);
// Continue with login. // Continue with login.
successfulLogin = true; successfulLogin = true;
...@@ -178,4 +174,29 @@ public final class DefaultAuthenticators { ...@@ -178,4 +174,29 @@ public final class DefaultAuthenticators {
request.getResponse().send("Authentication is not available with the default authentication method."); request.getResponse().send("Authentication is not available with the default authentication method.");
} }
} }
/**
* Handles authentication requests from OAuth sources.
*/
public static class OAuthAuthentication implements OAuthAuthenticator {
@Override public void handleLogin(AuthenticationRequest request) {
assert request.getResponse() != null;
request.getResponse().send("Authentication is not available with the default authentication method.");
}
@Override public void handleDesktopRedirection(AuthenticationRequest request) {
assert request.getResponse() != null;
request.getResponse().send("Authentication is not available with the default authentication method.");
}
@Override public void handleMobileRedirection(AuthenticationRequest request) {
assert request.getResponse() != null;
request.getResponse().send("Authentication is not available with the default authentication method.");
}
@Override public void handleTokenProcess(AuthenticationRequest request) {
assert request.getResponse() != null;
request.getResponse().send("Authentication is not available with the default authentication method.");
}
}
} }
package emu.grasscutter.auth;
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
/**
* Handles authentication via OAuth routes.
*/
public interface OAuthAuthenticator {
/**
* Called when an OAuth login request is made.
* @param request The authentication request.
*/
void handleLogin(AuthenticationRequest request);
/**
* Called when an client requests to redirect to login page.
* @param request The authentication request.
*/
void handleDesktopRedirection(AuthenticationRequest request);
void handleMobileRedirection(AuthenticationRequest request);
/**
* Called when an OAuth login requests callback.
* @param request The authentication request.
*/
void handleTokenProcess(AuthenticationRequest request);
}
...@@ -2,6 +2,8 @@ package emu.grasscutter.command; ...@@ -2,6 +2,8 @@ package emu.grasscutter.command;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.game.CommandResponseEvent;
import emu.grasscutter.server.event.types.ServerEvent;
import java.util.List; import java.util.List;
...@@ -19,6 +21,8 @@ public interface CommandHandler { ...@@ -19,6 +21,8 @@ public interface CommandHandler {
} else { } else {
player.dropMessage(message); player.dropMessage(message);
} }
CommandResponseEvent event = new CommandResponseEvent(ServerEvent.Type.GAME,player, message);
event.call();
} }
/** /**
......
...@@ -9,7 +9,7 @@ import java.util.List; ...@@ -9,7 +9,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
@Command(label = "resetshop", usage = "resetshop", permission = "server.resetshop", permissionTargeted = "server.resetshop.others", description = "commands.resetshop.description") @Command(label = "resetshop", usage = "resetshop <player id>", permission = "server.resetshop", permissionTargeted = "server.resetshop.others", description = "commands.resetShopLimit.description")
public final class ResetShopLimitCommand implements CommandHandler { public final class ResetShopLimitCommand implements CommandHandler {
@Override @Override
...@@ -19,6 +19,11 @@ public final class ResetShopLimitCommand implements CommandHandler { ...@@ -19,6 +19,11 @@ public final class ResetShopLimitCommand implements CommandHandler {
return; return;
} }
if (args.isEmpty()) {
CommandHandler.sendMessage(sender, translate(sender, "commands.resetShopLimit.usage"));
return;
}
targetPlayer.getShopLimit().forEach(x -> x.setNextRefreshTime(0)); targetPlayer.getShopLimit().forEach(x -> x.setNextRefreshTime(0));
targetPlayer.save(); targetPlayer.save();
CommandHandler.sendMessage(sender, translate(sender, "commands.status.success")); CommandHandler.sendMessage(sender, translate(sender, "commands.status.success"));
......
package emu.grasscutter.data;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.server.http.handlers.GachaHandler;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.Utils;
import java.io.*;
import java.nio.file.Path;
import java.util.List;
import static emu.grasscutter.Configuration.DATA;
public class DataLoader {
/**
* Load a data file by its name. If the file isn't found within the /data directory then it will fallback to the default within the jar resources
* @see #load(String, boolean)
* @param resourcePath The path to the data file to be loaded.
* @return InputStream of the data file.
* @throws FileNotFoundException
*/
public static InputStream load(String resourcePath) throws FileNotFoundException {
return load(resourcePath, true);
}
/**
* Load a data file by its name.
* @param resourcePath The path to the data file to be loaded.
* @param useFallback If the file does not exist in the /data directory, should it use the default file in the jar?
* @return InputStream of the data file.
* @throws FileNotFoundException
*/
public static InputStream load(String resourcePath, boolean useFallback) throws FileNotFoundException {
if(Utils.fileExists(DATA(resourcePath))) {
// Data is in the resource directory
return new FileInputStream(DATA(resourcePath));
} else {
if(useFallback) {
return FileUtils.readResourceAsStream("/defaults/data/" + resourcePath);
}
}
return null;
}
public static void CheckAllFiles() {
try {
List<Path> filenames = FileUtils.getPathsFromResource("/defaults/data/");
for (Path file : filenames) {
String relativePath = String.valueOf(file).split("/defaults/data/")[1];
CheckAndCopyData(relativePath);
}
} catch (Exception e) {
Grasscutter.getLogger().error("An error occurred while trying to check the data folder. \n" + e);
}
GenerateGachaMappings();
}
private static void CheckAndCopyData(String name) {
String filePath = Utils.toFilePath(DATA(name));
if (!Utils.fileExists(filePath)) {
// Check if file is in subdirectory
if (name.indexOf("/") != -1) {
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);
}
}
}
Grasscutter.getLogger().info("Creating default '" + name + "' data");
FileUtils.copyResource("/defaults/data/" + name, filePath);
}
}
private static void GenerateGachaMappings() {
if (!Utils.fileExists(GachaHandler.gachaMappings)) {
try {
Grasscutter.getLogger().info("Creating default '" + GachaHandler.gachaMappings + "' data");
Tools.createGachaMapping(GachaHandler.gachaMappings);
} catch (Exception exception) {
Grasscutter.getLogger().warn("Failed to create gacha mappings. \n" + exception);
}
}
}
}
package emu.grasscutter.data; package emu.grasscutter.data;
import java.io.File; import java.io.*;
import java.io.FileReader;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.regex.Matcher; import java.util.regex.Matcher;
...@@ -33,6 +32,8 @@ import static emu.grasscutter.Configuration.*; ...@@ -33,6 +32,8 @@ import static emu.grasscutter.Configuration.*;
public class ResourceLoader { public class ResourceLoader {
private static List<String> loadedResources = new ArrayList<String>();
public static List<Class<?>> getResourceDefClasses() { public static List<Class<?>> getResourceDefClasses() {
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName()); Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
Set<?> classes = reflections.getSubTypesOf(GameResource.class); Set<?> classes = reflections.getSubTypesOf(GameResource.class);
...@@ -98,6 +99,10 @@ public class ResourceLoader { ...@@ -98,6 +99,10 @@ public class ResourceLoader {
} }
public static void loadResources() { public static void loadResources() {
loadResources(false);
}
public static void loadResources(boolean doReload) {
for (Class<?> resourceDefinition : getResourceDefClasses()) { for (Class<?> resourceDefinition : getResourceDefClasses()) {
ResourceType type = resourceDefinition.getAnnotation(ResourceType.class); ResourceType type = resourceDefinition.getAnnotation(ResourceType.class);
...@@ -113,7 +118,7 @@ public class ResourceLoader { ...@@ -113,7 +118,7 @@ public class ResourceLoader {
} }
try { try {
loadFromResource(resourceDefinition, type, map); loadFromResource(resourceDefinition, type, map, doReload);
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e); Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e);
} }
...@@ -121,13 +126,16 @@ public class ResourceLoader { ...@@ -121,13 +126,16 @@ public class ResourceLoader {
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
protected static void loadFromResource(Class<?> c, ResourceType type, Int2ObjectMap map) throws Exception { protected static void loadFromResource(Class<?> c, ResourceType type, Int2ObjectMap map, boolean doReload) throws Exception {
for (String name : type.name()) { if(!loadedResources.contains(c.getSimpleName()) || doReload) {
loadFromResource(c, name, map); for (String name : type.name()) {
loadFromResource(c, name, map);
}
Grasscutter.getLogger().info("Loaded " + map.size() + " " + c.getSimpleName() + "s.");
loadedResources.add(c.getSimpleName());
} }
Grasscutter.getLogger().info("Loaded " + map.size() + " " + c.getSimpleName() + "s.");
} }
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
protected static void loadFromResource(Class<?> c, String fileName, Int2ObjectMap map) throws Exception { protected static void loadFromResource(Class<?> c, String fileName, Int2ObjectMap map) throws Exception {
FileReader fileReader = new FileReader(RESOURCE("ExcelBinOutput/" + fileName)); FileReader fileReader = new FileReader(RESOURCE("ExcelBinOutput/" + fileName));
...@@ -138,6 +146,9 @@ public class ResourceLoader { ...@@ -138,6 +146,9 @@ public class ResourceLoader {
Map<String, Object> tempMap = Utils.switchPropertiesUpperLowerCase((Map<String, Object>) o, c); Map<String, Object> tempMap = Utils.switchPropertiesUpperLowerCase((Map<String, Object>) o, c);
GameResource res = gson.fromJson(gson.toJson(tempMap), TypeToken.get(c).getType()); GameResource res = gson.fromJson(gson.toJson(tempMap), TypeToken.get(c).getType());
res.onLoad(); res.onLoad();
if(map.containsKey(res.getId())) {
map.remove(res.getId());
}
map.put(res.getId(), res); map.put(res.getId(), res);
} }
} }
...@@ -191,18 +202,14 @@ public class ResourceLoader { ...@@ -191,18 +202,14 @@ public class ResourceLoader {
} }
private static void loadAbilityEmbryos() { private static void loadAbilityEmbryos() {
// Read from cached file if exists
File embryoCache = new File(DATA("AbilityEmbryos.json"));
List<AbilityEmbryoEntry> embryoList = null; List<AbilityEmbryoEntry> embryoList = null;
if (embryoCache.exists()) { // Read from cached file if exists
// Load from cache try(InputStream embryoCache = DataLoader.load("AbilityEmbryos.json", false)) {
try (FileReader fileReader = new FileReader(embryoCache)) { embryoList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(embryoCache), TypeToken.getParameterized(Collection.class, AbilityEmbryoEntry.class).getType());
embryoList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, AbilityEmbryoEntry.class).getType()); } catch(Exception ignored) {}
} catch (Exception e) {
e.printStackTrace(); if(embryoList == null) {
}
} else {
// Load from BinOutput // Load from BinOutput
Pattern pattern = Pattern.compile("(?<=ConfigAvatar_)(.*?)(?=.json)"); Pattern pattern = Pattern.compile("(?<=ConfigAvatar_)(.*?)(?=.json)");
...@@ -316,18 +323,12 @@ public class ResourceLoader { ...@@ -316,18 +323,12 @@ public class ResourceLoader {
} }
private static void loadSpawnData() { private static void loadSpawnData() {
// Read from cached file if exists
File spawnDataEntries = new File(DATA("Spawns.json"));
List<SpawnGroupEntry> spawnEntryList = null; List<SpawnGroupEntry> spawnEntryList = null;
if (spawnDataEntries.exists()) { // Read from cached file if exists
// Load from cache try(InputStream spawnDataEntries = DataLoader.load("Spawns.json")) {
try (FileReader fileReader = new FileReader(spawnDataEntries)) { spawnEntryList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(spawnDataEntries), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
spawnEntryList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType()); } catch (Exception ignored) {}
} catch (Exception e) {
e.printStackTrace();
}
}
if (spawnEntryList == null || spawnEntryList.isEmpty()) { if (spawnEntryList == null || spawnEntryList.isEmpty()) {
Grasscutter.getLogger().error("No spawn data loaded!"); Grasscutter.getLogger().error("No spawn data loaded!");
...@@ -342,16 +343,13 @@ public class ResourceLoader { ...@@ -342,16 +343,13 @@ public class ResourceLoader {
private static void loadOpenConfig() { private static void loadOpenConfig() {
// Read from cached file if exists // Read from cached file if exists
File openConfigCache = new File(DATA("OpenConfig.json"));
List<OpenConfigEntry> list = null; List<OpenConfigEntry> list = null;
if (openConfigCache.exists()) { try(InputStream openConfigCache = DataLoader.load("OpenConfig.json", false)) {
try (FileReader fileReader = new FileReader(openConfigCache)) { list = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(openConfigCache), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
list = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, OpenConfigEntry.class).getType()); } catch (Exception ignored) {}
} catch (Exception e) {
e.printStackTrace(); if (list == null) {
}
} else {
Map<String, OpenConfigEntry> map = new TreeMap<>(); Map<String, OpenConfigEntry> map = new TreeMap<>();
java.lang.reflect.Type type = new TypeToken<Map<String, OpenConfigData[]>>() {}.getType(); java.lang.reflect.Type type = new TypeToken<Map<String, OpenConfigData[]>>() {}.getType();
String[] folderNames = {"BinOutput/Talent/EquipTalents/", "BinOutput/Talent/AvatarTalents/"}; String[] folderNames = {"BinOutput/Talent/EquipTalents/", "BinOutput/Talent/AvatarTalents/"};
......
package emu.grasscutter.game; package emu.grasscutter.game;
import dev.morphia.annotations.*; import dev.morphia.annotations.*;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.Crypto;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import java.util.ArrayList; import java.util.*;
import java.util.List; import java.util.stream.Stream;
import java.util.Locale;
import org.bson.Document; import org.bson.Document;
...@@ -144,19 +142,24 @@ public class Account { ...@@ -144,19 +142,24 @@ public class Account {
} }
public boolean hasPermission(String permission) { public boolean hasPermission(String permission) {
if (this.permissions.contains(permission)) return true;
if(this.permissions.contains("*") && this.permissions.size() == 1) return true; if(this.permissions.contains("*") && this.permissions.size() == 1) return true;
// Add default permissions if it doesn't exist
List<String> permissions = Stream.of(this.permissions, Arrays.asList(ACCOUNT.defaultPermissions))
.flatMap(Collection::stream)
.distinct().toList();
if (permissions.contains(permission)) return true;
String[] permissionParts = permission.split("\\."); String[] permissionParts = permission.split("\\.");
for (String p : this.permissions) { for (String p : permissions) {
if (p.startsWith("-") && permissionMatchesWildcard(p.substring(1), permissionParts)) return false; if (p.startsWith("-") && permissionMatchesWildcard(p.substring(1), permissionParts)) return false;
if (permissionMatchesWildcard(p, permissionParts)) return true; if (permissionMatchesWildcard(p, permissionParts)) return true;
} }
return this.permissions.contains("*"); return permissions.contains("*");
} }
public boolean removePermission(String permission) { public boolean removePermission(String permission) {
return this.permissions.remove(permission); return this.permissions.remove(permission);
} }
......
...@@ -2,6 +2,7 @@ package emu.grasscutter.game.drop; ...@@ -2,6 +2,7 @@ package emu.grasscutter.game.drop;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.ItemData; import emu.grasscutter.data.def.ItemData;
import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.EntityItem;
...@@ -17,12 +18,11 @@ import emu.grasscutter.utils.Utils; ...@@ -17,12 +18,11 @@ import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.FileReader; import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import static emu.grasscutter.Configuration.*;
public class DropManager { public class DropManager {
public GameServer getGameServer() { public GameServer getGameServer() {
return gameServer; return gameServer;
...@@ -43,7 +43,7 @@ public class DropManager { ...@@ -43,7 +43,7 @@ public class DropManager {
} }
public synchronized void load() { public synchronized void load() {
try (FileReader fileReader = new FileReader(DATA("Drop.json"))) { try (Reader fileReader = new InputStreamReader(DataLoader.load("Drop.json"))) {
getDropData().clear(); getDropData().clear();
List<DropInfo> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DropInfo.class).getType()); List<DropInfo> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DropInfo.class).getType());
if(banners.size() > 0) { if(banners.size() > 0) {
......
...@@ -2,11 +2,14 @@ package emu.grasscutter.game.expedition; ...@@ -2,11 +2,14 @@ package emu.grasscutter.game.expedition;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader;
import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServer;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.FileReader; import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
...@@ -30,7 +33,7 @@ public class ExpeditionManager { ...@@ -30,7 +33,7 @@ public class ExpeditionManager {
} }
public synchronized void load() { public synchronized void load() {
try (FileReader fileReader = new FileReader(DATA("ExpeditionReward.json"))) { try (Reader fileReader = new InputStreamReader(DataLoader.load("ExpeditionReward.json"))) {
getExpeditionRewardDataList().clear(); getExpeditionRewardDataList().clear();
List<ExpeditionRewardInfo> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ExpeditionRewardInfo.class).getType()); List<ExpeditionRewardInfo> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ExpeditionRewardInfo.class).getType());
if(banners.size() > 0) { if(banners.size() > 0) {
......
...@@ -2,6 +2,8 @@ package emu.grasscutter.game.gacha; ...@@ -2,6 +2,8 @@ package emu.grasscutter.game.gacha;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.file.*; import java.nio.file.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
...@@ -13,6 +15,7 @@ import com.google.gson.reflect.TypeToken; ...@@ -13,6 +15,7 @@ import com.google.gson.reflect.TypeToken;
import com.sun.nio.file.SensitivityWatchEventModifier; import com.sun.nio.file.SensitivityWatchEventModifier;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.def.ItemData; import emu.grasscutter.data.def.ItemData;
...@@ -74,7 +77,7 @@ public class GachaManager { ...@@ -74,7 +77,7 @@ public class GachaManager {
} }
public synchronized void load() { public synchronized void load() {
try (FileReader fileReader = new FileReader(DATA("Banners.json"))) { try (Reader fileReader = new InputStreamReader(DataLoader.load("Banners.json"))) {
getGachaBanners().clear(); getGachaBanners().clear();
List<GachaBanner> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, GachaBanner.class).getType()); List<GachaBanner> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, GachaBanner.class).getType());
if(banners.size() > 0) { if(banners.size() > 0) {
......
...@@ -2,6 +2,7 @@ package emu.grasscutter.game.shop; ...@@ -2,6 +2,7 @@ package emu.grasscutter.game.shop;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.def.ShopGoodsData; import emu.grasscutter.data.def.ShopGoodsData;
...@@ -11,6 +12,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; ...@@ -11,6 +12,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.FileReader; import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
...@@ -58,7 +61,7 @@ public class ShopManager { ...@@ -58,7 +61,7 @@ public class ShopManager {
} }
private void loadShop() { private void loadShop() {
try (FileReader fileReader = new FileReader(DATA("Shop.json"))) { try (Reader fileReader = new InputStreamReader(DataLoader.load("Shop.json"))) {
getShopData().clear(); getShopData().clear();
List<ShopTable> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopTable.class).getType()); List<ShopTable> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopTable.class).getType());
if(banners.size() > 0) { if(banners.size() > 0) {
...@@ -102,7 +105,7 @@ public class ShopManager { ...@@ -102,7 +105,7 @@ public class ShopManager {
} }
private void loadShopChest() { private void loadShopChest() {
try (FileReader fileReader = new FileReader(DATA("ShopChest.json"))) { try (Reader fileReader = new InputStreamReader(DataLoader.load("ShopChest.json"))) {
getShopChestData().clear(); getShopChestData().clear();
List<ShopChestTable> shopChestTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestTable.class).getType()); List<ShopChestTable> shopChestTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestTable.class).getType());
if (shopChestTableList.size() > 0) { if (shopChestTableList.size() > 0) {
...@@ -117,7 +120,7 @@ public class ShopManager { ...@@ -117,7 +120,7 @@ public class ShopManager {
} }
private void loadShopChestBatchUse() { private void loadShopChestBatchUse() {
try (FileReader fileReader = new FileReader(DATA("ShopChestBatchUse.json"))) { try (Reader fileReader = new InputStreamReader(DataLoader.load("ShopChestBatchUse.json"))) {
getShopChestBatchUseData().clear(); getShopChestBatchUseData().clear();
List<ShopChestBatchUseTable> shopChestBatchUseTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestBatchUseTable.class).getType()); List<ShopChestBatchUseTable> shopChestBatchUseTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestBatchUseTable.class).getType());
if (shopChestBatchUseTableList.size() > 0) { if (shopChestBatchUseTableList.size() > 0) {
......
package emu.grasscutter.game.tower; package emu.grasscutter.game.tower;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.def.TowerScheduleData; import emu.grasscutter.data.def.TowerScheduleData;
import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServer;
import java.io.FileReader; import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.List; import java.util.List;
import static emu.grasscutter.Configuration.*; import static emu.grasscutter.Configuration.*;
...@@ -25,7 +28,7 @@ public class TowerScheduleManager { ...@@ -25,7 +28,7 @@ public class TowerScheduleManager {
private TowerScheduleConfig towerScheduleConfig; private TowerScheduleConfig towerScheduleConfig;
public synchronized void load(){ public synchronized void load(){
try (FileReader fileReader = new FileReader(DATA("TowerSchedule.json"))) { try (Reader fileReader = new InputStreamReader(DataLoader.load("TowerSchedule.json"))) {
towerScheduleConfig = Grasscutter.getGsonFactory().fromJson(fileReader, TowerScheduleConfig.class); towerScheduleConfig = Grasscutter.getGsonFactory().fromJson(fileReader, TowerScheduleConfig.class);
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger().error("Unable to load tower schedule config.", e); Grasscutter.getLogger().error("Unable to load tower schedule config.", e);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment