Format code [skip actions]

This commit is contained in:
github-actions 2023-05-11 02:23:43 +00:00
parent f51fd55cb5
commit f9906c4492
730 changed files with 29212 additions and 29159 deletions

View File

@ -1,133 +1,133 @@
package emu.grasscutter.auth; package emu.grasscutter.auth;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.server.http.objects.*; import emu.grasscutter.server.http.objects.*;
import io.javalin.http.Context; import io.javalin.http.Context;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
/** Defines an authenticator for the server. Can be changed by plugins. */ /** Defines an authenticator for the server. Can be changed by plugins. */
public interface AuthenticationSystem { public interface AuthenticationSystem {
/** /**
* Generates an authentication request from a {@link LoginAccountRequestJson} object. * Generates an authentication request from a {@link LoginAccountRequestJson} object.
* *
* @param ctx The Javalin context. * @param ctx The Javalin context.
* @param jsonData The JSON data. * @param jsonData The JSON data.
* @return An authentication request. * @return An authentication request.
*/ */
static AuthenticationRequest fromPasswordRequest(Context ctx, LoginAccountRequestJson jsonData) { static AuthenticationRequest fromPasswordRequest(Context ctx, LoginAccountRequestJson jsonData) {
return AuthenticationRequest.builder().context(ctx).passwordRequest(jsonData).build(); return AuthenticationRequest.builder().context(ctx).passwordRequest(jsonData).build();
} }
/** /**
* Generates an authentication request from a {@link LoginTokenRequestJson} object. * Generates an authentication request from a {@link LoginTokenRequestJson} object.
* *
* @param ctx The Javalin context. * @param ctx The Javalin context.
* @param jsonData The JSON data. * @param jsonData The JSON data.
* @return An authentication request. * @return An authentication request.
*/ */
static AuthenticationRequest fromTokenRequest(Context ctx, LoginTokenRequestJson jsonData) { static AuthenticationRequest fromTokenRequest(Context ctx, LoginTokenRequestJson jsonData) {
return AuthenticationRequest.builder().context(ctx).tokenRequest(jsonData).build(); return AuthenticationRequest.builder().context(ctx).tokenRequest(jsonData).build();
} }
/** /**
* Generates an authentication request from a {@link ComboTokenReqJson} object. * Generates an authentication request from a {@link ComboTokenReqJson} object.
* *
* @param ctx The Javalin context. * @param ctx The Javalin context.
* @param jsonData The JSON data. * @param jsonData The JSON data.
* @return An authentication request. * @return An authentication request.
*/ */
static AuthenticationRequest fromComboTokenRequest( static AuthenticationRequest fromComboTokenRequest(
Context ctx, ComboTokenReqJson jsonData, ComboTokenReqJson.LoginTokenData tokenData) { Context ctx, ComboTokenReqJson jsonData, ComboTokenReqJson.LoginTokenData tokenData) {
return AuthenticationRequest.builder() return AuthenticationRequest.builder()
.context(ctx) .context(ctx)
.sessionKeyRequest(jsonData) .sessionKeyRequest(jsonData)
.sessionKeyData(tokenData) .sessionKeyData(tokenData)
.build(); .build();
} }
/** /**
* Generates an authentication request from a {@link Context} object. * Generates an authentication request from a {@link Context} object.
* *
* @param ctx The Javalin context. * @param ctx The Javalin context.
* @return An authentication request. * @return An authentication request.
*/ */
static AuthenticationRequest fromExternalRequest(Context ctx) { static AuthenticationRequest fromExternalRequest(Context ctx) {
return AuthenticationRequest.builder().context(ctx).build(); return AuthenticationRequest.builder().context(ctx).build();
} }
/** /**
* Called when a user requests to make an account. * Called when a user requests to make an account.
* *
* @param username The provided username. * @param username The provided username.
* @param password The provided password. (SHA-256'ed) * @param password The provided password. (SHA-256'ed)
*/ */
void createAccount(String username, String password); void createAccount(String username, String password);
/** /**
* Called when a user requests to reset their password. * Called when a user requests to reset their password.
* *
* @param username The username of the account to reset. * @param username The username of the account to reset.
*/ */
void resetPassword(String username); void resetPassword(String username);
/** /**
* Called by plugins to internally verify a user's identity. * Called by plugins to internally verify a user's identity.
* *
* @param details A unique identifier to identify the user. (For example: a JWT token) * @param details A unique identifier to identify the user. (For example: a JWT token)
* @return The user's account if the verification was successful, null if the user was unable to * @return The user's account if the verification was successful, null if the user was unable to
* be verified. * be verified.
*/ */
Account verifyUser(String details); Account verifyUser(String details);
/** /**
* This is the authenticator used for password authentication. * This is the authenticator used for password authentication.
* *
* @return An authenticator. * @return An authenticator.
*/ */
Authenticator<LoginResultJson> getPasswordAuthenticator(); Authenticator<LoginResultJson> getPasswordAuthenticator();
/** /**
* This is the authenticator used for token authentication. * This is the authenticator used for token authentication.
* *
* @return An authenticator. * @return An authenticator.
*/ */
Authenticator<LoginResultJson> getTokenAuthenticator(); Authenticator<LoginResultJson> getTokenAuthenticator();
/** /**
* This is the authenticator used for session authentication. * This is the authenticator used for session authentication.
* *
* @return An authenticator. * @return An authenticator.
*/ */
Authenticator<ComboTokenResJson> getSessionKeyAuthenticator(); Authenticator<ComboTokenResJson> getSessionKeyAuthenticator();
/** /**
* This is the authenticator used for handling external authentication requests. * This is the authenticator used for handling external authentication requests.
* *
* @return An authenticator. * @return An authenticator.
*/ */
ExternalAuthenticator getExternalAuthenticator(); ExternalAuthenticator getExternalAuthenticator();
/** /**
* This is the authenticator used for handling OAuth authentication requests. * This is the authenticator used for handling OAuth authentication requests.
* *
* @return An authenticator. * @return An authenticator.
*/ */
OAuthAuthenticator getOAuthAuthenticator(); OAuthAuthenticator getOAuthAuthenticator();
/** A data container that holds relevant data for authenticating a client. */ /** A data container that holds relevant data for authenticating a client. */
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@Getter @Getter
class AuthenticationRequest { class AuthenticationRequest {
private final Context context; private final Context context;
@Nullable private final LoginAccountRequestJson passwordRequest; @Nullable private final LoginAccountRequestJson passwordRequest;
@Nullable private final LoginTokenRequestJson tokenRequest; @Nullable private final LoginTokenRequestJson tokenRequest;
@Nullable private final ComboTokenReqJson sessionKeyRequest; @Nullable private final ComboTokenReqJson sessionKeyRequest;
@Nullable private final ComboTokenReqJson.LoginTokenData sessionKeyData; @Nullable private final ComboTokenReqJson.LoginTokenData sessionKeyData;
} }
} }

View File

@ -1,22 +1,22 @@
package emu.grasscutter.auth; package emu.grasscutter.auth;
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;
/** /**
* Handles username/password authentication from the client. * Handles username/password authentication from the client.
* *
* @param <T> The response object type. Should be {@link LoginResultJson} or {@link * @param <T> The response object type. Should be {@link LoginResultJson} or {@link
* ComboTokenResJson} * ComboTokenResJson}
*/ */
public interface Authenticator<T> { public interface Authenticator<T> {
/** /**
* Attempt to authenticate the client with the provided credentials. * Attempt to authenticate the client with the provided credentials.
* *
* @param request The authentication request wrapped in a {@link * @param request The authentication request wrapped in a {@link
* AuthenticationSystem.AuthenticationRequest} object. * AuthenticationSystem.AuthenticationRequest} object.
* @return The result of the login in an object. * @return The result of the login in an object.
*/ */
T authenticate(AuthenticationSystem.AuthenticationRequest request); T authenticate(AuthenticationSystem.AuthenticationRequest request);
} }

View File

@ -1,363 +1,363 @@
package emu.grasscutter.auth; package emu.grasscutter.auth;
import static emu.grasscutter.config.Configuration.ACCOUNT; import static emu.grasscutter.config.Configuration.ACCOUNT;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import at.favre.lib.crypto.bcrypt.BCrypt; import at.favre.lib.crypto.bcrypt.BCrypt;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest; import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account; 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;
import emu.grasscutter.utils.FileUtils; import emu.grasscutter.utils.FileUtils;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import javax.crypto.Cipher; import javax.crypto.Cipher;
/** A class containing default authenticators. */ /** A class containing default authenticators. */
public final class DefaultAuthenticators { public final class DefaultAuthenticators {
/** Handles the authentication request from the username and password form. */ /** Handles the authentication request from the username and password form. */
public static class PasswordAuthenticator implements Authenticator<LoginResultJson> { public static class PasswordAuthenticator implements Authenticator<LoginResultJson> {
@Override @Override
public LoginResultJson authenticate(AuthenticationRequest request) { public LoginResultJson authenticate(AuthenticationRequest request) {
var response = new LoginResultJson(); var response = new LoginResultJson();
var requestData = request.getPasswordRequest(); var requestData = request.getPasswordRequest();
assert requestData != null; // This should never be null. assert requestData != null; // This should never be null.
int playerCount = Grasscutter.getGameServer().getPlayers().size(); int playerCount = Grasscutter.getGameServer().getPlayers().size();
boolean successfulLogin = false; boolean successfulLogin = false;
String address = request.getContext().ip(); String address = request.getContext().ip();
String responseMessage = translate("messages.dispatch.account.username_error"); String responseMessage = translate("messages.dispatch.account.username_error");
String loggerMessage = ""; String loggerMessage = "";
// Get account from database. // Get account from database.
Account account = DatabaseHelper.getAccountByName(requestData.account); Account account = DatabaseHelper.getAccountByName(requestData.account);
if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) { if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) {
// Check if account exists. // Check if account exists.
if (account == null && ACCOUNT.autoCreate) { if (account == null && ACCOUNT.autoCreate) {
// This account has been created AUTOMATICALLY. There will be no permissions added. // This account has been created AUTOMATICALLY. There will be no permissions added.
account = DatabaseHelper.createAccountWithUid(requestData.account, 0); account = DatabaseHelper.createAccountWithUid(requestData.account, 0);
// Check if the account was created successfully. // Check if the account was created successfully.
if (account == null) { if (account == null) {
responseMessage = translate("messages.dispatch.account.username_create_error"); responseMessage = translate("messages.dispatch.account.username_create_error");
Grasscutter.getLogger() Grasscutter.getLogger()
.info(translate("messages.dispatch.account.account_login_create_error", address)); .info(translate("messages.dispatch.account.account_login_create_error", address));
} else { } else {
// Continue with login. // Continue with login.
successfulLogin = true; successfulLogin = true;
// Log the creation. // Log the creation.
Grasscutter.getLogger() Grasscutter.getLogger()
.info( .info(
translate( translate(
"messages.dispatch.account.account_login_create_success", "messages.dispatch.account.account_login_create_success",
address, address,
response.data.account.uid)); response.data.account.uid));
} }
} else if (account != null) successfulLogin = true; } else if (account != null) successfulLogin = true;
else else
loggerMessage = translate("messages.dispatch.account.account_login_exist_error", address); loggerMessage = translate("messages.dispatch.account.account_login_exist_error", address);
} else { } else {
responseMessage = translate("messages.dispatch.account.server_max_player_limit"); responseMessage = translate("messages.dispatch.account.server_max_player_limit");
loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address); loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address);
} }
// Set response data. // Set response data.
if (successfulLogin) { if (successfulLogin) {
response.message = "OK"; response.message = "OK";
response.data.account.uid = account.getId(); response.data.account.uid = account.getId();
response.data.account.token = account.generateSessionKey(); response.data.account.token = account.generateSessionKey();
response.data.account.email = account.getEmail(); response.data.account.email = account.getEmail();
loggerMessage = loggerMessage =
translate("messages.dispatch.account.login_success", address, account.getId()); translate("messages.dispatch.account.login_success", address, account.getId());
} else { } else {
response.retcode = -201; response.retcode = -201;
response.message = responseMessage; response.message = responseMessage;
} }
Grasscutter.getLogger().info(loggerMessage); Grasscutter.getLogger().info(loggerMessage);
return response; return response;
} }
} }
public static class ExperimentalPasswordAuthenticator implements Authenticator<LoginResultJson> { public static class ExperimentalPasswordAuthenticator implements Authenticator<LoginResultJson> {
@Override @Override
public LoginResultJson authenticate(AuthenticationRequest request) { public LoginResultJson authenticate(AuthenticationRequest request) {
var response = new LoginResultJson(); var response = new LoginResultJson();
var requestData = request.getPasswordRequest(); var requestData = request.getPasswordRequest();
assert requestData != null; // This should never be null. assert requestData != null; // This should never be null.
int playerCount = Grasscutter.getGameServer().getPlayers().size(); int playerCount = Grasscutter.getGameServer().getPlayers().size();
boolean successfulLogin = false; boolean successfulLogin = false;
String address = request.getContext().ip(); String address = request.getContext().ip();
String responseMessage = translate("messages.dispatch.account.username_error"); String responseMessage = translate("messages.dispatch.account.username_error");
String loggerMessage = ""; String loggerMessage = "";
String decryptedPassword = ""; String decryptedPassword = "";
try { try {
byte[] key = FileUtils.readResource("/keys/auth_private-key.der"); byte[] key = FileUtils.readResource("/keys/auth_private-key.der");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key);
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPrivateKey private_key = (RSAPrivateKey) keyFactory.generatePrivate(keySpec); RSAPrivateKey private_key = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, private_key); cipher.init(Cipher.DECRYPT_MODE, private_key);
decryptedPassword = decryptedPassword =
new String( new String(
cipher.doFinal(Utils.base64Decode(request.getPasswordRequest().password)), cipher.doFinal(Utils.base64Decode(request.getPasswordRequest().password)),
StandardCharsets.UTF_8); StandardCharsets.UTF_8);
} catch (Exception ignored) { } catch (Exception ignored) {
decryptedPassword = request.getPasswordRequest().password; decryptedPassword = request.getPasswordRequest().password;
} }
if (decryptedPassword == null) { if (decryptedPassword == null) {
successfulLogin = false; successfulLogin = false;
loggerMessage = translate("messages.dispatch.account.login_password_error", address); loggerMessage = translate("messages.dispatch.account.login_password_error", address);
responseMessage = translate("messages.dispatch.account.password_error"); responseMessage = translate("messages.dispatch.account.password_error");
} }
// Get account from database. // Get account from database.
Account account = DatabaseHelper.getAccountByName(requestData.account); Account account = DatabaseHelper.getAccountByName(requestData.account);
if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) { if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) {
// Check if account exists. // Check if account exists.
if (account == null && ACCOUNT.autoCreate) { if (account == null && ACCOUNT.autoCreate) {
// This account has been created AUTOMATICALLY. There will be no permissions added. // This account has been created AUTOMATICALLY. There will be no permissions added.
if (decryptedPassword.length() >= 8) { if (decryptedPassword.length() >= 8) {
account = DatabaseHelper.createAccountWithUid(requestData.account, 0); account = DatabaseHelper.createAccountWithUid(requestData.account, 0);
account.setPassword( account.setPassword(
BCrypt.withDefaults().hashToString(12, decryptedPassword.toCharArray())); BCrypt.withDefaults().hashToString(12, decryptedPassword.toCharArray()));
account.save(); account.save();
// Check if the account was created successfully. // Check if the account was created successfully.
if (account == null) { if (account == null) {
responseMessage = translate("messages.dispatch.account.username_create_error"); responseMessage = translate("messages.dispatch.account.username_create_error");
loggerMessage = loggerMessage =
translate("messages.dispatch.account.account_login_create_error", address); translate("messages.dispatch.account.account_login_create_error", address);
} else { } else {
// Continue with login. // Continue with login.
successfulLogin = true; successfulLogin = true;
// Log the creation. // Log the creation.
Grasscutter.getLogger() Grasscutter.getLogger()
.info( .info(
translate( translate(
"messages.dispatch.account.account_login_create_success", "messages.dispatch.account.account_login_create_success",
address, address,
response.data.account.uid)); response.data.account.uid));
} }
} else { } else {
successfulLogin = false; successfulLogin = false;
loggerMessage = translate("messages.dispatch.account.login_password_error", address); loggerMessage = translate("messages.dispatch.account.login_password_error", address);
responseMessage = translate("messages.dispatch.account.password_length_error"); responseMessage = translate("messages.dispatch.account.password_length_error");
} }
} else if (account != null) { } else if (account != null) {
if (account.getPassword() != null && !account.getPassword().isEmpty()) { if (account.getPassword() != null && !account.getPassword().isEmpty()) {
if (BCrypt.verifyer() if (BCrypt.verifyer()
.verify(decryptedPassword.toCharArray(), account.getPassword()) .verify(decryptedPassword.toCharArray(), account.getPassword())
.verified) { .verified) {
successfulLogin = true; successfulLogin = true;
} else { } else {
successfulLogin = false; successfulLogin = false;
loggerMessage = translate("messages.dispatch.account.login_password_error", address); loggerMessage = translate("messages.dispatch.account.login_password_error", address);
responseMessage = translate("messages.dispatch.account.password_error"); responseMessage = translate("messages.dispatch.account.password_error");
} }
} else { } else {
successfulLogin = false; successfulLogin = false;
loggerMessage = loggerMessage =
translate("messages.dispatch.account.login_password_storage_error", address); translate("messages.dispatch.account.login_password_storage_error", address);
responseMessage = translate("messages.dispatch.account.password_storage_error"); responseMessage = translate("messages.dispatch.account.password_storage_error");
} }
} else { } else {
loggerMessage = translate("messages.dispatch.account.account_login_exist_error", address); loggerMessage = translate("messages.dispatch.account.account_login_exist_error", address);
} }
} else { } else {
responseMessage = translate("messages.dispatch.account.server_max_player_limit"); responseMessage = translate("messages.dispatch.account.server_max_player_limit");
loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address); loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address);
} }
// Set response data. // Set response data.
if (successfulLogin) { if (successfulLogin) {
response.message = "OK"; response.message = "OK";
response.data.account.uid = account.getId(); response.data.account.uid = account.getId();
response.data.account.token = account.generateSessionKey(); response.data.account.token = account.generateSessionKey();
response.data.account.email = account.getEmail(); response.data.account.email = account.getEmail();
loggerMessage = loggerMessage =
translate("messages.dispatch.account.login_success", address, account.getId()); translate("messages.dispatch.account.login_success", address, account.getId());
} else { } else {
response.retcode = -201; response.retcode = -201;
response.message = responseMessage; response.message = responseMessage;
} }
Grasscutter.getLogger().info(loggerMessage); Grasscutter.getLogger().info(loggerMessage);
return response; return response;
} }
} }
/** Handles the authentication request from the game when using a registry token. */ /** Handles the authentication request from the game when using a registry token. */
public static class TokenAuthenticator implements Authenticator<LoginResultJson> { public static class TokenAuthenticator implements Authenticator<LoginResultJson> {
@Override @Override
public LoginResultJson authenticate(AuthenticationRequest request) { public LoginResultJson authenticate(AuthenticationRequest request) {
var response = new LoginResultJson(); var response = new LoginResultJson();
var requestData = request.getTokenRequest(); var requestData = request.getTokenRequest();
assert requestData != null; assert requestData != null;
boolean successfulLogin; boolean successfulLogin;
String address = request.getContext().ip(); String address = request.getContext().ip();
String loggerMessage; String loggerMessage;
int playerCount = Grasscutter.getGameServer().getPlayers().size(); int playerCount = Grasscutter.getGameServer().getPlayers().size();
// Log the attempt. // Log the attempt.
Grasscutter.getLogger() Grasscutter.getLogger()
.info(translate("messages.dispatch.account.login_token_attempt", address)); .info(translate("messages.dispatch.account.login_token_attempt", address));
if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) { if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) {
// Get account from database. // Get account from database.
Account account = DatabaseHelper.getAccountById(requestData.uid); Account account = DatabaseHelper.getAccountById(requestData.uid);
// Check if account exists/token is valid. // Check if account exists/token is valid.
successfulLogin = account != null && account.getSessionKey().equals(requestData.token); successfulLogin = account != null && account.getSessionKey().equals(requestData.token);
// Set response data. // Set response data.
if (successfulLogin) { if (successfulLogin) {
response.message = "OK"; response.message = "OK";
response.data.account.uid = account.getId(); response.data.account.uid = account.getId();
response.data.account.token = account.getSessionKey(); response.data.account.token = account.getSessionKey();
response.data.account.email = account.getEmail(); response.data.account.email = account.getEmail();
// Log the login. // Log the login.
loggerMessage = loggerMessage =
translate("messages.dispatch.account.login_token_success", address, requestData.uid); translate("messages.dispatch.account.login_token_success", address, requestData.uid);
} else { } else {
response.retcode = -201; response.retcode = -201;
response.message = translate("messages.dispatch.account.account_cache_error"); response.message = translate("messages.dispatch.account.account_cache_error");
// Log the failure. // Log the failure.
loggerMessage = translate("messages.dispatch.account.login_token_error", address); loggerMessage = translate("messages.dispatch.account.login_token_error", address);
} }
} else { } else {
response.retcode = -201; response.retcode = -201;
response.message = translate("messages.dispatch.account.server_max_player_limit"); response.message = translate("messages.dispatch.account.server_max_player_limit");
loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address); loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address);
} }
Grasscutter.getLogger().info(loggerMessage); Grasscutter.getLogger().info(loggerMessage);
return response; return response;
} }
} }
/** Handles the authentication request from the game when using a combo token/session key. */ /** Handles the authentication request from the game when using a combo token/session key. */
public static class SessionKeyAuthenticator implements Authenticator<ComboTokenResJson> { public static class SessionKeyAuthenticator implements Authenticator<ComboTokenResJson> {
@Override @Override
public ComboTokenResJson authenticate(AuthenticationRequest request) { public ComboTokenResJson authenticate(AuthenticationRequest request) {
var response = new ComboTokenResJson(); var response = new ComboTokenResJson();
var requestData = request.getSessionKeyRequest(); var requestData = request.getSessionKeyRequest();
var loginData = request.getSessionKeyData(); var loginData = request.getSessionKeyData();
assert requestData != null; assert requestData != null;
assert loginData != null; assert loginData != null;
boolean successfulLogin; boolean successfulLogin;
String address = request.getContext().ip(); String address = request.getContext().ip();
String loggerMessage; String loggerMessage;
int playerCount = Grasscutter.getGameServer().getPlayers().size(); int playerCount = Grasscutter.getGameServer().getPlayers().size();
if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) { if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) {
// Get account from database. // Get account from database.
Account account = DatabaseHelper.getAccountById(loginData.uid); Account account = DatabaseHelper.getAccountById(loginData.uid);
// Check if account exists/token is valid. // Check if account exists/token is valid.
successfulLogin = account != null && account.getSessionKey().equals(loginData.token); successfulLogin = account != null && account.getSessionKey().equals(loginData.token);
// Set response data. // Set response data.
if (successfulLogin) { if (successfulLogin) {
response.message = "OK"; response.message = "OK";
response.data.open_id = account.getId(); response.data.open_id = account.getId();
response.data.combo_id = "157795300"; response.data.combo_id = "157795300";
response.data.combo_token = account.generateLoginToken(); response.data.combo_token = account.generateLoginToken();
// Log the login. // Log the login.
loggerMessage = translate("messages.dispatch.account.combo_token_success", address); loggerMessage = translate("messages.dispatch.account.combo_token_success", address);
} else { } else {
response.retcode = -201; response.retcode = -201;
response.message = translate("messages.dispatch.account.session_key_error"); response.message = translate("messages.dispatch.account.session_key_error");
// Log the failure. // Log the failure.
loggerMessage = translate("messages.dispatch.account.combo_token_error", address); loggerMessage = translate("messages.dispatch.account.combo_token_error", address);
} }
} else { } else {
response.retcode = -201; response.retcode = -201;
response.message = translate("messages.dispatch.account.server_max_player_limit"); response.message = translate("messages.dispatch.account.server_max_player_limit");
loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address); loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address);
} }
Grasscutter.getLogger().info(loggerMessage); Grasscutter.getLogger().info(loggerMessage);
return response; return response;
} }
} }
/** Handles authentication requests from external sources. */ /** Handles authentication requests from external sources. */
public static class ExternalAuthentication implements ExternalAuthenticator { public static class ExternalAuthentication implements ExternalAuthenticator {
@Override @Override
public void handleLogin(AuthenticationRequest request) { public void handleLogin(AuthenticationRequest request) {
request request
.getContext() .getContext()
.result("Authentication is not available with the default authentication method."); .result("Authentication is not available with the default authentication method.");
} }
@Override @Override
public void handleAccountCreation(AuthenticationRequest request) { public void handleAccountCreation(AuthenticationRequest request) {
request request
.getContext() .getContext()
.result("Authentication is not available with the default authentication method."); .result("Authentication is not available with the default authentication method.");
} }
@Override @Override
public void handlePasswordReset(AuthenticationRequest request) { public void handlePasswordReset(AuthenticationRequest request) {
request request
.getContext() .getContext()
.result("Authentication is not available with the default authentication method."); .result("Authentication is not available with the default authentication method.");
} }
} }
/** Handles authentication requests from OAuth sources.Zenlith */ /** Handles authentication requests from OAuth sources.Zenlith */
public static class OAuthAuthentication implements OAuthAuthenticator { public static class OAuthAuthentication implements OAuthAuthenticator {
@Override @Override
public void handleLogin(AuthenticationRequest request) { public void handleLogin(AuthenticationRequest request) {
request request
.getContext() .getContext()
.result("Authentication is not available with the default authentication method."); .result("Authentication is not available with the default authentication method.");
} }
@Override @Override
public void handleRedirection(AuthenticationRequest request, ClientType type) { public void handleRedirection(AuthenticationRequest request, ClientType type) {
request request
.getContext() .getContext()
.result("Authentication is not available with the default authentication method."); .result("Authentication is not available with the default authentication method.");
} }
@Override @Override
public void handleTokenProcess(AuthenticationRequest request) { public void handleTokenProcess(AuthenticationRequest request) {
request request
.getContext() .getContext()
.result("Authentication is not available with the default authentication method."); .result("Authentication is not available with the default authentication method.");
} }
} }
} }

View File

@ -1,32 +1,32 @@
package emu.grasscutter.auth; package emu.grasscutter.auth;
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest; import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
/** Handles authentication via external routes. */ /** Handles authentication via external routes. */
public interface ExternalAuthenticator { public interface ExternalAuthenticator {
/** /**
* Called when an external login request is made. * Called when an external login request is made.
* *
* @param request The authentication request. * @param request The authentication request.
*/ */
void handleLogin(AuthenticationRequest request); void handleLogin(AuthenticationRequest request);
/** /**
* Called when an external account creation request is made. * Called when an external account creation request is made.
* *
* @param request The authentication request. * @param request The authentication request.
* <p>For developers: Use AuthenticationRequest#getRequest() to get the request body. Use * <p>For developers: Use AuthenticationRequest#getRequest() to get the request body. Use
* AuthenticationRequest#getResponse() to get the response body. * AuthenticationRequest#getResponse() to get the response body.
*/ */
void handleAccountCreation(AuthenticationRequest request); void handleAccountCreation(AuthenticationRequest request);
/** /**
* Called when an external password reset request is made. * Called when an external password reset request is made.
* *
* @param request The authentication request. * @param request The authentication request.
* <p>For developers: Use AuthenticationRequest#getRequest() to get the request body. Use * <p>For developers: Use AuthenticationRequest#getRequest() to get the request body. Use
* AuthenticationRequest#getResponse() to get the response body. * AuthenticationRequest#getResponse() to get the response body.
*/ */
void handlePasswordReset(AuthenticationRequest request); void handlePasswordReset(AuthenticationRequest request);
} }

View File

@ -1,34 +1,34 @@
package emu.grasscutter.auth; package emu.grasscutter.auth;
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest; import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
/** Handles authentication via OAuth routes. */ /** Handles authentication via OAuth routes. */
public interface OAuthAuthenticator { public interface OAuthAuthenticator {
/** /**
* Called when an OAuth login request is made. * Called when an OAuth login request is made.
* *
* @param request The authentication request. * @param request The authentication request.
*/ */
void handleLogin(AuthenticationRequest request); void handleLogin(AuthenticationRequest request);
/** /**
* Called when a client requests to redirect to login page. * Called when a client requests to redirect to login page.
* *
* @param request The authentication request. * @param request The authentication request.
*/ */
void handleRedirection(AuthenticationRequest request, ClientType clientType); void handleRedirection(AuthenticationRequest request, ClientType clientType);
/** /**
* Called when an OAuth login requests callback. * Called when an OAuth login requests callback.
* *
* @param request The authentication request. * @param request The authentication request.
*/ */
void handleTokenProcess(AuthenticationRequest request); void handleTokenProcess(AuthenticationRequest request);
/** The type of the client. Used for handling redirection. */ /** The type of the client. Used for handling redirection. */
enum ClientType { enum ClientType {
DESKTOP, DESKTOP,
MOBILE MOBILE
} }
} }

View File

@ -1,28 +1,28 @@
package emu.grasscutter.command; package emu.grasscutter.command;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Command { public @interface Command {
String label() default ""; String label() default "";
String[] aliases() default {}; String[] aliases() default {};
String[] usage() default {""}; String[] usage() default {""};
String permission() default ""; String permission() default "";
String permissionTargeted() default ""; String permissionTargeted() default "";
TargetRequirement targetRequirement() default TargetRequirement.ONLINE; TargetRequirement targetRequirement() default TargetRequirement.ONLINE;
boolean threading() default false; boolean threading() default false;
enum TargetRequirement { enum TargetRequirement {
NONE, // targetPlayer is not required NONE, // targetPlayer is not required
OFFLINE, // targetPlayer must be offline OFFLINE, // targetPlayer must be offline
PLAYER, // targetPlayer can be online or offline PLAYER, // targetPlayer can be online or offline
ONLINE // targetPlayer must be online ONLINE // targetPlayer must be online
} }
} }

View File

@ -1,88 +1,88 @@
package emu.grasscutter.command; package emu.grasscutter.command;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
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.ReceiveCommandFeedbackEvent; import emu.grasscutter.server.event.game.ReceiveCommandFeedbackEvent;
import java.util.List; import java.util.List;
import java.util.StringJoiner; import java.util.StringJoiner;
public interface CommandHandler { public interface CommandHandler {
/** /**
* Send a message to the target. * Send a message to the target.
* *
* @param player The player to send the message to, or null for the server console. * @param player The player to send the message to, or null for the server console.
* @param message The message to send. * @param message The message to send.
*/ */
static void sendMessage(Player player, String message) { static void sendMessage(Player player, String message) {
// Call command feedback event. // Call command feedback event.
ReceiveCommandFeedbackEvent event = new ReceiveCommandFeedbackEvent(player, message); ReceiveCommandFeedbackEvent event = new ReceiveCommandFeedbackEvent(player, message);
event.call(); event.call();
if (event.isCanceled()) { // If event is not cancelled, continue. if (event.isCanceled()) { // If event is not cancelled, continue.
return; return;
} }
// Send message to target. // Send message to target.
if (player == null) { if (player == null) {
Grasscutter.getLogger().info(event.getMessage()); Grasscutter.getLogger().info(event.getMessage());
} else { } else {
player.dropMessage(event.getMessage().replace("\n\t", "\n\n")); player.dropMessage(event.getMessage().replace("\n\t", "\n\n"));
} }
} }
static void sendTranslatedMessage(Player player, String messageKey, Object... args) { static void sendTranslatedMessage(Player player, String messageKey, Object... args) {
sendMessage(player, translate(player, messageKey, args)); sendMessage(player, translate(player, messageKey, args));
} }
default String getUsageString(Player player, String... args) { default String getUsageString(Player player, String... args) {
Command annotation = this.getClass().getAnnotation(Command.class); Command annotation = this.getClass().getAnnotation(Command.class);
String usage_prefix = translate(player, "commands.execution.usage_prefix"); String usage_prefix = translate(player, "commands.execution.usage_prefix");
String command = annotation.label(); String command = annotation.label();
for (String alias : annotation.aliases()) { for (String alias : annotation.aliases()) {
if (alias.length() < command.length()) command = alias; if (alias.length() < command.length()) command = alias;
} }
if (player != null) { if (player != null) {
command = "/" + command; command = "/" + command;
} }
String target = String target =
switch (annotation.targetRequirement()) { switch (annotation.targetRequirement()) {
case NONE -> ""; case NONE -> "";
case OFFLINE -> "@<UID> "; // TODO: make translation keys for offline and online players case OFFLINE -> "@<UID> "; // TODO: make translation keys for offline and online players
case ONLINE -> (player == null) case ONLINE -> (player == null)
? "@<UID> " ? "@<UID> "
: "[@<UID>] "; // TODO: make translation keys for offline and online players : "[@<UID>] "; // TODO: make translation keys for offline and online players
case PLAYER -> (player == null) ? "@<UID> " : "[@<UID>] "; case PLAYER -> (player == null) ? "@<UID> " : "[@<UID>] ";
}; };
String[] usages = annotation.usage(); String[] usages = annotation.usage();
StringJoiner joiner = new StringJoiner("\n\t"); StringJoiner joiner = new StringJoiner("\n\t");
for (String usage : usages) joiner.add(usage_prefix + command + " " + target + usage); for (String usage : usages) joiner.add(usage_prefix + command + " " + target + usage);
return joiner.toString(); return joiner.toString();
} }
default void sendUsageMessage(Player player, String... args) { default void sendUsageMessage(Player player, String... args) {
sendMessage(player, getUsageString(player, args)); sendMessage(player, getUsageString(player, args));
} }
default String getLabel() { default String getLabel() {
return this.getClass().getAnnotation(Command.class).label(); return this.getClass().getAnnotation(Command.class).label();
} }
default String getDescriptionKey() { default String getDescriptionKey() {
Command annotation = this.getClass().getAnnotation(Command.class); Command annotation = this.getClass().getAnnotation(Command.class);
return "commands.%s.description".formatted(annotation.label()); return "commands.%s.description".formatted(annotation.label());
} }
default String getDescriptionString(Player player) { default String getDescriptionString(Player player) {
return translate(player, getDescriptionKey()); return translate(player, getDescriptionKey());
} }
/** /**
* Called when a player/console invokes a command. * Called when a player/console invokes a command.
* *
* @param sender The player/console that invoked the command. * @param sender The player/console that invoked the command.
* @param args The arguments to the command. * @param args The arguments to the command.
*/ */
default void execute(Player sender, Player targetPlayer, List<String> args) {} default void execute(Player sender, Player targetPlayer, List<String> args) {}
} }

View File

@ -1,354 +1,354 @@
package emu.grasscutter.command; package emu.grasscutter.command;
import static emu.grasscutter.config.Configuration.SERVER; import static emu.grasscutter.config.Configuration.SERVER;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.*; import java.util.*;
import org.reflections.Reflections; import org.reflections.Reflections;
@SuppressWarnings({"UnusedReturnValue", "unused"}) @SuppressWarnings({"UnusedReturnValue", "unused"})
public final class CommandMap { public final class CommandMap {
private static final int INVALID_UID = Integer.MIN_VALUE; private static final int INVALID_UID = Integer.MIN_VALUE;
private static final String consoleId = "console"; private static final String consoleId = "console";
private final Map<String, CommandHandler> commands = new TreeMap<>(); private final Map<String, CommandHandler> commands = new TreeMap<>();
private final Map<String, CommandHandler> aliases = new TreeMap<>(); private final Map<String, CommandHandler> aliases = new TreeMap<>();
private final Map<String, Command> annotations = new TreeMap<>(); private final Map<String, Command> annotations = new TreeMap<>();
private final Object2IntMap<String> targetPlayerIds = new Object2IntOpenHashMap<>(); private final Object2IntMap<String> targetPlayerIds = new Object2IntOpenHashMap<>();
public CommandMap() { public CommandMap() {
this(false); this(false);
} }
public CommandMap(boolean scan) { public CommandMap(boolean scan) {
if (scan) this.scan(); if (scan) this.scan();
} }
public static CommandMap getInstance() { public static CommandMap getInstance() {
return Grasscutter.getCommandMap(); return Grasscutter.getCommandMap();
} }
private static int getUidFromString(String input) { private static int getUidFromString(String input) {
try { try {
return Integer.parseInt(input); return Integer.parseInt(input);
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
var account = DatabaseHelper.getAccountByName(input); var account = DatabaseHelper.getAccountByName(input);
if (account == null) return INVALID_UID; if (account == null) return INVALID_UID;
var player = DatabaseHelper.getPlayerByAccount(account); var player = DatabaseHelper.getPlayerByAccount(account);
if (player == null) return INVALID_UID; if (player == null) return INVALID_UID;
// We will be immediately fetching the player again after this, // We will be immediately fetching the player again after this,
// but offline vs online Player safety is more important than saving a lookup // but offline vs online Player safety is more important than saving a lookup
return player.getUid(); return player.getUid();
} }
} }
/** /**
* Register a command handler. * Register a command handler.
* *
* @param label The command label. * @param label The command label.
* @param command The command handler. * @param command The command handler.
* @return Instance chaining. * @return Instance chaining.
*/ */
public CommandMap registerCommand(String label, CommandHandler command) { public CommandMap registerCommand(String label, CommandHandler command) {
Grasscutter.getLogger().debug("Registered command: " + label); Grasscutter.getLogger().debug("Registered command: " + label);
label = label.toLowerCase(); label = label.toLowerCase();
// Get command data. // Get command data.
Command annotation = command.getClass().getAnnotation(Command.class); Command annotation = command.getClass().getAnnotation(Command.class);
this.annotations.put(label, annotation); this.annotations.put(label, annotation);
this.commands.put(label, command); this.commands.put(label, command);
// Register aliases. // Register aliases.
for (String alias : annotation.aliases()) { for (String alias : annotation.aliases()) {
this.aliases.put(alias, command); this.aliases.put(alias, command);
this.annotations.put(alias, annotation); this.annotations.put(alias, annotation);
} }
return this; return this;
} }
/** /**
* Removes a registered command handler. * Removes a registered command handler.
* *
* @param label The command label. * @param label The command label.
* @return Instance chaining. * @return Instance chaining.
*/ */
public CommandMap unregisterCommand(String label) { public CommandMap unregisterCommand(String label) {
Grasscutter.getLogger().debug("Unregistered command: " + label); Grasscutter.getLogger().debug("Unregistered command: " + label);
CommandHandler handler = this.commands.get(label); CommandHandler handler = this.commands.get(label);
if (handler == null) return this; if (handler == null) return this;
Command annotation = handler.getClass().getAnnotation(Command.class); Command annotation = handler.getClass().getAnnotation(Command.class);
this.annotations.remove(label); this.annotations.remove(label);
this.commands.remove(label); this.commands.remove(label);
// Unregister aliases. // Unregister aliases.
for (String alias : annotation.aliases()) { for (String alias : annotation.aliases()) {
this.aliases.remove(alias); this.aliases.remove(alias);
this.annotations.remove(alias); this.annotations.remove(alias);
} }
return this; return this;
} }
public List<Command> getAnnotationsAsList() { public List<Command> getAnnotationsAsList() {
return new ArrayList<>(this.annotations.values()); return new ArrayList<>(this.annotations.values());
} }
public Map<String, Command> getAnnotations() { public Map<String, Command> getAnnotations() {
return new LinkedHashMap<>(this.annotations); return new LinkedHashMap<>(this.annotations);
} }
/** /**
* Returns a list of all registered commands. * Returns a list of all registered commands.
* *
* @return All command handlers as a list. * @return All command handlers as a list.
*/ */
public List<CommandHandler> getHandlersAsList() { public List<CommandHandler> getHandlersAsList() {
return new ArrayList<>(this.commands.values()); return new ArrayList<>(this.commands.values());
} }
public Map<String, CommandHandler> getHandlers() { public Map<String, CommandHandler> getHandlers() {
return this.commands; return this.commands;
} }
/** /**
* Returns a handler by label/alias. * Returns a handler by label/alias.
* *
* @param label The command label. * @param label The command label.
* @return The command handler. * @return The command handler.
*/ */
public CommandHandler getHandler(String label) { public CommandHandler getHandler(String label) {
CommandHandler handler = this.commands.get(label); CommandHandler handler = this.commands.get(label);
if (handler == null) { if (handler == null) {
// Try getting by alias // Try getting by alias
handler = this.aliases.get(label); handler = this.aliases.get(label);
} }
return handler; return handler;
} }
private Player getTargetPlayer( private Player getTargetPlayer(
String playerId, Player player, Player targetPlayer, List<String> args) { String playerId, Player player, Player targetPlayer, List<String> args) {
// Top priority: If any @UID argument is present, override targetPlayer with it. // Top priority: If any @UID argument is present, override targetPlayer with it.
for (int i = 0; i < args.size(); i++) { for (int i = 0; i < args.size(); i++) {
String arg = args.get(i); String arg = args.get(i);
if (arg.startsWith("@")) { if (arg.startsWith("@")) {
arg = args.remove(i).substring(1); arg = args.remove(i).substring(1);
if (arg.equals("")) { if (arg.equals("")) {
// This is a special case to target nothing, distinct from failing to assign a target. // This is a special case to target nothing, distinct from failing to assign a target.
// This is specifically to allow in-game players to run a command without targeting // This is specifically to allow in-game players to run a command without targeting
// themselves or anyone else. // themselves or anyone else.
return null; return null;
} }
int uid = getUidFromString(arg); int uid = getUidFromString(arg);
if (uid == INVALID_UID) { if (uid == INVALID_UID) {
CommandHandler.sendTranslatedMessage(player, "commands.generic.invalid.uid"); CommandHandler.sendTranslatedMessage(player, "commands.generic.invalid.uid");
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid, true); targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid, true);
if (targetPlayer == null) { if (targetPlayer == null) {
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error"); CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
return targetPlayer; return targetPlayer;
} }
} }
// Next priority: If we invoked with a target, use that. // Next priority: If we invoked with a target, use that.
// By default, this only happens when you message another player in-game with a command. // By default, this only happens when you message another player in-game with a command.
if (targetPlayer != null) { if (targetPlayer != null) {
return targetPlayer; return targetPlayer;
} }
// Next priority: Use previously-set target. (see /target [[@]UID]) // Next priority: Use previously-set target. (see /target [[@]UID])
if (targetPlayerIds.containsKey(playerId)) { if (targetPlayerIds.containsKey(playerId)) {
targetPlayer = targetPlayer =
Grasscutter.getGameServer().getPlayerByUid(targetPlayerIds.getInt(playerId), true); Grasscutter.getGameServer().getPlayerByUid(targetPlayerIds.getInt(playerId), true);
// We check every time in case the target is deleted after being targeted // We check every time in case the target is deleted after being targeted
if (targetPlayer == null) { if (targetPlayer == null) {
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error"); CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
return targetPlayer; return targetPlayer;
} }
// Lowest priority: Target the player invoking the command. In the case of the console, this // Lowest priority: Target the player invoking the command. In the case of the console, this
// will return null. // will return null.
return player; return player;
} }
private boolean setPlayerTarget(String playerId, Player player, String targetUid) { private boolean setPlayerTarget(String playerId, Player player, String targetUid) {
if (targetUid.equals("")) { // Clears the default targetPlayer. if (targetUid.equals("")) { // Clears the default targetPlayer.
targetPlayerIds.removeInt(playerId); targetPlayerIds.removeInt(playerId);
CommandHandler.sendTranslatedMessage(player, "commands.execution.clear_target"); CommandHandler.sendTranslatedMessage(player, "commands.execution.clear_target");
return true; return true;
} }
// Sets default targetPlayer to the UID provided. // Sets default targetPlayer to the UID provided.
int uid = getUidFromString(targetUid); int uid = getUidFromString(targetUid);
if (uid == INVALID_UID) { if (uid == INVALID_UID) {
CommandHandler.sendTranslatedMessage(player, "commands.generic.invalid.uid"); CommandHandler.sendTranslatedMessage(player, "commands.generic.invalid.uid");
return false; return false;
} }
Player targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid, true); Player targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid, true);
if (targetPlayer == null) { if (targetPlayer == null) {
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error"); CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
return false; return false;
} }
targetPlayerIds.put(playerId, uid); targetPlayerIds.put(playerId, uid);
String target = uid + " (" + targetPlayer.getAccount().getUsername() + ")"; String target = uid + " (" + targetPlayer.getAccount().getUsername() + ")";
CommandHandler.sendTranslatedMessage(player, "commands.execution.set_target", target); CommandHandler.sendTranslatedMessage(player, "commands.execution.set_target", target);
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
player, player,
targetPlayer.isOnline() targetPlayer.isOnline()
? "commands.execution.set_target_online" ? "commands.execution.set_target_online"
: "commands.execution.set_target_offline", : "commands.execution.set_target_offline",
target); target);
return true; return true;
} }
/** /**
* Invoke a command handler with the given arguments. * Invoke a command handler with the given arguments.
* *
* @param player The player invoking the command or null for the server console. * @param player The player invoking the command or null for the server console.
* @param rawMessage The messaged used to invoke the command. * @param rawMessage The messaged used to invoke the command.
*/ */
public void invoke(Player player, Player targetPlayer, String rawMessage) { public void invoke(Player player, Player targetPlayer, String rawMessage) {
// The console outputs in-game command. [{Account Username} (Player UID: {Player Uid})] // The console outputs in-game command. [{Account Username} (Player UID: {Player Uid})]
if (SERVER.logCommands) { if (SERVER.logCommands) {
if (player != null) { if (player != null) {
Grasscutter.getLogger() Grasscutter.getLogger()
.info( .info(
"Command used by [" "Command used by ["
+ player.getAccount().getUsername() + player.getAccount().getUsername()
+ " (Player UID: " + " (Player UID: "
+ player.getUid() + player.getUid()
+ ")]: " + ")]: "
+ rawMessage); + rawMessage);
} else { } else {
Grasscutter.getLogger().info("Command used by server console: " + rawMessage); Grasscutter.getLogger().info("Command used by server console: " + rawMessage);
} }
} }
rawMessage = rawMessage.trim(); rawMessage = rawMessage.trim();
if (rawMessage.length() == 0) { if (rawMessage.length() == 0) {
CommandHandler.sendTranslatedMessage(player, "commands.generic.not_specified"); CommandHandler.sendTranslatedMessage(player, "commands.generic.not_specified");
return; return;
} }
// Parse message. // Parse message.
String[] split = rawMessage.split(" "); String[] split = rawMessage.split(" ");
String label = split[0].toLowerCase(); String label = split[0].toLowerCase();
List<String> args = new ArrayList<>(Arrays.asList(split).subList(1, split.length)); List<String> args = new ArrayList<>(Arrays.asList(split).subList(1, split.length));
String playerId = (player == null) ? consoleId : player.getAccount().getId(); String playerId = (player == null) ? consoleId : player.getAccount().getId();
// Check for special cases - currently only target command. // Check for special cases - currently only target command.
if (label.startsWith("@")) { // @[UID] if (label.startsWith("@")) { // @[UID]
this.setPlayerTarget(playerId, player, label.substring(1)); this.setPlayerTarget(playerId, player, label.substring(1));
return; return;
} else if (label.equalsIgnoreCase("target")) { // target [[@]UID] } else if (label.equalsIgnoreCase("target")) { // target [[@]UID]
if (args.size() > 0) { if (args.size() > 0) {
String targetUidStr = args.get(0); String targetUidStr = args.get(0);
if (targetUidStr.startsWith("@")) { if (targetUidStr.startsWith("@")) {
targetUidStr = targetUidStr.substring(1); targetUidStr = targetUidStr.substring(1);
} }
this.setPlayerTarget(playerId, player, targetUidStr); this.setPlayerTarget(playerId, player, targetUidStr);
return; return;
} else { } else {
this.setPlayerTarget(playerId, player, ""); this.setPlayerTarget(playerId, player, "");
return; return;
} }
} }
// Get command handler. // Get command handler.
CommandHandler handler = this.getHandler(label); CommandHandler handler = this.getHandler(label);
// Check if the handler is null. // Check if the handler is null.
if (handler == null) { if (handler == null) {
CommandHandler.sendTranslatedMessage(player, "commands.generic.unknown_command", label); CommandHandler.sendTranslatedMessage(player, "commands.generic.unknown_command", label);
return; return;
} }
// Get the command's annotation. // Get the command's annotation.
Command annotation = this.annotations.get(label); Command annotation = this.annotations.get(label);
// Resolve targetPlayer // Resolve targetPlayer
try { try {
targetPlayer = getTargetPlayer(playerId, player, targetPlayer, args); targetPlayer = getTargetPlayer(playerId, player, targetPlayer, args);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
return; return;
} }
// Check for permissions. // Check for permissions.
if (!Grasscutter.getPermissionHandler() if (!Grasscutter.getPermissionHandler()
.checkPermission( .checkPermission(
player, player,
targetPlayer, targetPlayer,
annotation.permission(), annotation.permission(),
this.annotations.get(label).permissionTargeted())) { this.annotations.get(label).permissionTargeted())) {
return; return;
} }
// Check if command has unfulfilled constraints on targetPlayer // Check if command has unfulfilled constraints on targetPlayer
Command.TargetRequirement targetRequirement = annotation.targetRequirement(); Command.TargetRequirement targetRequirement = annotation.targetRequirement();
if (targetRequirement != Command.TargetRequirement.NONE) { if (targetRequirement != Command.TargetRequirement.NONE) {
if (targetPlayer == null) { if (targetPlayer == null) {
handler.sendUsageMessage(player); handler.sendUsageMessage(player);
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target"); CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target");
return; return;
} }
if ((targetRequirement == Command.TargetRequirement.ONLINE) && !targetPlayer.isOnline()) { if ((targetRequirement == Command.TargetRequirement.ONLINE) && !targetPlayer.isOnline()) {
handler.sendUsageMessage(player); handler.sendUsageMessage(player);
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_online"); CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_online");
return; return;
} }
if ((targetRequirement == Command.TargetRequirement.OFFLINE) && targetPlayer.isOnline()) { if ((targetRequirement == Command.TargetRequirement.OFFLINE) && targetPlayer.isOnline()) {
handler.sendUsageMessage(player); handler.sendUsageMessage(player);
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_offline"); CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_offline");
return; return;
} }
} }
// Copy player and handler to final properties. // Copy player and handler to final properties.
final Player targetPlayerF = targetPlayer; // Is there a better way to do this? final Player targetPlayerF = targetPlayer; // Is there a better way to do this?
final CommandHandler handlerF = handler; // Is there a better way to do this? final CommandHandler handlerF = handler; // Is there a better way to do this?
// Invoke execute method for handler. // Invoke execute method for handler.
Runnable runnable = () -> handlerF.execute(player, targetPlayerF, args); Runnable runnable = () -> handlerF.execute(player, targetPlayerF, args);
if (annotation.threading()) { if (annotation.threading()) {
new Thread(runnable).start(); new Thread(runnable).start();
} else { } else {
runnable.run(); runnable.run();
} }
} }
/** Scans for all classes annotated with {@link Command} and registers them. */ /** Scans for all classes annotated with {@link Command} and registers them. */
private void scan() { private void scan() {
Reflections reflector = Grasscutter.reflector; Reflections reflector = Grasscutter.reflector;
Set<Class<?>> classes = reflector.getTypesAnnotatedWith(Command.class); Set<Class<?>> classes = reflector.getTypesAnnotatedWith(Command.class);
classes.forEach( classes.forEach(
annotated -> { annotated -> {
try { try {
Command cmdData = annotated.getAnnotation(Command.class); Command cmdData = annotated.getAnnotation(Command.class);
Object object = annotated.getDeclaredConstructor().newInstance(); Object object = annotated.getDeclaredConstructor().newInstance();
if (object instanceof CommandHandler) if (object instanceof CommandHandler)
this.registerCommand(cmdData.label(), (CommandHandler) object); this.registerCommand(cmdData.label(), (CommandHandler) object);
else else
Grasscutter.getLogger() Grasscutter.getLogger()
.error("Class " + annotated.getName() + " is not a CommandHandler!"); .error("Class " + annotated.getName() + " is not a CommandHandler!");
} catch (Exception exception) { } catch (Exception exception) {
Grasscutter.getLogger() Grasscutter.getLogger()
.error( .error(
"Failed to register command handler for " + annotated.getSimpleName(), "Failed to register command handler for " + annotated.getSimpleName(),
exception); exception);
} }
}); });
} }
} }

View File

@ -1,33 +1,33 @@
package emu.grasscutter.command; package emu.grasscutter.command;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
public class DefaultPermissionHandler implements PermissionHandler { public class DefaultPermissionHandler implements PermissionHandler {
@Override @Override
public boolean EnablePermissionCommand() { public boolean EnablePermissionCommand() {
return true; return true;
} }
@Override @Override
public boolean checkPermission( public boolean checkPermission(
Player player, Player targetPlayer, String permissionNode, String permissionNodeTargeted) { Player player, Player targetPlayer, String permissionNode, String permissionNodeTargeted) {
if (player == null) { if (player == null) {
return true; return true;
} }
Account account = player.getAccount(); Account account = player.getAccount();
if (player != targetPlayer) { // Additional permission required for targeting another player if (player != targetPlayer) { // Additional permission required for targeting another player
if (!permissionNodeTargeted.isEmpty() && !account.hasPermission(permissionNodeTargeted)) { if (!permissionNodeTargeted.isEmpty() && !account.hasPermission(permissionNodeTargeted)) {
CommandHandler.sendTranslatedMessage(player, "commands.generic.permission_error"); CommandHandler.sendTranslatedMessage(player, "commands.generic.permission_error");
return false; return false;
} }
} }
if (!permissionNode.isEmpty() && !account.hasPermission(permissionNode)) { if (!permissionNode.isEmpty() && !account.hasPermission(permissionNode)) {
CommandHandler.sendTranslatedMessage(player, "commands.generic.permission_error"); CommandHandler.sendTranslatedMessage(player, "commands.generic.permission_error");
return false; return false;
} }
return true; return true;
} }
} }

View File

@ -1,10 +1,10 @@
package emu.grasscutter.command; package emu.grasscutter.command;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
public interface PermissionHandler { public interface PermissionHandler {
boolean EnablePermissionCommand(); boolean EnablePermissionCommand();
boolean checkPermission( boolean checkPermission(
Player player, Player targetPlayer, String permissionNode, String permissionNodeTargeted); Player player, Player targetPlayer, String permissionNode, String permissionNodeTargeted);
} }

View File

@ -47,7 +47,7 @@ public final class AccountCommand implements CommandHandler {
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) { if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) {
if (args.size() < 3) { if (args.size() < 3) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, "EXPERIMENTAL_RealPassword requires a password argument"); sender, "EXPERIMENTAL_RealPassword requires a password argument");
CommandHandler.sendMessage(sender, "Usage: account create <username> <password> [uid]"); CommandHandler.sendMessage(sender, "Usage: account create <username> <password> [uid]");
return; return;
} }
@ -60,10 +60,10 @@ public final class AccountCommand implements CommandHandler {
CommandHandler.sendMessage(sender, translate(sender, "commands.account.invalid")); CommandHandler.sendMessage(sender, translate(sender, "commands.account.invalid"));
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) { if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, sender,
"EXPERIMENTAL_RealPassword requires argument 2 to be a password, not a uid"); "EXPERIMENTAL_RealPassword requires argument 2 to be a password, not a uid");
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, "Usage: account create <username> <password> [uid]"); sender, "Usage: account create <username> <password> [uid]");
} }
return; return;
} }
@ -90,7 +90,7 @@ public final class AccountCommand implements CommandHandler {
account.save(); // Save account to database. account.save(); // Save account to database.
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.account.create", account.getReservedPlayerUid())); sender, translate(sender, "commands.account.create", account.getReservedPlayerUid()));
} }
} }
case "delete" -> { case "delete" -> {
@ -106,7 +106,7 @@ public final class AccountCommand implements CommandHandler {
case "resetpass" -> { case "resetpass" -> {
if (!Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) { if (!Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, "resetpass requires EXPERIMENTAL_RealPassword to be true."); sender, "resetpass requires EXPERIMENTAL_RealPassword to be true.");
return; return;
} }
if (args.size() != 3) { if (args.size() != 3) {
@ -128,23 +128,27 @@ public final class AccountCommand implements CommandHandler {
} }
case "list" -> { case "list" -> {
CommandHandler.sendMessage(sender, "Note: This command might take a while to complete."); CommandHandler.sendMessage(sender, "Note: This command might take a while to complete.");
CommandHandler.sendMessage(sender, CommandHandler.sendMessage(
"Accounts: \n" + DatabaseManager.getAccountDatastore() sender,
.find(Account.class).stream() "Accounts: \n"
.map(acc -> "%s: %s (%s)".formatted( + DatabaseManager.getAccountDatastore().find(Account.class).stream()
acc.getId(), acc.getUsername(), .map(
acc.getReservedPlayerUid() == 0 ? acc ->
this.getPlayerUid(acc) : "%s: %s (%s)"
acc.getReservedPlayerUid())) .formatted(
.collect(Collectors.joining("\n")) acc.getId(),
); acc.getUsername(),
acc.getReservedPlayerUid() == 0
? this.getPlayerUid(acc)
: acc.getReservedPlayerUid()))
.collect(Collectors.joining("\n")));
} }
} }
} }
/** /**
* Returns the UID of the player associated with the given account. * Returns the UID of the player associated with the given account. If the player is not found,
* If the player is not found, returns "no UID". * returns "no UID".
* *
* @param account The account to get the UID of. * @param account The account to get the UID of.
* @return The UID of the player associated with the given account. * @return The UID of the player associated with the given account.

View File

@ -1,78 +1,78 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.PacketServerAnnounceNotify; import emu.grasscutter.server.packet.send.PacketServerAnnounceNotify;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
@Command( @Command(
label = "announce", label = "announce",
usage = {"<content>", "refresh", "(tpl|revoke) <templateId>"}, usage = {"<content>", "refresh", "(tpl|revoke) <templateId>"},
permission = "server.announce", permission = "server.announce",
aliases = {"a"}, aliases = {"a"},
targetRequirement = Command.TargetRequirement.NONE) targetRequirement = Command.TargetRequirement.NONE)
public final class AnnounceCommand implements CommandHandler { public final class AnnounceCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
var manager = Grasscutter.getGameServer().getAnnouncementSystem(); var manager = Grasscutter.getGameServer().getAnnouncementSystem();
if (args.size() < 1) { if (args.size() < 1) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
switch (args.get(0)) { switch (args.get(0)) {
case "tpl": case "tpl":
if (args.size() < 2) { if (args.size() < 2) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
var templateId = Integer.parseInt(args.get(1)); var templateId = Integer.parseInt(args.get(1));
var tpl = manager.getAnnounceConfigItemMap().get(templateId); var tpl = manager.getAnnounceConfigItemMap().get(templateId);
if (tpl == null) { if (tpl == null) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.announce.not_found", templateId)); sender, translate(sender, "commands.announce.not_found", templateId));
return; return;
} }
manager.broadcast(Collections.singletonList(tpl)); manager.broadcast(Collections.singletonList(tpl));
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.announce.send_success", tpl.getTemplateId())); sender, translate(sender, "commands.announce.send_success", tpl.getTemplateId()));
break; break;
case "refresh": case "refresh":
var num = manager.refresh(); var num = manager.refresh();
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.announce.refresh_success", num)); sender, translate(sender, "commands.announce.refresh_success", num));
break; break;
case "revoke": case "revoke":
if (args.size() < 2) { if (args.size() < 2) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
var templateId1 = Integer.parseInt(args.get(1)); var templateId1 = Integer.parseInt(args.get(1));
manager.revoke(templateId1); manager.revoke(templateId1);
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.announce.revoke_done", templateId1)); sender, translate(sender, "commands.announce.revoke_done", templateId1));
break; break;
default: default:
var id = new Random().nextInt(10000, 99999); var id = new Random().nextInt(10000, 99999);
var text = String.join(" ", args); var text = String.join(" ", args);
manager manager
.getOnlinePlayers() .getOnlinePlayers()
.forEach(i -> i.sendPacket(new PacketServerAnnounceNotify(text, id))); .forEach(i -> i.sendPacket(new PacketServerAnnounceNotify(text, id)));
CommandHandler.sendMessage(sender, translate(sender, "commands.announce.send_success", id)); CommandHandler.sendMessage(sender, translate(sender, "commands.announce.send_success", id));
} }
} }
} }

View File

@ -1,62 +1,62 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import java.util.List; import java.util.List;
@Command( @Command(
label = "ban", label = "ban",
usage = {"[<time> [<reason>]]"}, usage = {"[<time> [<reason>]]"},
permission = "server.ban", permission = "server.ban",
targetRequirement = Command.TargetRequirement.PLAYER) targetRequirement = Command.TargetRequirement.PLAYER)
public final class BanCommand implements CommandHandler { public final class BanCommand implements CommandHandler {
private boolean banAccount(Player targetPlayer, int time, String reason) { private boolean banAccount(Player targetPlayer, int time, String reason) {
Account account = targetPlayer.getAccount(); Account account = targetPlayer.getAccount();
if (account == null) { if (account == null) {
return false; return false;
} }
account.setBanReason(reason); account.setBanReason(reason);
account.setBanEndTime(time); account.setBanEndTime(time);
account.setBanStartTime((int) System.currentTimeMillis() / 1000); account.setBanStartTime((int) System.currentTimeMillis() / 1000);
account.setBanned(true); account.setBanned(true);
account.save(); account.save();
GameSession session = targetPlayer.getSession(); GameSession session = targetPlayer.getSession();
if (session != null) { if (session != null) {
session.close(); session.close();
} }
return true; return true;
} }
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
int time = 2051190000; int time = 2051190000;
String reason = "Reason not specified."; String reason = "Reason not specified.";
switch (args.size()) { switch (args.size()) {
case 2: case 2:
reason = args.get(1); // Fall-through reason = args.get(1); // Fall-through
case 1: case 1:
try { try {
time = Integer.parseInt(args.get(0)); time = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.ban.invalid_time"); CommandHandler.sendTranslatedMessage(sender, "commands.ban.invalid_time");
return; return;
} // Fall-through, unimportant } // Fall-through, unimportant
default: default:
break; break;
} }
if (banAccount(targetPlayer, time, reason)) { if (banAccount(targetPlayer, time, reason)) {
CommandHandler.sendTranslatedMessage(sender, "commands.ban.success"); CommandHandler.sendTranslatedMessage(sender, "commands.ban.success");
} else { } else {
CommandHandler.sendTranslatedMessage(sender, "commands.ban.failure"); CommandHandler.sendTranslatedMessage(sender, "commands.ban.failure");
} }
} }
} }

View File

@ -1,105 +1,105 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.command.CommandHelpers.*; import static emu.grasscutter.command.CommandHelpers.*;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.Inventory; import emu.grasscutter.game.inventory.Inventory;
import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Stream; import java.util.stream.Stream;
import lombok.Setter; import lombok.Setter;
@Command( @Command(
label = "clear", label = "clear",
usage = {"(all|wp|art|mat) [lv<max level>] [r<max refinement>] [<max rarity>*]"}, usage = {"(all|wp|art|mat) [lv<max level>] [r<max refinement>] [<max rarity>*]"},
permission = "player.clearinv", permission = "player.clearinv",
permissionTargeted = "player.clearinv.others") permissionTargeted = "player.clearinv.others")
public final class ClearCommand implements CommandHandler { public final class ClearCommand implements CommandHandler {
private static final Map<Pattern, BiConsumer<ClearItemParameters, Integer>> intCommandHandlers = private static final Map<Pattern, BiConsumer<ClearItemParameters, Integer>> intCommandHandlers =
Map.ofEntries( Map.ofEntries(
Map.entry(lvlRegex, ClearItemParameters::setLvl), Map.entry(lvlRegex, ClearItemParameters::setLvl),
Map.entry(refineRegex, ClearItemParameters::setRefinement), Map.entry(refineRegex, ClearItemParameters::setRefinement),
Map.entry(rankRegex, ClearItemParameters::setRank)); Map.entry(rankRegex, ClearItemParameters::setRank));
private Stream<GameItem> getOther( private Stream<GameItem> getOther(
ItemType type, Inventory playerInventory, ClearItemParameters param) { ItemType type, Inventory playerInventory, ClearItemParameters param) {
return playerInventory.getItems().values().stream() return playerInventory.getItems().values().stream()
.filter(item -> item.getItemType() == type) .filter(item -> item.getItemType() == type)
.filter(item -> item.getItemData().getRankLevel() <= param.rank) .filter(item -> item.getItemData().getRankLevel() <= param.rank)
.filter(item -> !item.isLocked() && !item.isEquipped()); .filter(item -> !item.isLocked() && !item.isEquipped());
} }
private Stream<GameItem> getWeapons(Inventory playerInventory, ClearItemParameters param) { private Stream<GameItem> getWeapons(Inventory playerInventory, ClearItemParameters param) {
return getOther(ItemType.ITEM_WEAPON, playerInventory, param) return getOther(ItemType.ITEM_WEAPON, playerInventory, param)
.filter(item -> item.getLevel() <= param.lvl) .filter(item -> item.getLevel() <= param.lvl)
.filter(item -> item.getRefinement() < param.refinement); .filter(item -> item.getRefinement() < param.refinement);
} }
private Stream<GameItem> getRelics(Inventory playerInventory, ClearItemParameters param) { private Stream<GameItem> getRelics(Inventory playerInventory, ClearItemParameters param) {
return getOther(ItemType.ITEM_RELIQUARY, playerInventory, param) return getOther(ItemType.ITEM_RELIQUARY, playerInventory, param)
.filter(item -> item.getLevel() <= param.lvl + 1); .filter(item -> item.getLevel() <= param.lvl + 1);
} }
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
Inventory playerInventory = targetPlayer.getInventory(); Inventory playerInventory = targetPlayer.getInventory();
ClearItemParameters param = new ClearItemParameters(); ClearItemParameters param = new ClearItemParameters();
// Extract any tagged int arguments (e.g. "lv90", "x100", "r5") // Extract any tagged int arguments (e.g. "lv90", "x100", "r5")
parseIntParameters(args, param, intCommandHandlers); parseIntParameters(args, param, intCommandHandlers);
if (args.size() < 1) { if (args.size() < 1) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
String playerString = targetPlayer.getNickname(); // Should probably be UID instead but whatever String playerString = targetPlayer.getNickname(); // Should probably be UID instead but whatever
switch (args.get(0)) { switch (args.get(0)) {
case "wp" -> { case "wp" -> {
playerInventory.removeItems(getWeapons(playerInventory, param).toList()); playerInventory.removeItems(getWeapons(playerInventory, param).toList());
CommandHandler.sendTranslatedMessage(sender, "commands.clear.weapons", playerString); CommandHandler.sendTranslatedMessage(sender, "commands.clear.weapons", playerString);
} }
case "art" -> { case "art" -> {
playerInventory.removeItems(getRelics(playerInventory, param).toList()); playerInventory.removeItems(getRelics(playerInventory, param).toList());
CommandHandler.sendTranslatedMessage(sender, "commands.clear.artifacts", playerString); CommandHandler.sendTranslatedMessage(sender, "commands.clear.artifacts", playerString);
} }
case "mat" -> { case "mat" -> {
playerInventory.removeItems( playerInventory.removeItems(
getOther(ItemType.ITEM_MATERIAL, playerInventory, param).toList()); getOther(ItemType.ITEM_MATERIAL, playerInventory, param).toList());
CommandHandler.sendTranslatedMessage(sender, "commands.clear.materials", playerString); CommandHandler.sendTranslatedMessage(sender, "commands.clear.materials", playerString);
} }
case "all" -> { case "all" -> {
playerInventory.removeItems(getRelics(playerInventory, param).toList()); playerInventory.removeItems(getRelics(playerInventory, param).toList());
CommandHandler.sendTranslatedMessage(sender, "commands.clear.artifacts", playerString); CommandHandler.sendTranslatedMessage(sender, "commands.clear.artifacts", playerString);
playerInventory.removeItems(getWeapons(playerInventory, param).toList()); playerInventory.removeItems(getWeapons(playerInventory, param).toList());
CommandHandler.sendTranslatedMessage(sender, "commands.clear.weapons", playerString); CommandHandler.sendTranslatedMessage(sender, "commands.clear.weapons", playerString);
playerInventory.removeItems( playerInventory.removeItems(
getOther(ItemType.ITEM_MATERIAL, playerInventory, param).toList()); getOther(ItemType.ITEM_MATERIAL, playerInventory, param).toList());
CommandHandler.sendTranslatedMessage(sender, "commands.clear.materials", playerString); CommandHandler.sendTranslatedMessage(sender, "commands.clear.materials", playerString);
playerInventory.removeItems( playerInventory.removeItems(
getOther(ItemType.ITEM_FURNITURE, playerInventory, param).toList()); getOther(ItemType.ITEM_FURNITURE, playerInventory, param).toList());
CommandHandler.sendTranslatedMessage(sender, "commands.clear.furniture", playerString); CommandHandler.sendTranslatedMessage(sender, "commands.clear.furniture", playerString);
playerInventory.removeItems( playerInventory.removeItems(
getOther(ItemType.ITEM_DISPLAY, playerInventory, param).toList()); getOther(ItemType.ITEM_DISPLAY, playerInventory, param).toList());
CommandHandler.sendTranslatedMessage(sender, "commands.clear.displays", playerString); CommandHandler.sendTranslatedMessage(sender, "commands.clear.displays", playerString);
playerInventory.removeItems( playerInventory.removeItems(
getOther(ItemType.ITEM_VIRTUAL, playerInventory, param).toList()); getOther(ItemType.ITEM_VIRTUAL, playerInventory, param).toList());
CommandHandler.sendTranslatedMessage(sender, "commands.clear.virtuals", playerString); CommandHandler.sendTranslatedMessage(sender, "commands.clear.virtuals", playerString);
CommandHandler.sendTranslatedMessage(sender, "commands.clear.everything", playerString); CommandHandler.sendTranslatedMessage(sender, "commands.clear.everything", playerString);
} }
} }
} }
private static class ClearItemParameters { private static class ClearItemParameters {
@Setter public int lvl = 1; @Setter public int lvl = 1;
@Setter public int refinement = 1; @Setter public int refinement = 1;
@Setter public int rank = 4; @Setter public int rank = 4;
} }
} }

View File

@ -1,57 +1,57 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.List; import java.util.List;
@Command( @Command(
label = "coop", label = "coop",
usage = {"[<host UID>]"}, usage = {"[<host UID>]"},
permission = "server.coop", permission = "server.coop",
permissionTargeted = "server.coop.others") permissionTargeted = "server.coop.others")
public final class CoopCommand implements CommandHandler { public final class CoopCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
Player host = sender; Player host = sender;
switch (args.size()) { switch (args.size()) {
case 0: // Summon target to self case 0: // Summon target to self
if (sender == null) { // Console doesn't have a self to summon to if (sender == null) { // Console doesn't have a self to summon to
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
break; break;
case 1: // Summon target to argument case 1: // Summon target to argument
try { try {
int hostId = Integer.parseInt(args.get(0)); int hostId = Integer.parseInt(args.get(0));
host = Grasscutter.getGameServer().getPlayerByUid(hostId); host = Grasscutter.getGameServer().getPlayerByUid(hostId);
if (host == null) { if (host == null) {
CommandHandler.sendTranslatedMessage(sender, "commands.execution.player_offline_error"); CommandHandler.sendTranslatedMessage(sender, "commands.execution.player_offline_error");
return; return;
} }
break; break;
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.uid"); CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.uid");
return; return;
} }
default: default:
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
// There's no target==host check but this just places them in multiplayer in their own world // There's no target==host check but this just places them in multiplayer in their own world
// which seems fine. // which seems fine.
if (targetPlayer.isInMultiplayer()) { if (targetPlayer.isInMultiplayer()) {
targetPlayer.getServer().getMultiplayerSystem().leaveCoop(targetPlayer); targetPlayer.getServer().getMultiplayerSystem().leaveCoop(targetPlayer);
} }
host.getServer().getMultiplayerSystem().applyEnterMp(targetPlayer, host.getUid()); host.getServer().getMultiplayerSystem().applyEnterMp(targetPlayer, host.getUid());
targetPlayer targetPlayer
.getServer() .getServer()
.getMultiplayerSystem() .getMultiplayerSystem()
.applyEnterMpReply(host, targetPlayer.getUid(), true); .applyEnterMpReply(host, targetPlayer.getUid(), true);
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.coop.success", targetPlayer.getNickname(), host.getNickname()); sender, "commands.coop.success", targetPlayer.getNickname(), host.getNickname());
} }
} }

View File

@ -1,50 +1,50 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.List; import java.util.List;
@Command( @Command(
label = "enter_dungeon", label = "enter_dungeon",
aliases = {"enterdungeon", "dungeon"}, aliases = {"enterdungeon", "dungeon"},
usage = {"<dungeonId>"}, usage = {"<dungeonId>"},
permission = "player.enterdungeon", permission = "player.enterdungeon",
permissionTargeted = "player.enterdungeon.others") permissionTargeted = "player.enterdungeon.others")
public final class EnterDungeonCommand implements CommandHandler { public final class EnterDungeonCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 1) { if (args.size() < 1) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
try { try {
int dungeonId = Integer.parseInt(args.get(0)); int dungeonId = Integer.parseInt(args.get(0));
if (dungeonId == targetPlayer.getSceneId()) { if (dungeonId == targetPlayer.getSceneId()) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.enter_dungeon.in_dungeon_error")); sender, translate(sender, "commands.enter_dungeon.in_dungeon_error"));
return; return;
} }
boolean result = boolean result =
targetPlayer targetPlayer
.getServer() .getServer()
.getDungeonSystem() .getDungeonSystem()
.enterDungeon(targetPlayer.getSession().getPlayer(), 0, dungeonId); .enterDungeon(targetPlayer.getSession().getPlayer(), 0, dungeonId);
if (!result) { if (!result) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.enter_dungeon.not_found_error")); sender, translate(sender, "commands.enter_dungeon.not_found_error"));
} else { } else {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.enter_dungeon.changed", dungeonId)); sender, translate(sender, "commands.enter_dungeon.changed", dungeonId));
} }
} catch (Exception e) { } catch (Exception e) {
sendUsageMessage(sender); sendUsageMessage(sender);
} }
} }
} }

View File

@ -1,11 +1,14 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.GameConstants.*;
import static emu.grasscutter.command.CommandHelpers.*;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot; import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.excels.avatar.AvatarData;
import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.avatar.AvatarData;
import emu.grasscutter.data.excels.reliquary.ReliquaryAffixData; import emu.grasscutter.data.excels.reliquary.ReliquaryAffixData;
import emu.grasscutter.data.excels.reliquary.ReliquaryMainPropData; import emu.grasscutter.data.excels.reliquary.ReliquaryMainPropData;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
@ -14,45 +17,50 @@ import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.utils.SparseSet;
import lombok.Setter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import lombok.Setter;
import static emu.grasscutter.GameConstants.*;
import static emu.grasscutter.command.CommandHelpers.*;
@Command( @Command(
label = "give", label = "give",
aliases = {"g", "item", "giveitem"}, aliases = {"g", "item", "giveitem"},
usage = { usage = {
"(<itemId>|<avatarId>|all|weapons|mats|avatars) [lv<level>] [r<refinement>] [x<amount>] [c<constellation>] [sl<skilllevel>]", "(<itemId>|<avatarId>|all|weapons|mats|avatars) [lv<level>] [r<refinement>] [x<amount>] [c<constellation>] [sl<skilllevel>]",
"<artifactId> [lv<level>] [x<amount>] [<mainPropId>] [<appendPropId>[,<times>]]..."}, "<artifactId> [lv<level>] [x<amount>] [<mainPropId>] [<appendPropId>[,<times>]]..."
permission = "player.give", },
permissionTargeted = "player.give.others", permission = "player.give",
threading = true) permissionTargeted = "player.give.others",
threading = true)
public final class GiveCommand implements CommandHandler { public final class GiveCommand implements CommandHandler {
private static final Map<Pattern, BiConsumer<GiveItemParameters, Integer>> intCommandHandlers = Map.ofEntries( private static final Map<Pattern, BiConsumer<GiveItemParameters, Integer>> intCommandHandlers =
Map.entry(lvlRegex, GiveItemParameters::setLvl), Map.ofEntries(
Map.entry(refineRegex, GiveItemParameters::setRefinement), Map.entry(lvlRegex, GiveItemParameters::setLvl),
Map.entry(amountRegex, GiveItemParameters::setAmount), Map.entry(refineRegex, GiveItemParameters::setRefinement),
Map.entry(constellationRegex, GiveItemParameters::setConstellation), Map.entry(amountRegex, GiveItemParameters::setAmount),
Map.entry(skillLevelRegex, GiveItemParameters::setSkillLevel) Map.entry(constellationRegex, GiveItemParameters::setConstellation),
); Map.entry(skillLevelRegex, GiveItemParameters::setSkillLevel));
private static Avatar makeAvatar(GiveItemParameters param) { private static Avatar makeAvatar(GiveItemParameters param) {
return makeAvatar(param.avatarData, param.lvl, Avatar.getMinPromoteLevel(param.lvl), param.constellation, param.skillLevel); return makeAvatar(
param.avatarData,
param.lvl,
Avatar.getMinPromoteLevel(param.lvl),
param.constellation,
param.skillLevel);
} }
private static Avatar makeAvatar(AvatarData avatarData, int level, int promoteLevel, int constellation, int skillLevel) { private static Avatar makeAvatar(
AvatarData avatarData, int level, int promoteLevel, int constellation, int skillLevel) {
Avatar avatar = new Avatar(avatarData); Avatar avatar = new Avatar(avatarData);
avatar.setLevel(level); avatar.setLevel(level);
avatar.setPromoteLevel(promoteLevel); avatar.setPromoteLevel(promoteLevel);
avatar.getSkillDepot().getSkillsAndEnergySkill().forEach(id -> avatar.setSkillLevel(id, skillLevel)); avatar
.getSkillDepot()
.getSkillsAndEnergySkill()
.forEach(id -> avatar.setSkillLevel(id, skillLevel));
avatar.forceConstellationLevel(constellation); avatar.forceConstellationLevel(constellation);
avatar.recalcStats(true); avatar.recalcStats(true);
avatar.save(); avatar.save();
@ -62,12 +70,16 @@ public final class GiveCommand implements CommandHandler {
private static void giveAllAvatars(Player player, GiveItemParameters param) { private static void giveAllAvatars(Player player, GiveItemParameters param) {
int promoteLevel = Avatar.getMinPromoteLevel(param.lvl); int promoteLevel = Avatar.getMinPromoteLevel(param.lvl);
if (param.constellation < 0 || param.constellation > 6) if (param.constellation < 0 || param.constellation > 6)
param.constellation = 6; // constellation's default is -1 so if no parameters set for constellations it'll automatically be 6 param.constellation =
6; // constellation's default is -1 so if no parameters set for constellations it'll
// automatically be 6
for (AvatarData avatarData : GameData.getAvatarDataMap().values()) { for (AvatarData avatarData : GameData.getAvatarDataMap().values()) {
int id = avatarData.getId(); int id = avatarData.getId();
if (id < 10000002 || id >= 11000000) continue; // Exclude test avatars if (id < 10000002 || id >= 11000000) continue; // Exclude test avatars
// Don't try to add each avatar to the current team // Don't try to add each avatar to the current team
player.addAvatar(makeAvatar(avatarData, param.lvl, promoteLevel, param.constellation, param.skillLevel), false); player.addAvatar(
makeAvatar(avatarData, param.lvl, promoteLevel, param.constellation, param.skillLevel),
false);
} }
} }
@ -76,8 +88,7 @@ public final class GiveCommand implements CommandHandler {
int totalExp = 0; int totalExp = 0;
if (param.data.getItemType() == ItemType.ITEM_WEAPON) { if (param.data.getItemType() == ItemType.ITEM_WEAPON) {
int rankLevel = param.data.getRankLevel(); int rankLevel = param.data.getRankLevel();
for (int i = 1; i < param.lvl; i++) for (int i = 1; i < param.lvl; i++) totalExp += GameData.getWeaponExpRequired(rankLevel, i);
totalExp += GameData.getWeaponExpRequired(rankLevel, i);
} }
List<GameItem> items = new ArrayList<>(param.amount); List<GameItem> items = new ArrayList<>(param.amount);
@ -87,7 +98,7 @@ public final class GiveCommand implements CommandHandler {
if (item.getItemType() == ItemType.ITEM_WEAPON) { if (item.getItemType() == ItemType.ITEM_WEAPON) {
item.setPromoteLevel(promoteLevel); item.setPromoteLevel(promoteLevel);
item.setTotalExp(totalExp); item.setTotalExp(totalExp);
item.setRefinement(param.refinement - 1); // Actual refinement data is 0..4 not 1..5 item.setRefinement(param.refinement - 1); // Actual refinement data is 0..4 not 1..5
} }
items.add(item); items.add(item);
} }
@ -98,8 +109,7 @@ public final class GiveCommand implements CommandHandler {
param.lvl = Math.min(param.lvl, param.data.getMaxLevel()); param.lvl = Math.min(param.lvl, param.data.getMaxLevel());
int rank = param.data.getRankLevel(); int rank = param.data.getRankLevel();
int totalExp = 0; int totalExp = 0;
for (int i = 1; i < param.lvl; i++) for (int i = 1; i < param.lvl; i++) totalExp += GameData.getRelicExpRequired(rank, i);
totalExp += GameData.getRelicExpRequired(rank, i);
List<GameItem> items = new ArrayList<>(param.amount); List<GameItem> items = new ArrayList<>(param.amount);
for (int i = 0; i < param.amount; i++) { for (int i = 0; i < param.amount; i++) {
@ -108,8 +118,8 @@ public final class GiveCommand implements CommandHandler {
item.setLevel(param.lvl); item.setLevel(param.lvl);
item.setTotalExp(totalExp); item.setTotalExp(totalExp);
int numAffixes = param.data.getAppendPropNum() + (param.lvl - 1) / 4; int numAffixes = param.data.getAppendPropNum() + (param.lvl - 1) / 4;
if (param.mainPropId > 0) // Keep random mainProp if we didn't specify one if (param.mainPropId > 0) // Keep random mainProp if we didn't specify one
item.setMainPropId(param.mainPropId); item.setMainPropId(param.mainPropId);
if (param.appendPropIdList != null) { if (param.appendPropIdList != null) {
item.getAppendPropIdList().clear(); item.getAppendPropIdList().clear();
item.getAppendPropIdList().addAll(param.appendPropIdList); item.getAppendPropIdList().addAll(param.appendPropIdList);
@ -121,15 +131,17 @@ public final class GiveCommand implements CommandHandler {
return items; return items;
} }
private static int getArtifactMainProp(ItemData itemData, FightProperty prop) throws IllegalArgumentException { private static int getArtifactMainProp(ItemData itemData, FightProperty prop)
throws IllegalArgumentException {
if (prop != FightProperty.FIGHT_PROP_NONE) if (prop != FightProperty.FIGHT_PROP_NONE)
for (ReliquaryMainPropData data : GameDepot.getRelicMainPropList(itemData.getMainPropDepotId())) for (ReliquaryMainPropData data :
if (data.getWeight() > 0 && data.getFightProp() == prop) GameDepot.getRelicMainPropList(itemData.getMainPropDepotId()))
return data.getId(); if (data.getWeight() > 0 && data.getFightProp() == prop) return data.getId();
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
private static List<Integer> getArtifactAffixes(ItemData itemData, FightProperty prop) throws IllegalArgumentException { private static List<Integer> getArtifactAffixes(ItemData itemData, FightProperty prop)
throws IllegalArgumentException {
if (prop == FightProperty.FIGHT_PROP_NONE) { if (prop == FightProperty.FIGHT_PROP_NONE) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
@ -142,7 +154,8 @@ public final class GiveCommand implements CommandHandler {
return affixes; return affixes;
} }
private static int getAppendPropId(String substatText, ItemData itemData) throws IllegalArgumentException { private static int getAppendPropId(String substatText, ItemData itemData)
throws IllegalArgumentException {
// If the given substat text is an integer, we just use that as the append prop ID. // If the given substat text is an integer, we just use that as the append prop ID.
try { try {
return Integer.parseInt(substatText); return Integer.parseInt(substatText);
@ -159,19 +172,21 @@ public final class GiveCommand implements CommandHandler {
substatTier = Integer.parseInt(substatArgs[1]); substatTier = Integer.parseInt(substatArgs[1]);
} }
List<Integer> substats = getArtifactAffixes(itemData, FightProperty.getPropByShortName(substatType)); List<Integer> substats =
getArtifactAffixes(itemData, FightProperty.getPropByShortName(substatType));
if (substats.isEmpty()) { if (substats.isEmpty()) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
substatTier -= 1; // 1-indexed to 0-indexed substatTier -= 1; // 1-indexed to 0-indexed
substatTier = Math.min(Math.max(0, substatTier), substats.size() - 1); substatTier = Math.min(Math.max(0, substatTier), substats.size() - 1);
return substats.get(substatTier); return substats.get(substatTier);
} }
} }
private static void parseRelicArgs(GiveItemParameters param, List<String> args) throws IllegalArgumentException { private static void parseRelicArgs(GiveItemParameters param, List<String> args)
throws IllegalArgumentException {
// Get the main stat from the arguments. // Get the main stat from the arguments.
// If the given argument is an integer, we use that. // If the given argument is an integer, we use that.
// If not, we check if the argument string is in the main prop map. // If not, we check if the argument string is in the main prop map.
@ -181,7 +196,8 @@ public final class GiveCommand implements CommandHandler {
param.mainPropId = Integer.parseInt(mainPropIdString); param.mainPropId = Integer.parseInt(mainPropIdString);
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
// This can in turn throw an exception which we don't want to catch here. // This can in turn throw an exception which we don't want to catch here.
param.mainPropId = getArtifactMainProp(param.data, FightProperty.getPropByShortName(mainPropIdString)); param.mainPropId =
getArtifactMainProp(param.data, FightProperty.getPropByShortName(mainPropIdString));
} }
// Get substats. // Get substats.
@ -219,7 +235,7 @@ public final class GiveCommand implements CommandHandler {
List<GameItem> itemList = new ArrayList<>(); List<GameItem> itemList = new ArrayList<>();
for (ItemData itemdata : GameData.getItemDataMap().values()) { for (ItemData itemdata : GameData.getItemDataMap().values()) {
int id = itemdata.getId(); int id = itemdata.getId();
if (id < 100_000) continue; // Nothing meaningful below this if (id < 100_000) continue; // Nothing meaningful below this
if (ILLEGAL_ITEMS.contains(id)) continue; if (ILLEGAL_ITEMS.contains(id)) continue;
if (itemdata.isEquip()) continue; if (itemdata.isEquip()) continue;
@ -239,7 +255,7 @@ public final class GiveCommand implements CommandHandler {
List<GameItem> itemList = new ArrayList<>(); List<GameItem> itemList = new ArrayList<>();
for (ItemData itemdata : GameData.getItemDataMap().values()) { for (ItemData itemdata : GameData.getItemDataMap().values()) {
int id = itemdata.getId(); int id = itemdata.getId();
if (id < 11100 || id > 16000) continue; // All extant weapons are within this range if (id < 11100 || id > 16000) continue; // All extant weapons are within this range
if (ILLEGAL_WEAPONS.contains(id)) continue; if (ILLEGAL_WEAPONS.contains(id)) continue;
if (!itemdata.isEquip()) continue; if (!itemdata.isEquip()) continue;
if (itemdata.getItemType() != ItemType.ITEM_WEAPON) continue; if (itemdata.getItemType() != ItemType.ITEM_WEAPON) continue;
@ -262,7 +278,8 @@ public final class GiveCommand implements CommandHandler {
giveAllWeapons(player, param); giveAllWeapons(player, param);
} }
private GiveItemParameters parseArgs(Player sender, List<String> args) throws IllegalArgumentException { private GiveItemParameters parseArgs(Player sender, List<String> args)
throws IllegalArgumentException {
GiveItemParameters param = new GiveItemParameters(); GiveItemParameters param = new GiveItemParameters();
// Extract any tagged arguments (e.g. "lv90", "x100", "r5") // Extract any tagged arguments (e.g. "lv90", "x100", "r5")
@ -270,7 +287,7 @@ public final class GiveCommand implements CommandHandler {
// At this point, first remaining argument MUST be itemId/avatarId // At this point, first remaining argument MUST be itemId/avatarId
if (args.size() < 1) { if (args.size() < 1) {
sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
String id = args.remove(0); String id = args.remove(0);
@ -304,7 +321,9 @@ public final class GiveCommand implements CommandHandler {
param.avatarData = GameData.getAvatarDataMap().get(param.id - 1000 + 10_000_000); param.avatarData = GameData.getAvatarDataMap().get(param.id - 1000 + 10_000_000);
isRelic = ((param.data != null) && (param.data.getItemType() == ItemType.ITEM_RELIQUARY)); isRelic = ((param.data != null) && (param.data.getItemType() == ItemType.ITEM_RELIQUARY));
if (!isRelic && !args.isEmpty() && (param.amount == 1)) { // A concession for the people that truly hate [x<amount>]. if (!isRelic
&& !args.isEmpty()
&& (param.amount == 1)) { // A concession for the people that truly hate [x<amount>].
try { try {
param.amount = Integer.parseInt(args.remove(0)); param.amount = Integer.parseInt(args.remove(0));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
@ -382,7 +401,8 @@ public final class GiveCommand implements CommandHandler {
if (param.avatarData != null) { if (param.avatarData != null) {
Avatar avatar = makeAvatar(param); Avatar avatar = makeAvatar(param);
targetPlayer.addAvatar(avatar); targetPlayer.addAvatar(avatar);
CommandHandler.sendTranslatedMessage(sender, "commands.give.given_avatar", param.id, param.lvl, targetPlayer.getUid()); CommandHandler.sendTranslatedMessage(
sender, "commands.give.given_avatar", param.id, param.lvl, targetPlayer.getUid());
return; return;
} }
// If it's not an avatar, it needs to be a valid item // If it's not an avatar, it needs to be a valid item
@ -393,16 +413,34 @@ public final class GiveCommand implements CommandHandler {
switch (param.data.getItemType()) { switch (param.data.getItemType()) {
case ITEM_WEAPON: case ITEM_WEAPON:
targetPlayer.getInventory().addItems(makeUnstackableItems(param), ActionReason.SubfieldDrop); targetPlayer
CommandHandler.sendTranslatedMessage(sender, "commands.give.given_with_level_and_refinement", param.id, param.lvl, param.refinement, param.amount, targetPlayer.getUid()); .getInventory()
.addItems(makeUnstackableItems(param), ActionReason.SubfieldDrop);
CommandHandler.sendTranslatedMessage(
sender,
"commands.give.given_with_level_and_refinement",
param.id,
param.lvl,
param.refinement,
param.amount,
targetPlayer.getUid());
return; return;
case ITEM_RELIQUARY: case ITEM_RELIQUARY:
targetPlayer.getInventory().addItems(makeArtifacts(param), ActionReason.SubfieldDrop); targetPlayer.getInventory().addItems(makeArtifacts(param), ActionReason.SubfieldDrop);
CommandHandler.sendTranslatedMessage(sender, "commands.give.given_level", param.id, param.lvl, param.amount, targetPlayer.getUid()); CommandHandler.sendTranslatedMessage(
sender,
"commands.give.given_level",
param.id,
param.lvl,
param.amount,
targetPlayer.getUid());
return; return;
default: default:
targetPlayer.getInventory().addItem(new GameItem(param.data, param.amount), ActionReason.SubfieldDrop); targetPlayer
CommandHandler.sendTranslatedMessage(sender, "commands.give.given", param.amount, param.id, targetPlayer.getUid()); .getInventory()
.addItem(new GameItem(param.data, param.amount), ActionReason.SubfieldDrop);
CommandHandler.sendTranslatedMessage(
sender, "commands.give.given", param.amount, param.id, targetPlayer.getUid());
} }
} catch (IllegalArgumentException ignored) { } catch (IllegalArgumentException ignored) {
} }
@ -418,16 +456,11 @@ public final class GiveCommand implements CommandHandler {
private static class GiveItemParameters { private static class GiveItemParameters {
public int id; public int id;
@Setter @Setter public int lvl = 0;
public int lvl = 0; @Setter public int amount = 1;
@Setter @Setter public int refinement = 1;
public int amount = 1; @Setter public int constellation = -1;
@Setter @Setter public int skillLevel = 1;
public int refinement = 1;
@Setter
public int constellation = -1;
@Setter
public int skillLevel = 1;
public int mainPropId = -1; public int mainPropId = -1;
public List<Integer> appendPropIdList; public List<Integer> appendPropIdList;
public ItemData data; public ItemData data;

View File

@ -1,44 +1,44 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify; import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
import java.util.List; import java.util.List;
@Command( @Command(
label = "heal", label = "heal",
aliases = {"h"}, aliases = {"h"},
permission = "player.heal", permission = "player.heal",
permissionTargeted = "player.heal.others") permissionTargeted = "player.heal.others")
public final class HealCommand implements CommandHandler { public final class HealCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
targetPlayer targetPlayer
.getTeamManager() .getTeamManager()
.getActiveTeam() .getActiveTeam()
.forEach( .forEach(
entity -> { entity -> {
boolean isAlive = entity.isAlive(); boolean isAlive = entity.isAlive();
entity.setFightProperty( entity.setFightProperty(
FightProperty.FIGHT_PROP_CUR_HP, FightProperty.FIGHT_PROP_CUR_HP,
entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP)); entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP));
entity entity
.getWorld() .getWorld()
.broadcastPacket( .broadcastPacket(
new PacketAvatarFightPropUpdateNotify( new PacketAvatarFightPropUpdateNotify(
entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP));
if (!isAlive) { if (!isAlive) {
entity entity
.getWorld() .getWorld()
.broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); .broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
} }
}); });
CommandHandler.sendMessage(sender, translate(sender, "commands.heal.success")); CommandHandler.sendMessage(sender, translate(sender, "commands.heal.success"));
} }
} }

View File

@ -1,92 +1,92 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.command.CommandMap; import emu.grasscutter.command.CommandMap;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@Command( @Command(
label = "help", label = "help",
usage = {"[<command>]"}, usage = {"[<command>]"},
targetRequirement = Command.TargetRequirement.NONE) targetRequirement = Command.TargetRequirement.NONE)
public final class HelpCommand implements CommandHandler { public final class HelpCommand implements CommandHandler {
private final boolean SHOW_COMMANDS_WITHOUT_PERMISSIONS = private final boolean SHOW_COMMANDS_WITHOUT_PERMISSIONS =
false; // TODO: Make this into a server config key false; // TODO: Make this into a server config key
private String createCommand(Player player, CommandHandler command, List<String> args) { private String createCommand(Player player, CommandHandler command, List<String> args) {
StringBuilder builder = StringBuilder builder =
new StringBuilder(command.getLabel()) new StringBuilder(command.getLabel())
.append(" - ") .append(" - ")
.append(command.getDescriptionString(player)) .append(command.getDescriptionString(player))
.append("\n\t") .append("\n\t")
.append(command.getUsageString(player, args.toArray(new String[0]))); .append(command.getUsageString(player, args.toArray(new String[0])));
Command annotation = command.getClass().getAnnotation(Command.class); Command annotation = command.getClass().getAnnotation(Command.class);
if (annotation.aliases().length > 0) { if (annotation.aliases().length > 0) {
builder.append("\n\t").append(translate(player, "commands.help.aliases")); builder.append("\n\t").append(translate(player, "commands.help.aliases"));
for (String alias : annotation.aliases()) { for (String alias : annotation.aliases()) {
builder.append(alias).append(" "); builder.append(alias).append(" ");
} }
} }
builder.append("\n\t").append(translate(player, "commands.help.tip_need_permission")); builder.append("\n\t").append(translate(player, "commands.help.tip_need_permission"));
if (!annotation.permission().isEmpty()) { if (!annotation.permission().isEmpty()) {
builder.append(annotation.permission()); builder.append(annotation.permission());
} else { } else {
builder.append(translate(player, "commands.help.tip_need_no_permission")); builder.append(translate(player, "commands.help.tip_need_no_permission"));
} }
if (!annotation.permissionTargeted().isEmpty()) { if (!annotation.permissionTargeted().isEmpty()) {
String permissionTargeted = annotation.permissionTargeted(); String permissionTargeted = annotation.permissionTargeted();
builder builder
.append(" ") .append(" ")
.append(translate(player, "commands.help.tip_permission_targeted", permissionTargeted)); .append(translate(player, "commands.help.tip_permission_targeted", permissionTargeted));
} }
return builder.toString(); return builder.toString();
} }
@Override @Override
public void execute(Player player, Player targetPlayer, List<String> args) { public void execute(Player player, Player targetPlayer, List<String> args) {
Account account = (player == null) ? null : player.getAccount(); Account account = (player == null) ? null : player.getAccount();
var commandMap = CommandMap.getInstance(); var commandMap = CommandMap.getInstance();
List<String> commands = new ArrayList<>(); List<String> commands = new ArrayList<>();
List<String> commands_no_permission = new ArrayList<>(); List<String> commands_no_permission = new ArrayList<>();
if (args.isEmpty()) { if (args.isEmpty()) {
commandMap commandMap
.getHandlers() .getHandlers()
.forEach( .forEach(
(key, command) -> { (key, command) -> {
Command annotation = command.getClass().getAnnotation(Command.class); Command annotation = command.getClass().getAnnotation(Command.class);
if (player == null || account.hasPermission(annotation.permission())) { if (player == null || account.hasPermission(annotation.permission())) {
commands.add(createCommand(player, command, args)); commands.add(createCommand(player, command, args));
} else if (SHOW_COMMANDS_WITHOUT_PERMISSIONS) { } else if (SHOW_COMMANDS_WITHOUT_PERMISSIONS) {
commands_no_permission.add(createCommand(player, command, args)); commands_no_permission.add(createCommand(player, command, args));
} }
}); });
CommandHandler.sendTranslatedMessage(player, "commands.help.available_commands"); CommandHandler.sendTranslatedMessage(player, "commands.help.available_commands");
} else { } else {
String command_str = args.remove(0).toLowerCase(); String command_str = args.remove(0).toLowerCase();
CommandHandler command = commandMap.getHandler(command_str); CommandHandler command = commandMap.getHandler(command_str);
if (command == null) { if (command == null) {
CommandHandler.sendTranslatedMessage(player, "commands.generic.command_exist_error"); CommandHandler.sendTranslatedMessage(player, "commands.generic.command_exist_error");
CommandHandler.sendMessage(player, "Command: " + command_str); CommandHandler.sendMessage(player, "Command: " + command_str);
return; return;
} else { } else {
Command annotation = command.getClass().getAnnotation(Command.class); Command annotation = command.getClass().getAnnotation(Command.class);
if (player == null || account.hasPermission(annotation.permission())) { if (player == null || account.hasPermission(annotation.permission())) {
commands.add(createCommand(player, command, args)); commands.add(createCommand(player, command, args));
} else { } else {
commands_no_permission.add(createCommand(player, command, args)); commands_no_permission.add(createCommand(player, command, args));
} }
} }
} }
final String suf = "\n\t" + translate(player, "commands.help.warn_player_has_no_permission"); final String suf = "\n\t" + translate(player, "commands.help.warn_player_has_no_permission");
commands.forEach(s -> CommandHandler.sendMessage(player, s)); commands.forEach(s -> CommandHandler.sendMessage(player, s));
commands_no_permission.forEach(s -> CommandHandler.sendMessage(player, s + suf)); commands_no_permission.forEach(s -> CommandHandler.sendMessage(player, s + suf));
} }
} }

View File

@ -1,34 +1,34 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.List; import java.util.List;
@Command( @Command(
label = "kick", label = "kick",
aliases = {"restart"}, aliases = {"restart"},
permissionTargeted = "server.kick") permissionTargeted = "server.kick")
public final class KickCommand implements CommandHandler { public final class KickCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (sender != null) { if (sender != null) {
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, sender,
"commands.kick.player_kick_player", "commands.kick.player_kick_player",
sender.getUid(), sender.getUid(),
sender.getAccount().getUsername(), sender.getAccount().getUsername(),
targetPlayer.getUid(), targetPlayer.getUid(),
targetPlayer.getAccount().getUsername()); targetPlayer.getAccount().getUsername());
} else { } else {
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, sender,
"commands.kick.server_kick_player", "commands.kick.server_kick_player",
targetPlayer.getUid(), targetPlayer.getUid(),
targetPlayer.getAccount().getUsername()); targetPlayer.getAccount().getUsername());
} }
targetPlayer.getSession().close(); targetPlayer.getSession().close();
} }
} }

View File

@ -1,54 +1,54 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import java.util.List; import java.util.List;
@Command( @Command(
label = "killall", label = "killall",
usage = {"[<sceneId>]"}, usage = {"[<sceneId>]"},
permission = "server.killall", permission = "server.killall",
permissionTargeted = "server.killall.others") permissionTargeted = "server.killall.others")
public final class KillAllCommand implements CommandHandler { public final class KillAllCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
Scene scene = targetPlayer.getScene(); Scene scene = targetPlayer.getScene();
try { try {
switch (args.size()) { switch (args.size()) {
case 0: // *No args* case 0: // *No args*
break; break;
case 1: // [sceneId] case 1: // [sceneId]
scene = targetPlayer.getWorld().getSceneById(Integer.parseInt(args.get(0))); scene = targetPlayer.getWorld().getSceneById(Integer.parseInt(args.get(0)));
break; break;
default: default:
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error")); CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error"));
} }
if (scene == null) { if (scene == null) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.killall.scene_not_found_in_player_world")); sender, translate(sender, "commands.killall.scene_not_found_in_player_world"));
return; return;
} }
// Separate into list to avoid concurrency issue // Separate into list to avoid concurrency issue
final Scene sceneF = scene; final Scene sceneF = scene;
List<GameEntity> toKill = List<GameEntity> toKill =
sceneF.getEntities().values().stream() sceneF.getEntities().values().stream()
.filter(entity -> entity instanceof EntityMonster) .filter(entity -> entity instanceof EntityMonster)
.toList(); .toList();
toKill.forEach(entity -> sceneF.killEntity(entity, 0)); toKill.forEach(entity -> sceneF.killEntity(entity, 0));
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, sender,
translate(sender, "commands.killall.kill_monsters_in_scene", toKill.size(), scene.getId())); translate(sender, "commands.killall.kill_monsters_in_scene", toKill.size(), scene.getId()));
} }
} }

View File

@ -1,41 +1,41 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState; import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import java.util.List; import java.util.List;
@Command( @Command(
label = "killCharacter", label = "killCharacter",
aliases = {"suicide", "kill"}, aliases = {"suicide", "kill"},
permission = "player.killcharacter", permission = "player.killcharacter",
permissionTargeted = "player.killcharacter.others") permissionTargeted = "player.killcharacter.others")
public final class KillCharacterCommand implements CommandHandler { public final class KillCharacterCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity(); EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f); entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
// Packets // Packets
entity entity
.getWorld() .getWorld()
.broadcastPacket( .broadcastPacket(
new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
entity entity
.getWorld() .getWorld()
.broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD)); .broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
// remove // remove
targetPlayer.getScene().removeEntity(entity); targetPlayer.getScene().removeEntity(entity);
entity.onDeath(0); entity.onDeath(0);
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.killCharacter.success", targetPlayer.getNickname())); sender, translate(sender, "commands.killCharacter.success", targetPlayer.getNickname()));
} }
} }

View File

@ -1,58 +1,58 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@Command( @Command(
label = "language", label = "language",
usage = {"[<language code>]"}, usage = {"[<language code>]"},
aliases = {"lang"}, aliases = {"lang"},
targetRequirement = Command.TargetRequirement.NONE) targetRequirement = Command.TargetRequirement.NONE)
public final class LanguageCommand implements CommandHandler { public final class LanguageCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) { if (args.isEmpty()) {
String curLangCode = null; String curLangCode = null;
if (sender != null) { if (sender != null) {
curLangCode = Utils.getLanguageCode(sender.getAccount().getLocale()); curLangCode = Utils.getLanguageCode(sender.getAccount().getLocale());
} else { } else {
curLangCode = Grasscutter.getLanguage().getLanguageCode(); curLangCode = Grasscutter.getLanguage().getLanguageCode();
} }
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.language.current_language", curLangCode)); sender, translate(sender, "commands.language.current_language", curLangCode));
return; return;
} }
String langCode = args.get(0); String langCode = args.get(0);
var languageInst = Grasscutter.getLanguage(langCode); var languageInst = Grasscutter.getLanguage(langCode);
var actualLangCode = languageInst.getLanguageCode(); var actualLangCode = languageInst.getLanguageCode();
var locale = Locale.forLanguageTag(actualLangCode); var locale = Locale.forLanguageTag(actualLangCode);
if (sender != null) { if (sender != null) {
var account = sender.getAccount(); var account = sender.getAccount();
account.setLocale(locale); account.setLocale(locale);
account.save(); account.save();
} else { } else {
Grasscutter.setLanguage(languageInst); Grasscutter.setLanguage(languageInst);
var config = Grasscutter.getConfig(); var config = Grasscutter.getConfig();
config.language.language = locale; config.language.language = locale;
Grasscutter.saveConfig(config); Grasscutter.saveConfig(config);
} }
if (!langCode.equals(actualLangCode)) { if (!langCode.equals(actualLangCode)) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.language.language_not_found", langCode)); sender, translate(sender, "commands.language.language_not_found", langCode));
} }
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.language.language_changed", actualLangCode)); sender, translate(sender, "commands.language.language_changed", actualLangCode));
} }
} }

View File

@ -1,56 +1,56 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@Command( @Command(
label = "list", label = "list",
aliases = {"players"}, aliases = {"players"},
usage = {"[<UID>]"}, usage = {"[<UID>]"},
targetRequirement = Command.TargetRequirement.NONE) targetRequirement = Command.TargetRequirement.NONE)
public final class ListCommand implements CommandHandler { public final class ListCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
Map<Integer, Player> playersMap = Grasscutter.getGameServer().getPlayers(); Map<Integer, Player> playersMap = Grasscutter.getGameServer().getPlayers();
boolean needUID = false; boolean needUID = false;
if (args.size() > 0) { if (args.size() > 0) {
needUID = args.get(0).equals("uid"); needUID = args.get(0).equals("uid");
} }
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.list.success", playersMap.size())); sender, translate(sender, "commands.list.success", playersMap.size()));
if (playersMap.size() != 0) { if (playersMap.size() != 0) {
StringBuilder playerSet = new StringBuilder(); StringBuilder playerSet = new StringBuilder();
boolean finalNeedUID = needUID; boolean finalNeedUID = needUID;
playersMap playersMap
.values() .values()
.forEach( .forEach(
player -> { player -> {
playerSet.append(player.getNickname()); playerSet.append(player.getNickname());
if (finalNeedUID) { if (finalNeedUID) {
if (sender != null) { if (sender != null) {
playerSet.append(" <color=green>(").append(player.getUid()).append(")</color>"); playerSet.append(" <color=green>(").append(player.getUid()).append(")</color>");
} else { } else {
playerSet.append(" (").append(player.getUid()).append(")"); playerSet.append(" (").append(player.getUid()).append(")");
} }
} }
playerSet.append(", "); playerSet.append(", ");
}); });
String players = playerSet.toString(); String players = playerSet.toString();
CommandHandler.sendMessage(sender, players.substring(0, players.length() - 2)); CommandHandler.sendMessage(sender, players.substring(0, players.length() - 2));
} }
} }
} }

View File

@ -1,74 +1,74 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.Command.TargetRequirement; import emu.grasscutter.command.Command.TargetRequirement;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.List; import java.util.List;
@Command( @Command(
label = "permission", label = "permission",
usage = {"add <permission>", "remove <permission>", "clear", "list"}, usage = {"add <permission>", "remove <permission>", "clear", "list"},
permission = "permission", permission = "permission",
targetRequirement = TargetRequirement.PLAYER) targetRequirement = TargetRequirement.PLAYER)
public final class PermissionCommand implements CommandHandler { public final class PermissionCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty() || args.size() > 2) { if (args.isEmpty() || args.size() > 2) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
if (!Grasscutter.getPermissionHandler().EnablePermissionCommand()) { if (!Grasscutter.getPermissionHandler().EnablePermissionCommand()) {
CommandHandler.sendTranslatedMessage(sender, "commands.generic.permission_error"); CommandHandler.sendTranslatedMessage(sender, "commands.generic.permission_error");
return; return;
} }
String action = args.get(0); String action = args.get(0);
String permission = ""; String permission = "";
if (args.size() > 1) { if (args.size() > 1) {
permission = args.get(1); permission = args.get(1);
} }
Account account = targetPlayer.getAccount(); Account account = targetPlayer.getAccount();
if (account == null) { if (account == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.account_error")); CommandHandler.sendMessage(sender, translate(sender, "commands.permission.account_error"));
return; return;
} }
switch (action) { switch (action) {
default: default:
sendUsageMessage(sender); sendUsageMessage(sender);
break; break;
case "add": case "add":
if (permission.isEmpty()) { if (permission.isEmpty()) {
sendUsageMessage(sender); sendUsageMessage(sender);
} else if (account.addPermission(permission)) { } else if (account.addPermission(permission)) {
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.add")); CommandHandler.sendMessage(sender, translate(sender, "commands.permission.add"));
} else } else
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.has_error")); CommandHandler.sendMessage(sender, translate(sender, "commands.permission.has_error"));
break; break;
case "remove": case "remove":
if (account.removePermission(permission)) { if (account.removePermission(permission)) {
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.remove")); CommandHandler.sendMessage(sender, translate(sender, "commands.permission.remove"));
} else } else
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.permission.not_have_error")); sender, translate(sender, "commands.permission.not_have_error"));
break; break;
case "clear": case "clear":
account.clearPermission(); account.clearPermission();
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.remove")); CommandHandler.sendMessage(sender, translate(sender, "commands.permission.remove"));
break; break;
case "list": case "list":
CommandHandler.sendMessage(sender, String.join("\n", account.getPermissions())); CommandHandler.sendMessage(sender, String.join("\n", account.getPermissions()));
break; break;
} }
account.save(); account.save();
} }
} }

View File

@ -1,29 +1,29 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import java.util.List; import java.util.List;
@Command( @Command(
label = "position", label = "position",
aliases = {"pos"}) aliases = {"pos"})
public final class PositionCommand implements CommandHandler { public final class PositionCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
Position pos = targetPlayer.getPosition(); Position pos = targetPlayer.getPosition();
Position rot = targetPlayer.getRotation(); Position rot = targetPlayer.getRotation();
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, sender,
"commands.position.success", "commands.position.success",
pos.getX(), pos.getX(),
pos.getY(), pos.getY(),
pos.getZ(), pos.getZ(),
rot.getX(), rot.getX(),
rot.getY(), rot.getY(),
rot.getZ(), rot.getZ(),
targetPlayer.getSceneId()); targetPlayer.getSceneId());
} }
} }

View File

@ -1,43 +1,43 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.List; import java.util.List;
@Command( @Command(
label = "resetConst", label = "resetConst",
aliases = {"resetconstellation"}, aliases = {"resetconstellation"},
usage = "[all]", usage = "[all]",
permission = "player.resetconstellation", permission = "player.resetconstellation",
permissionTargeted = "player.resetconstellation.others") permissionTargeted = "player.resetconstellation.others")
public final class ResetConstCommand implements CommandHandler { public final class ResetConstCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() > 0 && args.get(0).equalsIgnoreCase("all")) { if (args.size() > 0 && args.get(0).equalsIgnoreCase("all")) {
targetPlayer.getAvatars().forEach(this::resetConstellation); targetPlayer.getAvatars().forEach(this::resetConstellation);
CommandHandler.sendMessage(sender, translate(sender, "commands.resetConst.reset_all")); CommandHandler.sendMessage(sender, translate(sender, "commands.resetConst.reset_all"));
} else { } else {
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity(); EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
if (entity == null) { if (entity == null) {
return; return;
} }
Avatar avatar = entity.getAvatar(); Avatar avatar = entity.getAvatar();
this.resetConstellation(avatar); this.resetConstellation(avatar);
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, sender,
translate(sender, "commands.resetConst.success", avatar.getAvatarData().getName())); translate(sender, "commands.resetConst.success", avatar.getAvatarData().getName()));
} }
} }
private void resetConstellation(Avatar avatar) { private void resetConstellation(Avatar avatar) {
avatar.forceConstellationLevel(-1); avatar.forceConstellationLevel(-1);
} }
} }

View File

@ -1,23 +1,23 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.List; import java.util.List;
@Command( @Command(
label = "resetShopLimit", label = "resetShopLimit",
aliases = {"resetshop"}, aliases = {"resetshop"},
permission = "server.resetshop", permission = "server.resetshop",
permissionTargeted = "server.resetshop.others") permissionTargeted = "server.resetshop.others")
public final class ResetShopLimitCommand implements CommandHandler { public final class ResetShopLimitCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
targetPlayer.getShopLimit().forEach(x -> x.setNextRefreshTime(0)); targetPlayer.getShopLimit().forEach(x -> x.setNextRefreshTime(0));
targetPlayer.save(); targetPlayer.save();
CommandHandler.sendMessage(sender, translate(sender, "commands.resetShopLimit.success")); CommandHandler.sendMessage(sender, translate(sender, "commands.resetShopLimit.success"));
} }
} }

View File

@ -1,241 +1,241 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
@Command( @Command(
label = "sendMail", label = "sendMail",
usage = {"(<userId>|all) [<templateId>]", "help"}, usage = {"(<userId>|all) [<templateId>]", "help"},
permission = "server.sendmail", permission = "server.sendmail",
targetRequirement = Command.TargetRequirement.NONE) targetRequirement = Command.TargetRequirement.NONE)
public final class SendMailCommand implements CommandHandler { public final class SendMailCommand implements CommandHandler {
// TODO: You should be able to do /sendmail and then just send subsequent messages until you // TODO: You should be able to do /sendmail and then just send subsequent messages until you
// finish // finish
// However, due to the current nature of the command system, I don't think this is possible // However, due to the current nature of the command system, I don't think this is possible
// without rewriting // without rewriting
// the command system (again). For now this will do // the command system (again). For now this will do
// Key = User that is constructing the mail. // Key = User that is constructing the mail.
private static final HashMap<Integer, MailBuilder> mailBeingConstructed = private static final HashMap<Integer, MailBuilder> mailBeingConstructed =
new HashMap<Integer, MailBuilder>(); new HashMap<Integer, MailBuilder>();
// Yes this is awful and I hate it. // Yes this is awful and I hate it.
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
int senderId; int senderId;
if (sender != null) { if (sender != null) {
senderId = sender.getUid(); senderId = sender.getUid();
} else { } else {
senderId = -1; senderId = -1;
} }
if (!mailBeingConstructed.containsKey(senderId)) { if (!mailBeingConstructed.containsKey(senderId)) {
switch (args.size()) { switch (args.size()) {
case 1 -> { case 1 -> {
MailBuilder mailBuilder; MailBuilder mailBuilder;
switch (args.get(0).toLowerCase()) { switch (args.get(0).toLowerCase()) {
case "help" -> { case "help" -> {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
case "all" -> mailBuilder = new MailBuilder(true, new Mail()); case "all" -> mailBuilder = new MailBuilder(true, new Mail());
default -> { default -> {
if (DatabaseHelper.getPlayerByUid(Integer.parseInt(args.get(0))) != null) { if (DatabaseHelper.getPlayerByUid(Integer.parseInt(args.get(0))) != null) {
mailBuilder = new MailBuilder(Integer.parseInt(args.get(0)), new Mail()); mailBuilder = new MailBuilder(Integer.parseInt(args.get(0)), new Mail());
} else { } else {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.sendMail.user_not_exist", args.get(0))); sender, translate(sender, "commands.sendMail.user_not_exist", args.get(0)));
return; return;
} }
} }
} }
mailBeingConstructed.put(senderId, mailBuilder); mailBeingConstructed.put(senderId, mailBuilder);
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.sendMail.start_composition")); sender, translate(sender, "commands.sendMail.start_composition"));
} }
case 2 -> CommandHandler.sendMessage( case 2 -> CommandHandler.sendMessage(
sender, translate(sender, "commands.sendMail.templates")); sender, translate(sender, "commands.sendMail.templates"));
default -> CommandHandler.sendMessage( default -> CommandHandler.sendMessage(
sender, translate(sender, "commands.sendMail.invalid_arguments")); sender, translate(sender, "commands.sendMail.invalid_arguments"));
} }
} else { } else {
MailBuilder mailBuilder = mailBeingConstructed.get(senderId); MailBuilder mailBuilder = mailBeingConstructed.get(senderId);
if (args.size() >= 1) { if (args.size() >= 1) {
switch (args.get(0).toLowerCase()) { switch (args.get(0).toLowerCase()) {
case "stop" -> { case "stop" -> {
mailBeingConstructed.remove(senderId); mailBeingConstructed.remove(senderId);
CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send_cancel")); CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send_cancel"));
} }
case "finish" -> { case "finish" -> {
if (mailBuilder.constructionStage == 3) { if (mailBuilder.constructionStage == 3) {
if (!mailBuilder.sendToAll) { if (!mailBuilder.sendToAll) {
Grasscutter.getGameServer() Grasscutter.getGameServer()
.getPlayerByUid(mailBuilder.recipient, true) .getPlayerByUid(mailBuilder.recipient, true)
.sendMail(mailBuilder.mail); .sendMail(mailBuilder.mail);
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, sender,
translate(sender, "commands.sendMail.send_done", mailBuilder.recipient)); translate(sender, "commands.sendMail.send_done", mailBuilder.recipient));
} else { } else {
DatabaseHelper.getByGameClass(Player.class) DatabaseHelper.getByGameClass(Player.class)
.forEach( .forEach(
player -> { player -> {
var onlinePlayer = var onlinePlayer =
Grasscutter.getGameServer().getPlayerByUid(player.getUid(), false); Grasscutter.getGameServer().getPlayerByUid(player.getUid(), false);
Objects.requireNonNullElse(onlinePlayer, player) Objects.requireNonNullElse(onlinePlayer, player)
.sendMail(mailBuilder.mail); .sendMail(mailBuilder.mail);
}); });
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.sendMail.send_all_done")); sender, translate(sender, "commands.sendMail.send_all_done"));
} }
mailBeingConstructed.remove(senderId); mailBeingConstructed.remove(senderId);
} else { } else {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, sender,
translate( translate(
sender, sender,
"commands.sendMail.not_composition_end", "commands.sendMail.not_composition_end",
getConstructionArgs(mailBuilder.constructionStage, sender))); getConstructionArgs(mailBuilder.constructionStage, sender)));
} }
} }
case "help" -> { case "help" -> {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, sender,
translate( translate(
sender, sender,
"commands.sendMail.please_use", "commands.sendMail.please_use",
getConstructionArgs(mailBuilder.constructionStage, sender))); getConstructionArgs(mailBuilder.constructionStage, sender)));
} }
default -> { default -> {
switch (mailBuilder.constructionStage) { switch (mailBuilder.constructionStage) {
case 0 -> { case 0 -> {
String title = String.join(" ", args.subList(0, args.size())); String title = String.join(" ", args.subList(0, args.size()));
mailBuilder.mail.mailContent.title = title; mailBuilder.mail.mailContent.title = title;
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.sendMail.set_title", title)); sender, translate(sender, "commands.sendMail.set_title", title));
mailBuilder.constructionStage++; mailBuilder.constructionStage++;
} }
case 1 -> { case 1 -> {
String contents = String.join(" ", args.subList(0, args.size())); String contents = String.join(" ", args.subList(0, args.size()));
mailBuilder.mail.mailContent.content = contents; mailBuilder.mail.mailContent.content = contents;
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.sendMail.set_contents", contents)); sender, translate(sender, "commands.sendMail.set_contents", contents));
mailBuilder.constructionStage++; mailBuilder.constructionStage++;
} }
case 2 -> { case 2 -> {
String msgSender = String.join(" ", args.subList(0, args.size())); String msgSender = String.join(" ", args.subList(0, args.size()));
mailBuilder.mail.mailContent.sender = msgSender; mailBuilder.mail.mailContent.sender = msgSender;
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.sendMail.set_message_sender", msgSender)); sender, translate(sender, "commands.sendMail.set_message_sender", msgSender));
mailBuilder.constructionStage++; mailBuilder.constructionStage++;
} }
case 3 -> { case 3 -> {
int item; int item;
int lvl = 1; int lvl = 1;
int amount = 1; int amount = 1;
int refinement = 0; int refinement = 0;
switch (args.size()) { switch (args.size()) {
case 4: // <itemId|itemName> [amount] [level] [refinement] // TODO: this requires case 4: // <itemId|itemName> [amount] [level] [refinement] // TODO: this requires
// Mail support but there's no harm leaving it here for now // Mail support but there's no harm leaving it here for now
try { try {
refinement = Integer.parseInt(args.get(3)); refinement = Integer.parseInt(args.get(3));
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.generic.invalid.itemRefinement")); sender, translate(sender, "commands.generic.invalid.itemRefinement"));
return; return;
} // Fallthrough } // Fallthrough
case 3: // <itemId|itemName> [amount] [level] case 3: // <itemId|itemName> [amount] [level]
try { try {
lvl = Integer.parseInt(args.get(2)); lvl = Integer.parseInt(args.get(2));
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.generic.invalid.itemLevel")); sender, translate(sender, "commands.generic.invalid.itemLevel"));
return; return;
} // Fallthrough } // Fallthrough
case 2: // <itemId|itemName> [amount] case 2: // <itemId|itemName> [amount]
try { try {
amount = Integer.parseInt(args.get(1)); amount = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.generic.invalid.amount")); sender, translate(sender, "commands.generic.invalid.amount"));
return; return;
} // Fallthrough } // Fallthrough
case 1: // <itemId|itemName> case 1: // <itemId|itemName>
try { try {
item = Integer.parseInt(args.get(0)); item = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
// TODO: Parse from item name using GM Handbook. // TODO: Parse from item name using GM Handbook.
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.generic.invalid.itemId")); sender, translate(sender, "commands.generic.invalid.itemId"));
return; return;
} }
break; break;
default: // *No args* default: // *No args*
CommandHandler.sendTranslatedMessage(sender, "commands.sendMail.give_usage"); CommandHandler.sendTranslatedMessage(sender, "commands.sendMail.give_usage");
return; return;
} }
mailBuilder.mail.itemList.add(new Mail.MailItem(item, amount, lvl)); mailBuilder.mail.itemList.add(new Mail.MailItem(item, amount, lvl));
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.sendMail.send", amount, item, lvl)); sender, translate(sender, "commands.sendMail.send", amount, item, lvl));
} }
} }
} }
} }
} else { } else {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, sender,
translate( translate(
sender, sender,
"commands.sendMail.invalid_arguments_please_use", "commands.sendMail.invalid_arguments_please_use",
getConstructionArgs(mailBuilder.constructionStage, sender))); getConstructionArgs(mailBuilder.constructionStage, sender)));
} }
} }
} }
private String getConstructionArgs(int stage, Player sender) { private String getConstructionArgs(int stage, Player sender) {
return switch (stage) { return switch (stage) {
case 0 -> translate(sender, "commands.sendMail.title"); case 0 -> translate(sender, "commands.sendMail.title");
case 1 -> translate(sender, "commands.sendMail.message"); case 1 -> translate(sender, "commands.sendMail.message");
case 2 -> translate(sender, "commands.sendMail.sender"); case 2 -> translate(sender, "commands.sendMail.sender");
case 3 -> translate(sender, "commands.sendMail.arguments"); case 3 -> translate(sender, "commands.sendMail.arguments");
default -> translate(sender, "commands.sendMail.error", stage); default -> translate(sender, "commands.sendMail.error", stage);
}; };
} }
public static class MailBuilder { public static class MailBuilder {
public int recipient; public int recipient;
public boolean sendToAll; public boolean sendToAll;
public int constructionStage; public int constructionStage;
public Mail mail; public Mail mail;
public MailBuilder(int recipient, Mail mail) { public MailBuilder(int recipient, Mail mail) {
this.recipient = recipient; this.recipient = recipient;
this.sendToAll = false; this.sendToAll = false;
this.constructionStage = 0; this.constructionStage = 0;
this.mail = mail; this.mail = mail;
} }
public MailBuilder(boolean sendToAll, Mail mail) { public MailBuilder(boolean sendToAll, Mail mail) {
if (sendToAll) { if (sendToAll) {
this.recipient = 0; this.recipient = 0;
this.sendToAll = true; this.sendToAll = true;
this.constructionStage = 0; this.constructionStage = 0;
this.mail = mail; this.mail = mail;
} else { } else {
Grasscutter.getLogger().error("Please use MailBuilder(int, mail) when not sending to all"); Grasscutter.getLogger().error("Please use MailBuilder(int, mail) when not sending to all");
Thread.dumpStack(); Thread.dumpStack();
} }
} }
} }
} }

View File

@ -1,37 +1,37 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.Command.TargetRequirement; import emu.grasscutter.command.Command.TargetRequirement;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.List; import java.util.List;
@Command( @Command(
label = "sendMessage", label = "sendMessage",
aliases = {"say", "sendservmsg", "sendservermessage", "b", "broadcast"}, aliases = {"say", "sendservmsg", "sendservermessage", "b", "broadcast"},
usage = {"<message>"}, usage = {"<message>"},
permission = "server.sendmessage", permission = "server.sendmessage",
permissionTargeted = "server.sendmessage.others", permissionTargeted = "server.sendmessage.others",
targetRequirement = TargetRequirement.NONE) targetRequirement = TargetRequirement.NONE)
public final class SendMessageCommand implements CommandHandler { public final class SendMessageCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() == 0) { if (args.size() == 0) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
String message = String.join(" ", args); String message = String.join(" ", args);
if (targetPlayer == null) { if (targetPlayer == null) {
for (Player p : Grasscutter.getGameServer().getPlayers().values()) { for (Player p : Grasscutter.getGameServer().getPlayers().values()) {
CommandHandler.sendMessage(p, message); CommandHandler.sendMessage(p, message);
} }
} else { } else {
CommandHandler.sendMessage(targetPlayer, message); CommandHandler.sendMessage(targetPlayer, message);
} }
CommandHandler.sendTranslatedMessage(sender, "commands.sendMessage.success"); CommandHandler.sendTranslatedMessage(sender, "commands.sendMessage.success");
} }
} }

View File

@ -1,92 +1,92 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World; import emu.grasscutter.game.world.World;
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import java.util.List; import java.util.List;
@Command( @Command(
label = "setConst", label = "setConst",
aliases = {"setconstellation"}, aliases = {"setconstellation"},
usage = {"<constellation level> [all]"}, usage = {"<constellation level> [all]"},
permission = "player.setconstellation", permission = "player.setconstellation",
permissionTargeted = "player.setconstellation.others") permissionTargeted = "player.setconstellation.others")
public final class SetConstCommand implements CommandHandler { public final class SetConstCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 1) { if (args.size() < 1) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
try { try {
int constLevel = Integer.parseInt(args.get(0)); int constLevel = Integer.parseInt(args.get(0));
// Check if level is out of range // Check if level is out of range
if (constLevel < -1 || constLevel > 6) { if (constLevel < -1 || constLevel > 6) {
CommandHandler.sendTranslatedMessage(sender, "commands.setConst.range_error"); CommandHandler.sendTranslatedMessage(sender, "commands.setConst.range_error");
return; return;
} }
// If it's either empty or anything else other than "all" just do normal setConstellation // If it's either empty or anything else other than "all" just do normal setConstellation
if (args.size() == 1) { if (args.size() == 1) {
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity(); EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
if (entity == null) return; if (entity == null) return;
Avatar avatar = entity.getAvatar(); Avatar avatar = entity.getAvatar();
this.setConstellation(targetPlayer, avatar, constLevel); this.setConstellation(targetPlayer, avatar, constLevel);
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.setConst.success", avatar.getAvatarData().getName(), constLevel); sender, "commands.setConst.success", avatar.getAvatarData().getName(), constLevel);
return; return;
} }
// Check if there's an additional argument which is "all", if it does then go // Check if there's an additional argument which is "all", if it does then go
// setAllConstellation // setAllConstellation
if (args.size() > 1 && args.get(1).equalsIgnoreCase("all")) { if (args.size() > 1 && args.get(1).equalsIgnoreCase("all")) {
this.setAllConstellation(targetPlayer, constLevel); this.setAllConstellation(targetPlayer, constLevel);
CommandHandler.sendTranslatedMessage(sender, "commands.setConst.successall", constLevel); CommandHandler.sendTranslatedMessage(sender, "commands.setConst.successall", constLevel);
} else sendUsageMessage(sender); } else sendUsageMessage(sender);
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.setConst.level_error"); CommandHandler.sendTranslatedMessage(sender, "commands.setConst.level_error");
} }
} }
private void setConstellation(Player player, Avatar avatar, int constLevel) { private void setConstellation(Player player, Avatar avatar, int constLevel) {
int currentConstLevel = avatar.getCoreProudSkillLevel(); int currentConstLevel = avatar.getCoreProudSkillLevel();
avatar.forceConstellationLevel(constLevel); avatar.forceConstellationLevel(constLevel);
// force player to reload scene when necessary // force player to reload scene when necessary
if (constLevel < currentConstLevel) { if (constLevel < currentConstLevel) {
this.reloadScene(player); this.reloadScene(player);
} }
// ensure that all changes are visible to the player // ensure that all changes are visible to the player
avatar.recalcConstellations(); avatar.recalcConstellations();
avatar.recalcStats(true); avatar.recalcStats(true);
avatar.save(); avatar.save();
} }
private void setAllConstellation(Player player, int constLevel) { private void setAllConstellation(Player player, int constLevel) {
player player
.getAvatars() .getAvatars()
.forEach( .forEach(
avatar -> { avatar -> {
avatar.forceConstellationLevel(constLevel); avatar.forceConstellationLevel(constLevel);
avatar.recalcConstellations(); avatar.recalcConstellations();
avatar.recalcStats(true); avatar.recalcStats(true);
avatar.save(); avatar.save();
}); });
// Just reload scene once, shorter than having to check for each constLevel < currentConstLevel // Just reload scene once, shorter than having to check for each constLevel < currentConstLevel
this.reloadScene(player); this.reloadScene(player);
} }
private void reloadScene(Player player) { private void reloadScene(Player player) {
World world = player.getWorld(); World world = player.getWorld();
Scene scene = player.getScene(); Scene scene = player.getScene();
Position pos = player.getPosition(); Position pos = player.getPosition();
world.transferPlayerToScene(player, 1, pos); world.transferPlayerToScene(player, 1, pos);
world.transferPlayerToScene(player, scene.getId(), pos); world.transferPlayerToScene(player, scene.getId(), pos);
scene.broadcastPacket(new PacketSceneEntityAppearNotify(player)); scene.broadcastPacket(new PacketSceneEntityAppearNotify(player));
} }
} }

View File

@ -1,50 +1,50 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify; import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify;
import java.util.List; import java.util.List;
@Command( @Command(
label = "setFetterLevel", label = "setFetterLevel",
usage = {"<level>"}, usage = {"<level>"},
aliases = {"setfetterlvl", "setfriendship"}, aliases = {"setfetterlvl", "setfriendship"},
permission = "player.setfetterlevel", permission = "player.setfetterlevel",
permissionTargeted = "player.setfetterlevel.others") permissionTargeted = "player.setfetterlevel.others")
public final class SetFetterLevelCommand implements CommandHandler { public final class SetFetterLevelCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 1) { if (args.size() != 1) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
try { try {
int fetterLevel = Integer.parseInt(args.get(0)); int fetterLevel = Integer.parseInt(args.get(0));
if (fetterLevel < 0 || fetterLevel > 10) { if (fetterLevel < 0 || fetterLevel > 10) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.setFetterLevel.range_error")); sender, translate(sender, "commands.setFetterLevel.range_error"));
return; return;
} }
Avatar avatar = targetPlayer.getTeamManager().getCurrentAvatarEntity().getAvatar(); Avatar avatar = targetPlayer.getTeamManager().getCurrentAvatarEntity().getAvatar();
avatar.setFetterLevel(fetterLevel); avatar.setFetterLevel(fetterLevel);
if (fetterLevel != 10) { if (fetterLevel != 10) {
avatar.setFetterExp(GameData.getAvatarFetterLevelDataMap().get(fetterLevel).getExp()); avatar.setFetterExp(GameData.getAvatarFetterLevelDataMap().get(fetterLevel).getExp());
} }
avatar.save(); avatar.save();
targetPlayer.sendPacket(new PacketAvatarFetterDataNotify(avatar)); targetPlayer.sendPacket(new PacketAvatarFetterDataNotify(avatar));
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.setFetterLevel.success", fetterLevel)); sender, translate(sender, "commands.setFetterLevel.success", fetterLevel));
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate(sender, "commands.setFetterLevel.level_error")); CommandHandler.sendMessage(sender, translate(sender, "commands.setFetterLevel.level_error"));
} }
} }
} }

View File

@ -1,27 +1,27 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.List; import java.util.List;
@Command( @Command(
label = "stop", label = "stop",
aliases = {"shutdown"}, aliases = {"shutdown"},
permission = "server.stop", permission = "server.stop",
targetRequirement = Command.TargetRequirement.NONE) targetRequirement = Command.TargetRequirement.NONE)
public final class StopCommand implements CommandHandler { public final class StopCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
CommandHandler.sendMessage(null, translate("commands.stop.success")); CommandHandler.sendMessage(null, translate("commands.stop.success"));
for (Player p : Grasscutter.getGameServer().getPlayers().values()) { for (Player p : Grasscutter.getGameServer().getPlayers().values()) {
CommandHandler.sendMessage(p, translate(p, "commands.stop.success")); CommandHandler.sendMessage(p, translate(p, "commands.stop.success"));
} }
System.exit(1000); System.exit(1000);
} }
} }

View File

@ -1,268 +1,268 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS; import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.PacketChangeMpTeamAvatarRsp; import emu.grasscutter.server.packet.send.PacketChangeMpTeamAvatarRsp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@Command( @Command(
label = "team", label = "team",
usage = {"add <avatarId,...>", "(remove|set) [index|first|last|index-index,...]"}, usage = {"add <avatarId,...>", "(remove|set) [index|first|last|index-index,...]"},
permission = "player.team", permission = "player.team",
permissionTargeted = "player.team.others") permissionTargeted = "player.team.others")
public final class TeamCommand implements CommandHandler { public final class TeamCommand implements CommandHandler {
private static final int BASE_AVATARID = 10000000; private static final int BASE_AVATARID = 10000000;
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) { if (args.isEmpty()) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
switch (args.get(0)) { switch (args.get(0)) {
case "add": case "add":
if (!addCommand(sender, targetPlayer, args)) return; if (!addCommand(sender, targetPlayer, args)) return;
break; break;
case "remove": case "remove":
if (!removeCommand(sender, targetPlayer, args)) return; if (!removeCommand(sender, targetPlayer, args)) return;
break; break;
case "set": case "set":
if (!setCommand(sender, targetPlayer, args)) return; if (!setCommand(sender, targetPlayer, args)) return;
break; break;
default: default:
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage"); CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
targetPlayer targetPlayer
.getTeamManager() .getTeamManager()
.updateTeamEntities( .updateTeamEntities(
new PacketChangeMpTeamAvatarRsp( new PacketChangeMpTeamAvatarRsp(
targetPlayer, targetPlayer.getTeamManager().getCurrentTeamInfo())); targetPlayer, targetPlayer.getTeamManager().getCurrentTeamInfo()));
} }
private boolean addCommand(Player sender, Player targetPlayer, List<String> args) { private boolean addCommand(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 2) { if (args.size() < 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage"); CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
sendUsageMessage(sender); sendUsageMessage(sender);
return false; return false;
} }
int index = -1; int index = -1;
if (args.size() > 2) { if (args.size() > 2) {
try { try {
index = Integer.parseInt(args.get(2)) - 1; index = Integer.parseInt(args.get(2)) - 1;
if (index < 0) index = 0; if (index < 0) index = 0;
} catch (Exception e) { } catch (Exception e) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_index"); CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_index");
return false; return false;
} }
} }
var avatarIds = args.get(1).split(","); var avatarIds = args.get(1).split(",");
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars(); var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
if (currentTeamAvatars.size() + avatarIds.length > GAME_OPTIONS.avatarLimits.singlePlayerTeam) { if (currentTeamAvatars.size() + avatarIds.length > GAME_OPTIONS.avatarLimits.singlePlayerTeam) {
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.team.add_too_much", GAME_OPTIONS.avatarLimits.singlePlayerTeam); sender, "commands.team.add_too_much", GAME_OPTIONS.avatarLimits.singlePlayerTeam);
return false; return false;
} }
for (var avatarId : avatarIds) { for (var avatarId : avatarIds) {
int id = Integer.parseInt(avatarId); int id = Integer.parseInt(avatarId);
if (!addAvatar(sender, targetPlayer, id, index)) if (!addAvatar(sender, targetPlayer, id, index))
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.team.failed_to_add_avatar", avatarId); sender, "commands.team.failed_to_add_avatar", avatarId);
if (index > 0) ++index; if (index > 0) ++index;
} }
return true; return true;
} }
private boolean removeCommand(Player sender, Player targetPlayer, List<String> args) { private boolean removeCommand(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 2) { if (args.size() < 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage"); CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
sendUsageMessage(sender); sendUsageMessage(sender);
return false; return false;
} }
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars(); var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
var avatarCount = currentTeamAvatars.size(); var avatarCount = currentTeamAvatars.size();
var metaIndexList = args.get(1).split(","); var metaIndexList = args.get(1).split(",");
var indexes = new HashSet<Integer>(); var indexes = new HashSet<Integer>();
var ignoreList = new ArrayList<Integer>(); var ignoreList = new ArrayList<Integer>();
for (var metaIndex : metaIndexList) { for (var metaIndex : metaIndexList) {
// step 1: parse metaIndex to indexes // step 1: parse metaIndex to indexes
var subIndexes = transformToIndexes(metaIndex, avatarCount); var subIndexes = transformToIndexes(metaIndex, avatarCount);
if (subIndexes == null) { if (subIndexes == null) {
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.team.failed_to_parse_index", metaIndex); sender, "commands.team.failed_to_parse_index", metaIndex);
continue; continue;
} }
// step 2: get all of the avatar id through indexes // step 2: get all of the avatar id through indexes
for (var avatarIndex : subIndexes) { for (var avatarIndex : subIndexes) {
try { try {
indexes.add(currentTeamAvatars.get(avatarIndex - 1)); indexes.add(currentTeamAvatars.get(avatarIndex - 1));
} catch (Exception e) { } catch (Exception e) {
ignoreList.add(avatarIndex); ignoreList.add(avatarIndex);
continue; continue;
} }
} }
} }
// step 3: check if user remove all of the avatar // step 3: check if user remove all of the avatar
if (indexes.size() >= avatarCount) { if (indexes.size() >= avatarCount) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_too_much"); CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_too_much");
return false; return false;
} }
// step 4: hint user for ignore index // step 4: hint user for ignore index
if (!ignoreList.isEmpty()) { if (!ignoreList.isEmpty()) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.ignore_index", ignoreList); CommandHandler.sendTranslatedMessage(sender, "commands.team.ignore_index", ignoreList);
} }
// step 5: remove // step 5: remove
currentTeamAvatars.removeAll(indexes); currentTeamAvatars.removeAll(indexes);
return true; return true;
} }
private boolean setCommand(Player sender, Player targetPlayer, List<String> args) { private boolean setCommand(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 3) { if (args.size() < 3) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage"); CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
sendUsageMessage(sender); sendUsageMessage(sender);
return false; return false;
} }
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars(); var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
int index; int index;
try { try {
index = Integer.parseInt(args.get(1)) - 1; index = Integer.parseInt(args.get(1)) - 1;
if (index < 0) index = 0; if (index < 0) index = 0;
} catch (Exception e) { } catch (Exception e) {
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.team.failed_to_parse_index", args.get(1)); sender, "commands.team.failed_to_parse_index", args.get(1));
return false; return false;
} }
if (index + 1 > currentTeamAvatars.size()) { if (index + 1 > currentTeamAvatars.size()) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.index_out_of_range"); CommandHandler.sendTranslatedMessage(sender, "commands.team.index_out_of_range");
return false; return false;
} }
int avatarId; int avatarId;
try { try {
avatarId = Integer.parseInt(args.get(2)); avatarId = Integer.parseInt(args.get(2));
} catch (Exception e) { } catch (Exception e) {
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.team.failed_parse_avatar_id", args.get(2)); sender, "commands.team.failed_parse_avatar_id", args.get(2));
return false; return false;
} }
if (avatarId < BASE_AVATARID) { if (avatarId < BASE_AVATARID) {
avatarId += BASE_AVATARID; avatarId += BASE_AVATARID;
} }
if (currentTeamAvatars.contains(avatarId)) { if (currentTeamAvatars.contains(avatarId)) {
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.team.avatar_already_in_team", avatarId); sender, "commands.team.avatar_already_in_team", avatarId);
return false; return false;
} }
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) { if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId); CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
return false; return false;
} }
currentTeamAvatars.set(index, avatarId); currentTeamAvatars.set(index, avatarId);
return true; return true;
} }
private boolean addAvatar(Player sender, Player targetPlayer, int avatarId, int index) { private boolean addAvatar(Player sender, Player targetPlayer, int avatarId, int index) {
if (avatarId < BASE_AVATARID) { if (avatarId < BASE_AVATARID) {
avatarId += BASE_AVATARID; avatarId += BASE_AVATARID;
} }
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars(); var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
if (currentTeamAvatars.contains(avatarId)) { if (currentTeamAvatars.contains(avatarId)) {
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.team.avatar_already_in_team", avatarId); sender, "commands.team.avatar_already_in_team", avatarId);
return false; return false;
} }
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) { if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId); CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
return false; return false;
} }
if (index < 0) { if (index < 0) {
currentTeamAvatars.add(avatarId); currentTeamAvatars.add(avatarId);
} else { } else {
currentTeamAvatars.add(index, avatarId); currentTeamAvatars.add(index, avatarId);
} }
return true; return true;
} }
private List<Integer> transformToIndexes(String metaIndexes, int listLength) { private List<Integer> transformToIndexes(String metaIndexes, int listLength) {
// step 1: check if metaIndexes is a special constants // step 1: check if metaIndexes is a special constants
if (metaIndexes.equals("first")) { if (metaIndexes.equals("first")) {
return List.of(1); return List.of(1);
} else if (metaIndexes.equals("last")) { } else if (metaIndexes.equals("last")) {
return List.of(listLength); return List.of(listLength);
} }
// step 2: check if metaIndexes is a range // step 2: check if metaIndexes is a range
if (metaIndexes.contains("-")) { if (metaIndexes.contains("-")) {
var range = metaIndexes.split("-"); var range = metaIndexes.split("-");
if (range.length < 2) { if (range.length < 2) {
return null; return null;
} }
int min, max; int min, max;
try { try {
min = min =
switch (range[0]) { switch (range[0]) {
case "first" -> 1; case "first" -> 1;
case "last" -> listLength; case "last" -> listLength;
default -> Integer.parseInt(range[0]); default -> Integer.parseInt(range[0]);
}; };
max = max =
switch (range[1]) { switch (range[1]) {
case "first" -> 1; case "first" -> 1;
case "last" -> listLength; case "last" -> listLength;
default -> Integer.parseInt(range[1]); default -> Integer.parseInt(range[1]);
}; };
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
if (min > max) { if (min > max) {
min ^= max; min ^= max;
max ^= min; max ^= min;
min ^= max; min ^= max;
} }
var indexes = new ArrayList<Integer>(); var indexes = new ArrayList<Integer>();
for (int i = min; i <= max; ++i) { for (int i = min; i <= max; ++i) {
indexes.add(i); indexes.add(i);
} }
return indexes; return indexes;
} }
// step 3: index is a value, simply return // step 3: index is a value, simply return
try { try {
int index = Integer.parseInt(metaIndexes); int index = Integer.parseInt(metaIndexes);
return List.of(index); return List.of(index);
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
} }
} }

View File

@ -1,36 +1,36 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType; import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
import java.util.List; import java.util.List;
@Command( @Command(
label = "teleportAll", label = "teleportAll",
aliases = {"tpall"}, aliases = {"tpall"},
permission = "player.tpall", permission = "player.tpall",
permissionTargeted = "player.tpall.others") permissionTargeted = "player.tpall.others")
public final class TeleportAllCommand implements CommandHandler { public final class TeleportAllCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (!targetPlayer.getWorld().isMultiplayer()) { if (!targetPlayer.getWorld().isMultiplayer()) {
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.error")); CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.error"));
return; return;
} }
for (Player player : targetPlayer.getWorld().getPlayers()) { for (Player player : targetPlayer.getWorld().getPlayers()) {
if (player.equals(targetPlayer)) continue; if (player.equals(targetPlayer)) continue;
player player
.getWorld() .getWorld()
.transferPlayerToScene( .transferPlayerToScene(
player, targetPlayer.getSceneId(), TeleportType.COMMAND, targetPlayer.getPosition()); player, targetPlayer.getSceneId(), TeleportType.COMMAND, targetPlayer.getPosition());
} }
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.success")); CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.success"));
} }
} }

View File

@ -1,78 +1,78 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType; import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import java.util.List; import java.util.List;
@Command( @Command(
label = "teleport", label = "teleport",
aliases = {"tp"}, aliases = {"tp"},
usage = {"<x> <y> <z> [sceneId]"}, usage = {"<x> <y> <z> [sceneId]"},
permission = "player.teleport", permission = "player.teleport",
permissionTargeted = "player.teleport.others") permissionTargeted = "player.teleport.others")
public final class TeleportCommand implements CommandHandler { public final class TeleportCommand implements CommandHandler {
private float parseRelative( private float parseRelative(
String input, Float current) { // TODO: Maybe this will be useful elsewhere later String input, Float current) { // TODO: Maybe this will be useful elsewhere later
if (input.contains("~")) { // Relative if (input.contains("~")) { // Relative
if (!input.equals("~")) { // Relative with offset if (!input.equals("~")) { // Relative with offset
current += Float.parseFloat(input.replace("~", "")); current += Float.parseFloat(input.replace("~", ""));
} // Else no offset, no modification } // Else no offset, no modification
} else { // Absolute } else { // Absolute
current = Float.parseFloat(input); current = Float.parseFloat(input);
} }
return current; return current;
} }
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
Position pos = targetPlayer.getPosition(); Position pos = targetPlayer.getPosition();
float x = pos.getX(); float x = pos.getX();
float y = pos.getY(); float y = pos.getY();
float z = pos.getZ(); float z = pos.getZ();
int sceneId = targetPlayer.getSceneId(); int sceneId = targetPlayer.getSceneId();
switch (args.size()) { switch (args.size()) {
case 4: case 4:
try { try {
sceneId = Integer.parseInt(args.get(3)); sceneId = Integer.parseInt(args.get(3));
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.execution.argument_error")); sender, translate(sender, "commands.execution.argument_error"));
} // Fallthrough } // Fallthrough
case 3: case 3:
try { try {
x = this.parseRelative(args.get(0), x); x = this.parseRelative(args.get(0), x);
y = this.parseRelative(args.get(1), y); y = this.parseRelative(args.get(1), y);
z = this.parseRelative(args.get(2), z); z = this.parseRelative(args.get(2), z);
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.teleport.invalid_position")); sender, translate(sender, "commands.teleport.invalid_position"));
} }
break; break;
default: default:
this.sendUsageMessage(sender); this.sendUsageMessage(sender);
return; return;
} }
Position target_pos = new Position(x, y, z); Position target_pos = new Position(x, y, z);
boolean result = boolean result =
targetPlayer targetPlayer
.getWorld() .getWorld()
.transferPlayerToScene(targetPlayer, sceneId, TeleportType.COMMAND, target_pos); .transferPlayerToScene(targetPlayer, sceneId, TeleportType.COMMAND, target_pos);
if (!result) { if (!result) {
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.exists_error")); CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.exists_error"));
} else { } else {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, sender,
translate( translate(
sender, "commands.teleport.success", targetPlayer.getNickname(), x, y, z, sceneId)); sender, "commands.teleport.success", targetPlayer.getNickname(), x, y, z, sceneId));
} }
} }
} }

View File

@ -1,39 +1,39 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.List; import java.util.List;
@Command( @Command(
label = "unban", label = "unban",
permission = "server.ban", permission = "server.ban",
targetRequirement = Command.TargetRequirement.PLAYER) targetRequirement = Command.TargetRequirement.PLAYER)
public final class UnBanCommand implements CommandHandler { public final class UnBanCommand implements CommandHandler {
private boolean unBanAccount(Player targetPlayer) { private boolean unBanAccount(Player targetPlayer) {
Account account = targetPlayer.getAccount(); Account account = targetPlayer.getAccount();
if (account == null) { if (account == null) {
return false; return false;
} }
account.setBanReason(null); account.setBanReason(null);
account.setBanEndTime(0); account.setBanEndTime(0);
account.setBanStartTime(0); account.setBanStartTime(0);
account.setBanned(false); account.setBanned(false);
account.save(); account.save();
return true; return true;
} }
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (unBanAccount(targetPlayer)) { if (unBanAccount(targetPlayer)) {
CommandHandler.sendTranslatedMessage(sender, "commands.unban.success"); CommandHandler.sendTranslatedMessage(sender, "commands.unban.success");
} else { } else {
CommandHandler.sendTranslatedMessage(sender, "commands.unban.failure"); CommandHandler.sendTranslatedMessage(sender, "commands.unban.failure");
} }
} }
} }

View File

@ -1,43 +1,43 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.PlayerProgressManager; import emu.grasscutter.game.player.PlayerProgressManager;
import emu.grasscutter.server.packet.send.PacketOpenStateChangeNotify; import emu.grasscutter.server.packet.send.PacketOpenStateChangeNotify;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@Command( @Command(
label = "unlockall", label = "unlockall",
usage = {""}, usage = {""},
permission = "player.unlockall", permission = "player.unlockall",
permissionTargeted = "player.unlockall.others") permissionTargeted = "player.unlockall.others")
public final class UnlockAllCommand implements CommandHandler { public final class UnlockAllCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
Map<Integer, Integer> changed = new HashMap<>(); Map<Integer, Integer> changed = new HashMap<>();
for (var state : GameData.getOpenStateList()) { for (var state : GameData.getOpenStateList()) {
// Don't unlock blacklisted open states. // Don't unlock blacklisted open states.
if (PlayerProgressManager.BLACKLIST_OPEN_STATES.contains(state.getId())) { if (PlayerProgressManager.BLACKLIST_OPEN_STATES.contains(state.getId())) {
continue; continue;
} }
if (targetPlayer.getProgressManager().getOpenState(state.getId()) == 0) { if (targetPlayer.getProgressManager().getOpenState(state.getId()) == 0) {
targetPlayer.getOpenStates().put(state.getId(), 1); targetPlayer.getOpenStates().put(state.getId(), 1);
changed.put(state.getId(), 1); changed.put(state.getId(), 1);
} }
} }
targetPlayer.sendPacket(new PacketOpenStateChangeNotify(changed)); targetPlayer.sendPacket(new PacketOpenStateChangeNotify(changed));
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.unlockall.success", targetPlayer.getNickname())); sender, translate(sender, "commands.unlockall.success", targetPlayer.getNickname()));
} }
} }

View File

@ -1,52 +1,52 @@
package emu.grasscutter.command.commands; package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command; import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ClimateType; import emu.grasscutter.game.props.ClimateType;
import java.util.List; import java.util.List;
@Command( @Command(
label = "weather", label = "weather",
aliases = {"w"}, aliases = {"w"},
usage = {"weather [<weatherId>] [<climateType>]"}, usage = {"weather [<weatherId>] [<climateType>]"},
permission = "player.weather", permission = "player.weather",
permissionTargeted = "player.weather.others") permissionTargeted = "player.weather.others")
public final class WeatherCommand implements CommandHandler { public final class WeatherCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
int weatherId = targetPlayer.getWeatherId(); int weatherId = targetPlayer.getWeatherId();
ClimateType climate = ClimateType climate =
ClimateType ClimateType
.CLIMATE_NONE; // Sending ClimateType.CLIMATE_NONE to Scene.setWeather will use the .CLIMATE_NONE; // Sending ClimateType.CLIMATE_NONE to Scene.setWeather will use the
// default climate for that weather // default climate for that weather
if (args.isEmpty()) { if (args.isEmpty()) {
climate = targetPlayer.getClimate(); climate = targetPlayer.getClimate();
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.weather.status", weatherId, climate.getShortName()); sender, "commands.weather.status", weatherId, climate.getShortName());
return; return;
} }
for (String arg : args) { for (String arg : args) {
ClimateType c = ClimateType.getTypeByShortName(arg.toLowerCase()); ClimateType c = ClimateType.getTypeByShortName(arg.toLowerCase());
if (c != ClimateType.CLIMATE_NONE) { if (c != ClimateType.CLIMATE_NONE) {
climate = c; climate = c;
} else { } else {
try { try {
weatherId = Integer.parseInt(arg); weatherId = Integer.parseInt(arg);
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.id"); CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.id");
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
} }
} }
targetPlayer.setWeather(weatherId, climate); targetPlayer.setWeather(weatherId, climate);
climate = targetPlayer.getClimate(); // Might be different to what we set climate = targetPlayer.getClimate(); // Might be different to what we set
CommandHandler.sendTranslatedMessage( CommandHandler.sendTranslatedMessage(
sender, "commands.weather.success", weatherId, climate.getShortName()); sender, "commands.weather.success", weatherId, climate.getShortName());
} }
} }

View File

@ -1,10 +1,10 @@
package emu.grasscutter.data; package emu.grasscutter.data;
public abstract class GameResource { public abstract class GameResource {
public int getId() { public int getId() {
return 0; return 0;
} }
public void onLoad() {} public void onLoad() {}
} }

View File

@ -1,40 +1,40 @@
package emu.grasscutter.data; package emu.grasscutter.data;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface ResourceType { public @interface ResourceType {
/** Names of the file that this Resource loads from */ /** Names of the file that this Resource loads from */
String[] name(); String[] name();
/** /**
* Load priority - dictates which order to load this resource, with "highest" being loaded first * Load priority - dictates which order to load this resource, with "highest" being loaded first
*/ */
LoadPriority loadPriority() default LoadPriority.NORMAL; LoadPriority loadPriority() default LoadPriority.NORMAL;
enum LoadPriority { enum LoadPriority {
HIGHEST(4), HIGHEST(4),
HIGH(3), HIGH(3),
NORMAL(2), NORMAL(2),
LOW(1), LOW(1),
LOWEST(0); LOWEST(0);
private final int value; private final int value;
LoadPriority(int value) { LoadPriority(int value) {
this.value = value; this.value = value;
} }
public static List<LoadPriority> getInOrder() { public static List<LoadPriority> getInOrder() {
return Stream.of(LoadPriority.values()).sorted((x, y) -> y.value() - x.value()).toList(); return Stream.of(LoadPriority.values()).sorted((x, y) -> y.value() - x.value()).toList();
} }
public int value() { public int value() {
return value; return value;
} }
} }
} }

View File

@ -1,21 +1,21 @@
package emu.grasscutter.data.binout; package emu.grasscutter.data.binout;
public class AbilityEmbryoEntry { public class AbilityEmbryoEntry {
private String name; private String name;
private String[] abilities; private String[] abilities;
public AbilityEmbryoEntry() {} public AbilityEmbryoEntry() {}
public AbilityEmbryoEntry(String avatarName, String[] array) { public AbilityEmbryoEntry(String avatarName, String[] array) {
this.name = avatarName; this.name = avatarName;
this.abilities = array; this.abilities = array;
} }
public String getName() { public String getName() {
return name; return name;
} }
public String[] getAbilities() { public String[] getAbilities() {
return abilities; return abilities;
} }
} }

View File

@ -1,13 +1,13 @@
package emu.grasscutter.data.binout; package emu.grasscutter.data.binout;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Data; import lombok.Data;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class ConfigGadget { public class ConfigGadget {
// There are more values that can be added that might be useful in the json // There are more values that can be added that might be useful in the json
@Nullable ConfigGadgetCombat combat; @Nullable ConfigGadgetCombat combat;
} }

View File

@ -1,18 +1,18 @@
package emu.grasscutter.data.binout; package emu.grasscutter.data.binout;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Data; import lombok.Data;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class ConfigGadgetCombatProperty { public class ConfigGadgetCombatProperty {
float HP; float HP;
boolean isLockHP; boolean isLockHP;
boolean isInvincible; boolean isInvincible;
boolean isGhostToAllied; boolean isGhostToAllied;
float attack; float attack;
float defence; float defence;
float weight; float weight;
boolean useCreatorProperty; boolean useCreatorProperty;
} }

View File

@ -1,61 +1,61 @@
package emu.grasscutter.data.binout; package emu.grasscutter.data.binout;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import java.util.List; import java.util.List;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Data; import lombok.Data;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class HomeworldDefaultSaveData { public class HomeworldDefaultSaveData {
@SerializedName(value = "KFHBFNPDJBE", alternate = "PKACPHDGGEI") @SerializedName(value = "KFHBFNPDJBE", alternate = "PKACPHDGGEI")
List<HomeBlock> homeBlockLists; List<HomeBlock> homeBlockLists;
@SerializedName(value = "IJNPADKGNKE", alternate = "MINCKHBNING") @SerializedName(value = "IJNPADKGNKE", alternate = "MINCKHBNING")
Position bornPos; Position bornPos;
@SerializedName("IPIIGEMFLHK") @SerializedName("IPIIGEMFLHK")
Position bornRot; Position bornRot;
@SerializedName("HHOLBNPIHEM") @SerializedName("HHOLBNPIHEM")
Position djinPos; Position djinPos;
@SerializedName("KNHCJKHCOAN") @SerializedName("KNHCJKHCOAN")
HomeFurniture mainhouse; HomeFurniture mainhouse;
@SerializedName("NIHOJFEKFPG") @SerializedName("NIHOJFEKFPG")
List<HomeFurniture> doorLists; List<HomeFurniture> doorLists;
@SerializedName("EPGELGEFJFK") @SerializedName("EPGELGEFJFK")
List<HomeFurniture> stairLists; List<HomeFurniture> stairLists;
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public static class HomeBlock { public static class HomeBlock {
@SerializedName(value = "FGIJCELCGFI", alternate = "PGDPDIDJEEL") @SerializedName(value = "FGIJCELCGFI", alternate = "PGDPDIDJEEL")
int blockId; int blockId;
@SerializedName("BEAPOFELABD") @SerializedName("BEAPOFELABD")
List<HomeFurniture> furnitures; List<HomeFurniture> furnitures;
@SerializedName("MLIODLGDFHJ") @SerializedName("MLIODLGDFHJ")
List<HomeFurniture> persistentFurnitures; List<HomeFurniture> persistentFurnitures;
} }
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public static class HomeFurniture { public static class HomeFurniture {
@SerializedName(value = "ENHNGKJBJAB", alternate = "KMAAJJHPNBA") @SerializedName(value = "ENHNGKJBJAB", alternate = "KMAAJJHPNBA")
int id; int id;
@SerializedName(value = "NGIEEIOLPPO", alternate = "JFKAHNCPDME") @SerializedName(value = "NGIEEIOLPPO", alternate = "JFKAHNCPDME")
Position pos; Position pos;
// @SerializedName(value = "HEOCEHKEBFM", alternate = "LKCKOOGFDBM") // @SerializedName(value = "HEOCEHKEBFM", alternate = "LKCKOOGFDBM")
Position rot; Position rot;
} }
} }

View File

@ -1,24 +1,24 @@
package emu.grasscutter.data.binout; package emu.grasscutter.data.binout;
import com.github.davidmoten.rtreemulti.RTree; import com.github.davidmoten.rtreemulti.RTree;
import com.github.davidmoten.rtreemulti.geometry.Geometry; import com.github.davidmoten.rtreemulti.geometry.Geometry;
import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneGroup;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Data; import lombok.Data;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class SceneNpcBornData { public class SceneNpcBornData {
int sceneId; int sceneId;
List<SceneNpcBornEntry> bornPosList; List<SceneNpcBornEntry> bornPosList;
/** Spatial Index For NPC */ /** Spatial Index For NPC */
transient RTree<SceneNpcBornEntry, Geometry> index; transient RTree<SceneNpcBornEntry, Geometry> index;
/** npc groups */ /** npc groups */
transient Map<Integer, SceneGroup> groups = new ConcurrentHashMap<>(); transient Map<Integer, SceneGroup> groups = new ConcurrentHashMap<>();
} }

View File

@ -1,42 +1,42 @@
package emu.grasscutter.data.binout; package emu.grasscutter.data.binout;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import java.util.List; import java.util.List;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Data; import lombok.Data;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class SceneNpcBornEntry { public class SceneNpcBornEntry {
@SerializedName( @SerializedName(
value = "id", value = "id",
alternate = {"_id", "ID"}) alternate = {"_id", "ID"})
int id; int id;
@SerializedName( @SerializedName(
value = "configId", value = "configId",
alternate = {"_configId"}) alternate = {"_configId"})
int configId; int configId;
@SerializedName( @SerializedName(
value = "pos", value = "pos",
alternate = {"_pos"}) alternate = {"_pos"})
Position pos; Position pos;
@SerializedName( @SerializedName(
value = "rot", value = "rot",
alternate = {"_rot"}) alternate = {"_rot"})
Position rot; Position rot;
@SerializedName( @SerializedName(
value = "groupId", value = "groupId",
alternate = {"_groupId"}) alternate = {"_groupId"})
int groupId; int groupId;
@SerializedName( @SerializedName(
value = "suiteIdList", value = "suiteIdList",
alternate = {"_suiteIdList"}) alternate = {"_suiteIdList"})
List<Integer> suiteIdList; List<Integer> suiteIdList;
} }

View File

@ -1,24 +1,24 @@
package emu.grasscutter.data.binout; package emu.grasscutter.data.binout;
import emu.grasscutter.data.common.PointData; import emu.grasscutter.data.common.PointData;
import lombok.Getter; import lombok.Getter;
public class ScenePointEntry { public class ScenePointEntry {
@Getter private final int sceneId; @Getter private final int sceneId;
@Getter private final PointData pointData; @Getter private final PointData pointData;
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
public ScenePointEntry(String name, PointData pointData) { public ScenePointEntry(String name, PointData pointData) {
this.sceneId = Integer.parseInt(name.split("_")[0]); this.sceneId = Integer.parseInt(name.split("_")[0]);
this.pointData = pointData; this.pointData = pointData;
} }
public ScenePointEntry(int sceneId, PointData pointData) { public ScenePointEntry(int sceneId, PointData pointData) {
this.sceneId = sceneId; this.sceneId = sceneId;
this.pointData = pointData; this.pointData = pointData;
} }
public String getName() { public String getName() {
return this.sceneId + "_" + this.pointData.getId(); return this.sceneId + "_" + this.pointData.getId();
} }
} }

View File

@ -1,18 +1,18 @@
package emu.grasscutter.data.binout; package emu.grasscutter.data.binout;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import lombok.Data; import lombok.Data;
@Data @Data
public class ScriptSceneData { public class ScriptSceneData {
Map<String, ScriptObject> scriptObjectList; Map<String, ScriptObject> scriptObjectList;
@Data @Data
public static class ScriptObject { public static class ScriptObject {
// private SceneGroup groups; // private SceneGroup groups;
@SerializedName("dummy_points") @SerializedName("dummy_points")
private Map<String, List<Float>> dummyPoints; private Map<String, List<Float>> dummyPoints;
} }
} }

View File

@ -1,19 +1,19 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
public class CurveInfo { public class CurveInfo {
private String type; private String type;
private String arith; private String arith;
private float value; private float value;
public String getType() { public String getType() {
return type; return type;
} }
public String getArith() { public String getArith() {
return arith; return arith;
} }
public float getValue() { public float getValue() {
return value; return value;
} }
} }

View File

@ -1,105 +1,105 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
import it.unimi.dsi.fastutil.floats.FloatArrayList; import it.unimi.dsi.fastutil.floats.FloatArrayList;
import it.unimi.dsi.fastutil.objects.Object2FloatArrayMap; import it.unimi.dsi.fastutil.objects.Object2FloatArrayMap;
import it.unimi.dsi.fastutil.objects.Object2FloatMap; import it.unimi.dsi.fastutil.objects.Object2FloatMap;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import lombok.val; import lombok.val;
public class DynamicFloat { public class DynamicFloat {
public static DynamicFloat ZERO = new DynamicFloat(0f); public static DynamicFloat ZERO = new DynamicFloat(0f);
private List<StackOp> ops; private List<StackOp> ops;
private boolean dynamic = false; private boolean dynamic = false;
private float constant = 0f; private float constant = 0f;
public DynamicFloat(float constant) { public DynamicFloat(float constant) {
this.constant = constant; this.constant = constant;
} }
public DynamicFloat(String key) { public DynamicFloat(String key) {
this.dynamic = true; this.dynamic = true;
this.ops = List.of(new StackOp(key)); this.ops = List.of(new StackOp(key));
} }
public DynamicFloat(boolean b) { public DynamicFloat(boolean b) {
this.dynamic = true; this.dynamic = true;
this.ops = List.of(new StackOp(String.valueOf(b))); this.ops = List.of(new StackOp(String.valueOf(b)));
} }
public DynamicFloat(List<StackOp> ops) { public DynamicFloat(List<StackOp> ops) {
this.dynamic = true; this.dynamic = true;
this.ops = ops; this.ops = ops;
} }
public String toString(boolean nextBoolean) { public String toString(boolean nextBoolean) {
String key = String.valueOf(nextBoolean); String key = String.valueOf(nextBoolean);
this.ops = List.of(new StackOp(key)); this.ops = List.of(new StackOp(key));
return ops.toString(); return ops.toString();
} }
public float get() { public float get() {
return this.get(new Object2FloatArrayMap<String>()); return this.get(new Object2FloatArrayMap<String>());
} }
public float get(Object2FloatMap<String> props) { public float get(Object2FloatMap<String> props) {
if (!dynamic) return constant; if (!dynamic) return constant;
val fl = new FloatArrayList(); val fl = new FloatArrayList();
for (var op : this.ops) { for (var op : this.ops) {
switch (op.op) { switch (op.op) {
case CONSTANT -> fl.push(op.fValue); case CONSTANT -> fl.push(op.fValue);
case KEY -> fl.push(props.getOrDefault(op.sValue, 0f)); case KEY -> fl.push(props.getOrDefault(op.sValue, 0f));
case ADD -> fl.push(fl.popFloat() + fl.popFloat()); case ADD -> fl.push(fl.popFloat() + fl.popFloat());
case SUB -> fl.push( case SUB -> fl.push(
-fl.popFloat() + fl.popFloat()); // [f0, f1, f2] -> [f0, f1-f2] (opposite of RPN order) -fl.popFloat() + fl.popFloat()); // [f0, f1, f2] -> [f0, f1-f2] (opposite of RPN order)
case MUL -> fl.push(fl.popFloat() * fl.popFloat()); case MUL -> fl.push(fl.popFloat() * fl.popFloat());
case DIV -> fl.push((1f / fl.popFloat()) * fl.popFloat()); // [f0, f1, f2] -> [f0, f1/f2] case DIV -> fl.push((1f / fl.popFloat()) * fl.popFloat()); // [f0, f1, f2] -> [f0, f1/f2]
case NEXBOOLEAN -> fl.push(props.getOrDefault(Optional.of(op.bValue), 0f)); case NEXBOOLEAN -> fl.push(props.getOrDefault(Optional.of(op.bValue), 0f));
} }
} }
return fl.popFloat(); // well-formed data will always have only one value left at this point return fl.popFloat(); // well-formed data will always have only one value left at this point
} }
public static class StackOp { public static class StackOp {
public Op op; public Op op;
public float fValue; public float fValue;
public String sValue; public String sValue;
public boolean bValue; public boolean bValue;
public StackOp(String s) { public StackOp(String s) {
switch (s.toUpperCase()) { switch (s.toUpperCase()) {
case "ADD" -> this.op = Op.ADD; case "ADD" -> this.op = Op.ADD;
case "SUB" -> this.op = Op.SUB; case "SUB" -> this.op = Op.SUB;
case "MUL" -> this.op = Op.MUL; case "MUL" -> this.op = Op.MUL;
case "DIV" -> this.op = Op.DIV; case "DIV" -> this.op = Op.DIV;
default -> { default -> {
this.op = Op.KEY; this.op = Op.KEY;
this.sValue = s; this.sValue = s;
} }
} }
} }
public StackOp(boolean b) { public StackOp(boolean b) {
this.op = Op.NEXBOOLEAN; this.op = Op.NEXBOOLEAN;
this.bValue = Boolean.parseBoolean(String.valueOf(b)); this.bValue = Boolean.parseBoolean(String.valueOf(b));
} }
public StackOp(float f) { public StackOp(float f) {
this.op = Op.CONSTANT; this.op = Op.CONSTANT;
this.fValue = f; this.fValue = f;
} }
enum Op { enum Op {
CONSTANT, CONSTANT,
KEY, KEY,
ADD, ADD,
SUB, SUB,
MUL, MUL,
DIV, DIV,
NEXBOOLEAN NEXBOOLEAN
} }
} }
} }

View File

@ -1,25 +1,25 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
public class FightPropData { public class FightPropData {
private String propType; private String propType;
private FightProperty prop; private FightProperty prop;
private float value; private float value;
public String getPropType() { public String getPropType() {
return propType; return propType;
} }
public float getValue() { public float getValue() {
return value; return value;
} }
public FightProperty getProp() { public FightProperty getProp() {
return prop; return prop;
} }
public void onLoad() { public void onLoad() {
this.prop = FightProperty.getPropByName(propType); this.prop = FightProperty.getPropByName(propType);
} }
} }

View File

@ -1,39 +1,39 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
// Used in excels // Used in excels
public class ItemParamData { public class ItemParamData {
@SerializedName( @SerializedName(
value = "id", value = "id",
alternate = {"itemId"}) alternate = {"itemId"})
private int id; private int id;
@SerializedName( @SerializedName(
value = "count", value = "count",
alternate = {"itemCount"}) alternate = {"itemCount"})
private int count; private int count;
public ItemParamData() {} public ItemParamData() {}
public ItemParamData(int id, int count) { public ItemParamData(int id, int count) {
this.id = id; this.id = id;
this.count = count; this.count = count;
} }
public int getId() { public int getId() {
return id; return id;
} }
public int getItemId() { public int getItemId() {
return id; return id;
} }
public int getCount() { public int getCount() {
return count; return count;
} }
public int getItemCount() { public int getItemCount() {
return count; return count;
} }
} }

View File

@ -1,26 +1,26 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
public class ItemParamStringData { public class ItemParamStringData {
private int id; private int id;
private String count; private String count;
public ItemParamStringData() {} public ItemParamStringData() {}
public int getId() { public int getId() {
return id; return id;
} }
public String getCount() { public String getCount() {
return count; return count;
} }
public ItemParamData toItemParamData() { public ItemParamData toItemParamData() {
if (count.contains(";")) { if (count.contains(";")) {
String[] split = count.split(";"); String[] split = count.split(";");
count = count.split(";")[split.length - 1]; count = count.split(";")[split.length - 1];
} else if (count.contains(".")) { } else if (count.contains(".")) {
return new ItemParamData(id, (int) Math.ceil(Double.parseDouble(count))); return new ItemParamData(id, (int) Math.ceil(Double.parseDouble(count)));
} }
return new ItemParamData(id, Integer.parseInt(count)); return new ItemParamData(id, Integer.parseInt(count));
} }
} }

View File

@ -1,24 +1,24 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
import java.util.List; import java.util.List;
public class OpenCondData { public class OpenCondData {
private String condType; private String condType;
private List<Integer> paramList; private List<Integer> paramList;
public String getCondType() { public String getCondType() {
return condType; return condType;
} }
public void setCondType(String condType) { public void setCondType(String condType) {
condType = condType; condType = condType;
} }
public List<Integer> getParamList() { public List<Integer> getParamList() {
return paramList; return paramList;
} }
public void setParamList(List<Integer> paramList) { public void setParamList(List<Integer> paramList) {
paramList = paramList; paramList = paramList;
} }
} }

View File

@ -1,14 +1,14 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
public class PropGrowCurve { public class PropGrowCurve {
private String type; private String type;
private String growCurve; private String growCurve;
public String getType() { public String getType() {
return this.type; return this.type;
} }
public String getGrowCurve() { public String getGrowCurve() {
return this.growCurve; return this.growCurve;
} }
} }

View File

@ -1,74 +1,74 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.BattlePassMissionRefreshType; import emu.grasscutter.game.props.BattlePassMissionRefreshType;
import emu.grasscutter.game.props.WatcherTriggerType; import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.MissionStatus; import emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.MissionStatus;
import java.util.Arrays; import java.util.Arrays;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = {"BattlePassMissionExcelConfigData.json"}) @ResourceType(name = {"BattlePassMissionExcelConfigData.json"})
@Getter @Getter
public class BattlePassMissionData extends GameResource { public class BattlePassMissionData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
private int id; private int id;
private int addPoint; private int addPoint;
private int scheduleId; private int scheduleId;
private int progress; private int progress;
private TriggerConfig triggerConfig; private TriggerConfig triggerConfig;
private BattlePassMissionRefreshType refreshType; private BattlePassMissionRefreshType refreshType;
private transient Set<Integer> mainParams; private transient Set<Integer> mainParams;
public WatcherTriggerType getTriggerType() { public WatcherTriggerType getTriggerType() {
return this.getTriggerConfig().getTriggerType(); return this.getTriggerConfig().getTriggerType();
} }
public boolean isCycleRefresh() { public boolean isCycleRefresh() {
return getRefreshType() == null return getRefreshType() == null
|| getRefreshType() || getRefreshType()
== BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE; == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE;
} }
public boolean isValidRefreshType() { public boolean isValidRefreshType() {
return getRefreshType() == null return getRefreshType() == null
|| getRefreshType() || getRefreshType()
== BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE
|| getScheduleId() == 2701; || getScheduleId() == 2701;
} }
@Override @Override
public void onLoad() { public void onLoad() {
if (this.getTriggerConfig() != null) { if (this.getTriggerConfig() != null) {
var params = getTriggerConfig().getParamList()[0]; var params = getTriggerConfig().getParamList()[0];
if ((params != null) && !params.isEmpty()) { if ((params != null) && !params.isEmpty()) {
this.mainParams = this.mainParams =
Arrays.stream(params.split("[:;,]")).map(Integer::parseInt).collect(Collectors.toSet()); Arrays.stream(params.split("[:;,]")).map(Integer::parseInt).collect(Collectors.toSet());
} }
} }
} }
public emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission toProto() { public emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission toProto() {
var protoBuilder = var protoBuilder =
emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.newBuilder(); emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.newBuilder();
protoBuilder protoBuilder
.setMissionId(getId()) .setMissionId(getId())
.setTotalProgress(this.getProgress()) .setTotalProgress(this.getProgress())
.setRewardBattlePassPoint(this.getAddPoint()) .setRewardBattlePassPoint(this.getAddPoint())
.setMissionStatus(MissionStatus.MISSION_STATUS_UNFINISHED) .setMissionStatus(MissionStatus.MISSION_STATUS_UNFINISHED)
.setMissionType(this.getRefreshType() == null ? 0 : this.getRefreshType().getValue()); .setMissionType(this.getRefreshType() == null ? 0 : this.getRefreshType().getValue());
return protoBuilder.build(); return protoBuilder.build();
} }
@Getter @Getter
public static class TriggerConfig { public static class TriggerConfig {
private WatcherTriggerType triggerType; private WatcherTriggerType triggerType;
private String[] paramList; private String[] paramList;
} }
} }

View File

@ -1,25 +1,25 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import java.util.List; import java.util.List;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = "BattlePassRewardExcelConfigData.json") @ResourceType(name = "BattlePassRewardExcelConfigData.json")
@Getter @Getter
public class BattlePassRewardData extends GameResource { public class BattlePassRewardData extends GameResource {
private int indexId; private int indexId;
private int level; private int level;
private List<Integer> freeRewardIdList; private List<Integer> freeRewardIdList;
private List<Integer> paidRewardIdList; private List<Integer> paidRewardIdList;
@Override @Override
public int getId() { public int getId() {
// Reward ID is a combination of index and level. // Reward ID is a combination of index and level.
// We do this to get a unique ID. // We do this to get a unique ID.
return this.indexId * 100 + this.level; return this.indexId * 100 + this.level;
} }
@Override @Override
public void onLoad() {} public void onLoad() {}
} }

View File

@ -1,46 +1,46 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import java.util.List; import java.util.List;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = "BlossomRefreshExcelConfigData.json") @ResourceType(name = "BlossomRefreshExcelConfigData.json")
@Getter @Getter
public class BlossomRefreshExcelConfigData extends GameResource { public class BlossomRefreshExcelConfigData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
private int id; private int id;
// Map details // Map details
private long nameTextMapHash; private long nameTextMapHash;
private long descTextMapHash; private long descTextMapHash;
private String icon; private String icon;
private String clientShowType; // BLOSSOM_SHOWTYPE_CHALLENGE, BLOSSOM_SHOWTYPE_NPCTALK private String clientShowType; // BLOSSOM_SHOWTYPE_CHALLENGE, BLOSSOM_SHOWTYPE_NPCTALK
// Refresh details // Refresh details
private String refreshType; // Leyline blossoms, magical ore outcrops private String refreshType; // Leyline blossoms, magical ore outcrops
private int private int
refreshCount; // Number of entries to spawn at refresh (1 for each leyline type for each city, refreshCount; // Number of entries to spawn at refresh (1 for each leyline type for each city,
// 4 for magical ore for each city) // 4 for magical ore for each city)
private String refreshTime; // Server time-of-day to refresh at private String refreshTime; // Server time-of-day to refresh at
private RefreshCond[] refreshCondVec; // AR requirements etc. private RefreshCond[] refreshCondVec; // AR requirements etc.
private int cityId; private int cityId;
private int blossomChestId; // 1 for mora, 2 for exp private int blossomChestId; // 1 for mora, 2 for exp
private Drop[] dropVec; private Drop[] dropVec;
// Unknown details // Unknown details
// @Getter private int reviseLevel; // @Getter private int reviseLevel;
// @Getter private int campUpdateNeedCount; // Always 1 if specified // @Getter private int campUpdateNeedCount; // Always 1 if specified
@Getter @Getter
public static class Drop { public static class Drop {
int dropId; int dropId;
int previewReward; int previewReward;
} }
@Getter @Getter
public static class RefreshCond { public static class RefreshCond {
String type; String type;
List<Integer> param; List<Integer> param;
} }
} }

View File

@ -1,28 +1,28 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.ServerBuffType; import emu.grasscutter.game.props.ServerBuffType;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = "BuffExcelConfigData.json") @ResourceType(name = "BuffExcelConfigData.json")
@Getter @Getter
public class BuffData extends GameResource { public class BuffData extends GameResource {
private int groupId; private int groupId;
private int serverBuffId; private int serverBuffId;
private float time; private float time;
private boolean isPersistent; private boolean isPersistent;
private ServerBuffType serverBuffType; private ServerBuffType serverBuffType;
private String abilityName; private String abilityName;
private String modifierName; private String modifierName;
@Override @Override
public int getId() { public int getId() {
return this.serverBuffId; return this.serverBuffId;
} }
public void onLoad() { public void onLoad() {
this.serverBuffType = this.serverBuffType =
this.serverBuffType != null ? this.serverBuffType : ServerBuffType.SERVER_BUFF_NONE; this.serverBuffType != null ? this.serverBuffType : ServerBuffType.SERVER_BUFF_NONE;
} }
} }

View File

@ -1,24 +1,24 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import java.util.List; import java.util.List;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@ResourceType(name = "CityConfigData.json", loadPriority = ResourceType.LoadPriority.HIGH) @ResourceType(name = "CityConfigData.json", loadPriority = ResourceType.LoadPriority.HIGH)
@Getter @Getter
@Setter @Setter
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class CityData extends GameResource { public class CityData extends GameResource {
int cityId; int cityId;
int sceneId; int sceneId;
List<Integer> areaIdVec; List<Integer> areaIdVec;
@Override @Override
public int getId() { public int getId() {
return this.cityId; return this.cityId;
} }
} }

View File

@ -1,82 +1,82 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ResourceType(name = "CombineExcelConfigData.json") @ResourceType(name = "CombineExcelConfigData.json")
public class CombineData extends GameResource { public class CombineData extends GameResource {
private int combineId; private int combineId;
private int playerLevel; private int playerLevel;
private boolean isDefaultShow; private boolean isDefaultShow;
private int combineType; private int combineType;
private int subCombineType; private int subCombineType;
private int resultItemId; private int resultItemId;
private int resultItemCount; private int resultItemCount;
private int scoinCost; private int scoinCost;
private List<ItemParamData> randomItems; private List<ItemParamData> randomItems;
private List<ItemParamData> materialItems; private List<ItemParamData> materialItems;
private String recipeType; private String recipeType;
@Override @Override
public int getId() { public int getId() {
return this.combineId; return this.combineId;
} }
@Override @Override
public void onLoad() { public void onLoad() {
super.onLoad(); super.onLoad();
// clean data // clean data
randomItems = randomItems =
randomItems.stream().filter(item -> item.getId() > 0).collect(Collectors.toList()); randomItems.stream().filter(item -> item.getId() > 0).collect(Collectors.toList());
materialItems = materialItems =
materialItems.stream().filter(item -> item.getId() > 0).collect(Collectors.toList()); materialItems.stream().filter(item -> item.getId() > 0).collect(Collectors.toList());
} }
public int getCombineId() { public int getCombineId() {
return combineId; return combineId;
} }
public int getPlayerLevel() { public int getPlayerLevel() {
return playerLevel; return playerLevel;
} }
public boolean isDefaultShow() { public boolean isDefaultShow() {
return isDefaultShow; return isDefaultShow;
} }
public int getCombineType() { public int getCombineType() {
return combineType; return combineType;
} }
public int getSubCombineType() { public int getSubCombineType() {
return subCombineType; return subCombineType;
} }
public int getResultItemId() { public int getResultItemId() {
return resultItemId; return resultItemId;
} }
public int getResultItemCount() { public int getResultItemCount() {
return resultItemCount; return resultItemCount;
} }
public int getScoinCost() { public int getScoinCost() {
return scoinCost; return scoinCost;
} }
public List<ItemParamData> getRandomItems() { public List<ItemParamData> getRandomItems() {
return randomItems; return randomItems;
} }
public List<ItemParamData> getMaterialItems() { public List<ItemParamData> getMaterialItems() {
return materialItems; return materialItems;
} }
public String getRecipeType() { public String getRecipeType() {
return recipeType; return recipeType;
} }
} }

View File

@ -1,43 +1,43 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
@ResourceType( @ResourceType(
name = {"CookBonusExcelConfigData.json"}, name = {"CookBonusExcelConfigData.json"},
loadPriority = LoadPriority.LOW) loadPriority = LoadPriority.LOW)
public class CookBonusData extends GameResource { public class CookBonusData extends GameResource {
private int avatarId; private int avatarId;
private int recipeId; private int recipeId;
private int[] paramVec; private int[] paramVec;
private int[] complexParamVec; private int[] complexParamVec;
@Override @Override
public int getId() { public int getId() {
return this.avatarId; return this.avatarId;
} }
public int getAvatarId() { public int getAvatarId() {
return avatarId; return avatarId;
} }
public int getRecipeId() { public int getRecipeId() {
return recipeId; return recipeId;
} }
public int[] getParamVec() { public int[] getParamVec() {
return paramVec; return paramVec;
} }
public int[] getComplexParamVec() { public int[] getComplexParamVec() {
return complexParamVec; return complexParamVec;
} }
public int getReplacementItemId() { public int getReplacementItemId() {
return this.paramVec[0]; return this.paramVec[0];
} }
@Override @Override
public void onLoad() {} public void onLoad() {}
} }

View File

@ -1,24 +1,24 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import java.util.List; import java.util.List;
import lombok.Getter; import lombok.Getter;
@ResourceType( @ResourceType(
name = {"CookRecipeExcelConfigData.json"}, name = {"CookRecipeExcelConfigData.json"},
loadPriority = LoadPriority.LOW) loadPriority = LoadPriority.LOW)
@Getter @Getter
public class CookRecipeData extends GameResource { public class CookRecipeData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
private int id; private int id;
private int rankLevel; private int rankLevel;
private boolean isDefaultUnlocked; private boolean isDefaultUnlocked;
private int maxProficiency; private int maxProficiency;
private List<ItemParamData> qualityOutputVec; private List<ItemParamData> qualityOutputVec;
private List<ItemParamData> inputVec; private List<ItemParamData> inputVec;
} }

View File

@ -1,36 +1,36 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import java.util.List; import java.util.List;
@ResourceType( @ResourceType(
name = "EnvAnimalGatherExcelConfigData.json", name = "EnvAnimalGatherExcelConfigData.json",
loadPriority = ResourceType.LoadPriority.LOW) loadPriority = ResourceType.LoadPriority.LOW)
public class EnvAnimalGatherConfigData extends GameResource { public class EnvAnimalGatherConfigData extends GameResource {
private int animalId; private int animalId;
private String entityType; private String entityType;
private List<ItemParamData> gatherItemId; private List<ItemParamData> gatherItemId;
private String excludeWeathers; private String excludeWeathers;
private int aliveTime; private int aliveTime;
private int escapeTime; private int escapeTime;
private int escapeRadius; private int escapeRadius;
@Override @Override
public int getId() { public int getId() {
return animalId; return animalId;
} }
public int getAnimalId() { public int getAnimalId() {
return animalId; return animalId;
} }
public String getEntityType() { public String getEntityType() {
return entityType; return entityType;
} }
public ItemParamData getGatherItem() { public ItemParamData getGatherItem() {
return gatherItemId.size() > 0 ? gatherItemId.get(0) : null; return gatherItemId.size() > 0 ? gatherItemId.get(0) : null;
} }
} }

View File

@ -1,59 +1,59 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.FightPropData; import emu.grasscutter.data.common.FightPropData;
import java.util.ArrayList; import java.util.ArrayList;
@ResourceType(name = "EquipAffixExcelConfigData.json") @ResourceType(name = "EquipAffixExcelConfigData.json")
public class EquipAffixData extends GameResource { public class EquipAffixData extends GameResource {
private int affixId; private int affixId;
private int id; private int id;
private int level; private int level;
private long nameTextMapHash; private long nameTextMapHash;
private String openConfig; private String openConfig;
private FightPropData[] addProps; private FightPropData[] addProps;
private float[] paramList; private float[] paramList;
@Override @Override
public int getId() { public int getId() {
return affixId; return affixId;
} }
public int getMainId() { public int getMainId() {
return id; return id;
} }
public int getLevel() { public int getLevel() {
return level; return level;
} }
public long getNameTextMapHash() { public long getNameTextMapHash() {
return nameTextMapHash; return nameTextMapHash;
} }
public String getOpenConfig() { public String getOpenConfig() {
return openConfig; return openConfig;
} }
public FightPropData[] getAddProps() { public FightPropData[] getAddProps() {
return addProps; return addProps;
} }
public float[] getParamList() { public float[] getParamList() {
return paramList; return paramList;
} }
@Override @Override
public void onLoad() { public void onLoad() {
ArrayList<FightPropData> parsed = new ArrayList<FightPropData>(getAddProps().length); ArrayList<FightPropData> parsed = new ArrayList<FightPropData>(getAddProps().length);
for (FightPropData prop : getAddProps()) { for (FightPropData prop : getAddProps()) {
if (prop.getPropType() != null && prop.getValue() != 0f) { if (prop.getPropType() != null && prop.getValue() != 0f) {
prop.onLoad(); prop.onLoad();
parsed.add(prop); parsed.add(prop);
} }
} }
this.addProps = parsed.toArray(new FightPropData[parsed.size()]); this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
} }
} }

View File

@ -1,23 +1,23 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
@ResourceType(name = "FetterCharacterCardExcelConfigData.json", loadPriority = LoadPriority.HIGHEST) @ResourceType(name = "FetterCharacterCardExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
public class FetterCharacterCardData extends GameResource { public class FetterCharacterCardData extends GameResource {
private int avatarId; private int avatarId;
private int rewardId; private int rewardId;
@Override @Override
public int getId() { public int getId() {
return avatarId; return avatarId;
} }
public int getRewardId() { public int getRewardId() {
return rewardId; return rewardId;
} }
@Override @Override
public void onLoad() {} public void onLoad() {}
} }

View File

@ -1,38 +1,38 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.OpenCondData; import emu.grasscutter.data.common.OpenCondData;
import java.util.List; import java.util.List;
@ResourceType( @ResourceType(
name = { name = {
"FetterInfoExcelConfigData.json", "FetterInfoExcelConfigData.json",
"FettersExcelConfigData.json", "FettersExcelConfigData.json",
"FetterStoryExcelConfigData.json", "FetterStoryExcelConfigData.json",
"PhotographExpressionExcelConfigData.json", "PhotographExpressionExcelConfigData.json",
"PhotographPosenameExcelConfigData.json" "PhotographPosenameExcelConfigData.json"
}, },
loadPriority = LoadPriority.HIGHEST) loadPriority = LoadPriority.HIGHEST)
public class FetterData extends GameResource { public class FetterData extends GameResource {
private int avatarId; private int avatarId;
private int fetterId; private int fetterId;
private List<OpenCondData> openCond; private List<OpenCondData> openCond;
@Override @Override
public int getId() { public int getId() {
return fetterId; return fetterId;
} }
public int getAvatarId() { public int getAvatarId() {
return avatarId; return avatarId;
} }
public List<OpenCondData> getOpenConds() { public List<OpenCondData> getOpenConds() {
return openCond; return openCond;
} }
@Override @Override
public void onLoad() {} public void onLoad() {}
} }

View File

@ -1,29 +1,29 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import java.util.List; import java.util.List;
import lombok.Getter; import lombok.Getter;
@ResourceType( @ResourceType(
name = {"ForgeExcelConfigData.json"}, name = {"ForgeExcelConfigData.json"},
loadPriority = LoadPriority.HIGHEST) loadPriority = LoadPriority.HIGHEST)
@Getter @Getter
public class ForgeData extends GameResource { public class ForgeData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
private int id; private int id;
private int playerLevel; private int playerLevel;
private int forgeType; private int forgeType;
private int showItemId; private int showItemId;
private int resultItemId; private int resultItemId;
private int resultItemCount; private int resultItemCount;
private int forgeTime; private int forgeTime;
private int queueNum; private int queueNum;
private int scoinCost; private int scoinCost;
private int priority; private int priority;
private int forgePoint; private int forgePoint;
private List<ItemParamData> materialItems; private List<ItemParamData> materialItems;
} }

View File

@ -1,34 +1,34 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import java.util.List; import java.util.List;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@Getter @Getter
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
@ResourceType(name = {"FurnitureMakeExcelConfigData.json"}) @ResourceType(name = {"FurnitureMakeExcelConfigData.json"})
public class FurnitureMakeConfigData extends GameResource { public class FurnitureMakeConfigData extends GameResource {
int configID; int configID;
int furnitureItemID; int furnitureItemID;
int count; int count;
int exp; int exp;
List<ItemParamData> materialItems; List<ItemParamData> materialItems;
int makeTime; int makeTime;
int maxAccelerateTime; int maxAccelerateTime;
int quickFetchMaterialNum; int quickFetchMaterialNum;
@Override @Override
public int getId() { public int getId() {
return configID; return configID;
} }
@Override @Override
public void onLoad() { public void onLoad() {
this.materialItems = materialItems.stream().filter(x -> x.getId() > 0).toList(); this.materialItems = materialItems.stream().filter(x -> x.getId() > 0).toList();
} }
} }

View File

@ -1,47 +1,47 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
@ResourceType(name = "GatherExcelConfigData.json") @ResourceType(name = "GatherExcelConfigData.json")
public class GatherData extends GameResource { public class GatherData extends GameResource {
private int pointType; private int pointType;
private int id; private int id;
private int gadgetId; private int gadgetId;
private int itemId; private int itemId;
private int cd; // Probably hours private int cd; // Probably hours
private boolean isForbidGuest; private boolean isForbidGuest;
private boolean initDisableInteract; private boolean initDisableInteract;
@Override @Override
public int getId() { public int getId() {
return this.pointType; return this.pointType;
} }
public int getGatherId() { public int getGatherId() {
return id; return id;
} }
public int getGadgetId() { public int getGadgetId() {
return gadgetId; return gadgetId;
} }
public int getItemId() { public int getItemId() {
return itemId; return itemId;
} }
public int getCd() { public int getCd() {
return cd; return cd;
} }
public boolean isForbidGuest() { public boolean isForbidGuest() {
return isForbidGuest; return isForbidGuest;
} }
public boolean initDisableInteract() { public boolean initDisableInteract() {
return initDisableInteract; return initDisableInteract;
} }
@Override @Override
public void onLoad() {} public void onLoad() {}
} }

View File

@ -1,31 +1,31 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@Getter @Getter
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
@ResourceType(name = {"HomeWorldBgmExcelConfigData.json"}) @ResourceType(name = {"HomeWorldBgmExcelConfigData.json"})
public class HomeWorldBgmData extends GameResource { public class HomeWorldBgmData extends GameResource {
@SerializedName(value = "homeBgmId", alternate = "MJJENLEBKEF") @SerializedName(value = "homeBgmId", alternate = "MJJENLEBKEF")
private int homeBgmId; private int homeBgmId;
private boolean isDefaultUnlock; private boolean isDefaultUnlock;
private boolean NBIDHGOOCKD; private boolean NBIDHGOOCKD;
private boolean JJMNJMCCOKP; private boolean JJMNJMCCOKP;
private int cityId; private int cityId;
private int sortOrder; private int sortOrder;
private String GEGHMJBJMGB; private String GEGHMJBJMGB;
@SerializedName(value = "bgmNameTextMapHash", alternate = "LMLNBMJFFML") @SerializedName(value = "bgmNameTextMapHash", alternate = "LMLNBMJFFML")
private long bgmNameTextMapHash; private long bgmNameTextMapHash;
@Override @Override
public int getId() { public int getId() {
return this.homeBgmId; return this.homeBgmId;
} }
} }

View File

@ -1,31 +1,31 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import java.util.List; import java.util.List;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@Getter @Getter
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
@ResourceType(name = {"HomeworldLevelExcelConfigData.json"}) @ResourceType(name = {"HomeworldLevelExcelConfigData.json"})
public class HomeWorldLevelData extends GameResource { public class HomeWorldLevelData extends GameResource {
int level; int level;
int exp; int exp;
int homeCoinStoreLimit; int homeCoinStoreLimit;
int homeFetterExpStoreLimit; int homeFetterExpStoreLimit;
int rewardId; int rewardId;
int furnitureMakeSlotCount; int furnitureMakeSlotCount;
int outdoorUnlockBlockCount; int outdoorUnlockBlockCount;
int freeUnlockModuleCount; int freeUnlockModuleCount;
int deployNpcCount; int deployNpcCount;
int limitShopGoodsCount; int limitShopGoodsCount;
List<String> levelFuncs; List<String> levelFuncs;
@Override @Override
public int getId() { public int getId() {
return level; return level;
} }
} }

View File

@ -1,33 +1,33 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import java.util.List; import java.util.List;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@ResourceType( @ResourceType(
name = "InvestigationMonsterConfigData.json", name = "InvestigationMonsterConfigData.json",
loadPriority = ResourceType.LoadPriority.LOW) loadPriority = ResourceType.LoadPriority.LOW)
@Getter @Getter
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class InvestigationMonsterData extends GameResource { public class InvestigationMonsterData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
int id; int id;
int cityId; int cityId;
List<Integer> monsterIdList; List<Integer> monsterIdList;
List<Integer> groupIdList; List<Integer> groupIdList;
int rewardPreviewId; int rewardPreviewId;
String mapMarkCreateType; String mapMarkCreateType;
String monsterCategory; String monsterCategory;
CityData cityData; CityData cityData;
@Override @Override
public void onLoad() { public void onLoad() {
this.cityData = GameData.getCityDataMap().get(cityId); this.cityData = GameData.getCityDataMap().get(cityId);
} }
} }

View File

@ -1,18 +1,18 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@ResourceType(name = "MusicGameBasicConfigData.json") @ResourceType(name = "MusicGameBasicConfigData.json")
@Getter @Getter
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class MusicGameBasicData extends GameResource { public class MusicGameBasicData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
int id; int id;
int musicID; int musicID;
int musicLevel; int musicLevel;
} }

View File

@ -1,23 +1,23 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import java.util.List; import java.util.List;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@ResourceType(name = "PersonalLineExcelConfigData.json") @ResourceType(name = "PersonalLineExcelConfigData.json")
@Getter @Getter
@Setter // TODO: remove setters next API break @Setter // TODO: remove setters next API break
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class PersonalLineData extends GameResource { public class PersonalLineData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
int id; int id;
int avatarID; int avatarID;
List<Integer> preQuestId; List<Integer> preQuestId;
int startQuestId; int startQuestId;
int chapterId; int chapterId;
} }

View File

@ -1,57 +1,57 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import dev.morphia.annotations.Transient; import dev.morphia.annotations.Transient;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.FightPropData; import emu.grasscutter.data.common.FightPropData;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = "ProudSkillExcelConfigData.json") @ResourceType(name = "ProudSkillExcelConfigData.json")
public class ProudSkillData extends GameResource { public class ProudSkillData extends GameResource {
private int proudSkillId; private int proudSkillId;
@Getter private int proudSkillGroupId; @Getter private int proudSkillGroupId;
@Getter private int level; @Getter private int level;
@Getter private int coinCost; @Getter private int coinCost;
@Getter private int breakLevel; @Getter private int breakLevel;
@Getter private int proudSkillType; @Getter private int proudSkillType;
@Getter private String openConfig; @Getter private String openConfig;
@Getter private List<ItemParamData> costItems; @Getter private List<ItemParamData> costItems;
@Getter private List<String> filterConds; @Getter private List<String> filterConds;
@Getter private List<String> lifeEffectParams; @Getter private List<String> lifeEffectParams;
@Getter private FightPropData[] addProps; @Getter private FightPropData[] addProps;
@Getter private float[] paramList; @Getter private float[] paramList;
@Getter private long[] paramDescList; @Getter private long[] paramDescList;
@Getter private long nameTextMapHash; @Getter private long nameTextMapHash;
@Transient private Iterable<ItemParamData> totalCostItems; @Transient private Iterable<ItemParamData> totalCostItems;
@Override @Override
public int getId() { public int getId() {
return proudSkillId; return proudSkillId;
} }
public Iterable<ItemParamData> getTotalCostItems() { public Iterable<ItemParamData> getTotalCostItems() {
if (this.totalCostItems == null) { if (this.totalCostItems == null) {
ArrayList<ItemParamData> total = ArrayList<ItemParamData> total =
(this.costItems != null) ? new ArrayList<>(this.costItems) : new ArrayList<>(1); (this.costItems != null) ? new ArrayList<>(this.costItems) : new ArrayList<>(1);
if (this.coinCost > 0) total.add(new ItemParamData(202, this.coinCost)); if (this.coinCost > 0) total.add(new ItemParamData(202, this.coinCost));
this.totalCostItems = total; this.totalCostItems = total;
} }
return this.totalCostItems; return this.totalCostItems;
} }
@Override @Override
public void onLoad() { public void onLoad() {
// Fight props // Fight props
ArrayList<FightPropData> parsed = new ArrayList<FightPropData>(getAddProps().length); ArrayList<FightPropData> parsed = new ArrayList<FightPropData>(getAddProps().length);
for (FightPropData prop : getAddProps()) { for (FightPropData prop : getAddProps()) {
if (prop.getPropType() != null && prop.getValue() != 0f) { if (prop.getPropType() != null && prop.getValue() != 0f) {
prop.onLoad(); prop.onLoad();
parsed.add(prop); parsed.add(prop);
} }
} }
this.addProps = parsed.toArray(new FightPropData[parsed.size()]); this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
} }
} }

View File

@ -1,26 +1,26 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import java.util.List; import java.util.List;
@ResourceType(name = "RewardExcelConfigData.json") @ResourceType(name = "RewardExcelConfigData.json")
public class RewardData extends GameResource { public class RewardData extends GameResource {
public int rewardId; public int rewardId;
public List<ItemParamData> rewardItemList; public List<ItemParamData> rewardItemList;
@Override @Override
public int getId() { public int getId() {
return rewardId; return rewardId;
} }
public List<ItemParamData> getRewardItemList() { public List<ItemParamData> getRewardItemList() {
return rewardItemList; return rewardItemList;
} }
@Override @Override
public void onLoad() { public void onLoad() {
rewardItemList = rewardItemList.stream().filter(i -> i.getId() > 0).toList(); rewardItemList = rewardItemList.stream().filter(i -> i.getId() > 0).toList();
} }
} }

View File

@ -1,35 +1,35 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.common.ItemParamStringData; import emu.grasscutter.data.common.ItemParamStringData;
import java.util.Arrays; import java.util.Arrays;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH) @ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH)
public class RewardPreviewData extends GameResource { public class RewardPreviewData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
private int id; private int id;
private ItemParamStringData[] previewItems; private ItemParamStringData[] previewItems;
private ItemParamData[] previewItemsArray; private ItemParamData[] previewItemsArray;
public ItemParamData[] getPreviewItems() { public ItemParamData[] getPreviewItems() {
return previewItemsArray; return previewItemsArray;
} }
@Override @Override
public void onLoad() { public void onLoad() {
if (this.previewItems != null && this.previewItems.length > 0) { if (this.previewItems != null && this.previewItems.length > 0) {
this.previewItemsArray = this.previewItemsArray =
Arrays.stream(this.previewItems) Arrays.stream(this.previewItems)
.filter(d -> d.getId() > 0 && d.getCount() != null && !d.getCount().isEmpty()) .filter(d -> d.getId() > 0 && d.getCount() != null && !d.getCount().isEmpty())
.map(ItemParamStringData::toItemParamData) .map(ItemParamStringData::toItemParamData)
.toArray(size -> new ItemParamData[size]); .toArray(size -> new ItemParamData[size]);
} else { } else {
this.previewItemsArray = new ItemParamData[0]; this.previewItemsArray = new ItemParamData[0];
} }
} }
} }

View File

@ -1,112 +1,112 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.game.shop.ShopInfo; import emu.grasscutter.game.shop.ShopInfo;
import java.util.List; import java.util.List;
@ResourceType(name = "ShopGoodsExcelConfigData.json") @ResourceType(name = "ShopGoodsExcelConfigData.json")
public class ShopGoodsData extends GameResource { public class ShopGoodsData extends GameResource {
private int goodsId; private int goodsId;
private int shopType; private int shopType;
private int itemId; private int itemId;
private int itemCount; private int itemCount;
private int costScoin; private int costScoin;
private int costHcoin; private int costHcoin;
private int costMcoin; private int costMcoin;
private List<ItemParamData> costItems; private List<ItemParamData> costItems;
private int minPlayerLevel; private int minPlayerLevel;
private int maxPlayerLevel; private int maxPlayerLevel;
private int buyLimit; private int buyLimit;
@SerializedName( @SerializedName(
value = "subTabId", value = "subTabId",
alternate = {"secondarySheetId"}) alternate = {"secondarySheetId"})
private int subTabId; private int subTabId;
private String refreshType; private String refreshType;
private transient ShopInfo.ShopRefreshType refreshTypeEnum; private transient ShopInfo.ShopRefreshType refreshTypeEnum;
private int refreshParam; private int refreshParam;
@Override @Override
public void onLoad() { public void onLoad() {
if (this.refreshType == null) this.refreshTypeEnum = ShopInfo.ShopRefreshType.NONE; if (this.refreshType == null) this.refreshTypeEnum = ShopInfo.ShopRefreshType.NONE;
else { else {
this.refreshTypeEnum = this.refreshTypeEnum =
switch (this.refreshType) { switch (this.refreshType) {
case "SHOP_REFRESH_DAILY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_DAILY; case "SHOP_REFRESH_DAILY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_DAILY;
case "SHOP_REFRESH_WEEKLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_WEEKLY; case "SHOP_REFRESH_WEEKLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_WEEKLY;
case "SHOP_REFRESH_MONTHLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_MONTHLY; case "SHOP_REFRESH_MONTHLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_MONTHLY;
default -> ShopInfo.ShopRefreshType.NONE; default -> ShopInfo.ShopRefreshType.NONE;
}; };
} }
} }
@Override @Override
public int getId() { public int getId() {
return getGoodsId(); return getGoodsId();
} }
public int getGoodsId() { public int getGoodsId() {
return goodsId; return goodsId;
} }
public int getShopType() { public int getShopType() {
return shopType; return shopType;
} }
public int getItemId() { public int getItemId() {
return itemId; return itemId;
} }
public int getItemCount() { public int getItemCount() {
return itemCount; return itemCount;
} }
public int getCostScoin() { public int getCostScoin() {
return costScoin; return costScoin;
} }
public int getCostHcoin() { public int getCostHcoin() {
return costHcoin; return costHcoin;
} }
public int getCostMcoin() { public int getCostMcoin() {
return costMcoin; return costMcoin;
} }
public List<ItemParamData> getCostItems() { public List<ItemParamData> getCostItems() {
return costItems; return costItems;
} }
public int getMinPlayerLevel() { public int getMinPlayerLevel() {
return minPlayerLevel; return minPlayerLevel;
} }
public int getMaxPlayerLevel() { public int getMaxPlayerLevel() {
return maxPlayerLevel; return maxPlayerLevel;
} }
public int getBuyLimit() { public int getBuyLimit() {
return buyLimit; return buyLimit;
} }
public int getSubTabId() { public int getSubTabId() {
return subTabId; return subTabId;
} }
public ShopInfo.ShopRefreshType getRefreshType() { public ShopInfo.ShopRefreshType getRefreshType() {
return refreshTypeEnum; return refreshTypeEnum;
} }
public int getRefreshParam() { public int getRefreshParam() {
return refreshParam; return refreshParam;
} }
} }

View File

@ -1,14 +1,14 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = "TriggerExcelConfigData.json") @ResourceType(name = "TriggerExcelConfigData.json")
@Getter @Getter
public class TriggerExcelConfigData extends GameResource { public class TriggerExcelConfigData extends GameResource {
@Getter private int id; @Getter private int id;
private int sceneId; private int sceneId;
private int groupId; private int groupId;
private String triggerName; private String triggerName;
} }

View File

@ -1,22 +1,22 @@
package emu.grasscutter.database; package emu.grasscutter.database;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id; import dev.morphia.annotations.Id;
@Entity(value = "counters", useDiscriminator = false) @Entity(value = "counters", useDiscriminator = false)
public class DatabaseCounter { public class DatabaseCounter {
@Id private String id; @Id private String id;
private int count; private int count;
public DatabaseCounter() {} public DatabaseCounter() {}
public DatabaseCounter(String id) { public DatabaseCounter(String id) {
this.id = id; this.id = id;
this.count = 10000; this.count = 10000;
} }
public int getNextId() { public int getNextId() {
int id = ++count; int id = ++count;
return id; return id;
} }
} }

View File

@ -1,126 +1,126 @@
package emu.grasscutter.database; package emu.grasscutter.database;
import static emu.grasscutter.config.Configuration.DATABASE; import static emu.grasscutter.config.Configuration.DATABASE;
import static emu.grasscutter.config.Configuration.SERVER; import static emu.grasscutter.config.Configuration.SERVER;
import com.mongodb.MongoCommandException; import com.mongodb.MongoCommandException;
import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients; import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase; import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoIterable; import com.mongodb.client.MongoIterable;
import dev.morphia.Datastore; import dev.morphia.Datastore;
import dev.morphia.Morphia; import dev.morphia.Morphia;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.mapping.Mapper; import dev.morphia.mapping.Mapper;
import dev.morphia.mapping.MapperOptions; import dev.morphia.mapping.MapperOptions;
import dev.morphia.query.experimental.filters.Filters; import dev.morphia.query.experimental.filters.Filters;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerRunMode; import emu.grasscutter.Grasscutter.ServerRunMode;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import org.reflections.Reflections; import org.reflections.Reflections;
public final class DatabaseManager { public final class DatabaseManager {
private static Datastore gameDatastore; private static Datastore gameDatastore;
private static Datastore dispatchDatastore; private static Datastore dispatchDatastore;
public static Datastore getGameDatastore() { public static Datastore getGameDatastore() {
return gameDatastore; return gameDatastore;
} }
public static MongoDatabase getGameDatabase() { public static MongoDatabase getGameDatabase() {
return getGameDatastore().getDatabase(); return getGameDatastore().getDatabase();
} }
// Yes. I very dislike this method. However, this will be good for now. // Yes. I very dislike this method. However, this will be good for now.
// TODO: Add dispatch routes for player account management // TODO: Add dispatch routes for player account management
public static Datastore getAccountDatastore() { public static Datastore getAccountDatastore() {
if (SERVER.runMode == ServerRunMode.GAME_ONLY) { if (SERVER.runMode == ServerRunMode.GAME_ONLY) {
return dispatchDatastore; return dispatchDatastore;
} else { } else {
return gameDatastore; return gameDatastore;
} }
} }
public static void initialize() { public static void initialize() {
// Initialize // Initialize
MongoClient gameMongoClient = MongoClients.create(DATABASE.game.connectionUri); MongoClient gameMongoClient = MongoClients.create(DATABASE.game.connectionUri);
// Set mapper options. // Set mapper options.
MapperOptions mapperOptions = MapperOptions mapperOptions =
MapperOptions.builder().storeEmpties(true).storeNulls(false).build(); MapperOptions.builder().storeEmpties(true).storeNulls(false).build();
// Create data store. // Create data store.
gameDatastore = gameDatastore =
Morphia.createDatastore(gameMongoClient, DATABASE.game.collection, mapperOptions); Morphia.createDatastore(gameMongoClient, DATABASE.game.collection, mapperOptions);
// Map classes. // Map classes.
Class<?>[] entities = Class<?>[] entities =
new Reflections(Grasscutter.class.getPackageName()) new Reflections(Grasscutter.class.getPackageName())
.getTypesAnnotatedWith(Entity.class).stream() .getTypesAnnotatedWith(Entity.class).stream()
.filter( .filter(
cls -> { cls -> {
Entity e = cls.getAnnotation(Entity.class); Entity e = cls.getAnnotation(Entity.class);
return e != null && !e.value().equals(Mapper.IGNORED_FIELDNAME); return e != null && !e.value().equals(Mapper.IGNORED_FIELDNAME);
}) })
.toArray(Class<?>[]::new); .toArray(Class<?>[]::new);
gameDatastore.getMapper().map(entities); gameDatastore.getMapper().map(entities);
// Ensure indexes for the game datastore // Ensure indexes for the game datastore
ensureIndexes(gameDatastore); ensureIndexes(gameDatastore);
if (SERVER.runMode == ServerRunMode.GAME_ONLY) { if (SERVER.runMode == ServerRunMode.GAME_ONLY) {
MongoClient dispatchMongoClient = MongoClients.create(DATABASE.server.connectionUri); MongoClient dispatchMongoClient = MongoClients.create(DATABASE.server.connectionUri);
dispatchDatastore = dispatchDatastore =
Morphia.createDatastore(dispatchMongoClient, DATABASE.server.collection, mapperOptions); Morphia.createDatastore(dispatchMongoClient, DATABASE.server.collection, mapperOptions);
dispatchDatastore.getMapper().map(new Class<?>[] {DatabaseCounter.class, Account.class}); dispatchDatastore.getMapper().map(new Class<?>[] {DatabaseCounter.class, Account.class});
// Ensure indexes for dispatch datastore // Ensure indexes for dispatch datastore
ensureIndexes(dispatchDatastore); ensureIndexes(dispatchDatastore);
} }
} }
/** /**
* Ensures the database indexes exist and rebuilds them if there is an error with them * Ensures the database indexes exist and rebuilds them if there is an error with them
* *
* @param datastore The datastore to ensure indexes on * @param datastore The datastore to ensure indexes on
*/ */
private static void ensureIndexes(Datastore datastore) { private static void ensureIndexes(Datastore datastore) {
try { try {
datastore.ensureIndexes(); datastore.ensureIndexes();
} catch (MongoCommandException e) { } catch (MongoCommandException e) {
Grasscutter.getLogger().info("Mongo index error: ", e); Grasscutter.getLogger().info("Mongo index error: ", e);
// Duplicate index error // Duplicate index error
if (e.getCode() == 85) { if (e.getCode() == 85) {
// Drop all indexes and re add them // Drop all indexes and re add them
MongoIterable<String> collections = datastore.getDatabase().listCollectionNames(); MongoIterable<String> collections = datastore.getDatabase().listCollectionNames();
for (String name : collections) { for (String name : collections) {
datastore.getDatabase().getCollection(name).dropIndexes(); datastore.getDatabase().getCollection(name).dropIndexes();
} }
// Add back indexes // Add back indexes
datastore.ensureIndexes(); datastore.ensureIndexes();
} }
} }
} }
public static synchronized int getNextId(Class<?> c) { public static synchronized int getNextId(Class<?> c) {
DatabaseCounter counter = DatabaseCounter counter =
getGameDatastore() getGameDatastore()
.find(DatabaseCounter.class) .find(DatabaseCounter.class)
.filter(Filters.eq("_id", c.getSimpleName())) .filter(Filters.eq("_id", c.getSimpleName()))
.first(); .first();
if (counter == null) { if (counter == null) {
counter = new DatabaseCounter(c.getSimpleName()); counter = new DatabaseCounter(c.getSimpleName());
} }
try { try {
return counter.getNextId(); return counter.getNextId();
} finally { } finally {
getGameDatastore().save(counter); getGameDatastore().save(counter);
} }
} }
public static synchronized int getNextId(Object o) { public static synchronized int getNextId(Object o) {
return getNextId(o.getClass()); return getNextId(o.getClass());
} }
} }

View File

@ -1,31 +1,31 @@
package emu.grasscutter.game; package emu.grasscutter.game;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
public class CoopRequest { public class CoopRequest {
private final Player requester; private final Player requester;
private final long requestTime; private final long requestTime;
private final long expireTime; private final long expireTime;
public CoopRequest(Player requester) { public CoopRequest(Player requester) {
this.requester = requester; this.requester = requester;
this.requestTime = System.currentTimeMillis(); this.requestTime = System.currentTimeMillis();
this.expireTime = this.requestTime + 10000; this.expireTime = this.requestTime + 10000;
} }
public Player getRequester() { public Player getRequester() {
return requester; return requester;
} }
public long getRequestTime() { public long getRequestTime() {
return requestTime; return requestTime;
} }
public long getExpireTime() { public long getExpireTime() {
return expireTime; return expireTime;
} }
public boolean isExpired() { public boolean isExpired() {
return System.currentTimeMillis() > getExpireTime(); return System.currentTimeMillis() > getExpireTime();
} }
} }

View File

@ -1,47 +1,47 @@
package emu.grasscutter.game.achievement; package emu.grasscutter.game.achievement;
import lombok.Getter; import lombok.Getter;
@Getter @Getter
public class AchievementControlReturns { public class AchievementControlReturns {
private final Return ret; private final Return ret;
private final int changedAchievementStatusNum; private final int changedAchievementStatusNum;
private AchievementControlReturns(Return ret) { private AchievementControlReturns(Return ret) {
this(ret, 0); this(ret, 0);
} }
private AchievementControlReturns(Return ret, int changedAchievementStatusNum) { private AchievementControlReturns(Return ret, int changedAchievementStatusNum) {
this.ret = ret; this.ret = ret;
this.changedAchievementStatusNum = changedAchievementStatusNum; this.changedAchievementStatusNum = changedAchievementStatusNum;
} }
public static AchievementControlReturns success(int changedAchievementStatusNum) { public static AchievementControlReturns success(int changedAchievementStatusNum) {
return new AchievementControlReturns(Return.SUCCESS, changedAchievementStatusNum); return new AchievementControlReturns(Return.SUCCESS, changedAchievementStatusNum);
} }
public static AchievementControlReturns achievementNotFound() { public static AchievementControlReturns achievementNotFound() {
return new AchievementControlReturns(Return.ACHIEVEMENT_NOT_FOUND); return new AchievementControlReturns(Return.ACHIEVEMENT_NOT_FOUND);
} }
public static AchievementControlReturns alreadyAchieved() { public static AchievementControlReturns alreadyAchieved() {
return new AchievementControlReturns(Return.ALREADY_ACHIEVED); return new AchievementControlReturns(Return.ALREADY_ACHIEVED);
} }
public static AchievementControlReturns notYetAchieved() { public static AchievementControlReturns notYetAchieved() {
return new AchievementControlReturns(Return.NOT_YET_ACHIEVED); return new AchievementControlReturns(Return.NOT_YET_ACHIEVED);
} }
public enum Return { public enum Return {
SUCCESS("commands.achievement.success."), SUCCESS("commands.achievement.success."),
ACHIEVEMENT_NOT_FOUND("commands.achievement.fail.achievement_not_found"), ACHIEVEMENT_NOT_FOUND("commands.achievement.fail.achievement_not_found"),
ALREADY_ACHIEVED("commands.achievement.fail.already_achieved"), ALREADY_ACHIEVED("commands.achievement.fail.already_achieved"),
NOT_YET_ACHIEVED("commands.achievement.fail.not_yet_achieved"); NOT_YET_ACHIEVED("commands.achievement.fail.not_yet_achieved");
@Getter private final String key; @Getter private final String key;
Return(String key) { Return(String key) {
this.key = key; this.key = key;
} }
} }
} }

View File

@ -1,13 +1,13 @@
package emu.grasscutter.game.activity; package emu.grasscutter.game.activity;
import emu.grasscutter.game.props.WatcherTriggerType; import emu.grasscutter.game.props.WatcherTriggerType;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface ActivityWatcherType { public @interface ActivityWatcherType {
WatcherTriggerType value(); WatcherTriggerType value();
} }

View File

@ -1,15 +1,15 @@
package emu.grasscutter.game.activity; package emu.grasscutter.game.activity;
import emu.grasscutter.game.props.ActivityType; import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.net.proto.ActivityInfoOuterClass; import emu.grasscutter.net.proto.ActivityInfoOuterClass;
@GameActivity(ActivityType.NONE) @GameActivity(ActivityType.NONE)
public class DefaultActivityHandler extends ActivityHandler { public class DefaultActivityHandler extends ActivityHandler {
@Override @Override
public void onProtoBuild( public void onProtoBuild(
PlayerActivityData playerActivityData, PlayerActivityData playerActivityData,
ActivityInfoOuterClass.ActivityInfo.Builder activityInfo) {} ActivityInfoOuterClass.ActivityInfo.Builder activityInfo) {}
@Override @Override
public void onInitPlayerActivityData(PlayerActivityData playerActivityData) {} public void onInitPlayerActivityData(PlayerActivityData playerActivityData) {}
} }

View File

@ -1,11 +1,11 @@
package emu.grasscutter.game.activity; package emu.grasscutter.game.activity;
import emu.grasscutter.game.props.WatcherTriggerType; import emu.grasscutter.game.props.WatcherTriggerType;
@ActivityWatcherType(WatcherTriggerType.TRIGGER_NONE) @ActivityWatcherType(WatcherTriggerType.TRIGGER_NONE)
public class DefaultWatcher extends ActivityWatcher { public class DefaultWatcher extends ActivityWatcher {
@Override @Override
protected boolean isMeet(String... param) { protected boolean isMeet(String... param) {
return false; return false;
} }
} }

View File

@ -1,13 +1,13 @@
package emu.grasscutter.game.activity; package emu.grasscutter.game.activity;
import emu.grasscutter.game.props.ActivityType; import emu.grasscutter.game.props.ActivityType;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface GameActivity { public @interface GameActivity {
ActivityType value(); ActivityType value();
} }

View File

@ -1,105 +1,105 @@
package emu.grasscutter.game.activity.musicgame; package emu.grasscutter.game.activity.musicgame;
import emu.grasscutter.game.activity.ActivityHandler; import emu.grasscutter.game.activity.ActivityHandler;
import emu.grasscutter.game.activity.GameActivity; import emu.grasscutter.game.activity.GameActivity;
import emu.grasscutter.game.activity.PlayerActivityData; import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.props.ActivityType; import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.net.proto.ActivityInfoOuterClass; import emu.grasscutter.net.proto.ActivityInfoOuterClass;
import emu.grasscutter.net.proto.MusicGameActivityDetailInfoOuterClass; import emu.grasscutter.net.proto.MusicGameActivityDetailInfoOuterClass;
import emu.grasscutter.utils.JsonUtils; import emu.grasscutter.utils.JsonUtils;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@GameActivity(ActivityType.NEW_ACTIVITY_MUSIC_GAME) @GameActivity(ActivityType.NEW_ACTIVITY_MUSIC_GAME)
public class MusicGameActivityHandler extends ActivityHandler { public class MusicGameActivityHandler extends ActivityHandler {
@Override @Override
public void onInitPlayerActivityData(PlayerActivityData playerActivityData) { public void onInitPlayerActivityData(PlayerActivityData playerActivityData) {
var musicGamePlayerData = MusicGamePlayerData.create(); var musicGamePlayerData = MusicGamePlayerData.create();
playerActivityData.setDetail(musicGamePlayerData); playerActivityData.setDetail(musicGamePlayerData);
} }
@Override @Override
public void onProtoBuild( public void onProtoBuild(
PlayerActivityData playerActivityData, PlayerActivityData playerActivityData,
ActivityInfoOuterClass.ActivityInfo.Builder activityInfo) { ActivityInfoOuterClass.ActivityInfo.Builder activityInfo) {
MusicGamePlayerData musicGamePlayerData = getMusicGamePlayerData(playerActivityData); MusicGamePlayerData musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
activityInfo.setMusicGameInfo( activityInfo.setMusicGameInfo(
MusicGameActivityDetailInfoOuterClass.MusicGameActivityDetailInfo.newBuilder() MusicGameActivityDetailInfoOuterClass.MusicGameActivityDetailInfo.newBuilder()
.putAllMusicGameRecordMap( .putAllMusicGameRecordMap(
musicGamePlayerData.getMusicGameRecord().values().stream() musicGamePlayerData.getMusicGameRecord().values().stream()
.collect( .collect(
Collectors.toMap( Collectors.toMap(
MusicGamePlayerData.MusicGameRecord::getMusicId, MusicGamePlayerData.MusicGameRecord::getMusicId,
MusicGamePlayerData.MusicGameRecord::toProto))) MusicGamePlayerData.MusicGameRecord::toProto)))
// //
// .addAllPersonCustomBeatmap(musicGamePlayerData.getPersonalCustomBeatmapRecord().values().stream() // .addAllPersonCustomBeatmap(musicGamePlayerData.getPersonalCustomBeatmapRecord().values().stream()
// .map(MusicGamePlayerData.CustomBeatmapRecord::toPersonalBriefProto) // .map(MusicGamePlayerData.CustomBeatmapRecord::toPersonalBriefProto)
// .map(UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder::build) // .map(UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder::build)
// .toList()) // .toList())
// //
// //
// .addAllOthersCustomBeatmap(musicGamePlayerData.getOthersCustomBeatmapRecord().values().stream() // .addAllOthersCustomBeatmap(musicGamePlayerData.getOthersCustomBeatmapRecord().values().stream()
// .map(MusicGamePlayerData.CustomBeatmapRecord::toOthersBriefProto) // .map(MusicGamePlayerData.CustomBeatmapRecord::toOthersBriefProto)
// .map(UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder::build) // .map(UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder::build)
// .toList()) // .toList())
.build()); .build());
} }
public MusicGamePlayerData getMusicGamePlayerData(PlayerActivityData playerActivityData) { public MusicGamePlayerData getMusicGamePlayerData(PlayerActivityData playerActivityData) {
if (playerActivityData.getDetail() == null || playerActivityData.getDetail().isBlank()) { if (playerActivityData.getDetail() == null || playerActivityData.getDetail().isBlank()) {
onInitPlayerActivityData(playerActivityData); onInitPlayerActivityData(playerActivityData);
playerActivityData.save(); playerActivityData.save();
} }
return JsonUtils.decode(playerActivityData.getDetail(), MusicGamePlayerData.class); return JsonUtils.decode(playerActivityData.getDetail(), MusicGamePlayerData.class);
} }
public boolean setMusicGameRecord( public boolean setMusicGameRecord(
PlayerActivityData playerActivityData, MusicGamePlayerData.MusicGameRecord newRecord) { PlayerActivityData playerActivityData, MusicGamePlayerData.MusicGameRecord newRecord) {
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData); var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
var saveRecord = musicGamePlayerData.getMusicGameRecord().get(newRecord.getMusicId()); var saveRecord = musicGamePlayerData.getMusicGameRecord().get(newRecord.getMusicId());
saveRecord.setMaxCombo(Math.max(newRecord.getMaxCombo(), saveRecord.getMaxCombo())); saveRecord.setMaxCombo(Math.max(newRecord.getMaxCombo(), saveRecord.getMaxCombo()));
saveRecord.setMaxScore(Math.max(newRecord.getMaxScore(), saveRecord.getMaxScore())); saveRecord.setMaxScore(Math.max(newRecord.getMaxScore(), saveRecord.getMaxScore()));
playerActivityData.setDetail(musicGamePlayerData); playerActivityData.setDetail(musicGamePlayerData);
playerActivityData.save(); playerActivityData.save();
return newRecord.getMaxScore() > saveRecord.getMaxScore(); return newRecord.getMaxScore() > saveRecord.getMaxScore();
} }
public void setMusicGameCustomBeatmapRecord( public void setMusicGameCustomBeatmapRecord(
PlayerActivityData playerActivityData, MusicGamePlayerData.CustomBeatmapRecord newRecord) { PlayerActivityData playerActivityData, MusicGamePlayerData.CustomBeatmapRecord newRecord) {
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData); var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
musicGamePlayerData.getOthersCustomBeatmapRecord().put(newRecord.getMusicShareId(), newRecord); musicGamePlayerData.getOthersCustomBeatmapRecord().put(newRecord.getMusicShareId(), newRecord);
playerActivityData.setDetail(musicGamePlayerData); playerActivityData.setDetail(musicGamePlayerData);
playerActivityData.save(); playerActivityData.save();
} }
public void addPersonalBeatmap( public void addPersonalBeatmap(
PlayerActivityData playerActivityData, MusicGameBeatmap musicGameBeatmap) { PlayerActivityData playerActivityData, MusicGameBeatmap musicGameBeatmap) {
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData); var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
musicGamePlayerData musicGamePlayerData
.getPersonalCustomBeatmapRecord() .getPersonalCustomBeatmapRecord()
.put( .put(
musicGameBeatmap.getMusicShareId(), musicGameBeatmap.getMusicShareId(),
MusicGamePlayerData.CustomBeatmapRecord.of() MusicGamePlayerData.CustomBeatmapRecord.of()
.musicShareId(musicGameBeatmap.getMusicShareId()) .musicShareId(musicGameBeatmap.getMusicShareId())
.build()); .build());
playerActivityData.setDetail(musicGamePlayerData); playerActivityData.setDetail(musicGamePlayerData);
playerActivityData.save(); playerActivityData.save();
} }
public void removePersonalBeatmap( public void removePersonalBeatmap(
PlayerActivityData playerActivityData, MusicGameBeatmap musicGameBeatmap) { PlayerActivityData playerActivityData, MusicGameBeatmap musicGameBeatmap) {
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData); var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
musicGamePlayerData.getPersonalCustomBeatmapRecord().remove(musicGameBeatmap.getMusicShareId()); musicGamePlayerData.getPersonalCustomBeatmapRecord().remove(musicGameBeatmap.getMusicShareId());
playerActivityData.setDetail(musicGamePlayerData); playerActivityData.setDetail(musicGamePlayerData);
playerActivityData.save(); playerActivityData.save();
} }
} }

View File

@ -1,97 +1,97 @@
package emu.grasscutter.game.activity.musicgame; package emu.grasscutter.game.activity.musicgame;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id; import dev.morphia.annotations.Id;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.net.proto.UgcMusicBriefInfoOuterClass; import emu.grasscutter.net.proto.UgcMusicBriefInfoOuterClass;
import emu.grasscutter.net.proto.UgcMusicNoteOuterClass; import emu.grasscutter.net.proto.UgcMusicNoteOuterClass;
import emu.grasscutter.net.proto.UgcMusicRecordOuterClass; import emu.grasscutter.net.proto.UgcMusicRecordOuterClass;
import emu.grasscutter.net.proto.UgcMusicTrackOuterClass; import emu.grasscutter.net.proto.UgcMusicTrackOuterClass;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@Entity("music_game_beatmaps") @Entity("music_game_beatmaps")
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of") @Builder(builderMethodName = "of")
public class MusicGameBeatmap { public class MusicGameBeatmap {
@Id long musicShareId; @Id long musicShareId;
int authorUid; int authorUid;
int musicId; int musicId;
int musicNoteCount; int musicNoteCount;
int savePosition; int savePosition;
int maxScore; int maxScore;
int createTime; int createTime;
List<List<BeatmapNote>> beatmap; List<List<BeatmapNote>> beatmap;
public static MusicGameBeatmap getByShareId(long musicShareId) { public static MusicGameBeatmap getByShareId(long musicShareId) {
return DatabaseHelper.getMusicGameBeatmap(musicShareId); return DatabaseHelper.getMusicGameBeatmap(musicShareId);
} }
public static List<List<BeatmapNote>> parse( public static List<List<BeatmapNote>> parse(
List<UgcMusicTrackOuterClass.UgcMusicTrack> beatmapItemListList) { List<UgcMusicTrackOuterClass.UgcMusicTrack> beatmapItemListList) {
return beatmapItemListList.stream() return beatmapItemListList.stream()
.map(item -> item.getMusicNoteListList().stream().map(BeatmapNote::parse).toList()) .map(item -> item.getMusicNoteListList().stream().map(BeatmapNote::parse).toList())
.toList(); .toList();
} }
public void save() { public void save() {
if (musicShareId == 0) { if (musicShareId == 0) {
musicShareId = new Random().nextLong(100000000000000L, 999999999999999L); musicShareId = new Random().nextLong(100000000000000L, 999999999999999L);
} }
DatabaseHelper.saveMusicGameBeatmap(this); DatabaseHelper.saveMusicGameBeatmap(this);
} }
public UgcMusicRecordOuterClass.UgcMusicRecord toProto() { public UgcMusicRecordOuterClass.UgcMusicRecord toProto() {
return UgcMusicRecordOuterClass.UgcMusicRecord.newBuilder() return UgcMusicRecordOuterClass.UgcMusicRecord.newBuilder()
.setMusicId(musicId) .setMusicId(musicId)
.addAllMusicTrackList(beatmap.stream().map(this::musicBeatmapListToProto).toList()) .addAllMusicTrackList(beatmap.stream().map(this::musicBeatmapListToProto).toList())
.build(); .build();
} }
public UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder toBriefProto() { public UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder toBriefProto() {
var player = DatabaseHelper.getPlayerByUid(authorUid); var player = DatabaseHelper.getPlayerByUid(authorUid);
return UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.newBuilder() return UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.newBuilder()
.setMusicId(musicId) .setMusicId(musicId)
// .setMusicNoteCount(musicNoteCount) // .setMusicNoteCount(musicNoteCount)
.setUgcGuid(musicShareId) .setUgcGuid(musicShareId)
.setMaxScore(maxScore) .setMaxScore(maxScore)
// .setShareTime(createTime) // .setShareTime(createTime)
.setCreatorNickname(player.getNickname()) .setCreatorNickname(player.getNickname())
.setVersion(1); .setVersion(1);
} }
private UgcMusicTrackOuterClass.UgcMusicTrack musicBeatmapListToProto( private UgcMusicTrackOuterClass.UgcMusicTrack musicBeatmapListToProto(
List<BeatmapNote> beatmapNoteList) { List<BeatmapNote> beatmapNoteList) {
return UgcMusicTrackOuterClass.UgcMusicTrack.newBuilder() return UgcMusicTrackOuterClass.UgcMusicTrack.newBuilder()
.addAllMusicNoteList(beatmapNoteList.stream().map(BeatmapNote::toProto).toList()) .addAllMusicNoteList(beatmapNoteList.stream().map(BeatmapNote::toProto).toList())
.build(); .build();
} }
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of") @Builder(builderMethodName = "of")
@Entity @Entity
public static class BeatmapNote { public static class BeatmapNote {
int startTime; int startTime;
int endTime; int endTime;
public static BeatmapNote parse(UgcMusicNoteOuterClass.UgcMusicNote note) { public static BeatmapNote parse(UgcMusicNoteOuterClass.UgcMusicNote note) {
return BeatmapNote.of().startTime(note.getStartTime()).endTime(note.getEndTime()).build(); return BeatmapNote.of().startTime(note.getStartTime()).endTime(note.getEndTime()).build();
} }
public UgcMusicNoteOuterClass.UgcMusicNote toProto() { public UgcMusicNoteOuterClass.UgcMusicNote toProto() {
return UgcMusicNoteOuterClass.UgcMusicNote.newBuilder() return UgcMusicNoteOuterClass.UgcMusicNote.newBuilder()
.setStartTime(startTime) .setStartTime(startTime)
.setEndTime(endTime) .setEndTime(endTime)
.build(); .build();
} }
} }
} }

View File

@ -1,86 +1,86 @@
package emu.grasscutter.game.activity.musicgame; package emu.grasscutter.game.activity.musicgame;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.MusicGameBasicData; import emu.grasscutter.data.excels.MusicGameBasicData;
import emu.grasscutter.net.proto.MusicGameRecordOuterClass; import emu.grasscutter.net.proto.MusicGameRecordOuterClass;
import emu.grasscutter.net.proto.UgcMusicBriefInfoOuterClass; import emu.grasscutter.net.proto.UgcMusicBriefInfoOuterClass;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of") @Builder(builderMethodName = "of")
public class MusicGamePlayerData { public class MusicGamePlayerData {
Map<Integer, MusicGameRecord> musicGameRecord; Map<Integer, MusicGameRecord> musicGameRecord;
Map<Long, CustomBeatmapRecord> personalCustomBeatmapRecord; Map<Long, CustomBeatmapRecord> personalCustomBeatmapRecord;
Map<Long, CustomBeatmapRecord> othersCustomBeatmapRecord; Map<Long, CustomBeatmapRecord> othersCustomBeatmapRecord;
public static MusicGamePlayerData create() { public static MusicGamePlayerData create() {
return MusicGamePlayerData.of() return MusicGamePlayerData.of()
.musicGameRecord( .musicGameRecord(
GameData.getMusicGameBasicDataMap().values().stream() GameData.getMusicGameBasicDataMap().values().stream()
.collect( .collect(
Collectors.toMap( Collectors.toMap(
MusicGameBasicData::getId, MusicGamePlayerData.MusicGameRecord::create))) MusicGameBasicData::getId, MusicGamePlayerData.MusicGameRecord::create)))
.personalCustomBeatmapRecord(new HashMap<>()) .personalCustomBeatmapRecord(new HashMap<>())
.othersCustomBeatmapRecord(new HashMap<>()) .othersCustomBeatmapRecord(new HashMap<>())
.build(); .build();
} }
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of") @Builder(builderMethodName = "of")
public static class MusicGameRecord { public static class MusicGameRecord {
int musicId; int musicId;
int maxCombo; int maxCombo;
int maxScore; int maxScore;
public static MusicGameRecord create(MusicGameBasicData musicGameBasicData) { public static MusicGameRecord create(MusicGameBasicData musicGameBasicData) {
return MusicGameRecord.of().musicId(musicGameBasicData.getId()).build(); return MusicGameRecord.of().musicId(musicGameBasicData.getId()).build();
} }
public MusicGameRecordOuterClass.MusicGameRecord toProto() { public MusicGameRecordOuterClass.MusicGameRecord toProto() {
return MusicGameRecordOuterClass.MusicGameRecord.newBuilder() return MusicGameRecordOuterClass.MusicGameRecord.newBuilder()
.setIsUnlock(true) .setIsUnlock(true)
.setMaxCombo(maxCombo) .setMaxCombo(maxCombo)
.setMaxScore(maxScore) .setMaxScore(maxScore)
.build(); .build();
} }
} }
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of") @Builder(builderMethodName = "of")
public static class CustomBeatmapRecord { public static class CustomBeatmapRecord {
long musicShareId; long musicShareId;
int score; int score;
boolean settle; boolean settle;
public UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder toPersonalBriefProto() { public UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder toPersonalBriefProto() {
var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId); var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId);
return UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.newBuilder() return UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.newBuilder()
// .setCanShare(true) // .setCanShare(true)
// .setCreateTime(musicGameBeatmap.getCreateTime()) // .setCreateTime(musicGameBeatmap.getCreateTime())
.setMusicId(musicGameBeatmap.getMusicId()) .setMusicId(musicGameBeatmap.getMusicId())
.setMaxScore(musicGameBeatmap.getMaxScore()) .setMaxScore(musicGameBeatmap.getMaxScore())
// .setPosition(musicGameBeatmap.getSavePosition()) // .setPosition(musicGameBeatmap.getSavePosition())
// .setMusicNoteCount(musicGameBeatmap.getMusicNoteCount()) // .setMusicNoteCount(musicGameBeatmap.getMusicNoteCount())
.setUgcGuid(musicShareId); .setUgcGuid(musicShareId);
} }
public UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder toOthersBriefProto() { public UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder toOthersBriefProto() {
var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId); var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId);
return musicGameBeatmap.toBriefProto() return musicGameBeatmap.toBriefProto()
// .setScore(score) // .setScore(score)
// .setSettle(settle) // .setSettle(settle)
; ;
} }
} }
} }

View File

@ -1,23 +1,23 @@
package emu.grasscutter.game.activity.musicgame; package emu.grasscutter.game.activity.musicgame;
import emu.grasscutter.game.activity.ActivityWatcher; import emu.grasscutter.game.activity.ActivityWatcher;
import emu.grasscutter.game.activity.ActivityWatcherType; import emu.grasscutter.game.activity.ActivityWatcherType;
import emu.grasscutter.game.props.WatcherTriggerType; import emu.grasscutter.game.props.WatcherTriggerType;
@ActivityWatcherType(WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE) @ActivityWatcherType(WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE)
public class MusicGameScoreTrigger extends ActivityWatcher { public class MusicGameScoreTrigger extends ActivityWatcher {
@Override @Override
protected boolean isMeet(String... param) { protected boolean isMeet(String... param) {
if (param.length != 2) { if (param.length != 2) {
return false; return false;
} }
var paramList = getActivityWatcherData().getTriggerConfig().getParamList(); var paramList = getActivityWatcherData().getTriggerConfig().getParamList();
if (!paramList.get(0).equals(param[0])) { if (!paramList.get(0).equals(param[0])) {
return false; return false;
} }
var score = Integer.parseInt(param[1]); var score = Integer.parseInt(param[1]);
var target = Integer.parseInt(paramList.get(1)); var target = Integer.parseInt(paramList.get(1));
return score >= target; return score >= target;
} }
} }

View File

@ -1,415 +1,415 @@
package emu.grasscutter.game.battlepass; package emu.grasscutter.game.battlepass;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id; import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed; import dev.morphia.annotations.Indexed;
import emu.grasscutter.GameConstants; import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
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.excels.BattlePassRewardData; import emu.grasscutter.data.excels.BattlePassRewardData;
import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.RewardData; import emu.grasscutter.data.excels.RewardData;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.MaterialType; import emu.grasscutter.game.inventory.MaterialType;
import emu.grasscutter.game.player.BasePlayerDataManager; import emu.grasscutter.game.player.BasePlayerDataManager;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.BattlePassMissionRefreshType; import emu.grasscutter.game.props.BattlePassMissionRefreshType;
import emu.grasscutter.game.props.BattlePassMissionStatus; import emu.grasscutter.game.props.BattlePassMissionStatus;
import emu.grasscutter.game.props.ItemUseOp; import emu.grasscutter.game.props.ItemUseOp;
import emu.grasscutter.game.props.WatcherTriggerType; import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.BattlePassCycleOuterClass.BattlePassCycle; import emu.grasscutter.net.proto.BattlePassCycleOuterClass.BattlePassCycle;
import emu.grasscutter.net.proto.BattlePassRewardTakeOptionOuterClass.BattlePassRewardTakeOption; import emu.grasscutter.net.proto.BattlePassRewardTakeOptionOuterClass.BattlePassRewardTakeOption;
import emu.grasscutter.net.proto.BattlePassScheduleOuterClass.BattlePassSchedule; import emu.grasscutter.net.proto.BattlePassScheduleOuterClass.BattlePassSchedule;
import emu.grasscutter.net.proto.BattlePassUnlockStatusOuterClass.BattlePassUnlockStatus; import emu.grasscutter.net.proto.BattlePassUnlockStatusOuterClass.BattlePassUnlockStatus;
import emu.grasscutter.server.packet.send.PacketBattlePassCurScheduleUpdateNotify; import emu.grasscutter.server.packet.send.PacketBattlePassCurScheduleUpdateNotify;
import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify; import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify;
import emu.grasscutter.server.packet.send.PacketTakeBattlePassRewardRsp; import emu.grasscutter.server.packet.send.PacketTakeBattlePassRewardRsp;
import java.time.DayOfWeek; import java.time.DayOfWeek;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import lombok.Getter; import lombok.Getter;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
@Entity(value = "battlepass", useDiscriminator = false) @Entity(value = "battlepass", useDiscriminator = false)
public class BattlePassManager extends BasePlayerDataManager { public class BattlePassManager extends BasePlayerDataManager {
@Id @Getter private ObjectId id; @Id @Getter private ObjectId id;
@Indexed private int ownerUid; @Indexed private int ownerUid;
@Getter private int point; @Getter private int point;
@Getter private int cyclePoints; // Weekly maximum cap @Getter private int cyclePoints; // Weekly maximum cap
@Getter private int level; @Getter private int level;
@Getter private boolean viewed; @Getter private boolean viewed;
private boolean paid; private boolean paid;
private Map<Integer, BattlePassMission> missions; private Map<Integer, BattlePassMission> missions;
private Map<Integer, BattlePassReward> takenRewards; private Map<Integer, BattlePassReward> takenRewards;
@Deprecated // Morphia only @Deprecated // Morphia only
public BattlePassManager() {} public BattlePassManager() {}
public BattlePassManager(Player player) { public BattlePassManager(Player player) {
super(player); super(player);
this.ownerUid = player.getUid(); this.ownerUid = player.getUid();
} }
public void setPlayer(Player player) { public void setPlayer(Player player) {
this.player = player; this.player = player;
this.ownerUid = player.getUid(); this.ownerUid = player.getUid();
} }
public void updateViewed() { public void updateViewed() {
this.viewed = true; this.viewed = true;
} }
public boolean setLevel(int level) { public boolean setLevel(int level) {
if (level >= 0 && level <= GameConstants.BATTLE_PASS_MAX_LEVEL) { if (level >= 0 && level <= GameConstants.BATTLE_PASS_MAX_LEVEL) {
this.level = level; this.level = level;
this.point = 0; this.point = 0;
this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.player)); this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.player));
return true; return true;
} }
return false; return false;
} }
public void addPoints(int points) { public void addPoints(int points) {
this.addPointsDirectly(points, false); this.addPointsDirectly(points, false);
this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(player)); this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(player));
this.save(); this.save();
} }
public void addPointsDirectly(int points, boolean isWeekly) { public void addPointsDirectly(int points, boolean isWeekly) {
int amount = points; int amount = points;
if (isWeekly) { if (isWeekly) {
amount = Math.min(amount, GameConstants.BATTLE_PASS_POINT_PER_WEEK - this.cyclePoints); amount = Math.min(amount, GameConstants.BATTLE_PASS_POINT_PER_WEEK - this.cyclePoints);
} }
if (amount <= 0) { if (amount <= 0) {
return; return;
} }
this.point += amount; this.point += amount;
this.cyclePoints += amount; this.cyclePoints += amount;
if (this.point >= GameConstants.BATTLE_PASS_POINT_PER_LEVEL if (this.point >= GameConstants.BATTLE_PASS_POINT_PER_LEVEL
&& this.getLevel() < GameConstants.BATTLE_PASS_MAX_LEVEL) { && this.getLevel() < GameConstants.BATTLE_PASS_MAX_LEVEL) {
int levelups = Math.floorDiv(this.point, GameConstants.BATTLE_PASS_POINT_PER_LEVEL); int levelups = Math.floorDiv(this.point, GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
// Make sure player cant go above max BP level // Make sure player cant go above max BP level
levelups = Math.min(levelups, GameConstants.BATTLE_PASS_MAX_LEVEL - levelups); levelups = Math.min(levelups, GameConstants.BATTLE_PASS_MAX_LEVEL - levelups);
// Set new points after level up // Set new points after level up
this.point = this.point - (levelups * GameConstants.BATTLE_PASS_POINT_PER_LEVEL); this.point = this.point - (levelups * GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
this.level += levelups; this.level += levelups;
} }
} }
public Map<Integer, BattlePassMission> getMissions() { public Map<Integer, BattlePassMission> getMissions() {
if (this.missions == null) this.missions = new HashMap<>(); if (this.missions == null) this.missions = new HashMap<>();
return this.missions; return this.missions;
} }
// Will return a new empty mission if the mission id is not found // Will return a new empty mission if the mission id is not found
public BattlePassMission loadMissionById(int id) { public BattlePassMission loadMissionById(int id) {
return getMissions().computeIfAbsent(id, i -> new BattlePassMission(i)); return getMissions().computeIfAbsent(id, i -> new BattlePassMission(i));
} }
public boolean hasMission(int id) { public boolean hasMission(int id) {
return getMissions().containsKey(id); return getMissions().containsKey(id);
} }
public boolean isPaid() { public boolean isPaid() {
// ToDo: Change this when we actually support unlocking "paid" BP. // ToDo: Change this when we actually support unlocking "paid" BP.
return true; return true;
} }
public Map<Integer, BattlePassReward> getTakenRewards() { public Map<Integer, BattlePassReward> getTakenRewards() {
if (this.takenRewards == null) this.takenRewards = new HashMap<>(); if (this.takenRewards == null) this.takenRewards = new HashMap<>();
return this.takenRewards; return this.takenRewards;
} }
// Mission triggers // Mission triggers
public void triggerMission(WatcherTriggerType triggerType) { public void triggerMission(WatcherTriggerType triggerType) {
getPlayer().getServer().getBattlePassSystem().triggerMission(getPlayer(), triggerType); getPlayer().getServer().getBattlePassSystem().triggerMission(getPlayer(), triggerType);
} }
public void triggerMission(WatcherTriggerType triggerType, int param, int progress) { public void triggerMission(WatcherTriggerType triggerType, int param, int progress) {
getPlayer() getPlayer()
.getServer() .getServer()
.getBattlePassSystem() .getBattlePassSystem()
.triggerMission(getPlayer(), triggerType, param, progress); .triggerMission(getPlayer(), triggerType, param, progress);
} }
// Handlers // Handlers
public void takeMissionPoint(List<Integer> missionIdList) { public void takeMissionPoint(List<Integer> missionIdList) {
// Obvious exploit check // Obvious exploit check
if (missionIdList.size() > GameData.getBattlePassMissionDataMap().size()) { if (missionIdList.size() > GameData.getBattlePassMissionDataMap().size()) {
return; return;
} }
List<BattlePassMission> updatedMissions = new ArrayList<>(missionIdList.size()); List<BattlePassMission> updatedMissions = new ArrayList<>(missionIdList.size());
for (int id : missionIdList) { for (int id : missionIdList) {
// Skip if we dont have this mission // Skip if we dont have this mission
if (!this.hasMission(id)) { if (!this.hasMission(id)) {
continue; continue;
} }
BattlePassMission mission = this.loadMissionById(id); BattlePassMission mission = this.loadMissionById(id);
if (mission.getData() == null) { if (mission.getData() == null) {
this.getMissions().remove(mission.getId()); this.getMissions().remove(mission.getId());
continue; continue;
} }
// Take reward // Take reward
if (mission.getStatus() == BattlePassMissionStatus.MISSION_STATUS_FINISHED) { if (mission.getStatus() == BattlePassMissionStatus.MISSION_STATUS_FINISHED) {
this.addPointsDirectly(mission.getData().getAddPoint(), mission.getData().isCycleRefresh()); this.addPointsDirectly(mission.getData().getAddPoint(), mission.getData().isCycleRefresh());
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_POINT_TAKEN); mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_POINT_TAKEN);
updatedMissions.add(mission); updatedMissions.add(mission);
} }
} }
if (updatedMissions.size() > 0) { if (updatedMissions.size() > 0) {
// Save to db // Save to db
this.save(); this.save();
// Packet // Packet
getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(updatedMissions)); getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(updatedMissions));
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer())); getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
} }
} }
private void takeRewardsFromSelectChest( private void takeRewardsFromSelectChest(
ItemData rewardItemData, int index, ItemParamData entry, List<GameItem> rewardItems) { ItemData rewardItemData, int index, ItemParamData entry, List<GameItem> rewardItems) {
// Sanity checks. // Sanity checks.
if (rewardItemData.getItemUse().size() < 1) { if (rewardItemData.getItemUse().size() < 1) {
return; return;
} }
// Get possible item choices. // Get possible item choices.
String[] choices = rewardItemData.getItemUse().get(0).getUseParam()[0].split(","); String[] choices = rewardItemData.getItemUse().get(0).getUseParam()[0].split(",");
if (choices.length < index) { if (choices.length < index) {
return; return;
} }
// Get data for the selected item. // Get data for the selected item.
// This depends on the type of chest. // This depends on the type of chest.
int chosenId = Integer.parseInt(choices[index - 1]); int chosenId = Integer.parseInt(choices[index - 1]);
// For ITEM_USE_ADD_SELECT_ITEM chests, we can directly add the item specified in the chest's // For ITEM_USE_ADD_SELECT_ITEM chests, we can directly add the item specified in the chest's
// data. // data.
if (rewardItemData.getItemUse().get(0).getUseOp() == ItemUseOp.ITEM_USE_ADD_SELECT_ITEM) { if (rewardItemData.getItemUse().get(0).getUseOp() == ItemUseOp.ITEM_USE_ADD_SELECT_ITEM) {
GameItem rewardItem = GameItem rewardItem =
new GameItem(GameData.getItemDataMap().get(chosenId), entry.getItemCount()); new GameItem(GameData.getItemDataMap().get(chosenId), entry.getItemCount());
rewardItems.add(rewardItem); rewardItems.add(rewardItem);
} }
// For ITEM_USE_GRANT_SELECT_REWARD chests, we have to again look up reward data. // For ITEM_USE_GRANT_SELECT_REWARD chests, we have to again look up reward data.
else if (rewardItemData.getItemUse().get(0).getUseOp() else if (rewardItemData.getItemUse().get(0).getUseOp()
== ItemUseOp.ITEM_USE_GRANT_SELECT_REWARD) { == ItemUseOp.ITEM_USE_GRANT_SELECT_REWARD) {
RewardData selectedReward = GameData.getRewardDataMap().get(chosenId); RewardData selectedReward = GameData.getRewardDataMap().get(chosenId);
for (var r : selectedReward.getRewardItemList()) { for (var r : selectedReward.getRewardItemList()) {
GameItem rewardItem = GameItem rewardItem =
new GameItem(GameData.getItemDataMap().get(r.getItemId()), r.getItemCount()); new GameItem(GameData.getItemDataMap().get(r.getItemId()), r.getItemCount());
rewardItems.add(rewardItem); rewardItems.add(rewardItem);
} }
} else { } else {
Grasscutter.getLogger().error("Invalid chest type for BP reward."); Grasscutter.getLogger().error("Invalid chest type for BP reward.");
} }
} }
public void takeReward(List<BattlePassRewardTakeOption> takeOptionList) { public void takeReward(List<BattlePassRewardTakeOption> takeOptionList) {
List<BattlePassRewardTakeOption> rewardList = new ArrayList<>(); List<BattlePassRewardTakeOption> rewardList = new ArrayList<>();
for (BattlePassRewardTakeOption option : takeOptionList) { for (BattlePassRewardTakeOption option : takeOptionList) {
// Duplicate check // Duplicate check
if (option.getTag().getRewardId() == 0 if (option.getTag().getRewardId() == 0
|| getTakenRewards().containsKey(option.getTag().getRewardId())) { || getTakenRewards().containsKey(option.getTag().getRewardId())) {
continue; continue;
} }
// Level check // Level check
if (option.getTag().getLevel() > this.getLevel()) { if (option.getTag().getLevel() > this.getLevel()) {
continue; continue;
} }
BattlePassRewardData rewardData = BattlePassRewardData rewardData =
GameData.getBattlePassRewardDataMap() GameData.getBattlePassRewardDataMap()
.get(GameConstants.BATTLE_PASS_CURRENT_INDEX * 100 + option.getTag().getLevel()); .get(GameConstants.BATTLE_PASS_CURRENT_INDEX * 100 + option.getTag().getLevel());
// Sanity check with excel data // Sanity check with excel data
if (rewardData.getFreeRewardIdList().contains(option.getTag().getRewardId())) { if (rewardData.getFreeRewardIdList().contains(option.getTag().getRewardId())) {
rewardList.add(option); rewardList.add(option);
} else if (this.isPaid() } else if (this.isPaid()
&& rewardData.getPaidRewardIdList().contains(option.getTag().getRewardId())) { && rewardData.getPaidRewardIdList().contains(option.getTag().getRewardId())) {
rewardList.add(option); rewardList.add(option);
} else { } else {
Grasscutter.getLogger().info("Not in rewards list: {}", option.getTag().getRewardId()); Grasscutter.getLogger().info("Not in rewards list: {}", option.getTag().getRewardId());
} }
} }
// Get rewards // Get rewards
List<GameItem> rewardItems = null; List<GameItem> rewardItems = null;
if (rewardList.size() > 0) { if (rewardList.size() > 0) {
rewardItems = new ArrayList<>(); rewardItems = new ArrayList<>();
for (var option : rewardList) { for (var option : rewardList) {
var tag = option.getTag(); var tag = option.getTag();
int index = option.getOptionIdx(); int index = option.getOptionIdx();
// Make sure we have reward data. // Make sure we have reward data.
RewardData reward = GameData.getRewardDataMap().get(tag.getRewardId()); RewardData reward = GameData.getRewardDataMap().get(tag.getRewardId());
if (reward == null) { if (reward == null) {
continue; continue;
} }
// Add reward items. // Add reward items.
for (var entry : reward.getRewardItemList()) { for (var entry : reward.getRewardItemList()) {
ItemData rewardItemData = GameData.getItemDataMap().get(entry.getItemId()); ItemData rewardItemData = GameData.getItemDataMap().get(entry.getItemId());
// Some rewards are chests where the user can select the item they want. // Some rewards are chests where the user can select the item they want.
if (rewardItemData.getMaterialType() == MaterialType.MATERIAL_SELECTABLE_CHEST) { if (rewardItemData.getMaterialType() == MaterialType.MATERIAL_SELECTABLE_CHEST) {
this.takeRewardsFromSelectChest(rewardItemData, index, entry, rewardItems); this.takeRewardsFromSelectChest(rewardItemData, index, entry, rewardItems);
} }
// All other rewards directly give us the right item. // All other rewards directly give us the right item.
else { else {
GameItem rewardItem = new GameItem(rewardItemData, entry.getItemCount()); GameItem rewardItem = new GameItem(rewardItemData, entry.getItemCount());
rewardItems.add(rewardItem); rewardItems.add(rewardItem);
} }
} }
// Construct the reward and set as taken. // Construct the reward and set as taken.
BattlePassReward bpReward = BattlePassReward bpReward =
new BattlePassReward( new BattlePassReward(
tag.getLevel(), tag.getLevel(),
tag.getRewardId(), tag.getRewardId(),
tag.getUnlockStatus() == BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID); tag.getUnlockStatus() == BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID);
this.getTakenRewards().put(bpReward.getRewardId(), bpReward); this.getTakenRewards().put(bpReward.getRewardId(), bpReward);
} }
// Save to db // Save to db
this.save(); this.save();
// Add items and send battle pass schedule packet // Add items and send battle pass schedule packet
getPlayer().getInventory().addItems(rewardItems); getPlayer().getInventory().addItems(rewardItems);
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer())); getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
} }
getPlayer().sendPacket(new PacketTakeBattlePassRewardRsp(takeOptionList, rewardItems)); getPlayer().sendPacket(new PacketTakeBattlePassRewardRsp(takeOptionList, rewardItems));
} }
public int buyLevels(int buyLevel) { public int buyLevels(int buyLevel) {
int boughtLevels = Math.min(buyLevel, GameConstants.BATTLE_PASS_MAX_LEVEL - buyLevel); int boughtLevels = Math.min(buyLevel, GameConstants.BATTLE_PASS_MAX_LEVEL - buyLevel);
if (boughtLevels > 0) { if (boughtLevels > 0) {
int price = GameConstants.BATTLE_PASS_LEVEL_PRICE * boughtLevels; int price = GameConstants.BATTLE_PASS_LEVEL_PRICE * boughtLevels;
if (getPlayer().getPrimogems() < price) { if (getPlayer().getPrimogems() < price) {
return 0; return 0;
} }
this.level += boughtLevels; this.level += boughtLevels;
this.save(); this.save();
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer())); getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
} }
return boughtLevels; return boughtLevels;
} }
public void resetDailyMissions() { public void resetDailyMissions() {
var resetMissions = new ArrayList<BattlePassMission>(); var resetMissions = new ArrayList<BattlePassMission>();
for (var mission : this.missions.values()) { for (var mission : this.missions.values()) {
if (mission.getData().getRefreshType() == null if (mission.getData().getRefreshType() == null
|| mission.getData().getRefreshType() || mission.getData().getRefreshType()
== BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_DAILY) { == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_DAILY) {
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED); mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED);
mission.setProgress(0); mission.setProgress(0);
resetMissions.add(mission); resetMissions.add(mission);
} }
} }
this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions)); this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer())); this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
} }
public void resetWeeklyMissions() { public void resetWeeklyMissions() {
var resetMissions = new ArrayList<BattlePassMission>(); var resetMissions = new ArrayList<BattlePassMission>();
for (var mission : this.missions.values()) { for (var mission : this.missions.values()) {
if (mission.getData().getRefreshType() if (mission.getData().getRefreshType()
== BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE) { == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE) {
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED); mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED);
mission.setProgress(0); mission.setProgress(0);
resetMissions.add(mission); resetMissions.add(mission);
} }
} }
this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions)); this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer())); this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
} }
// //
public BattlePassSchedule getScheduleProto() { public BattlePassSchedule getScheduleProto() {
var currentDate = LocalDate.now(); var currentDate = LocalDate.now();
var nextSundayDate = var nextSundayDate =
(currentDate.getDayOfWeek() == DayOfWeek.SUNDAY) (currentDate.getDayOfWeek() == DayOfWeek.SUNDAY)
? currentDate ? currentDate
: LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); : LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
var nextSundayTime = var nextSundayTime =
LocalDateTime.of( LocalDateTime.of(
nextSundayDate.getYear(), nextSundayDate.getYear(),
nextSundayDate.getMonthValue(), nextSundayDate.getMonthValue(),
nextSundayDate.getDayOfMonth(), nextSundayDate.getDayOfMonth(),
23, 23,
59, 59,
59); 59);
BattlePassSchedule.Builder schedule = BattlePassSchedule.Builder schedule =
BattlePassSchedule.newBuilder() BattlePassSchedule.newBuilder()
.setScheduleId(2700) .setScheduleId(2700)
.setLevel(this.getLevel()) .setLevel(this.getLevel())
.setPoint(this.getPoint()) .setPoint(this.getPoint())
.setBeginTime(0) .setBeginTime(0)
.setEndTime(2059483200) .setEndTime(2059483200)
.setIsViewed(this.isViewed()) .setIsViewed(this.isViewed())
.setUnlockStatus( .setUnlockStatus(
this.isPaid() this.isPaid()
? BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID ? BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID
: BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_FREE) : BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_FREE)
.setPaidPlatformFlags(2) // Not bought on Playstation. .setPaidPlatformFlags(2) // Not bought on Playstation.
.setCurCyclePoints(this.getCyclePoints()) .setCurCyclePoints(this.getCyclePoints())
.setCurCycle( .setCurCycle(
BattlePassCycle.newBuilder() BattlePassCycle.newBuilder()
.setBeginTime(0) .setBeginTime(0)
.setEndTime((int) nextSundayTime.atZone(ZoneId.systemDefault()).toEpochSecond()) .setEndTime((int) nextSundayTime.atZone(ZoneId.systemDefault()).toEpochSecond())
.setCycleIdx(3)); .setCycleIdx(3));
for (BattlePassReward reward : getTakenRewards().values()) { for (BattlePassReward reward : getTakenRewards().values()) {
schedule.addRewardTakenList(reward.toProto()); schedule.addRewardTakenList(reward.toProto());
} }
return schedule.build(); return schedule.build();
} }
public void save() { public void save() {
DatabaseHelper.saveBattlePass(this); DatabaseHelper.saveBattlePass(this);
} }
} }

View File

@ -1,75 +1,75 @@
package emu.grasscutter.game.battlepass; package emu.grasscutter.game.battlepass;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Transient; import dev.morphia.annotations.Transient;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.BattlePassMissionData; import emu.grasscutter.data.excels.BattlePassMissionData;
import emu.grasscutter.game.props.BattlePassMissionStatus; import emu.grasscutter.game.props.BattlePassMissionStatus;
@Entity @Entity
public class BattlePassMission { public class BattlePassMission {
private int id; private int id;
private int progress; private int progress;
private BattlePassMissionStatus status; private BattlePassMissionStatus status;
@Transient private BattlePassMissionData data; @Transient private BattlePassMissionData data;
@Deprecated // Morphia only @Deprecated // Morphia only
public BattlePassMission() {} public BattlePassMission() {}
public BattlePassMission(int id) { public BattlePassMission(int id) {
this.id = id; this.id = id;
} }
public int getId() { public int getId() {
return id; return id;
} }
public BattlePassMissionData getData() { public BattlePassMissionData getData() {
if (this.data == null) { if (this.data == null) {
this.data = GameData.getBattlePassMissionDataMap().get(getId()); this.data = GameData.getBattlePassMissionDataMap().get(getId());
} }
return this.data; return this.data;
} }
public int getProgress() { public int getProgress() {
return progress; return progress;
} }
public void setProgress(int value) { public void setProgress(int value) {
this.progress = value; this.progress = value;
} }
public void addProgress(int addProgress, int maxProgress) { public void addProgress(int addProgress, int maxProgress) {
this.progress = Math.min(addProgress + this.progress, maxProgress); this.progress = Math.min(addProgress + this.progress, maxProgress);
} }
public BattlePassMissionStatus getStatus() { public BattlePassMissionStatus getStatus() {
if (status == null) status = BattlePassMissionStatus.MISSION_STATUS_UNFINISHED; if (status == null) status = BattlePassMissionStatus.MISSION_STATUS_UNFINISHED;
return status; return status;
} }
public void setStatus(BattlePassMissionStatus status) { public void setStatus(BattlePassMissionStatus status) {
this.status = status; this.status = status;
} }
public boolean isFinshed() { public boolean isFinshed() {
return getStatus().getValue() >= 2; return getStatus().getValue() >= 2;
} }
public emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission toProto() { public emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission toProto() {
var protoBuilder = var protoBuilder =
emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.newBuilder(); emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.newBuilder();
protoBuilder protoBuilder
.setMissionId(getId()) .setMissionId(getId())
.setCurProgress(getProgress()) .setCurProgress(getProgress())
.setTotalProgress(getData().getProgress()) .setTotalProgress(getData().getProgress())
.setRewardBattlePassPoint(getData().getAddPoint()) .setRewardBattlePassPoint(getData().getAddPoint())
.setMissionStatus(getStatus().getMissionStatus()) .setMissionStatus(getStatus().getMissionStatus())
.setMissionType( .setMissionType(
getData().getRefreshType() == null ? 0 : getData().getRefreshType().getValue()); getData().getRefreshType() == null ? 0 : getData().getRefreshType().getValue());
return protoBuilder.build(); return protoBuilder.build();
} }
} }

View File

@ -1,51 +1,51 @@
package emu.grasscutter.game.battlepass; package emu.grasscutter.game.battlepass;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Transient; import dev.morphia.annotations.Transient;
import emu.grasscutter.data.excels.BattlePassMissionData; import emu.grasscutter.data.excels.BattlePassMissionData;
import emu.grasscutter.net.proto.BattlePassRewardTagOuterClass.BattlePassRewardTag; import emu.grasscutter.net.proto.BattlePassRewardTagOuterClass.BattlePassRewardTag;
import emu.grasscutter.net.proto.BattlePassUnlockStatusOuterClass.BattlePassUnlockStatus; import emu.grasscutter.net.proto.BattlePassUnlockStatusOuterClass.BattlePassUnlockStatus;
@Entity @Entity
public class BattlePassReward { public class BattlePassReward {
private int level; private int level;
private int rewardId; private int rewardId;
private boolean paid; private boolean paid;
@Transient private BattlePassMissionData data; @Transient private BattlePassMissionData data;
@Deprecated // Morphia only @Deprecated // Morphia only
public BattlePassReward() {} public BattlePassReward() {}
public BattlePassReward(int level, int rewardId, boolean paid) { public BattlePassReward(int level, int rewardId, boolean paid) {
this.level = level; this.level = level;
this.rewardId = rewardId; this.rewardId = rewardId;
this.paid = paid; this.paid = paid;
} }
public int getLevel() { public int getLevel() {
return level; return level;
} }
public int getRewardId() { public int getRewardId() {
return rewardId; return rewardId;
} }
public boolean isPaid() { public boolean isPaid() {
return paid; return paid;
} }
public BattlePassRewardTag toProto() { public BattlePassRewardTag toProto() {
var protoBuilder = BattlePassRewardTag.newBuilder(); var protoBuilder = BattlePassRewardTag.newBuilder();
protoBuilder protoBuilder
.setLevel(this.getLevel()) .setLevel(this.getLevel())
.setRewardId(this.getRewardId()) .setRewardId(this.getRewardId())
.setUnlockStatus( .setUnlockStatus(
this.isPaid() this.isPaid()
? BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID ? BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID
: BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_FREE); : BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_FREE);
return protoBuilder.build(); return protoBuilder.build();
} }
} }

View File

@ -1,80 +1,80 @@
package emu.grasscutter.game.battlepass; package emu.grasscutter.game.battlepass;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.BattlePassMissionData; import emu.grasscutter.data.excels.BattlePassMissionData;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.BattlePassMissionStatus; import emu.grasscutter.game.props.BattlePassMissionStatus;
import emu.grasscutter.game.props.WatcherTriggerType; import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.server.game.BaseGameSystem; import emu.grasscutter.server.game.BaseGameSystem;
import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify; import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class BattlePassSystem extends BaseGameSystem { public class BattlePassSystem extends BaseGameSystem {
private final Map<WatcherTriggerType, List<BattlePassMissionData>> cachedTriggers; private final Map<WatcherTriggerType, List<BattlePassMissionData>> cachedTriggers;
// BP Mission manager for the server, contains cached triggers so we dont have to load it for each // BP Mission manager for the server, contains cached triggers so we dont have to load it for each
// player // player
public BattlePassSystem(GameServer server) { public BattlePassSystem(GameServer server) {
super(server); super(server);
this.cachedTriggers = new HashMap<>(); this.cachedTriggers = new HashMap<>();
for (BattlePassMissionData missionData : GameData.getBattlePassMissionDataMap().values()) { for (BattlePassMissionData missionData : GameData.getBattlePassMissionDataMap().values()) {
if (missionData.isValidRefreshType()) { if (missionData.isValidRefreshType()) {
List<BattlePassMissionData> triggerList = List<BattlePassMissionData> triggerList =
getTriggers().computeIfAbsent(missionData.getTriggerType(), e -> new ArrayList<>()); getTriggers().computeIfAbsent(missionData.getTriggerType(), e -> new ArrayList<>());
triggerList.add(missionData); triggerList.add(missionData);
} }
} }
} }
public GameServer getServer() { public GameServer getServer() {
return server; return server;
} }
private Map<WatcherTriggerType, List<BattlePassMissionData>> getTriggers() { private Map<WatcherTriggerType, List<BattlePassMissionData>> getTriggers() {
return cachedTriggers; return cachedTriggers;
} }
public void triggerMission(Player player, WatcherTriggerType triggerType) { public void triggerMission(Player player, WatcherTriggerType triggerType) {
triggerMission(player, triggerType, 0, 1); triggerMission(player, triggerType, 0, 1);
} }
public void triggerMission( public void triggerMission(
Player player, WatcherTriggerType triggerType, int param, int progress) { Player player, WatcherTriggerType triggerType, int param, int progress) {
List<BattlePassMissionData> triggerList = getTriggers().get(triggerType); List<BattlePassMissionData> triggerList = getTriggers().get(triggerType);
if (triggerList == null || triggerList.isEmpty()) return; if (triggerList == null || triggerList.isEmpty()) return;
for (BattlePassMissionData data : triggerList) { for (BattlePassMissionData data : triggerList) {
// Skip params check if param == 0 // Skip params check if param == 0
if (param != 0) { if (param != 0) {
if (!data.getMainParams().contains(param)) { if (!data.getMainParams().contains(param)) {
continue; continue;
} }
} }
// Get mission from player, if it doesnt exist, then we make one // Get mission from player, if it doesnt exist, then we make one
BattlePassMission mission = player.getBattlePassManager().loadMissionById(data.getId()); BattlePassMission mission = player.getBattlePassManager().loadMissionById(data.getId());
if (mission.isFinshed()) continue; if (mission.isFinshed()) continue;
// Add progress // Add progress
mission.addProgress(progress, data.getProgress()); mission.addProgress(progress, data.getProgress());
if (mission.getProgress() >= data.getProgress()) { if (mission.getProgress() >= data.getProgress()) {
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_FINISHED); mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_FINISHED);
} }
// Save to db // Save to db
player.getBattlePassManager().save(); player.getBattlePassManager().save();
// Packet // Packet
player.sendPacket(new PacketBattlePassMissionUpdateNotify(mission)); player.sendPacket(new PacketBattlePassMissionUpdateNotify(mission));
} }
} }
} }

View File

@ -1,215 +1,215 @@
package emu.grasscutter.game.chat; package emu.grasscutter.game.chat;
import static emu.grasscutter.config.Configuration.GAME_INFO; import static emu.grasscutter.config.Configuration.GAME_INFO;
import emu.grasscutter.GameConstants; import emu.grasscutter.GameConstants;
import emu.grasscutter.command.CommandMap; import emu.grasscutter.command.CommandMap;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo; import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo;
import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketPlayerChatNotify; import emu.grasscutter.server.packet.send.PacketPlayerChatNotify;
import emu.grasscutter.server.packet.send.PacketPrivateChatNotify; import emu.grasscutter.server.packet.send.PacketPrivateChatNotify;
import emu.grasscutter.server.packet.send.PacketPullPrivateChatRsp; import emu.grasscutter.server.packet.send.PacketPullPrivateChatRsp;
import emu.grasscutter.server.packet.send.PacketPullRecentChatRsp; import emu.grasscutter.server.packet.send.PacketPullRecentChatRsp;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class ChatSystem implements ChatSystemHandler { public class ChatSystem implements ChatSystemHandler {
static final String PREFIXES = "[/!]"; static final String PREFIXES = "[/!]";
static final Pattern RE_PREFIXES = Pattern.compile(PREFIXES); static final Pattern RE_PREFIXES = Pattern.compile(PREFIXES);
static final Pattern RE_COMMANDS = Pattern.compile("\n" + PREFIXES); static final Pattern RE_COMMANDS = Pattern.compile("\n" + PREFIXES);
// We store the chat history for ongoing sessions in the form // We store the chat history for ongoing sessions in the form
// user id -> chat partner id -> [messages] // user id -> chat partner id -> [messages]
private final Map<Integer, Map<Integer, List<ChatInfo>>> history = new HashMap<>(); private final Map<Integer, Map<Integer, List<ChatInfo>>> history = new HashMap<>();
private final GameServer server; private final GameServer server;
public ChatSystem(GameServer server) { public ChatSystem(GameServer server) {
this.server = server; this.server = server;
} }
public GameServer getServer() { public GameServer getServer() {
return server; return server;
} }
private boolean tryInvokeCommand(Player sender, Player target, String rawMessage) { private boolean tryInvokeCommand(Player sender, Player target, String rawMessage) {
if (!RE_PREFIXES.matcher(rawMessage.substring(0, 1)).matches()) return false; if (!RE_PREFIXES.matcher(rawMessage.substring(0, 1)).matches()) return false;
for (String line : rawMessage.substring(1).split("\n[/!]")) for (String line : rawMessage.substring(1).split("\n[/!]"))
CommandMap.getInstance().invoke(sender, target, line); CommandMap.getInstance().invoke(sender, target, line);
return true; return true;
} }
/******************** /********************
* Chat history handling * Chat history handling
********************/ ********************/
private void putInHistory(int uid, int partnerId, ChatInfo info) { private void putInHistory(int uid, int partnerId, ChatInfo info) {
this.history this.history
.computeIfAbsent(uid, x -> new HashMap<>()) .computeIfAbsent(uid, x -> new HashMap<>())
.computeIfAbsent(partnerId, x -> new ArrayList<>()) .computeIfAbsent(partnerId, x -> new ArrayList<>())
.add(info); .add(info);
} }
public void clearHistoryOnLogout(Player player) { public void clearHistoryOnLogout(Player player) {
this.history.remove(player.getUid()); this.history.remove(player.getUid());
} }
public void handlePullPrivateChatReq(Player player, int partnerId) { public void handlePullPrivateChatReq(Player player, int partnerId) {
var chatHistory = var chatHistory =
this.history this.history
.computeIfAbsent(player.getUid(), x -> new HashMap<>()) .computeIfAbsent(player.getUid(), x -> new HashMap<>())
.computeIfAbsent(partnerId, x -> new ArrayList<>()); .computeIfAbsent(partnerId, x -> new ArrayList<>());
player.sendPacket(new PacketPullPrivateChatRsp(chatHistory)); player.sendPacket(new PacketPullPrivateChatRsp(chatHistory));
} }
public void handlePullRecentChatReq(Player player) { public void handlePullRecentChatReq(Player player) {
// If this user has no chat history yet, create it by sending the server welcome messages. // If this user has no chat history yet, create it by sending the server welcome messages.
if (!this.history if (!this.history
.computeIfAbsent(player.getUid(), x -> new HashMap<>()) .computeIfAbsent(player.getUid(), x -> new HashMap<>())
.containsKey(GameConstants.SERVER_CONSOLE_UID)) { .containsKey(GameConstants.SERVER_CONSOLE_UID)) {
this.sendServerWelcomeMessages(player); this.sendServerWelcomeMessages(player);
} }
// For now, we send the list three messages from the server for the recent chat history. // For now, we send the list three messages from the server for the recent chat history.
// This matches the previous behavior, but ultimately, we should probably keep track of the last // This matches the previous behavior, but ultimately, we should probably keep track of the last
// chat partner // chat partner
// for every given player and return the last messages exchanged with that partner. // for every given player and return the last messages exchanged with that partner.
int historyLength = int historyLength =
this.history.get(player.getUid()).get(GameConstants.SERVER_CONSOLE_UID).size(); this.history.get(player.getUid()).get(GameConstants.SERVER_CONSOLE_UID).size();
var messages = var messages =
this.history this.history
.get(player.getUid()) .get(player.getUid())
.get(GameConstants.SERVER_CONSOLE_UID) .get(GameConstants.SERVER_CONSOLE_UID)
.subList(Math.max(historyLength - 3, 0), historyLength); .subList(Math.max(historyLength - 3, 0), historyLength);
player.sendPacket(new PacketPullRecentChatRsp(messages)); player.sendPacket(new PacketPullRecentChatRsp(messages));
} }
/******************** /********************
* Sending messages * Sending messages
********************/ ********************/
public void sendPrivateMessageFromServer(int targetUid, String message) { public void sendPrivateMessageFromServer(int targetUid, String message) {
// Sanity checks. // Sanity checks.
if (message == null || message.length() == 0) { if (message == null || message.length() == 0) {
return; return;
} }
// Get target. // Get target.
Player target = getServer().getPlayerByUid(targetUid); Player target = getServer().getPlayerByUid(targetUid);
if (target == null) { if (target == null) {
return; return;
} }
// Create chat packet and put in history. // Create chat packet and put in history.
var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, message); var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, message);
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo()); putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
// Send. // Send.
target.sendPacket(packet); target.sendPacket(packet);
} }
public void sendPrivateMessageFromServer(int targetUid, int emote) { public void sendPrivateMessageFromServer(int targetUid, int emote) {
// Get target. // Get target.
Player target = getServer().getPlayerByUid(targetUid); Player target = getServer().getPlayerByUid(targetUid);
if (target == null) { if (target == null) {
return; return;
} }
// Create chat packet and put in history. // Create chat packet and put in history.
var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, emote); var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, emote);
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo()); putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
// Send. // Send.
target.sendPacket(packet); target.sendPacket(packet);
} }
public void sendPrivateMessage(Player player, int targetUid, String message) { public void sendPrivateMessage(Player player, int targetUid, String message) {
// Sanity checks. // Sanity checks.
if (message == null || message.length() == 0) { if (message == null || message.length() == 0) {
return; return;
} }
// Get target. // Get target.
Player target = getServer().getPlayerByUid(targetUid); Player target = getServer().getPlayerByUid(targetUid);
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) { if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
return; return;
} }
// Create chat packet. // Create chat packet.
var packet = new PacketPrivateChatNotify(player.getUid(), targetUid, message); var packet = new PacketPrivateChatNotify(player.getUid(), targetUid, message);
// Send and put in history. // Send and put in history.
player.sendPacket(packet); player.sendPacket(packet);
putInHistory(player.getUid(), targetUid, packet.getChatInfo()); putInHistory(player.getUid(), targetUid, packet.getChatInfo());
// Check if command // Check if command
boolean isCommand = tryInvokeCommand(player, target, message); boolean isCommand = tryInvokeCommand(player, target, message);
if ((target != null) && (!isCommand)) { if ((target != null) && (!isCommand)) {
target.sendPacket(packet); target.sendPacket(packet);
putInHistory(targetUid, player.getUid(), packet.getChatInfo()); putInHistory(targetUid, player.getUid(), packet.getChatInfo());
} }
} }
public void sendPrivateMessage(Player player, int targetUid, int emote) { public void sendPrivateMessage(Player player, int targetUid, int emote) {
// Get target. // Get target.
Player target = getServer().getPlayerByUid(targetUid); Player target = getServer().getPlayerByUid(targetUid);
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) { if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
return; return;
} }
// Create chat packet. // Create chat packet.
var packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), emote); var packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), emote);
// Send and put is history. // Send and put is history.
player.sendPacket(packet); player.sendPacket(packet);
putInHistory(player.getUid(), targetUid, packet.getChatInfo()); putInHistory(player.getUid(), targetUid, packet.getChatInfo());
if (target != null) { if (target != null) {
target.sendPacket(packet); target.sendPacket(packet);
putInHistory(targetUid, player.getUid(), packet.getChatInfo()); putInHistory(targetUid, player.getUid(), packet.getChatInfo());
} }
} }
public void sendTeamMessage(Player player, int channel, String message) { public void sendTeamMessage(Player player, int channel, String message) {
// Sanity checks // Sanity checks
if (message == null || message.length() == 0) { if (message == null || message.length() == 0) {
return; return;
} }
// Check if command // Check if command
if (tryInvokeCommand(player, null, message)) { if (tryInvokeCommand(player, null, message)) {
return; return;
} }
// Create and send chat packet // Create and send chat packet
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, message)); player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, message));
} }
public void sendTeamMessage(Player player, int channel, int icon) { public void sendTeamMessage(Player player, int channel, int icon) {
// Create and send chat packet // Create and send chat packet
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, icon)); player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, icon));
} }
/******************** /********************
* Welcome messages * Welcome messages
********************/ ********************/
private void sendServerWelcomeMessages(Player player) { private void sendServerWelcomeMessages(Player player) {
var joinOptions = GAME_INFO.joinOptions; var joinOptions = GAME_INFO.joinOptions;
if (joinOptions.welcomeEmotes != null && joinOptions.welcomeEmotes.length > 0) { if (joinOptions.welcomeEmotes != null && joinOptions.welcomeEmotes.length > 0) {
this.sendPrivateMessageFromServer( this.sendPrivateMessageFromServer(
player.getUid(), player.getUid(),
joinOptions.welcomeEmotes[Utils.randomRange(0, joinOptions.welcomeEmotes.length - 1)]); joinOptions.welcomeEmotes[Utils.randomRange(0, joinOptions.welcomeEmotes.length - 1)]);
} }
if (joinOptions.welcomeMessage != null && joinOptions.welcomeMessage.length() > 0) { if (joinOptions.welcomeMessage != null && joinOptions.welcomeMessage.length() > 0) {
this.sendPrivateMessageFromServer(player.getUid(), joinOptions.welcomeMessage); this.sendPrivateMessageFromServer(player.getUid(), joinOptions.welcomeMessage);
} }
} }
} }

View File

@ -1,26 +1,26 @@
package emu.grasscutter.game.chat; package emu.grasscutter.game.chat;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServer;
public interface ChatSystemHandler { public interface ChatSystemHandler {
GameServer getServer(); GameServer getServer();
void sendPrivateMessage(Player player, int targetUid, String message); void sendPrivateMessage(Player player, int targetUid, String message);
void sendPrivateMessage(Player player, int targetUid, int emote); void sendPrivateMessage(Player player, int targetUid, int emote);
void sendTeamMessage(Player player, int channel, String message); void sendTeamMessage(Player player, int channel, String message);
void sendTeamMessage(Player player, int channel, int icon); void sendTeamMessage(Player player, int channel, int icon);
void sendPrivateMessageFromServer(int targetUid, String message); void sendPrivateMessageFromServer(int targetUid, String message);
void sendPrivateMessageFromServer(int targetUid, int emote); void sendPrivateMessageFromServer(int targetUid, int emote);
void handlePullPrivateChatReq(Player player, int targetUid); void handlePullPrivateChatReq(Player player, int targetUid);
void clearHistoryOnLogout(Player player); void clearHistoryOnLogout(Player player);
void handlePullRecentChatReq(Player player); void handlePullRecentChatReq(Player player);
} }

View File

@ -1,143 +1,143 @@
package emu.grasscutter.game.combine; package emu.grasscutter.game.combine;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader; 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.excels.CombineData; import emu.grasscutter.data.excels.CombineData;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.RetcodeOuterClass; import emu.grasscutter.net.proto.RetcodeOuterClass;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
import emu.grasscutter.server.game.BaseGameSystem; import emu.grasscutter.server.game.BaseGameSystem;
import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketCombineFormulaDataNotify; import emu.grasscutter.server.packet.send.PacketCombineFormulaDataNotify;
import emu.grasscutter.server.packet.send.PacketCombineRsp; import emu.grasscutter.server.packet.send.PacketCombineRsp;
import emu.grasscutter.server.packet.send.PacketReliquaryDecomposeRsp; import emu.grasscutter.server.packet.send.PacketReliquaryDecomposeRsp;
import emu.grasscutter.utils.Utils; 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.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class CombineManger extends BaseGameSystem { public class CombineManger extends BaseGameSystem {
private static final Int2ObjectMap<List<Integer>> reliquaryDecomposeData = private static final Int2ObjectMap<List<Integer>> reliquaryDecomposeData =
new Int2ObjectOpenHashMap<>(); new Int2ObjectOpenHashMap<>();
public CombineManger(GameServer server) { public CombineManger(GameServer server) {
super(server); super(server);
} }
public static void initialize() { public static void initialize() {
// Read the data we need for strongbox. // Read the data we need for strongbox.
try { try {
DataLoader.loadList("ReliquaryDecompose.json", ReliquaryDecomposeEntry.class) DataLoader.loadList("ReliquaryDecompose.json", ReliquaryDecomposeEntry.class)
.forEach( .forEach(
entry -> { entry -> {
reliquaryDecomposeData.put(entry.getConfigId(), entry.getItems()); reliquaryDecomposeData.put(entry.getConfigId(), entry.getItems());
}); });
Grasscutter.getLogger() Grasscutter.getLogger()
.debug("Loaded {} reliquary decompose entries.", reliquaryDecomposeData.size()); .debug("Loaded {} reliquary decompose entries.", reliquaryDecomposeData.size());
} catch (Exception ex) { } catch (Exception ex) {
Grasscutter.getLogger().error("Unable to load reliquary decompose data.", ex); Grasscutter.getLogger().error("Unable to load reliquary decompose data.", ex);
} }
} }
public boolean unlockCombineDiagram(Player player, int combineId) { public boolean unlockCombineDiagram(Player player, int combineId) {
if (!player.getUnlockedCombines().add(combineId)) { if (!player.getUnlockedCombines().add(combineId)) {
return false; // Already unlocked return false; // Already unlocked
} }
// Tell the client that this diagram is now unlocked and add the unlocked item to the player. // Tell the client that this diagram is now unlocked and add the unlocked item to the player.
player.sendPacket(new PacketCombineFormulaDataNotify(combineId)); player.sendPacket(new PacketCombineFormulaDataNotify(combineId));
return true; return true;
} }
public CombineResult combineItem(Player player, int cid, int count) { public CombineResult combineItem(Player player, int cid, int count) {
// check config exist // check config exist
if (!GameData.getCombineDataMap().containsKey(cid)) { if (!GameData.getCombineDataMap().containsKey(cid)) {
player.getWorld().getHost().sendPacket(new PacketCombineRsp()); player.getWorld().getHost().sendPacket(new PacketCombineRsp());
return null; return null;
} }
CombineData combineData = GameData.getCombineDataMap().get(cid); CombineData combineData = GameData.getCombineDataMap().get(cid);
if (combineData.getPlayerLevel() > player.getLevel()) { if (combineData.getPlayerLevel() > player.getLevel()) {
return null; return null;
} }
// consume items // consume items
List<ItemParamData> material = new ArrayList<>(combineData.getMaterialItems()); List<ItemParamData> material = new ArrayList<>(combineData.getMaterialItems());
material.add(new ItemParamData(202, combineData.getScoinCost())); material.add(new ItemParamData(202, combineData.getScoinCost()));
boolean success = player.getInventory().payItems(material, count, ActionReason.Combine); boolean success = player.getInventory().payItems(material, count, ActionReason.Combine);
// abort if not enough material // abort if not enough material
if (!success) { if (!success) {
player.sendPacket( player.sendPacket(
new PacketCombineRsp(RetcodeOuterClass.Retcode.RET_ITEM_COMBINE_COUNT_NOT_ENOUGH_VALUE)); new PacketCombineRsp(RetcodeOuterClass.Retcode.RET_ITEM_COMBINE_COUNT_NOT_ENOUGH_VALUE));
} }
// make the result // make the result
player player
.getInventory() .getInventory()
.addItem(combineData.getResultItemId(), combineData.getResultItemCount() * count); .addItem(combineData.getResultItemId(), combineData.getResultItemCount() * count);
CombineResult result = new CombineResult(); CombineResult result = new CombineResult();
result.setMaterial(List.of()); result.setMaterial(List.of());
result.setResult( result.setResult(
List.of( List.of(
new ItemParamData( new ItemParamData(
combineData.getResultItemId(), combineData.getResultItemCount() * count))); combineData.getResultItemId(), combineData.getResultItemCount() * count)));
// TODO lucky characters // TODO lucky characters
result.setExtra(List.of()); result.setExtra(List.of());
result.setBack(List.of()); result.setBack(List.of());
return result; return result;
} }
public synchronized void decomposeReliquaries( public synchronized void decomposeReliquaries(
Player player, int configId, int count, List<Long> input) { Player player, int configId, int count, List<Long> input) {
// Check if the configId is legal. // Check if the configId is legal.
List<Integer> possibleDrops = reliquaryDecomposeData.get(configId); List<Integer> possibleDrops = reliquaryDecomposeData.get(configId);
if (possibleDrops == null) { if (possibleDrops == null) {
player.sendPacket( player.sendPacket(
new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR)); new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR));
return; return;
} }
// Check if the number of input items matches the output count. // Check if the number of input items matches the output count.
if (input.size() != count * 3) { if (input.size() != count * 3) {
player.sendPacket( player.sendPacket(
new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR)); new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR));
return; return;
} }
// Check if all the input reliquaries actually are in the player's inventory. // Check if all the input reliquaries actually are in the player's inventory.
for (long guid : input) { for (long guid : input) {
if (player.getInventory().getItemByGuid(guid) == null) { if (player.getInventory().getItemByGuid(guid) == null) {
player.sendPacket( player.sendPacket(
new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR)); new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR));
return; return;
} }
} }
// Delete the input reliquaries. // Delete the input reliquaries.
for (long guid : input) { for (long guid : input) {
player.getInventory().removeItem(guid); player.getInventory().removeItem(guid);
} }
// Generate outoput reliquaries. // Generate outoput reliquaries.
List<Long> resultItems = new ArrayList<>(); List<Long> resultItems = new ArrayList<>();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
int itemId = Utils.drawRandomListElement(possibleDrops); int itemId = Utils.drawRandomListElement(possibleDrops);
GameItem newReliquary = new GameItem(itemId, 1); GameItem newReliquary = new GameItem(itemId, 1);
player.getInventory().addItem(newReliquary); player.getInventory().addItem(newReliquary);
resultItems.add(newReliquary.getGuid()); resultItems.add(newReliquary.getGuid());
} }
// Send packet. // Send packet.
player.sendPacket(new PacketReliquaryDecomposeRsp(resultItems)); player.sendPacket(new PacketReliquaryDecomposeRsp(resultItems));
} }
} }

Some files were not shown because too many files have changed in this diff Show More