mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-24 14:22:53 +08:00
Format code [skip actions]
This commit is contained in:
parent
f51fd55cb5
commit
f9906c4492
@ -1,133 +1,133 @@
|
||||
package emu.grasscutter.auth;
|
||||
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.server.http.objects.*;
|
||||
import io.javalin.http.Context;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/** Defines an authenticator for the server. Can be changed by plugins. */
|
||||
public interface AuthenticationSystem {
|
||||
|
||||
/**
|
||||
* Generates an authentication request from a {@link LoginAccountRequestJson} object.
|
||||
*
|
||||
* @param ctx The Javalin context.
|
||||
* @param jsonData The JSON data.
|
||||
* @return An authentication request.
|
||||
*/
|
||||
static AuthenticationRequest fromPasswordRequest(Context ctx, LoginAccountRequestJson jsonData) {
|
||||
return AuthenticationRequest.builder().context(ctx).passwordRequest(jsonData).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an authentication request from a {@link LoginTokenRequestJson} object.
|
||||
*
|
||||
* @param ctx The Javalin context.
|
||||
* @param jsonData The JSON data.
|
||||
* @return An authentication request.
|
||||
*/
|
||||
static AuthenticationRequest fromTokenRequest(Context ctx, LoginTokenRequestJson jsonData) {
|
||||
return AuthenticationRequest.builder().context(ctx).tokenRequest(jsonData).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an authentication request from a {@link ComboTokenReqJson} object.
|
||||
*
|
||||
* @param ctx The Javalin context.
|
||||
* @param jsonData The JSON data.
|
||||
* @return An authentication request.
|
||||
*/
|
||||
static AuthenticationRequest fromComboTokenRequest(
|
||||
Context ctx, ComboTokenReqJson jsonData, ComboTokenReqJson.LoginTokenData tokenData) {
|
||||
return AuthenticationRequest.builder()
|
||||
.context(ctx)
|
||||
.sessionKeyRequest(jsonData)
|
||||
.sessionKeyData(tokenData)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an authentication request from a {@link Context} object.
|
||||
*
|
||||
* @param ctx The Javalin context.
|
||||
* @return An authentication request.
|
||||
*/
|
||||
static AuthenticationRequest fromExternalRequest(Context ctx) {
|
||||
return AuthenticationRequest.builder().context(ctx).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a user requests to make an account.
|
||||
*
|
||||
* @param username The provided username.
|
||||
* @param password The provided password. (SHA-256'ed)
|
||||
*/
|
||||
void createAccount(String username, String password);
|
||||
|
||||
/**
|
||||
* Called when a user requests to reset their password.
|
||||
*
|
||||
* @param username The username of the account to reset.
|
||||
*/
|
||||
void resetPassword(String username);
|
||||
|
||||
/**
|
||||
* Called by plugins to internally verify a user's identity.
|
||||
*
|
||||
* @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
|
||||
* be verified.
|
||||
*/
|
||||
Account verifyUser(String details);
|
||||
|
||||
/**
|
||||
* This is the authenticator used for password authentication.
|
||||
*
|
||||
* @return An authenticator.
|
||||
*/
|
||||
Authenticator<LoginResultJson> getPasswordAuthenticator();
|
||||
|
||||
/**
|
||||
* This is the authenticator used for token authentication.
|
||||
*
|
||||
* @return An authenticator.
|
||||
*/
|
||||
Authenticator<LoginResultJson> getTokenAuthenticator();
|
||||
|
||||
/**
|
||||
* This is the authenticator used for session authentication.
|
||||
*
|
||||
* @return An authenticator.
|
||||
*/
|
||||
Authenticator<ComboTokenResJson> getSessionKeyAuthenticator();
|
||||
|
||||
/**
|
||||
* This is the authenticator used for handling external authentication requests.
|
||||
*
|
||||
* @return An authenticator.
|
||||
*/
|
||||
ExternalAuthenticator getExternalAuthenticator();
|
||||
|
||||
/**
|
||||
* This is the authenticator used for handling OAuth authentication requests.
|
||||
*
|
||||
* @return An authenticator.
|
||||
*/
|
||||
OAuthAuthenticator getOAuthAuthenticator();
|
||||
|
||||
/** A data container that holds relevant data for authenticating a client. */
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
class AuthenticationRequest {
|
||||
private final Context context;
|
||||
|
||||
@Nullable private final LoginAccountRequestJson passwordRequest;
|
||||
@Nullable private final LoginTokenRequestJson tokenRequest;
|
||||
@Nullable private final ComboTokenReqJson sessionKeyRequest;
|
||||
@Nullable private final ComboTokenReqJson.LoginTokenData sessionKeyData;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.auth;
|
||||
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.server.http.objects.*;
|
||||
import io.javalin.http.Context;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/** Defines an authenticator for the server. Can be changed by plugins. */
|
||||
public interface AuthenticationSystem {
|
||||
|
||||
/**
|
||||
* Generates an authentication request from a {@link LoginAccountRequestJson} object.
|
||||
*
|
||||
* @param ctx The Javalin context.
|
||||
* @param jsonData The JSON data.
|
||||
* @return An authentication request.
|
||||
*/
|
||||
static AuthenticationRequest fromPasswordRequest(Context ctx, LoginAccountRequestJson jsonData) {
|
||||
return AuthenticationRequest.builder().context(ctx).passwordRequest(jsonData).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an authentication request from a {@link LoginTokenRequestJson} object.
|
||||
*
|
||||
* @param ctx The Javalin context.
|
||||
* @param jsonData The JSON data.
|
||||
* @return An authentication request.
|
||||
*/
|
||||
static AuthenticationRequest fromTokenRequest(Context ctx, LoginTokenRequestJson jsonData) {
|
||||
return AuthenticationRequest.builder().context(ctx).tokenRequest(jsonData).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an authentication request from a {@link ComboTokenReqJson} object.
|
||||
*
|
||||
* @param ctx The Javalin context.
|
||||
* @param jsonData The JSON data.
|
||||
* @return An authentication request.
|
||||
*/
|
||||
static AuthenticationRequest fromComboTokenRequest(
|
||||
Context ctx, ComboTokenReqJson jsonData, ComboTokenReqJson.LoginTokenData tokenData) {
|
||||
return AuthenticationRequest.builder()
|
||||
.context(ctx)
|
||||
.sessionKeyRequest(jsonData)
|
||||
.sessionKeyData(tokenData)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an authentication request from a {@link Context} object.
|
||||
*
|
||||
* @param ctx The Javalin context.
|
||||
* @return An authentication request.
|
||||
*/
|
||||
static AuthenticationRequest fromExternalRequest(Context ctx) {
|
||||
return AuthenticationRequest.builder().context(ctx).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a user requests to make an account.
|
||||
*
|
||||
* @param username The provided username.
|
||||
* @param password The provided password. (SHA-256'ed)
|
||||
*/
|
||||
void createAccount(String username, String password);
|
||||
|
||||
/**
|
||||
* Called when a user requests to reset their password.
|
||||
*
|
||||
* @param username The username of the account to reset.
|
||||
*/
|
||||
void resetPassword(String username);
|
||||
|
||||
/**
|
||||
* Called by plugins to internally verify a user's identity.
|
||||
*
|
||||
* @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
|
||||
* be verified.
|
||||
*/
|
||||
Account verifyUser(String details);
|
||||
|
||||
/**
|
||||
* This is the authenticator used for password authentication.
|
||||
*
|
||||
* @return An authenticator.
|
||||
*/
|
||||
Authenticator<LoginResultJson> getPasswordAuthenticator();
|
||||
|
||||
/**
|
||||
* This is the authenticator used for token authentication.
|
||||
*
|
||||
* @return An authenticator.
|
||||
*/
|
||||
Authenticator<LoginResultJson> getTokenAuthenticator();
|
||||
|
||||
/**
|
||||
* This is the authenticator used for session authentication.
|
||||
*
|
||||
* @return An authenticator.
|
||||
*/
|
||||
Authenticator<ComboTokenResJson> getSessionKeyAuthenticator();
|
||||
|
||||
/**
|
||||
* This is the authenticator used for handling external authentication requests.
|
||||
*
|
||||
* @return An authenticator.
|
||||
*/
|
||||
ExternalAuthenticator getExternalAuthenticator();
|
||||
|
||||
/**
|
||||
* This is the authenticator used for handling OAuth authentication requests.
|
||||
*
|
||||
* @return An authenticator.
|
||||
*/
|
||||
OAuthAuthenticator getOAuthAuthenticator();
|
||||
|
||||
/** A data container that holds relevant data for authenticating a client. */
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
class AuthenticationRequest {
|
||||
private final Context context;
|
||||
|
||||
@Nullable private final LoginAccountRequestJson passwordRequest;
|
||||
@Nullable private final LoginTokenRequestJson tokenRequest;
|
||||
@Nullable private final ComboTokenReqJson sessionKeyRequest;
|
||||
@Nullable private final ComboTokenReqJson.LoginTokenData sessionKeyData;
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,22 @@
|
||||
package emu.grasscutter.auth;
|
||||
|
||||
import emu.grasscutter.server.http.objects.ComboTokenResJson;
|
||||
import emu.grasscutter.server.http.objects.LoginResultJson;
|
||||
|
||||
/**
|
||||
* Handles username/password authentication from the client.
|
||||
*
|
||||
* @param <T> The response object type. Should be {@link LoginResultJson} or {@link
|
||||
* ComboTokenResJson}
|
||||
*/
|
||||
public interface Authenticator<T> {
|
||||
|
||||
/**
|
||||
* Attempt to authenticate the client with the provided credentials.
|
||||
*
|
||||
* @param request The authentication request wrapped in a {@link
|
||||
* AuthenticationSystem.AuthenticationRequest} object.
|
||||
* @return The result of the login in an object.
|
||||
*/
|
||||
T authenticate(AuthenticationSystem.AuthenticationRequest request);
|
||||
}
|
||||
package emu.grasscutter.auth;
|
||||
|
||||
import emu.grasscutter.server.http.objects.ComboTokenResJson;
|
||||
import emu.grasscutter.server.http.objects.LoginResultJson;
|
||||
|
||||
/**
|
||||
* Handles username/password authentication from the client.
|
||||
*
|
||||
* @param <T> The response object type. Should be {@link LoginResultJson} or {@link
|
||||
* ComboTokenResJson}
|
||||
*/
|
||||
public interface Authenticator<T> {
|
||||
|
||||
/**
|
||||
* Attempt to authenticate the client with the provided credentials.
|
||||
*
|
||||
* @param request The authentication request wrapped in a {@link
|
||||
* AuthenticationSystem.AuthenticationRequest} object.
|
||||
* @return The result of the login in an object.
|
||||
*/
|
||||
T authenticate(AuthenticationSystem.AuthenticationRequest request);
|
||||
}
|
||||
|
@ -1,363 +1,363 @@
|
||||
package emu.grasscutter.auth;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.ACCOUNT;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import at.favre.lib.crypto.bcrypt.BCrypt;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.server.http.objects.ComboTokenResJson;
|
||||
import emu.grasscutter.server.http.objects.LoginResultJson;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
/** A class containing default authenticators. */
|
||||
public final class DefaultAuthenticators {
|
||||
|
||||
/** Handles the authentication request from the username and password form. */
|
||||
public static class PasswordAuthenticator implements Authenticator<LoginResultJson> {
|
||||
@Override
|
||||
public LoginResultJson authenticate(AuthenticationRequest request) {
|
||||
var response = new LoginResultJson();
|
||||
|
||||
var requestData = request.getPasswordRequest();
|
||||
assert requestData != null; // This should never be null.
|
||||
int playerCount = Grasscutter.getGameServer().getPlayers().size();
|
||||
|
||||
boolean successfulLogin = false;
|
||||
String address = request.getContext().ip();
|
||||
String responseMessage = translate("messages.dispatch.account.username_error");
|
||||
String loggerMessage = "";
|
||||
|
||||
// Get account from database.
|
||||
Account account = DatabaseHelper.getAccountByName(requestData.account);
|
||||
if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) {
|
||||
// Check if account exists.
|
||||
if (account == null && ACCOUNT.autoCreate) {
|
||||
// This account has been created AUTOMATICALLY. There will be no permissions added.
|
||||
account = DatabaseHelper.createAccountWithUid(requestData.account, 0);
|
||||
|
||||
// Check if the account was created successfully.
|
||||
if (account == null) {
|
||||
responseMessage = translate("messages.dispatch.account.username_create_error");
|
||||
Grasscutter.getLogger()
|
||||
.info(translate("messages.dispatch.account.account_login_create_error", address));
|
||||
} else {
|
||||
// Continue with login.
|
||||
successfulLogin = true;
|
||||
|
||||
// Log the creation.
|
||||
Grasscutter.getLogger()
|
||||
.info(
|
||||
translate(
|
||||
"messages.dispatch.account.account_login_create_success",
|
||||
address,
|
||||
response.data.account.uid));
|
||||
}
|
||||
} else if (account != null) successfulLogin = true;
|
||||
else
|
||||
loggerMessage = translate("messages.dispatch.account.account_login_exist_error", address);
|
||||
|
||||
} else {
|
||||
responseMessage = translate("messages.dispatch.account.server_max_player_limit");
|
||||
loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address);
|
||||
}
|
||||
|
||||
// Set response data.
|
||||
if (successfulLogin) {
|
||||
response.message = "OK";
|
||||
response.data.account.uid = account.getId();
|
||||
response.data.account.token = account.generateSessionKey();
|
||||
response.data.account.email = account.getEmail();
|
||||
|
||||
loggerMessage =
|
||||
translate("messages.dispatch.account.login_success", address, account.getId());
|
||||
} else {
|
||||
response.retcode = -201;
|
||||
response.message = responseMessage;
|
||||
}
|
||||
Grasscutter.getLogger().info(loggerMessage);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExperimentalPasswordAuthenticator implements Authenticator<LoginResultJson> {
|
||||
@Override
|
||||
public LoginResultJson authenticate(AuthenticationRequest request) {
|
||||
var response = new LoginResultJson();
|
||||
|
||||
var requestData = request.getPasswordRequest();
|
||||
assert requestData != null; // This should never be null.
|
||||
int playerCount = Grasscutter.getGameServer().getPlayers().size();
|
||||
|
||||
boolean successfulLogin = false;
|
||||
String address = request.getContext().ip();
|
||||
String responseMessage = translate("messages.dispatch.account.username_error");
|
||||
String loggerMessage = "";
|
||||
String decryptedPassword = "";
|
||||
try {
|
||||
byte[] key = FileUtils.readResource("/keys/auth_private-key.der");
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
RSAPrivateKey private_key = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
|
||||
|
||||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||
|
||||
cipher.init(Cipher.DECRYPT_MODE, private_key);
|
||||
|
||||
decryptedPassword =
|
||||
new String(
|
||||
cipher.doFinal(Utils.base64Decode(request.getPasswordRequest().password)),
|
||||
StandardCharsets.UTF_8);
|
||||
} catch (Exception ignored) {
|
||||
decryptedPassword = request.getPasswordRequest().password;
|
||||
}
|
||||
|
||||
if (decryptedPassword == null) {
|
||||
successfulLogin = false;
|
||||
loggerMessage = translate("messages.dispatch.account.login_password_error", address);
|
||||
responseMessage = translate("messages.dispatch.account.password_error");
|
||||
}
|
||||
|
||||
// Get account from database.
|
||||
Account account = DatabaseHelper.getAccountByName(requestData.account);
|
||||
if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) {
|
||||
// Check if account exists.
|
||||
if (account == null && ACCOUNT.autoCreate) {
|
||||
// This account has been created AUTOMATICALLY. There will be no permissions added.
|
||||
if (decryptedPassword.length() >= 8) {
|
||||
account = DatabaseHelper.createAccountWithUid(requestData.account, 0);
|
||||
account.setPassword(
|
||||
BCrypt.withDefaults().hashToString(12, decryptedPassword.toCharArray()));
|
||||
account.save();
|
||||
|
||||
// Check if the account was created successfully.
|
||||
if (account == null) {
|
||||
responseMessage = translate("messages.dispatch.account.username_create_error");
|
||||
loggerMessage =
|
||||
translate("messages.dispatch.account.account_login_create_error", address);
|
||||
} else {
|
||||
// Continue with login.
|
||||
successfulLogin = true;
|
||||
|
||||
// Log the creation.
|
||||
Grasscutter.getLogger()
|
||||
.info(
|
||||
translate(
|
||||
"messages.dispatch.account.account_login_create_success",
|
||||
address,
|
||||
response.data.account.uid));
|
||||
}
|
||||
} else {
|
||||
successfulLogin = false;
|
||||
loggerMessage = translate("messages.dispatch.account.login_password_error", address);
|
||||
responseMessage = translate("messages.dispatch.account.password_length_error");
|
||||
}
|
||||
} else if (account != null) {
|
||||
if (account.getPassword() != null && !account.getPassword().isEmpty()) {
|
||||
if (BCrypt.verifyer()
|
||||
.verify(decryptedPassword.toCharArray(), account.getPassword())
|
||||
.verified) {
|
||||
successfulLogin = true;
|
||||
} else {
|
||||
successfulLogin = false;
|
||||
loggerMessage = translate("messages.dispatch.account.login_password_error", address);
|
||||
responseMessage = translate("messages.dispatch.account.password_error");
|
||||
}
|
||||
} else {
|
||||
successfulLogin = false;
|
||||
loggerMessage =
|
||||
translate("messages.dispatch.account.login_password_storage_error", address);
|
||||
responseMessage = translate("messages.dispatch.account.password_storage_error");
|
||||
}
|
||||
} else {
|
||||
loggerMessage = translate("messages.dispatch.account.account_login_exist_error", address);
|
||||
}
|
||||
} else {
|
||||
responseMessage = translate("messages.dispatch.account.server_max_player_limit");
|
||||
loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address);
|
||||
}
|
||||
|
||||
// Set response data.
|
||||
if (successfulLogin) {
|
||||
response.message = "OK";
|
||||
response.data.account.uid = account.getId();
|
||||
response.data.account.token = account.generateSessionKey();
|
||||
response.data.account.email = account.getEmail();
|
||||
|
||||
loggerMessage =
|
||||
translate("messages.dispatch.account.login_success", address, account.getId());
|
||||
} else {
|
||||
response.retcode = -201;
|
||||
response.message = responseMessage;
|
||||
}
|
||||
Grasscutter.getLogger().info(loggerMessage);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles the authentication request from the game when using a registry token. */
|
||||
public static class TokenAuthenticator implements Authenticator<LoginResultJson> {
|
||||
@Override
|
||||
public LoginResultJson authenticate(AuthenticationRequest request) {
|
||||
var response = new LoginResultJson();
|
||||
|
||||
var requestData = request.getTokenRequest();
|
||||
assert requestData != null;
|
||||
|
||||
boolean successfulLogin;
|
||||
String address = request.getContext().ip();
|
||||
String loggerMessage;
|
||||
int playerCount = Grasscutter.getGameServer().getPlayers().size();
|
||||
|
||||
// Log the attempt.
|
||||
Grasscutter.getLogger()
|
||||
.info(translate("messages.dispatch.account.login_token_attempt", address));
|
||||
|
||||
if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) {
|
||||
|
||||
// Get account from database.
|
||||
Account account = DatabaseHelper.getAccountById(requestData.uid);
|
||||
|
||||
// Check if account exists/token is valid.
|
||||
successfulLogin = account != null && account.getSessionKey().equals(requestData.token);
|
||||
|
||||
// Set response data.
|
||||
if (successfulLogin) {
|
||||
response.message = "OK";
|
||||
response.data.account.uid = account.getId();
|
||||
response.data.account.token = account.getSessionKey();
|
||||
response.data.account.email = account.getEmail();
|
||||
|
||||
// Log the login.
|
||||
loggerMessage =
|
||||
translate("messages.dispatch.account.login_token_success", address, requestData.uid);
|
||||
} else {
|
||||
response.retcode = -201;
|
||||
response.message = translate("messages.dispatch.account.account_cache_error");
|
||||
|
||||
// Log the failure.
|
||||
loggerMessage = translate("messages.dispatch.account.login_token_error", address);
|
||||
}
|
||||
|
||||
} else {
|
||||
response.retcode = -201;
|
||||
response.message = translate("messages.dispatch.account.server_max_player_limit");
|
||||
|
||||
loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address);
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().info(loggerMessage);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles the authentication request from the game when using a combo token/session key. */
|
||||
public static class SessionKeyAuthenticator implements Authenticator<ComboTokenResJson> {
|
||||
@Override
|
||||
public ComboTokenResJson authenticate(AuthenticationRequest request) {
|
||||
var response = new ComboTokenResJson();
|
||||
|
||||
var requestData = request.getSessionKeyRequest();
|
||||
var loginData = request.getSessionKeyData();
|
||||
assert requestData != null;
|
||||
assert loginData != null;
|
||||
|
||||
boolean successfulLogin;
|
||||
String address = request.getContext().ip();
|
||||
String loggerMessage;
|
||||
int playerCount = Grasscutter.getGameServer().getPlayers().size();
|
||||
|
||||
if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) {
|
||||
// Get account from database.
|
||||
Account account = DatabaseHelper.getAccountById(loginData.uid);
|
||||
|
||||
// Check if account exists/token is valid.
|
||||
successfulLogin = account != null && account.getSessionKey().equals(loginData.token);
|
||||
|
||||
// Set response data.
|
||||
if (successfulLogin) {
|
||||
response.message = "OK";
|
||||
response.data.open_id = account.getId();
|
||||
response.data.combo_id = "157795300";
|
||||
response.data.combo_token = account.generateLoginToken();
|
||||
|
||||
// Log the login.
|
||||
loggerMessage = translate("messages.dispatch.account.combo_token_success", address);
|
||||
|
||||
} else {
|
||||
response.retcode = -201;
|
||||
response.message = translate("messages.dispatch.account.session_key_error");
|
||||
|
||||
// Log the failure.
|
||||
loggerMessage = translate("messages.dispatch.account.combo_token_error", address);
|
||||
}
|
||||
} else {
|
||||
response.retcode = -201;
|
||||
response.message = translate("messages.dispatch.account.server_max_player_limit");
|
||||
|
||||
loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address);
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().info(loggerMessage);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles authentication requests from external sources. */
|
||||
public static class ExternalAuthentication implements ExternalAuthenticator {
|
||||
@Override
|
||||
public void handleLogin(AuthenticationRequest request) {
|
||||
request
|
||||
.getContext()
|
||||
.result("Authentication is not available with the default authentication method.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAccountCreation(AuthenticationRequest request) {
|
||||
request
|
||||
.getContext()
|
||||
.result("Authentication is not available with the default authentication method.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePasswordReset(AuthenticationRequest request) {
|
||||
request
|
||||
.getContext()
|
||||
.result("Authentication is not available with the default authentication method.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles authentication requests from OAuth sources.Zenlith */
|
||||
public static class OAuthAuthentication implements OAuthAuthenticator {
|
||||
@Override
|
||||
public void handleLogin(AuthenticationRequest request) {
|
||||
request
|
||||
.getContext()
|
||||
.result("Authentication is not available with the default authentication method.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRedirection(AuthenticationRequest request, ClientType type) {
|
||||
request
|
||||
.getContext()
|
||||
.result("Authentication is not available with the default authentication method.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTokenProcess(AuthenticationRequest request) {
|
||||
request
|
||||
.getContext()
|
||||
.result("Authentication is not available with the default authentication method.");
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.auth;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.ACCOUNT;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import at.favre.lib.crypto.bcrypt.BCrypt;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.server.http.objects.ComboTokenResJson;
|
||||
import emu.grasscutter.server.http.objects.LoginResultJson;
|
||||
import emu.grasscutter.utils.FileUtils;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
/** A class containing default authenticators. */
|
||||
public final class DefaultAuthenticators {
|
||||
|
||||
/** Handles the authentication request from the username and password form. */
|
||||
public static class PasswordAuthenticator implements Authenticator<LoginResultJson> {
|
||||
@Override
|
||||
public LoginResultJson authenticate(AuthenticationRequest request) {
|
||||
var response = new LoginResultJson();
|
||||
|
||||
var requestData = request.getPasswordRequest();
|
||||
assert requestData != null; // This should never be null.
|
||||
int playerCount = Grasscutter.getGameServer().getPlayers().size();
|
||||
|
||||
boolean successfulLogin = false;
|
||||
String address = request.getContext().ip();
|
||||
String responseMessage = translate("messages.dispatch.account.username_error");
|
||||
String loggerMessage = "";
|
||||
|
||||
// Get account from database.
|
||||
Account account = DatabaseHelper.getAccountByName(requestData.account);
|
||||
if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) {
|
||||
// Check if account exists.
|
||||
if (account == null && ACCOUNT.autoCreate) {
|
||||
// This account has been created AUTOMATICALLY. There will be no permissions added.
|
||||
account = DatabaseHelper.createAccountWithUid(requestData.account, 0);
|
||||
|
||||
// Check if the account was created successfully.
|
||||
if (account == null) {
|
||||
responseMessage = translate("messages.dispatch.account.username_create_error");
|
||||
Grasscutter.getLogger()
|
||||
.info(translate("messages.dispatch.account.account_login_create_error", address));
|
||||
} else {
|
||||
// Continue with login.
|
||||
successfulLogin = true;
|
||||
|
||||
// Log the creation.
|
||||
Grasscutter.getLogger()
|
||||
.info(
|
||||
translate(
|
||||
"messages.dispatch.account.account_login_create_success",
|
||||
address,
|
||||
response.data.account.uid));
|
||||
}
|
||||
} else if (account != null) successfulLogin = true;
|
||||
else
|
||||
loggerMessage = translate("messages.dispatch.account.account_login_exist_error", address);
|
||||
|
||||
} else {
|
||||
responseMessage = translate("messages.dispatch.account.server_max_player_limit");
|
||||
loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address);
|
||||
}
|
||||
|
||||
// Set response data.
|
||||
if (successfulLogin) {
|
||||
response.message = "OK";
|
||||
response.data.account.uid = account.getId();
|
||||
response.data.account.token = account.generateSessionKey();
|
||||
response.data.account.email = account.getEmail();
|
||||
|
||||
loggerMessage =
|
||||
translate("messages.dispatch.account.login_success", address, account.getId());
|
||||
} else {
|
||||
response.retcode = -201;
|
||||
response.message = responseMessage;
|
||||
}
|
||||
Grasscutter.getLogger().info(loggerMessage);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExperimentalPasswordAuthenticator implements Authenticator<LoginResultJson> {
|
||||
@Override
|
||||
public LoginResultJson authenticate(AuthenticationRequest request) {
|
||||
var response = new LoginResultJson();
|
||||
|
||||
var requestData = request.getPasswordRequest();
|
||||
assert requestData != null; // This should never be null.
|
||||
int playerCount = Grasscutter.getGameServer().getPlayers().size();
|
||||
|
||||
boolean successfulLogin = false;
|
||||
String address = request.getContext().ip();
|
||||
String responseMessage = translate("messages.dispatch.account.username_error");
|
||||
String loggerMessage = "";
|
||||
String decryptedPassword = "";
|
||||
try {
|
||||
byte[] key = FileUtils.readResource("/keys/auth_private-key.der");
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
RSAPrivateKey private_key = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
|
||||
|
||||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||
|
||||
cipher.init(Cipher.DECRYPT_MODE, private_key);
|
||||
|
||||
decryptedPassword =
|
||||
new String(
|
||||
cipher.doFinal(Utils.base64Decode(request.getPasswordRequest().password)),
|
||||
StandardCharsets.UTF_8);
|
||||
} catch (Exception ignored) {
|
||||
decryptedPassword = request.getPasswordRequest().password;
|
||||
}
|
||||
|
||||
if (decryptedPassword == null) {
|
||||
successfulLogin = false;
|
||||
loggerMessage = translate("messages.dispatch.account.login_password_error", address);
|
||||
responseMessage = translate("messages.dispatch.account.password_error");
|
||||
}
|
||||
|
||||
// Get account from database.
|
||||
Account account = DatabaseHelper.getAccountByName(requestData.account);
|
||||
if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) {
|
||||
// Check if account exists.
|
||||
if (account == null && ACCOUNT.autoCreate) {
|
||||
// This account has been created AUTOMATICALLY. There will be no permissions added.
|
||||
if (decryptedPassword.length() >= 8) {
|
||||
account = DatabaseHelper.createAccountWithUid(requestData.account, 0);
|
||||
account.setPassword(
|
||||
BCrypt.withDefaults().hashToString(12, decryptedPassword.toCharArray()));
|
||||
account.save();
|
||||
|
||||
// Check if the account was created successfully.
|
||||
if (account == null) {
|
||||
responseMessage = translate("messages.dispatch.account.username_create_error");
|
||||
loggerMessage =
|
||||
translate("messages.dispatch.account.account_login_create_error", address);
|
||||
} else {
|
||||
// Continue with login.
|
||||
successfulLogin = true;
|
||||
|
||||
// Log the creation.
|
||||
Grasscutter.getLogger()
|
||||
.info(
|
||||
translate(
|
||||
"messages.dispatch.account.account_login_create_success",
|
||||
address,
|
||||
response.data.account.uid));
|
||||
}
|
||||
} else {
|
||||
successfulLogin = false;
|
||||
loggerMessage = translate("messages.dispatch.account.login_password_error", address);
|
||||
responseMessage = translate("messages.dispatch.account.password_length_error");
|
||||
}
|
||||
} else if (account != null) {
|
||||
if (account.getPassword() != null && !account.getPassword().isEmpty()) {
|
||||
if (BCrypt.verifyer()
|
||||
.verify(decryptedPassword.toCharArray(), account.getPassword())
|
||||
.verified) {
|
||||
successfulLogin = true;
|
||||
} else {
|
||||
successfulLogin = false;
|
||||
loggerMessage = translate("messages.dispatch.account.login_password_error", address);
|
||||
responseMessage = translate("messages.dispatch.account.password_error");
|
||||
}
|
||||
} else {
|
||||
successfulLogin = false;
|
||||
loggerMessage =
|
||||
translate("messages.dispatch.account.login_password_storage_error", address);
|
||||
responseMessage = translate("messages.dispatch.account.password_storage_error");
|
||||
}
|
||||
} else {
|
||||
loggerMessage = translate("messages.dispatch.account.account_login_exist_error", address);
|
||||
}
|
||||
} else {
|
||||
responseMessage = translate("messages.dispatch.account.server_max_player_limit");
|
||||
loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address);
|
||||
}
|
||||
|
||||
// Set response data.
|
||||
if (successfulLogin) {
|
||||
response.message = "OK";
|
||||
response.data.account.uid = account.getId();
|
||||
response.data.account.token = account.generateSessionKey();
|
||||
response.data.account.email = account.getEmail();
|
||||
|
||||
loggerMessage =
|
||||
translate("messages.dispatch.account.login_success", address, account.getId());
|
||||
} else {
|
||||
response.retcode = -201;
|
||||
response.message = responseMessage;
|
||||
}
|
||||
Grasscutter.getLogger().info(loggerMessage);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles the authentication request from the game when using a registry token. */
|
||||
public static class TokenAuthenticator implements Authenticator<LoginResultJson> {
|
||||
@Override
|
||||
public LoginResultJson authenticate(AuthenticationRequest request) {
|
||||
var response = new LoginResultJson();
|
||||
|
||||
var requestData = request.getTokenRequest();
|
||||
assert requestData != null;
|
||||
|
||||
boolean successfulLogin;
|
||||
String address = request.getContext().ip();
|
||||
String loggerMessage;
|
||||
int playerCount = Grasscutter.getGameServer().getPlayers().size();
|
||||
|
||||
// Log the attempt.
|
||||
Grasscutter.getLogger()
|
||||
.info(translate("messages.dispatch.account.login_token_attempt", address));
|
||||
|
||||
if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) {
|
||||
|
||||
// Get account from database.
|
||||
Account account = DatabaseHelper.getAccountById(requestData.uid);
|
||||
|
||||
// Check if account exists/token is valid.
|
||||
successfulLogin = account != null && account.getSessionKey().equals(requestData.token);
|
||||
|
||||
// Set response data.
|
||||
if (successfulLogin) {
|
||||
response.message = "OK";
|
||||
response.data.account.uid = account.getId();
|
||||
response.data.account.token = account.getSessionKey();
|
||||
response.data.account.email = account.getEmail();
|
||||
|
||||
// Log the login.
|
||||
loggerMessage =
|
||||
translate("messages.dispatch.account.login_token_success", address, requestData.uid);
|
||||
} else {
|
||||
response.retcode = -201;
|
||||
response.message = translate("messages.dispatch.account.account_cache_error");
|
||||
|
||||
// Log the failure.
|
||||
loggerMessage = translate("messages.dispatch.account.login_token_error", address);
|
||||
}
|
||||
|
||||
} else {
|
||||
response.retcode = -201;
|
||||
response.message = translate("messages.dispatch.account.server_max_player_limit");
|
||||
|
||||
loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address);
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().info(loggerMessage);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles the authentication request from the game when using a combo token/session key. */
|
||||
public static class SessionKeyAuthenticator implements Authenticator<ComboTokenResJson> {
|
||||
@Override
|
||||
public ComboTokenResJson authenticate(AuthenticationRequest request) {
|
||||
var response = new ComboTokenResJson();
|
||||
|
||||
var requestData = request.getSessionKeyRequest();
|
||||
var loginData = request.getSessionKeyData();
|
||||
assert requestData != null;
|
||||
assert loginData != null;
|
||||
|
||||
boolean successfulLogin;
|
||||
String address = request.getContext().ip();
|
||||
String loggerMessage;
|
||||
int playerCount = Grasscutter.getGameServer().getPlayers().size();
|
||||
|
||||
if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) {
|
||||
// Get account from database.
|
||||
Account account = DatabaseHelper.getAccountById(loginData.uid);
|
||||
|
||||
// Check if account exists/token is valid.
|
||||
successfulLogin = account != null && account.getSessionKey().equals(loginData.token);
|
||||
|
||||
// Set response data.
|
||||
if (successfulLogin) {
|
||||
response.message = "OK";
|
||||
response.data.open_id = account.getId();
|
||||
response.data.combo_id = "157795300";
|
||||
response.data.combo_token = account.generateLoginToken();
|
||||
|
||||
// Log the login.
|
||||
loggerMessage = translate("messages.dispatch.account.combo_token_success", address);
|
||||
|
||||
} else {
|
||||
response.retcode = -201;
|
||||
response.message = translate("messages.dispatch.account.session_key_error");
|
||||
|
||||
// Log the failure.
|
||||
loggerMessage = translate("messages.dispatch.account.combo_token_error", address);
|
||||
}
|
||||
} else {
|
||||
response.retcode = -201;
|
||||
response.message = translate("messages.dispatch.account.server_max_player_limit");
|
||||
|
||||
loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address);
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().info(loggerMessage);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles authentication requests from external sources. */
|
||||
public static class ExternalAuthentication implements ExternalAuthenticator {
|
||||
@Override
|
||||
public void handleLogin(AuthenticationRequest request) {
|
||||
request
|
||||
.getContext()
|
||||
.result("Authentication is not available with the default authentication method.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAccountCreation(AuthenticationRequest request) {
|
||||
request
|
||||
.getContext()
|
||||
.result("Authentication is not available with the default authentication method.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePasswordReset(AuthenticationRequest request) {
|
||||
request
|
||||
.getContext()
|
||||
.result("Authentication is not available with the default authentication method.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles authentication requests from OAuth sources.Zenlith */
|
||||
public static class OAuthAuthentication implements OAuthAuthenticator {
|
||||
@Override
|
||||
public void handleLogin(AuthenticationRequest request) {
|
||||
request
|
||||
.getContext()
|
||||
.result("Authentication is not available with the default authentication method.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRedirection(AuthenticationRequest request, ClientType type) {
|
||||
request
|
||||
.getContext()
|
||||
.result("Authentication is not available with the default authentication method.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTokenProcess(AuthenticationRequest request) {
|
||||
request
|
||||
.getContext()
|
||||
.result("Authentication is not available with the default authentication method.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,32 @@
|
||||
package emu.grasscutter.auth;
|
||||
|
||||
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
|
||||
|
||||
/** Handles authentication via external routes. */
|
||||
public interface ExternalAuthenticator {
|
||||
|
||||
/**
|
||||
* Called when an external login request is made.
|
||||
*
|
||||
* @param request The authentication request.
|
||||
*/
|
||||
void handleLogin(AuthenticationRequest request);
|
||||
|
||||
/**
|
||||
* Called when an external account creation request is made.
|
||||
*
|
||||
* @param request The authentication request.
|
||||
* <p>For developers: Use AuthenticationRequest#getRequest() to get the request body. Use
|
||||
* AuthenticationRequest#getResponse() to get the response body.
|
||||
*/
|
||||
void handleAccountCreation(AuthenticationRequest request);
|
||||
|
||||
/**
|
||||
* Called when an external password reset request is made.
|
||||
*
|
||||
* @param request The authentication request.
|
||||
* <p>For developers: Use AuthenticationRequest#getRequest() to get the request body. Use
|
||||
* AuthenticationRequest#getResponse() to get the response body.
|
||||
*/
|
||||
void handlePasswordReset(AuthenticationRequest request);
|
||||
}
|
||||
package emu.grasscutter.auth;
|
||||
|
||||
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
|
||||
|
||||
/** Handles authentication via external routes. */
|
||||
public interface ExternalAuthenticator {
|
||||
|
||||
/**
|
||||
* Called when an external login request is made.
|
||||
*
|
||||
* @param request The authentication request.
|
||||
*/
|
||||
void handleLogin(AuthenticationRequest request);
|
||||
|
||||
/**
|
||||
* Called when an external account creation request is made.
|
||||
*
|
||||
* @param request The authentication request.
|
||||
* <p>For developers: Use AuthenticationRequest#getRequest() to get the request body. Use
|
||||
* AuthenticationRequest#getResponse() to get the response body.
|
||||
*/
|
||||
void handleAccountCreation(AuthenticationRequest request);
|
||||
|
||||
/**
|
||||
* Called when an external password reset request is made.
|
||||
*
|
||||
* @param request The authentication request.
|
||||
* <p>For developers: Use AuthenticationRequest#getRequest() to get the request body. Use
|
||||
* AuthenticationRequest#getResponse() to get the response body.
|
||||
*/
|
||||
void handlePasswordReset(AuthenticationRequest request);
|
||||
}
|
||||
|
@ -1,34 +1,34 @@
|
||||
package emu.grasscutter.auth;
|
||||
|
||||
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
|
||||
|
||||
/** Handles authentication via OAuth routes. */
|
||||
public interface OAuthAuthenticator {
|
||||
|
||||
/**
|
||||
* Called when an OAuth login request is made.
|
||||
*
|
||||
* @param request The authentication request.
|
||||
*/
|
||||
void handleLogin(AuthenticationRequest request);
|
||||
|
||||
/**
|
||||
* Called when a client requests to redirect to login page.
|
||||
*
|
||||
* @param request The authentication request.
|
||||
*/
|
||||
void handleRedirection(AuthenticationRequest request, ClientType clientType);
|
||||
|
||||
/**
|
||||
* Called when an OAuth login requests callback.
|
||||
*
|
||||
* @param request The authentication request.
|
||||
*/
|
||||
void handleTokenProcess(AuthenticationRequest request);
|
||||
|
||||
/** The type of the client. Used for handling redirection. */
|
||||
enum ClientType {
|
||||
DESKTOP,
|
||||
MOBILE
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.auth;
|
||||
|
||||
import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest;
|
||||
|
||||
/** Handles authentication via OAuth routes. */
|
||||
public interface OAuthAuthenticator {
|
||||
|
||||
/**
|
||||
* Called when an OAuth login request is made.
|
||||
*
|
||||
* @param request The authentication request.
|
||||
*/
|
||||
void handleLogin(AuthenticationRequest request);
|
||||
|
||||
/**
|
||||
* Called when a client requests to redirect to login page.
|
||||
*
|
||||
* @param request The authentication request.
|
||||
*/
|
||||
void handleRedirection(AuthenticationRequest request, ClientType clientType);
|
||||
|
||||
/**
|
||||
* Called when an OAuth login requests callback.
|
||||
*
|
||||
* @param request The authentication request.
|
||||
*/
|
||||
void handleTokenProcess(AuthenticationRequest request);
|
||||
|
||||
/** The type of the client. Used for handling redirection. */
|
||||
enum ClientType {
|
||||
DESKTOP,
|
||||
MOBILE
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,28 @@
|
||||
package emu.grasscutter.command;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Command {
|
||||
String label() default "";
|
||||
|
||||
String[] aliases() default {};
|
||||
|
||||
String[] usage() default {""};
|
||||
|
||||
String permission() default "";
|
||||
|
||||
String permissionTargeted() default "";
|
||||
|
||||
TargetRequirement targetRequirement() default TargetRequirement.ONLINE;
|
||||
|
||||
boolean threading() default false;
|
||||
|
||||
enum TargetRequirement {
|
||||
NONE, // targetPlayer is not required
|
||||
OFFLINE, // targetPlayer must be offline
|
||||
PLAYER, // targetPlayer can be online or offline
|
||||
ONLINE // targetPlayer must be online
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Command {
|
||||
String label() default "";
|
||||
|
||||
String[] aliases() default {};
|
||||
|
||||
String[] usage() default {""};
|
||||
|
||||
String permission() default "";
|
||||
|
||||
String permissionTargeted() default "";
|
||||
|
||||
TargetRequirement targetRequirement() default TargetRequirement.ONLINE;
|
||||
|
||||
boolean threading() default false;
|
||||
|
||||
enum TargetRequirement {
|
||||
NONE, // targetPlayer is not required
|
||||
OFFLINE, // targetPlayer must be offline
|
||||
PLAYER, // targetPlayer can be online or offline
|
||||
ONLINE // targetPlayer must be online
|
||||
}
|
||||
}
|
||||
|
@ -1,88 +1,88 @@
|
||||
package emu.grasscutter.command;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.event.game.ReceiveCommandFeedbackEvent;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public interface CommandHandler {
|
||||
|
||||
/**
|
||||
* Send a message to the target.
|
||||
*
|
||||
* @param player The player to send the message to, or null for the server console.
|
||||
* @param message The message to send.
|
||||
*/
|
||||
static void sendMessage(Player player, String message) {
|
||||
// Call command feedback event.
|
||||
ReceiveCommandFeedbackEvent event = new ReceiveCommandFeedbackEvent(player, message);
|
||||
event.call();
|
||||
if (event.isCanceled()) { // If event is not cancelled, continue.
|
||||
return;
|
||||
}
|
||||
|
||||
// Send message to target.
|
||||
if (player == null) {
|
||||
Grasscutter.getLogger().info(event.getMessage());
|
||||
} else {
|
||||
player.dropMessage(event.getMessage().replace("\n\t", "\n\n"));
|
||||
}
|
||||
}
|
||||
|
||||
static void sendTranslatedMessage(Player player, String messageKey, Object... args) {
|
||||
sendMessage(player, translate(player, messageKey, args));
|
||||
}
|
||||
|
||||
default String getUsageString(Player player, String... args) {
|
||||
Command annotation = this.getClass().getAnnotation(Command.class);
|
||||
String usage_prefix = translate(player, "commands.execution.usage_prefix");
|
||||
String command = annotation.label();
|
||||
for (String alias : annotation.aliases()) {
|
||||
if (alias.length() < command.length()) command = alias;
|
||||
}
|
||||
if (player != null) {
|
||||
command = "/" + command;
|
||||
}
|
||||
String target =
|
||||
switch (annotation.targetRequirement()) {
|
||||
case NONE -> "";
|
||||
case OFFLINE -> "@<UID> "; // TODO: make translation keys for offline and online players
|
||||
case ONLINE -> (player == null)
|
||||
? "@<UID> "
|
||||
: "[@<UID>] "; // TODO: make translation keys for offline and online players
|
||||
case PLAYER -> (player == null) ? "@<UID> " : "[@<UID>] ";
|
||||
};
|
||||
String[] usages = annotation.usage();
|
||||
StringJoiner joiner = new StringJoiner("\n\t");
|
||||
for (String usage : usages) joiner.add(usage_prefix + command + " " + target + usage);
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
default void sendUsageMessage(Player player, String... args) {
|
||||
sendMessage(player, getUsageString(player, args));
|
||||
}
|
||||
|
||||
default String getLabel() {
|
||||
return this.getClass().getAnnotation(Command.class).label();
|
||||
}
|
||||
|
||||
default String getDescriptionKey() {
|
||||
Command annotation = this.getClass().getAnnotation(Command.class);
|
||||
return "commands.%s.description".formatted(annotation.label());
|
||||
}
|
||||
|
||||
default String getDescriptionString(Player player) {
|
||||
return translate(player, getDescriptionKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a player/console invokes a command.
|
||||
*
|
||||
* @param sender The player/console that invoked the command.
|
||||
* @param args The arguments to the command.
|
||||
*/
|
||||
default void execute(Player sender, Player targetPlayer, List<String> args) {}
|
||||
}
|
||||
package emu.grasscutter.command;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.event.game.ReceiveCommandFeedbackEvent;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public interface CommandHandler {
|
||||
|
||||
/**
|
||||
* Send a message to the target.
|
||||
*
|
||||
* @param player The player to send the message to, or null for the server console.
|
||||
* @param message The message to send.
|
||||
*/
|
||||
static void sendMessage(Player player, String message) {
|
||||
// Call command feedback event.
|
||||
ReceiveCommandFeedbackEvent event = new ReceiveCommandFeedbackEvent(player, message);
|
||||
event.call();
|
||||
if (event.isCanceled()) { // If event is not cancelled, continue.
|
||||
return;
|
||||
}
|
||||
|
||||
// Send message to target.
|
||||
if (player == null) {
|
||||
Grasscutter.getLogger().info(event.getMessage());
|
||||
} else {
|
||||
player.dropMessage(event.getMessage().replace("\n\t", "\n\n"));
|
||||
}
|
||||
}
|
||||
|
||||
static void sendTranslatedMessage(Player player, String messageKey, Object... args) {
|
||||
sendMessage(player, translate(player, messageKey, args));
|
||||
}
|
||||
|
||||
default String getUsageString(Player player, String... args) {
|
||||
Command annotation = this.getClass().getAnnotation(Command.class);
|
||||
String usage_prefix = translate(player, "commands.execution.usage_prefix");
|
||||
String command = annotation.label();
|
||||
for (String alias : annotation.aliases()) {
|
||||
if (alias.length() < command.length()) command = alias;
|
||||
}
|
||||
if (player != null) {
|
||||
command = "/" + command;
|
||||
}
|
||||
String target =
|
||||
switch (annotation.targetRequirement()) {
|
||||
case NONE -> "";
|
||||
case OFFLINE -> "@<UID> "; // TODO: make translation keys for offline and online players
|
||||
case ONLINE -> (player == null)
|
||||
? "@<UID> "
|
||||
: "[@<UID>] "; // TODO: make translation keys for offline and online players
|
||||
case PLAYER -> (player == null) ? "@<UID> " : "[@<UID>] ";
|
||||
};
|
||||
String[] usages = annotation.usage();
|
||||
StringJoiner joiner = new StringJoiner("\n\t");
|
||||
for (String usage : usages) joiner.add(usage_prefix + command + " " + target + usage);
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
default void sendUsageMessage(Player player, String... args) {
|
||||
sendMessage(player, getUsageString(player, args));
|
||||
}
|
||||
|
||||
default String getLabel() {
|
||||
return this.getClass().getAnnotation(Command.class).label();
|
||||
}
|
||||
|
||||
default String getDescriptionKey() {
|
||||
Command annotation = this.getClass().getAnnotation(Command.class);
|
||||
return "commands.%s.description".formatted(annotation.label());
|
||||
}
|
||||
|
||||
default String getDescriptionString(Player player) {
|
||||
return translate(player, getDescriptionKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a player/console invokes a command.
|
||||
*
|
||||
* @param sender The player/console that invoked the command.
|
||||
* @param args The arguments to the command.
|
||||
*/
|
||||
default void execute(Player sender, Player targetPlayer, List<String> args) {}
|
||||
}
|
||||
|
@ -1,354 +1,354 @@
|
||||
package emu.grasscutter.command;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.SERVER;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import java.util.*;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
@SuppressWarnings({"UnusedReturnValue", "unused"})
|
||||
public final class CommandMap {
|
||||
private static final int INVALID_UID = Integer.MIN_VALUE;
|
||||
private static final String consoleId = "console";
|
||||
private final Map<String, CommandHandler> commands = new TreeMap<>();
|
||||
private final Map<String, CommandHandler> aliases = new TreeMap<>();
|
||||
private final Map<String, Command> annotations = new TreeMap<>();
|
||||
private final Object2IntMap<String> targetPlayerIds = new Object2IntOpenHashMap<>();
|
||||
|
||||
public CommandMap() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public CommandMap(boolean scan) {
|
||||
if (scan) this.scan();
|
||||
}
|
||||
|
||||
public static CommandMap getInstance() {
|
||||
return Grasscutter.getCommandMap();
|
||||
}
|
||||
|
||||
private static int getUidFromString(String input) {
|
||||
try {
|
||||
return Integer.parseInt(input);
|
||||
} catch (NumberFormatException ignored) {
|
||||
var account = DatabaseHelper.getAccountByName(input);
|
||||
if (account == null) return INVALID_UID;
|
||||
var player = DatabaseHelper.getPlayerByAccount(account);
|
||||
if (player == null) return INVALID_UID;
|
||||
// We will be immediately fetching the player again after this,
|
||||
// but offline vs online Player safety is more important than saving a lookup
|
||||
return player.getUid();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a command handler.
|
||||
*
|
||||
* @param label The command label.
|
||||
* @param command The command handler.
|
||||
* @return Instance chaining.
|
||||
*/
|
||||
public CommandMap registerCommand(String label, CommandHandler command) {
|
||||
Grasscutter.getLogger().debug("Registered command: " + label);
|
||||
label = label.toLowerCase();
|
||||
|
||||
// Get command data.
|
||||
Command annotation = command.getClass().getAnnotation(Command.class);
|
||||
this.annotations.put(label, annotation);
|
||||
this.commands.put(label, command);
|
||||
|
||||
// Register aliases.
|
||||
for (String alias : annotation.aliases()) {
|
||||
this.aliases.put(alias, command);
|
||||
this.annotations.put(alias, annotation);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a registered command handler.
|
||||
*
|
||||
* @param label The command label.
|
||||
* @return Instance chaining.
|
||||
*/
|
||||
public CommandMap unregisterCommand(String label) {
|
||||
Grasscutter.getLogger().debug("Unregistered command: " + label);
|
||||
|
||||
CommandHandler handler = this.commands.get(label);
|
||||
if (handler == null) return this;
|
||||
|
||||
Command annotation = handler.getClass().getAnnotation(Command.class);
|
||||
this.annotations.remove(label);
|
||||
this.commands.remove(label);
|
||||
|
||||
// Unregister aliases.
|
||||
for (String alias : annotation.aliases()) {
|
||||
this.aliases.remove(alias);
|
||||
this.annotations.remove(alias);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<Command> getAnnotationsAsList() {
|
||||
return new ArrayList<>(this.annotations.values());
|
||||
}
|
||||
|
||||
public Map<String, Command> getAnnotations() {
|
||||
return new LinkedHashMap<>(this.annotations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all registered commands.
|
||||
*
|
||||
* @return All command handlers as a list.
|
||||
*/
|
||||
public List<CommandHandler> getHandlersAsList() {
|
||||
return new ArrayList<>(this.commands.values());
|
||||
}
|
||||
|
||||
public Map<String, CommandHandler> getHandlers() {
|
||||
return this.commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a handler by label/alias.
|
||||
*
|
||||
* @param label The command label.
|
||||
* @return The command handler.
|
||||
*/
|
||||
public CommandHandler getHandler(String label) {
|
||||
CommandHandler handler = this.commands.get(label);
|
||||
if (handler == null) {
|
||||
// Try getting by alias
|
||||
handler = this.aliases.get(label);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
private Player getTargetPlayer(
|
||||
String playerId, Player player, Player targetPlayer, List<String> args) {
|
||||
// Top priority: If any @UID argument is present, override targetPlayer with it.
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
String arg = args.get(i);
|
||||
if (arg.startsWith("@")) {
|
||||
arg = args.remove(i).substring(1);
|
||||
if (arg.equals("")) {
|
||||
// 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
|
||||
// themselves or anyone else.
|
||||
return null;
|
||||
}
|
||||
int uid = getUidFromString(arg);
|
||||
if (uid == INVALID_UID) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.invalid.uid");
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid, true);
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return targetPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (targetPlayer != null) {
|
||||
return targetPlayer;
|
||||
}
|
||||
|
||||
// Next priority: Use previously-set target. (see /target [[@]UID])
|
||||
if (targetPlayerIds.containsKey(playerId)) {
|
||||
targetPlayer =
|
||||
Grasscutter.getGameServer().getPlayerByUid(targetPlayerIds.getInt(playerId), true);
|
||||
// We check every time in case the target is deleted after being targeted
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return targetPlayer;
|
||||
}
|
||||
|
||||
// Lowest priority: Target the player invoking the command. In the case of the console, this
|
||||
// will return null.
|
||||
return player;
|
||||
}
|
||||
|
||||
private boolean setPlayerTarget(String playerId, Player player, String targetUid) {
|
||||
if (targetUid.equals("")) { // Clears the default targetPlayer.
|
||||
targetPlayerIds.removeInt(playerId);
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.clear_target");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sets default targetPlayer to the UID provided.
|
||||
int uid = getUidFromString(targetUid);
|
||||
if (uid == INVALID_UID) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.invalid.uid");
|
||||
return false;
|
||||
}
|
||||
Player targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid, true);
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
|
||||
return false;
|
||||
}
|
||||
|
||||
targetPlayerIds.put(playerId, uid);
|
||||
String target = uid + " (" + targetPlayer.getAccount().getUsername() + ")";
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.set_target", target);
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
player,
|
||||
targetPlayer.isOnline()
|
||||
? "commands.execution.set_target_online"
|
||||
: "commands.execution.set_target_offline",
|
||||
target);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a command handler with the given arguments.
|
||||
*
|
||||
* @param player The player invoking the command or null for the server console.
|
||||
* @param rawMessage The messaged used to invoke the command.
|
||||
*/
|
||||
public void invoke(Player player, Player targetPlayer, String rawMessage) {
|
||||
// The console outputs in-game command. [{Account Username} (Player UID: {Player Uid})]
|
||||
if (SERVER.logCommands) {
|
||||
if (player != null) {
|
||||
Grasscutter.getLogger()
|
||||
.info(
|
||||
"Command used by ["
|
||||
+ player.getAccount().getUsername()
|
||||
+ " (Player UID: "
|
||||
+ player.getUid()
|
||||
+ ")]: "
|
||||
+ rawMessage);
|
||||
} else {
|
||||
Grasscutter.getLogger().info("Command used by server console: " + rawMessage);
|
||||
}
|
||||
}
|
||||
|
||||
rawMessage = rawMessage.trim();
|
||||
if (rawMessage.length() == 0) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.not_specified");
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse message.
|
||||
String[] split = rawMessage.split(" ");
|
||||
String label = split[0].toLowerCase();
|
||||
List<String> args = new ArrayList<>(Arrays.asList(split).subList(1, split.length));
|
||||
String playerId = (player == null) ? consoleId : player.getAccount().getId();
|
||||
|
||||
// Check for special cases - currently only target command.
|
||||
if (label.startsWith("@")) { // @[UID]
|
||||
this.setPlayerTarget(playerId, player, label.substring(1));
|
||||
return;
|
||||
} else if (label.equalsIgnoreCase("target")) { // target [[@]UID]
|
||||
if (args.size() > 0) {
|
||||
String targetUidStr = args.get(0);
|
||||
if (targetUidStr.startsWith("@")) {
|
||||
targetUidStr = targetUidStr.substring(1);
|
||||
}
|
||||
this.setPlayerTarget(playerId, player, targetUidStr);
|
||||
return;
|
||||
} else {
|
||||
this.setPlayerTarget(playerId, player, "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get command handler.
|
||||
CommandHandler handler = this.getHandler(label);
|
||||
|
||||
// Check if the handler is null.
|
||||
if (handler == null) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.unknown_command", label);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the command's annotation.
|
||||
Command annotation = this.annotations.get(label);
|
||||
|
||||
// Resolve targetPlayer
|
||||
try {
|
||||
targetPlayer = getTargetPlayer(playerId, player, targetPlayer, args);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for permissions.
|
||||
if (!Grasscutter.getPermissionHandler()
|
||||
.checkPermission(
|
||||
player,
|
||||
targetPlayer,
|
||||
annotation.permission(),
|
||||
this.annotations.get(label).permissionTargeted())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if command has unfulfilled constraints on targetPlayer
|
||||
Command.TargetRequirement targetRequirement = annotation.targetRequirement();
|
||||
if (targetRequirement != Command.TargetRequirement.NONE) {
|
||||
if (targetPlayer == null) {
|
||||
handler.sendUsageMessage(player);
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((targetRequirement == Command.TargetRequirement.ONLINE) && !targetPlayer.isOnline()) {
|
||||
handler.sendUsageMessage(player);
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_online");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((targetRequirement == Command.TargetRequirement.OFFLINE) && targetPlayer.isOnline()) {
|
||||
handler.sendUsageMessage(player);
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_offline");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy player and handler to final properties.
|
||||
final Player targetPlayerF = targetPlayer; // 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.
|
||||
Runnable runnable = () -> handlerF.execute(player, targetPlayerF, args);
|
||||
if (annotation.threading()) {
|
||||
new Thread(runnable).start();
|
||||
} else {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
/** Scans for all classes annotated with {@link Command} and registers them. */
|
||||
private void scan() {
|
||||
Reflections reflector = Grasscutter.reflector;
|
||||
Set<Class<?>> classes = reflector.getTypesAnnotatedWith(Command.class);
|
||||
|
||||
classes.forEach(
|
||||
annotated -> {
|
||||
try {
|
||||
Command cmdData = annotated.getAnnotation(Command.class);
|
||||
Object object = annotated.getDeclaredConstructor().newInstance();
|
||||
if (object instanceof CommandHandler)
|
||||
this.registerCommand(cmdData.label(), (CommandHandler) object);
|
||||
else
|
||||
Grasscutter.getLogger()
|
||||
.error("Class " + annotated.getName() + " is not a CommandHandler!");
|
||||
} catch (Exception exception) {
|
||||
Grasscutter.getLogger()
|
||||
.error(
|
||||
"Failed to register command handler for " + annotated.getSimpleName(),
|
||||
exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.SERVER;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import java.util.*;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
@SuppressWarnings({"UnusedReturnValue", "unused"})
|
||||
public final class CommandMap {
|
||||
private static final int INVALID_UID = Integer.MIN_VALUE;
|
||||
private static final String consoleId = "console";
|
||||
private final Map<String, CommandHandler> commands = new TreeMap<>();
|
||||
private final Map<String, CommandHandler> aliases = new TreeMap<>();
|
||||
private final Map<String, Command> annotations = new TreeMap<>();
|
||||
private final Object2IntMap<String> targetPlayerIds = new Object2IntOpenHashMap<>();
|
||||
|
||||
public CommandMap() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public CommandMap(boolean scan) {
|
||||
if (scan) this.scan();
|
||||
}
|
||||
|
||||
public static CommandMap getInstance() {
|
||||
return Grasscutter.getCommandMap();
|
||||
}
|
||||
|
||||
private static int getUidFromString(String input) {
|
||||
try {
|
||||
return Integer.parseInt(input);
|
||||
} catch (NumberFormatException ignored) {
|
||||
var account = DatabaseHelper.getAccountByName(input);
|
||||
if (account == null) return INVALID_UID;
|
||||
var player = DatabaseHelper.getPlayerByAccount(account);
|
||||
if (player == null) return INVALID_UID;
|
||||
// We will be immediately fetching the player again after this,
|
||||
// but offline vs online Player safety is more important than saving a lookup
|
||||
return player.getUid();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a command handler.
|
||||
*
|
||||
* @param label The command label.
|
||||
* @param command The command handler.
|
||||
* @return Instance chaining.
|
||||
*/
|
||||
public CommandMap registerCommand(String label, CommandHandler command) {
|
||||
Grasscutter.getLogger().debug("Registered command: " + label);
|
||||
label = label.toLowerCase();
|
||||
|
||||
// Get command data.
|
||||
Command annotation = command.getClass().getAnnotation(Command.class);
|
||||
this.annotations.put(label, annotation);
|
||||
this.commands.put(label, command);
|
||||
|
||||
// Register aliases.
|
||||
for (String alias : annotation.aliases()) {
|
||||
this.aliases.put(alias, command);
|
||||
this.annotations.put(alias, annotation);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a registered command handler.
|
||||
*
|
||||
* @param label The command label.
|
||||
* @return Instance chaining.
|
||||
*/
|
||||
public CommandMap unregisterCommand(String label) {
|
||||
Grasscutter.getLogger().debug("Unregistered command: " + label);
|
||||
|
||||
CommandHandler handler = this.commands.get(label);
|
||||
if (handler == null) return this;
|
||||
|
||||
Command annotation = handler.getClass().getAnnotation(Command.class);
|
||||
this.annotations.remove(label);
|
||||
this.commands.remove(label);
|
||||
|
||||
// Unregister aliases.
|
||||
for (String alias : annotation.aliases()) {
|
||||
this.aliases.remove(alias);
|
||||
this.annotations.remove(alias);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<Command> getAnnotationsAsList() {
|
||||
return new ArrayList<>(this.annotations.values());
|
||||
}
|
||||
|
||||
public Map<String, Command> getAnnotations() {
|
||||
return new LinkedHashMap<>(this.annotations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all registered commands.
|
||||
*
|
||||
* @return All command handlers as a list.
|
||||
*/
|
||||
public List<CommandHandler> getHandlersAsList() {
|
||||
return new ArrayList<>(this.commands.values());
|
||||
}
|
||||
|
||||
public Map<String, CommandHandler> getHandlers() {
|
||||
return this.commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a handler by label/alias.
|
||||
*
|
||||
* @param label The command label.
|
||||
* @return The command handler.
|
||||
*/
|
||||
public CommandHandler getHandler(String label) {
|
||||
CommandHandler handler = this.commands.get(label);
|
||||
if (handler == null) {
|
||||
// Try getting by alias
|
||||
handler = this.aliases.get(label);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
private Player getTargetPlayer(
|
||||
String playerId, Player player, Player targetPlayer, List<String> args) {
|
||||
// Top priority: If any @UID argument is present, override targetPlayer with it.
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
String arg = args.get(i);
|
||||
if (arg.startsWith("@")) {
|
||||
arg = args.remove(i).substring(1);
|
||||
if (arg.equals("")) {
|
||||
// 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
|
||||
// themselves or anyone else.
|
||||
return null;
|
||||
}
|
||||
int uid = getUidFromString(arg);
|
||||
if (uid == INVALID_UID) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.invalid.uid");
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid, true);
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return targetPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (targetPlayer != null) {
|
||||
return targetPlayer;
|
||||
}
|
||||
|
||||
// Next priority: Use previously-set target. (see /target [[@]UID])
|
||||
if (targetPlayerIds.containsKey(playerId)) {
|
||||
targetPlayer =
|
||||
Grasscutter.getGameServer().getPlayerByUid(targetPlayerIds.getInt(playerId), true);
|
||||
// We check every time in case the target is deleted after being targeted
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return targetPlayer;
|
||||
}
|
||||
|
||||
// Lowest priority: Target the player invoking the command. In the case of the console, this
|
||||
// will return null.
|
||||
return player;
|
||||
}
|
||||
|
||||
private boolean setPlayerTarget(String playerId, Player player, String targetUid) {
|
||||
if (targetUid.equals("")) { // Clears the default targetPlayer.
|
||||
targetPlayerIds.removeInt(playerId);
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.clear_target");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sets default targetPlayer to the UID provided.
|
||||
int uid = getUidFromString(targetUid);
|
||||
if (uid == INVALID_UID) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.invalid.uid");
|
||||
return false;
|
||||
}
|
||||
Player targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid, true);
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error");
|
||||
return false;
|
||||
}
|
||||
|
||||
targetPlayerIds.put(playerId, uid);
|
||||
String target = uid + " (" + targetPlayer.getAccount().getUsername() + ")";
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.set_target", target);
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
player,
|
||||
targetPlayer.isOnline()
|
||||
? "commands.execution.set_target_online"
|
||||
: "commands.execution.set_target_offline",
|
||||
target);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a command handler with the given arguments.
|
||||
*
|
||||
* @param player The player invoking the command or null for the server console.
|
||||
* @param rawMessage The messaged used to invoke the command.
|
||||
*/
|
||||
public void invoke(Player player, Player targetPlayer, String rawMessage) {
|
||||
// The console outputs in-game command. [{Account Username} (Player UID: {Player Uid})]
|
||||
if (SERVER.logCommands) {
|
||||
if (player != null) {
|
||||
Grasscutter.getLogger()
|
||||
.info(
|
||||
"Command used by ["
|
||||
+ player.getAccount().getUsername()
|
||||
+ " (Player UID: "
|
||||
+ player.getUid()
|
||||
+ ")]: "
|
||||
+ rawMessage);
|
||||
} else {
|
||||
Grasscutter.getLogger().info("Command used by server console: " + rawMessage);
|
||||
}
|
||||
}
|
||||
|
||||
rawMessage = rawMessage.trim();
|
||||
if (rawMessage.length() == 0) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.not_specified");
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse message.
|
||||
String[] split = rawMessage.split(" ");
|
||||
String label = split[0].toLowerCase();
|
||||
List<String> args = new ArrayList<>(Arrays.asList(split).subList(1, split.length));
|
||||
String playerId = (player == null) ? consoleId : player.getAccount().getId();
|
||||
|
||||
// Check for special cases - currently only target command.
|
||||
if (label.startsWith("@")) { // @[UID]
|
||||
this.setPlayerTarget(playerId, player, label.substring(1));
|
||||
return;
|
||||
} else if (label.equalsIgnoreCase("target")) { // target [[@]UID]
|
||||
if (args.size() > 0) {
|
||||
String targetUidStr = args.get(0);
|
||||
if (targetUidStr.startsWith("@")) {
|
||||
targetUidStr = targetUidStr.substring(1);
|
||||
}
|
||||
this.setPlayerTarget(playerId, player, targetUidStr);
|
||||
return;
|
||||
} else {
|
||||
this.setPlayerTarget(playerId, player, "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get command handler.
|
||||
CommandHandler handler = this.getHandler(label);
|
||||
|
||||
// Check if the handler is null.
|
||||
if (handler == null) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.unknown_command", label);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the command's annotation.
|
||||
Command annotation = this.annotations.get(label);
|
||||
|
||||
// Resolve targetPlayer
|
||||
try {
|
||||
targetPlayer = getTargetPlayer(playerId, player, targetPlayer, args);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for permissions.
|
||||
if (!Grasscutter.getPermissionHandler()
|
||||
.checkPermission(
|
||||
player,
|
||||
targetPlayer,
|
||||
annotation.permission(),
|
||||
this.annotations.get(label).permissionTargeted())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if command has unfulfilled constraints on targetPlayer
|
||||
Command.TargetRequirement targetRequirement = annotation.targetRequirement();
|
||||
if (targetRequirement != Command.TargetRequirement.NONE) {
|
||||
if (targetPlayer == null) {
|
||||
handler.sendUsageMessage(player);
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((targetRequirement == Command.TargetRequirement.ONLINE) && !targetPlayer.isOnline()) {
|
||||
handler.sendUsageMessage(player);
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_online");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((targetRequirement == Command.TargetRequirement.OFFLINE) && targetPlayer.isOnline()) {
|
||||
handler.sendUsageMessage(player);
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_offline");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy player and handler to final properties.
|
||||
final Player targetPlayerF = targetPlayer; // 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.
|
||||
Runnable runnable = () -> handlerF.execute(player, targetPlayerF, args);
|
||||
if (annotation.threading()) {
|
||||
new Thread(runnable).start();
|
||||
} else {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
/** Scans for all classes annotated with {@link Command} and registers them. */
|
||||
private void scan() {
|
||||
Reflections reflector = Grasscutter.reflector;
|
||||
Set<Class<?>> classes = reflector.getTypesAnnotatedWith(Command.class);
|
||||
|
||||
classes.forEach(
|
||||
annotated -> {
|
||||
try {
|
||||
Command cmdData = annotated.getAnnotation(Command.class);
|
||||
Object object = annotated.getDeclaredConstructor().newInstance();
|
||||
if (object instanceof CommandHandler)
|
||||
this.registerCommand(cmdData.label(), (CommandHandler) object);
|
||||
else
|
||||
Grasscutter.getLogger()
|
||||
.error("Class " + annotated.getName() + " is not a CommandHandler!");
|
||||
} catch (Exception exception) {
|
||||
Grasscutter.getLogger()
|
||||
.error(
|
||||
"Failed to register command handler for " + annotated.getSimpleName(),
|
||||
exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,33 @@
|
||||
package emu.grasscutter.command;
|
||||
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
public class DefaultPermissionHandler implements PermissionHandler {
|
||||
@Override
|
||||
public boolean EnablePermissionCommand() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPermission(
|
||||
Player player, Player targetPlayer, String permissionNode, String permissionNodeTargeted) {
|
||||
if (player == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Account account = player.getAccount();
|
||||
if (player != targetPlayer) { // Additional permission required for targeting another player
|
||||
if (!permissionNodeTargeted.isEmpty() && !account.hasPermission(permissionNodeTargeted)) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.permission_error");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!permissionNode.isEmpty() && !account.hasPermission(permissionNode)) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.permission_error");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command;
|
||||
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
public class DefaultPermissionHandler implements PermissionHandler {
|
||||
@Override
|
||||
public boolean EnablePermissionCommand() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPermission(
|
||||
Player player, Player targetPlayer, String permissionNode, String permissionNodeTargeted) {
|
||||
if (player == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Account account = player.getAccount();
|
||||
if (player != targetPlayer) { // Additional permission required for targeting another player
|
||||
if (!permissionNodeTargeted.isEmpty() && !account.hasPermission(permissionNodeTargeted)) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.permission_error");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!permissionNode.isEmpty() && !account.hasPermission(permissionNode)) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.permission_error");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package emu.grasscutter.command;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
public interface PermissionHandler {
|
||||
boolean EnablePermissionCommand();
|
||||
|
||||
boolean checkPermission(
|
||||
Player player, Player targetPlayer, String permissionNode, String permissionNodeTargeted);
|
||||
}
|
||||
package emu.grasscutter.command;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
public interface PermissionHandler {
|
||||
boolean EnablePermissionCommand();
|
||||
|
||||
boolean checkPermission(
|
||||
Player player, Player targetPlayer, String permissionNode, String permissionNodeTargeted);
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ public final class AccountCommand implements CommandHandler {
|
||||
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) {
|
||||
if (args.size() < 3) {
|
||||
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]");
|
||||
return;
|
||||
}
|
||||
@ -60,10 +60,10 @@ public final class AccountCommand implements CommandHandler {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.account.invalid"));
|
||||
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) {
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
"EXPERIMENTAL_RealPassword requires argument 2 to be a password, not a uid");
|
||||
sender,
|
||||
"EXPERIMENTAL_RealPassword requires argument 2 to be a password, not a uid");
|
||||
CommandHandler.sendMessage(
|
||||
sender, "Usage: account create <username> <password> [uid]");
|
||||
sender, "Usage: account create <username> <password> [uid]");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -90,7 +90,7 @@ public final class AccountCommand implements CommandHandler {
|
||||
account.save(); // Save account to database.
|
||||
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.account.create", account.getReservedPlayerUid()));
|
||||
sender, translate(sender, "commands.account.create", account.getReservedPlayerUid()));
|
||||
}
|
||||
}
|
||||
case "delete" -> {
|
||||
@ -106,7 +106,7 @@ public final class AccountCommand implements CommandHandler {
|
||||
case "resetpass" -> {
|
||||
if (!Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, "resetpass requires EXPERIMENTAL_RealPassword to be true.");
|
||||
sender, "resetpass requires EXPERIMENTAL_RealPassword to be true.");
|
||||
return;
|
||||
}
|
||||
if (args.size() != 3) {
|
||||
@ -128,23 +128,27 @@ public final class AccountCommand implements CommandHandler {
|
||||
}
|
||||
case "list" -> {
|
||||
CommandHandler.sendMessage(sender, "Note: This command might take a while to complete.");
|
||||
CommandHandler.sendMessage(sender,
|
||||
"Accounts: \n" + DatabaseManager.getAccountDatastore()
|
||||
.find(Account.class).stream()
|
||||
.map(acc -> "%s: %s (%s)".formatted(
|
||||
acc.getId(), acc.getUsername(),
|
||||
acc.getReservedPlayerUid() == 0 ?
|
||||
this.getPlayerUid(acc) :
|
||||
acc.getReservedPlayerUid()))
|
||||
.collect(Collectors.joining("\n"))
|
||||
);
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
"Accounts: \n"
|
||||
+ DatabaseManager.getAccountDatastore().find(Account.class).stream()
|
||||
.map(
|
||||
acc ->
|
||||
"%s: %s (%s)"
|
||||
.formatted(
|
||||
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.
|
||||
* If the player is not found, returns "no UID".
|
||||
* Returns the UID of the player associated with the given account. If the player is not found,
|
||||
* returns "no UID".
|
||||
*
|
||||
* @param account The account to get the UID of.
|
||||
* @return The UID of the player associated with the given account.
|
||||
|
@ -1,78 +1,78 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketServerAnnounceNotify;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
@Command(
|
||||
label = "announce",
|
||||
usage = {"<content>", "refresh", "(tpl|revoke) <templateId>"},
|
||||
permission = "server.announce",
|
||||
aliases = {"a"},
|
||||
targetRequirement = Command.TargetRequirement.NONE)
|
||||
public final class AnnounceCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
var manager = Grasscutter.getGameServer().getAnnouncementSystem();
|
||||
if (args.size() < 1) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args.get(0)) {
|
||||
case "tpl":
|
||||
if (args.size() < 2) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
var templateId = Integer.parseInt(args.get(1));
|
||||
var tpl = manager.getAnnounceConfigItemMap().get(templateId);
|
||||
if (tpl == null) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.announce.not_found", templateId));
|
||||
return;
|
||||
}
|
||||
|
||||
manager.broadcast(Collections.singletonList(tpl));
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.announce.send_success", tpl.getTemplateId()));
|
||||
break;
|
||||
|
||||
case "refresh":
|
||||
var num = manager.refresh();
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.announce.refresh_success", num));
|
||||
break;
|
||||
|
||||
case "revoke":
|
||||
if (args.size() < 2) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
var templateId1 = Integer.parseInt(args.get(1));
|
||||
manager.revoke(templateId1);
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.announce.revoke_done", templateId1));
|
||||
break;
|
||||
|
||||
default:
|
||||
var id = new Random().nextInt(10000, 99999);
|
||||
var text = String.join(" ", args);
|
||||
manager
|
||||
.getOnlinePlayers()
|
||||
.forEach(i -> i.sendPacket(new PacketServerAnnounceNotify(text, id)));
|
||||
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.announce.send_success", id));
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketServerAnnounceNotify;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
@Command(
|
||||
label = "announce",
|
||||
usage = {"<content>", "refresh", "(tpl|revoke) <templateId>"},
|
||||
permission = "server.announce",
|
||||
aliases = {"a"},
|
||||
targetRequirement = Command.TargetRequirement.NONE)
|
||||
public final class AnnounceCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
var manager = Grasscutter.getGameServer().getAnnouncementSystem();
|
||||
if (args.size() < 1) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args.get(0)) {
|
||||
case "tpl":
|
||||
if (args.size() < 2) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
var templateId = Integer.parseInt(args.get(1));
|
||||
var tpl = manager.getAnnounceConfigItemMap().get(templateId);
|
||||
if (tpl == null) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.announce.not_found", templateId));
|
||||
return;
|
||||
}
|
||||
|
||||
manager.broadcast(Collections.singletonList(tpl));
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.announce.send_success", tpl.getTemplateId()));
|
||||
break;
|
||||
|
||||
case "refresh":
|
||||
var num = manager.refresh();
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.announce.refresh_success", num));
|
||||
break;
|
||||
|
||||
case "revoke":
|
||||
if (args.size() < 2) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
var templateId1 = Integer.parseInt(args.get(1));
|
||||
manager.revoke(templateId1);
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.announce.revoke_done", templateId1));
|
||||
break;
|
||||
|
||||
default:
|
||||
var id = new Random().nextInt(10000, 99999);
|
||||
var text = String.join(" ", args);
|
||||
manager
|
||||
.getOnlinePlayers()
|
||||
.forEach(i -> i.sendPacket(new PacketServerAnnounceNotify(text, id)));
|
||||
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.announce.send_success", id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,62 +1,62 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "ban",
|
||||
usage = {"[<time> [<reason>]]"},
|
||||
permission = "server.ban",
|
||||
targetRequirement = Command.TargetRequirement.PLAYER)
|
||||
public final class BanCommand implements CommandHandler {
|
||||
|
||||
private boolean banAccount(Player targetPlayer, int time, String reason) {
|
||||
Account account = targetPlayer.getAccount();
|
||||
|
||||
if (account == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
account.setBanReason(reason);
|
||||
account.setBanEndTime(time);
|
||||
account.setBanStartTime((int) System.currentTimeMillis() / 1000);
|
||||
account.setBanned(true);
|
||||
account.save();
|
||||
|
||||
GameSession session = targetPlayer.getSession();
|
||||
if (session != null) {
|
||||
session.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
int time = 2051190000;
|
||||
String reason = "Reason not specified.";
|
||||
|
||||
switch (args.size()) {
|
||||
case 2:
|
||||
reason = args.get(1); // Fall-through
|
||||
case 1:
|
||||
try {
|
||||
time = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.ban.invalid_time");
|
||||
return;
|
||||
} // Fall-through, unimportant
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (banAccount(targetPlayer, time, reason)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.ban.success");
|
||||
} else {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.ban.failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "ban",
|
||||
usage = {"[<time> [<reason>]]"},
|
||||
permission = "server.ban",
|
||||
targetRequirement = Command.TargetRequirement.PLAYER)
|
||||
public final class BanCommand implements CommandHandler {
|
||||
|
||||
private boolean banAccount(Player targetPlayer, int time, String reason) {
|
||||
Account account = targetPlayer.getAccount();
|
||||
|
||||
if (account == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
account.setBanReason(reason);
|
||||
account.setBanEndTime(time);
|
||||
account.setBanStartTime((int) System.currentTimeMillis() / 1000);
|
||||
account.setBanned(true);
|
||||
account.save();
|
||||
|
||||
GameSession session = targetPlayer.getSession();
|
||||
if (session != null) {
|
||||
session.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
int time = 2051190000;
|
||||
String reason = "Reason not specified.";
|
||||
|
||||
switch (args.size()) {
|
||||
case 2:
|
||||
reason = args.get(1); // Fall-through
|
||||
case 1:
|
||||
try {
|
||||
time = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.ban.invalid_time");
|
||||
return;
|
||||
} // Fall-through, unimportant
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (banAccount(targetPlayer, time, reason)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.ban.success");
|
||||
} else {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.ban.failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,105 +1,105 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.command.CommandHelpers.*;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.Inventory;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import lombok.Setter;
|
||||
|
||||
@Command(
|
||||
label = "clear",
|
||||
usage = {"(all|wp|art|mat) [lv<max level>] [r<max refinement>] [<max rarity>*]"},
|
||||
permission = "player.clearinv",
|
||||
permissionTargeted = "player.clearinv.others")
|
||||
public final class ClearCommand implements CommandHandler {
|
||||
|
||||
private static final Map<Pattern, BiConsumer<ClearItemParameters, Integer>> intCommandHandlers =
|
||||
Map.ofEntries(
|
||||
Map.entry(lvlRegex, ClearItemParameters::setLvl),
|
||||
Map.entry(refineRegex, ClearItemParameters::setRefinement),
|
||||
Map.entry(rankRegex, ClearItemParameters::setRank));
|
||||
|
||||
private Stream<GameItem> getOther(
|
||||
ItemType type, Inventory playerInventory, ClearItemParameters param) {
|
||||
return playerInventory.getItems().values().stream()
|
||||
.filter(item -> item.getItemType() == type)
|
||||
.filter(item -> item.getItemData().getRankLevel() <= param.rank)
|
||||
.filter(item -> !item.isLocked() && !item.isEquipped());
|
||||
}
|
||||
|
||||
private Stream<GameItem> getWeapons(Inventory playerInventory, ClearItemParameters param) {
|
||||
return getOther(ItemType.ITEM_WEAPON, playerInventory, param)
|
||||
.filter(item -> item.getLevel() <= param.lvl)
|
||||
.filter(item -> item.getRefinement() < param.refinement);
|
||||
}
|
||||
|
||||
private Stream<GameItem> getRelics(Inventory playerInventory, ClearItemParameters param) {
|
||||
return getOther(ItemType.ITEM_RELIQUARY, playerInventory, param)
|
||||
.filter(item -> item.getLevel() <= param.lvl + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Inventory playerInventory = targetPlayer.getInventory();
|
||||
ClearItemParameters param = new ClearItemParameters();
|
||||
|
||||
// Extract any tagged int arguments (e.g. "lv90", "x100", "r5")
|
||||
parseIntParameters(args, param, intCommandHandlers);
|
||||
|
||||
if (args.size() < 1) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
String playerString = targetPlayer.getNickname(); // Should probably be UID instead but whatever
|
||||
switch (args.get(0)) {
|
||||
case "wp" -> {
|
||||
playerInventory.removeItems(getWeapons(playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.weapons", playerString);
|
||||
}
|
||||
case "art" -> {
|
||||
playerInventory.removeItems(getRelics(playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.artifacts", playerString);
|
||||
}
|
||||
case "mat" -> {
|
||||
playerInventory.removeItems(
|
||||
getOther(ItemType.ITEM_MATERIAL, playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.materials", playerString);
|
||||
}
|
||||
case "all" -> {
|
||||
playerInventory.removeItems(getRelics(playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.artifacts", playerString);
|
||||
playerInventory.removeItems(getWeapons(playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.weapons", playerString);
|
||||
playerInventory.removeItems(
|
||||
getOther(ItemType.ITEM_MATERIAL, playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.materials", playerString);
|
||||
playerInventory.removeItems(
|
||||
getOther(ItemType.ITEM_FURNITURE, playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.furniture", playerString);
|
||||
playerInventory.removeItems(
|
||||
getOther(ItemType.ITEM_DISPLAY, playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.displays", playerString);
|
||||
playerInventory.removeItems(
|
||||
getOther(ItemType.ITEM_VIRTUAL, playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.virtuals", playerString);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.everything", playerString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ClearItemParameters {
|
||||
@Setter public int lvl = 1;
|
||||
@Setter public int refinement = 1;
|
||||
@Setter public int rank = 4;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.command.CommandHelpers.*;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.Inventory;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import lombok.Setter;
|
||||
|
||||
@Command(
|
||||
label = "clear",
|
||||
usage = {"(all|wp|art|mat) [lv<max level>] [r<max refinement>] [<max rarity>*]"},
|
||||
permission = "player.clearinv",
|
||||
permissionTargeted = "player.clearinv.others")
|
||||
public final class ClearCommand implements CommandHandler {
|
||||
|
||||
private static final Map<Pattern, BiConsumer<ClearItemParameters, Integer>> intCommandHandlers =
|
||||
Map.ofEntries(
|
||||
Map.entry(lvlRegex, ClearItemParameters::setLvl),
|
||||
Map.entry(refineRegex, ClearItemParameters::setRefinement),
|
||||
Map.entry(rankRegex, ClearItemParameters::setRank));
|
||||
|
||||
private Stream<GameItem> getOther(
|
||||
ItemType type, Inventory playerInventory, ClearItemParameters param) {
|
||||
return playerInventory.getItems().values().stream()
|
||||
.filter(item -> item.getItemType() == type)
|
||||
.filter(item -> item.getItemData().getRankLevel() <= param.rank)
|
||||
.filter(item -> !item.isLocked() && !item.isEquipped());
|
||||
}
|
||||
|
||||
private Stream<GameItem> getWeapons(Inventory playerInventory, ClearItemParameters param) {
|
||||
return getOther(ItemType.ITEM_WEAPON, playerInventory, param)
|
||||
.filter(item -> item.getLevel() <= param.lvl)
|
||||
.filter(item -> item.getRefinement() < param.refinement);
|
||||
}
|
||||
|
||||
private Stream<GameItem> getRelics(Inventory playerInventory, ClearItemParameters param) {
|
||||
return getOther(ItemType.ITEM_RELIQUARY, playerInventory, param)
|
||||
.filter(item -> item.getLevel() <= param.lvl + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Inventory playerInventory = targetPlayer.getInventory();
|
||||
ClearItemParameters param = new ClearItemParameters();
|
||||
|
||||
// Extract any tagged int arguments (e.g. "lv90", "x100", "r5")
|
||||
parseIntParameters(args, param, intCommandHandlers);
|
||||
|
||||
if (args.size() < 1) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
String playerString = targetPlayer.getNickname(); // Should probably be UID instead but whatever
|
||||
switch (args.get(0)) {
|
||||
case "wp" -> {
|
||||
playerInventory.removeItems(getWeapons(playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.weapons", playerString);
|
||||
}
|
||||
case "art" -> {
|
||||
playerInventory.removeItems(getRelics(playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.artifacts", playerString);
|
||||
}
|
||||
case "mat" -> {
|
||||
playerInventory.removeItems(
|
||||
getOther(ItemType.ITEM_MATERIAL, playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.materials", playerString);
|
||||
}
|
||||
case "all" -> {
|
||||
playerInventory.removeItems(getRelics(playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.artifacts", playerString);
|
||||
playerInventory.removeItems(getWeapons(playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.weapons", playerString);
|
||||
playerInventory.removeItems(
|
||||
getOther(ItemType.ITEM_MATERIAL, playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.materials", playerString);
|
||||
playerInventory.removeItems(
|
||||
getOther(ItemType.ITEM_FURNITURE, playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.furniture", playerString);
|
||||
playerInventory.removeItems(
|
||||
getOther(ItemType.ITEM_DISPLAY, playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.displays", playerString);
|
||||
playerInventory.removeItems(
|
||||
getOther(ItemType.ITEM_VIRTUAL, playerInventory, param).toList());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.virtuals", playerString);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.clear.everything", playerString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ClearItemParameters {
|
||||
@Setter public int lvl = 1;
|
||||
@Setter public int refinement = 1;
|
||||
@Setter public int rank = 4;
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +1,57 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "coop",
|
||||
usage = {"[<host UID>]"},
|
||||
permission = "server.coop",
|
||||
permissionTargeted = "server.coop.others")
|
||||
public final class CoopCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Player host = sender;
|
||||
switch (args.size()) {
|
||||
case 0: // Summon target to self
|
||||
if (sender == null) { // Console doesn't have a self to summon to
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 1: // Summon target to argument
|
||||
try {
|
||||
int hostId = Integer.parseInt(args.get(0));
|
||||
host = Grasscutter.getGameServer().getPlayerByUid(hostId);
|
||||
if (host == null) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.execution.player_offline_error");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.uid");
|
||||
return;
|
||||
}
|
||||
default:
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
// There's no target==host check but this just places them in multiplayer in their own world
|
||||
// which seems fine.
|
||||
if (targetPlayer.isInMultiplayer()) {
|
||||
targetPlayer.getServer().getMultiplayerSystem().leaveCoop(targetPlayer);
|
||||
}
|
||||
host.getServer().getMultiplayerSystem().applyEnterMp(targetPlayer, host.getUid());
|
||||
targetPlayer
|
||||
.getServer()
|
||||
.getMultiplayerSystem()
|
||||
.applyEnterMpReply(host, targetPlayer.getUid(), true);
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.coop.success", targetPlayer.getNickname(), host.getNickname());
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "coop",
|
||||
usage = {"[<host UID>]"},
|
||||
permission = "server.coop",
|
||||
permissionTargeted = "server.coop.others")
|
||||
public final class CoopCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Player host = sender;
|
||||
switch (args.size()) {
|
||||
case 0: // Summon target to self
|
||||
if (sender == null) { // Console doesn't have a self to summon to
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 1: // Summon target to argument
|
||||
try {
|
||||
int hostId = Integer.parseInt(args.get(0));
|
||||
host = Grasscutter.getGameServer().getPlayerByUid(hostId);
|
||||
if (host == null) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.execution.player_offline_error");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.uid");
|
||||
return;
|
||||
}
|
||||
default:
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
// There's no target==host check but this just places them in multiplayer in their own world
|
||||
// which seems fine.
|
||||
if (targetPlayer.isInMultiplayer()) {
|
||||
targetPlayer.getServer().getMultiplayerSystem().leaveCoop(targetPlayer);
|
||||
}
|
||||
host.getServer().getMultiplayerSystem().applyEnterMp(targetPlayer, host.getUid());
|
||||
targetPlayer
|
||||
.getServer()
|
||||
.getMultiplayerSystem()
|
||||
.applyEnterMpReply(host, targetPlayer.getUid(), true);
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.coop.success", targetPlayer.getNickname(), host.getNickname());
|
||||
}
|
||||
}
|
||||
|
@ -1,50 +1,50 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "enter_dungeon",
|
||||
aliases = {"enterdungeon", "dungeon"},
|
||||
usage = {"<dungeonId>"},
|
||||
permission = "player.enterdungeon",
|
||||
permissionTargeted = "player.enterdungeon.others")
|
||||
public final class EnterDungeonCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 1) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int dungeonId = Integer.parseInt(args.get(0));
|
||||
if (dungeonId == targetPlayer.getSceneId()) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.enter_dungeon.in_dungeon_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean result =
|
||||
targetPlayer
|
||||
.getServer()
|
||||
.getDungeonSystem()
|
||||
.enterDungeon(targetPlayer.getSession().getPlayer(), 0, dungeonId);
|
||||
|
||||
if (!result) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.enter_dungeon.not_found_error"));
|
||||
} else {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.enter_dungeon.changed", dungeonId));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendUsageMessage(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "enter_dungeon",
|
||||
aliases = {"enterdungeon", "dungeon"},
|
||||
usage = {"<dungeonId>"},
|
||||
permission = "player.enterdungeon",
|
||||
permissionTargeted = "player.enterdungeon.others")
|
||||
public final class EnterDungeonCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 1) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int dungeonId = Integer.parseInt(args.get(0));
|
||||
if (dungeonId == targetPlayer.getSceneId()) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.enter_dungeon.in_dungeon_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean result =
|
||||
targetPlayer
|
||||
.getServer()
|
||||
.getDungeonSystem()
|
||||
.enterDungeon(targetPlayer.getSession().getPlayer(), 0, dungeonId);
|
||||
|
||||
if (!result) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.enter_dungeon.not_found_error"));
|
||||
} else {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.enter_dungeon.changed", dungeonId));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendUsageMessage(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
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.CommandHandler;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameDepot;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarData;
|
||||
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.ReliquaryMainPropData;
|
||||
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.props.ActionReason;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.utils.SparseSet;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static emu.grasscutter.GameConstants.*;
|
||||
import static emu.grasscutter.command.CommandHelpers.*;
|
||||
import lombok.Setter;
|
||||
|
||||
@Command(
|
||||
label = "give",
|
||||
aliases = {"g", "item", "giveitem"},
|
||||
usage = {
|
||||
"(<itemId>|<avatarId>|all|weapons|mats|avatars) [lv<level>] [r<refinement>] [x<amount>] [c<constellation>] [sl<skilllevel>]",
|
||||
"<artifactId> [lv<level>] [x<amount>] [<mainPropId>] [<appendPropId>[,<times>]]..."},
|
||||
permission = "player.give",
|
||||
permissionTargeted = "player.give.others",
|
||||
threading = true)
|
||||
label = "give",
|
||||
aliases = {"g", "item", "giveitem"},
|
||||
usage = {
|
||||
"(<itemId>|<avatarId>|all|weapons|mats|avatars) [lv<level>] [r<refinement>] [x<amount>] [c<constellation>] [sl<skilllevel>]",
|
||||
"<artifactId> [lv<level>] [x<amount>] [<mainPropId>] [<appendPropId>[,<times>]]..."
|
||||
},
|
||||
permission = "player.give",
|
||||
permissionTargeted = "player.give.others",
|
||||
threading = true)
|
||||
public final class GiveCommand implements CommandHandler {
|
||||
private static final Map<Pattern, BiConsumer<GiveItemParameters, Integer>> intCommandHandlers = Map.ofEntries(
|
||||
Map.entry(lvlRegex, GiveItemParameters::setLvl),
|
||||
Map.entry(refineRegex, GiveItemParameters::setRefinement),
|
||||
Map.entry(amountRegex, GiveItemParameters::setAmount),
|
||||
Map.entry(constellationRegex, GiveItemParameters::setConstellation),
|
||||
Map.entry(skillLevelRegex, GiveItemParameters::setSkillLevel)
|
||||
);
|
||||
private static final Map<Pattern, BiConsumer<GiveItemParameters, Integer>> intCommandHandlers =
|
||||
Map.ofEntries(
|
||||
Map.entry(lvlRegex, GiveItemParameters::setLvl),
|
||||
Map.entry(refineRegex, GiveItemParameters::setRefinement),
|
||||
Map.entry(amountRegex, GiveItemParameters::setAmount),
|
||||
Map.entry(constellationRegex, GiveItemParameters::setConstellation),
|
||||
Map.entry(skillLevelRegex, GiveItemParameters::setSkillLevel));
|
||||
|
||||
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.setLevel(level);
|
||||
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.recalcStats(true);
|
||||
avatar.save();
|
||||
@ -62,12 +70,16 @@ public final class GiveCommand implements CommandHandler {
|
||||
private static void giveAllAvatars(Player player, GiveItemParameters param) {
|
||||
int promoteLevel = Avatar.getMinPromoteLevel(param.lvl);
|
||||
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()) {
|
||||
int id = avatarData.getId();
|
||||
if (id < 10000002 || id >= 11000000) continue; // Exclude test avatars
|
||||
// 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;
|
||||
if (param.data.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
int rankLevel = param.data.getRankLevel();
|
||||
for (int i = 1; i < param.lvl; i++)
|
||||
totalExp += GameData.getWeaponExpRequired(rankLevel, i);
|
||||
for (int i = 1; i < param.lvl; i++) totalExp += GameData.getWeaponExpRequired(rankLevel, i);
|
||||
}
|
||||
|
||||
List<GameItem> items = new ArrayList<>(param.amount);
|
||||
@ -87,7 +98,7 @@ public final class GiveCommand implements CommandHandler {
|
||||
if (item.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
item.setPromoteLevel(promoteLevel);
|
||||
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);
|
||||
}
|
||||
@ -98,8 +109,7 @@ public final class GiveCommand implements CommandHandler {
|
||||
param.lvl = Math.min(param.lvl, param.data.getMaxLevel());
|
||||
int rank = param.data.getRankLevel();
|
||||
int totalExp = 0;
|
||||
for (int i = 1; i < param.lvl; i++)
|
||||
totalExp += GameData.getRelicExpRequired(rank, i);
|
||||
for (int i = 1; i < param.lvl; i++) totalExp += GameData.getRelicExpRequired(rank, i);
|
||||
|
||||
List<GameItem> items = new ArrayList<>(param.amount);
|
||||
for (int i = 0; i < param.amount; i++) {
|
||||
@ -108,8 +118,8 @@ public final class GiveCommand implements CommandHandler {
|
||||
item.setLevel(param.lvl);
|
||||
item.setTotalExp(totalExp);
|
||||
int numAffixes = param.data.getAppendPropNum() + (param.lvl - 1) / 4;
|
||||
if (param.mainPropId > 0) // Keep random mainProp if we didn't specify one
|
||||
item.setMainPropId(param.mainPropId);
|
||||
if (param.mainPropId > 0) // Keep random mainProp if we didn't specify one
|
||||
item.setMainPropId(param.mainPropId);
|
||||
if (param.appendPropIdList != null) {
|
||||
item.getAppendPropIdList().clear();
|
||||
item.getAppendPropIdList().addAll(param.appendPropIdList);
|
||||
@ -121,15 +131,17 @@ public final class GiveCommand implements CommandHandler {
|
||||
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)
|
||||
for (ReliquaryMainPropData data : GameDepot.getRelicMainPropList(itemData.getMainPropDepotId()))
|
||||
if (data.getWeight() > 0 && data.getFightProp() == prop)
|
||||
return data.getId();
|
||||
for (ReliquaryMainPropData data :
|
||||
GameDepot.getRelicMainPropList(itemData.getMainPropDepotId()))
|
||||
if (data.getWeight() > 0 && data.getFightProp() == prop) return data.getId();
|
||||
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) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
@ -142,7 +154,8 @@ public final class GiveCommand implements CommandHandler {
|
||||
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.
|
||||
try {
|
||||
return Integer.parseInt(substatText);
|
||||
@ -159,19 +172,21 @@ public final class GiveCommand implements CommandHandler {
|
||||
substatTier = Integer.parseInt(substatArgs[1]);
|
||||
}
|
||||
|
||||
List<Integer> substats = getArtifactAffixes(itemData, FightProperty.getPropByShortName(substatType));
|
||||
List<Integer> substats =
|
||||
getArtifactAffixes(itemData, FightProperty.getPropByShortName(substatType));
|
||||
|
||||
if (substats.isEmpty()) {
|
||||
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);
|
||||
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.
|
||||
// If the given argument is an integer, we use that.
|
||||
// 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);
|
||||
} catch (NumberFormatException ignored) {
|
||||
// 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.
|
||||
@ -219,7 +235,7 @@ public final class GiveCommand implements CommandHandler {
|
||||
List<GameItem> itemList = new ArrayList<>();
|
||||
for (ItemData itemdata : GameData.getItemDataMap().values()) {
|
||||
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 (itemdata.isEquip()) continue;
|
||||
|
||||
@ -239,7 +255,7 @@ public final class GiveCommand implements CommandHandler {
|
||||
List<GameItem> itemList = new ArrayList<>();
|
||||
for (ItemData itemdata : GameData.getItemDataMap().values()) {
|
||||
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 (!itemdata.isEquip()) continue;
|
||||
if (itemdata.getItemType() != ItemType.ITEM_WEAPON) continue;
|
||||
@ -262,7 +278,8 @@ public final class GiveCommand implements CommandHandler {
|
||||
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();
|
||||
|
||||
// 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
|
||||
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();
|
||||
}
|
||||
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);
|
||||
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 {
|
||||
param.amount = Integer.parseInt(args.remove(0));
|
||||
} catch (NumberFormatException e) {
|
||||
@ -382,7 +401,8 @@ public final class GiveCommand implements CommandHandler {
|
||||
if (param.avatarData != null) {
|
||||
Avatar avatar = makeAvatar(param);
|
||||
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;
|
||||
}
|
||||
// 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()) {
|
||||
case ITEM_WEAPON:
|
||||
targetPlayer.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());
|
||||
targetPlayer
|
||||
.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;
|
||||
case ITEM_RELIQUARY:
|
||||
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;
|
||||
default:
|
||||
targetPlayer.getInventory().addItem(new GameItem(param.data, param.amount), ActionReason.SubfieldDrop);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.give.given", param.amount, param.id, targetPlayer.getUid());
|
||||
targetPlayer
|
||||
.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) {
|
||||
}
|
||||
@ -418,16 +456,11 @@ public final class GiveCommand implements CommandHandler {
|
||||
|
||||
private static class GiveItemParameters {
|
||||
public int id;
|
||||
@Setter
|
||||
public int lvl = 0;
|
||||
@Setter
|
||||
public int amount = 1;
|
||||
@Setter
|
||||
public int refinement = 1;
|
||||
@Setter
|
||||
public int constellation = -1;
|
||||
@Setter
|
||||
public int skillLevel = 1;
|
||||
@Setter public int lvl = 0;
|
||||
@Setter public int amount = 1;
|
||||
@Setter public int refinement = 1;
|
||||
@Setter public int constellation = -1;
|
||||
@Setter public int skillLevel = 1;
|
||||
public int mainPropId = -1;
|
||||
public List<Integer> appendPropIdList;
|
||||
public ItemData data;
|
||||
|
@ -1,44 +1,44 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "heal",
|
||||
aliases = {"h"},
|
||||
permission = "player.heal",
|
||||
permissionTargeted = "player.heal.others")
|
||||
public final class HealCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
targetPlayer
|
||||
.getTeamManager()
|
||||
.getActiveTeam()
|
||||
.forEach(
|
||||
entity -> {
|
||||
boolean isAlive = entity.isAlive();
|
||||
entity.setFightProperty(
|
||||
FightProperty.FIGHT_PROP_CUR_HP,
|
||||
entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP));
|
||||
entity
|
||||
.getWorld()
|
||||
.broadcastPacket(
|
||||
new PacketAvatarFightPropUpdateNotify(
|
||||
entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP));
|
||||
if (!isAlive) {
|
||||
entity
|
||||
.getWorld()
|
||||
.broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
|
||||
}
|
||||
});
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.heal.success"));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "heal",
|
||||
aliases = {"h"},
|
||||
permission = "player.heal",
|
||||
permissionTargeted = "player.heal.others")
|
||||
public final class HealCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
targetPlayer
|
||||
.getTeamManager()
|
||||
.getActiveTeam()
|
||||
.forEach(
|
||||
entity -> {
|
||||
boolean isAlive = entity.isAlive();
|
||||
entity.setFightProperty(
|
||||
FightProperty.FIGHT_PROP_CUR_HP,
|
||||
entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP));
|
||||
entity
|
||||
.getWorld()
|
||||
.broadcastPacket(
|
||||
new PacketAvatarFightPropUpdateNotify(
|
||||
entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP));
|
||||
if (!isAlive) {
|
||||
entity
|
||||
.getWorld()
|
||||
.broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
|
||||
}
|
||||
});
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.heal.success"));
|
||||
}
|
||||
}
|
||||
|
@ -1,92 +1,92 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.command.CommandMap;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "help",
|
||||
usage = {"[<command>]"},
|
||||
targetRequirement = Command.TargetRequirement.NONE)
|
||||
public final class HelpCommand implements CommandHandler {
|
||||
private final boolean SHOW_COMMANDS_WITHOUT_PERMISSIONS =
|
||||
false; // TODO: Make this into a server config key
|
||||
|
||||
private String createCommand(Player player, CommandHandler command, List<String> args) {
|
||||
StringBuilder builder =
|
||||
new StringBuilder(command.getLabel())
|
||||
.append(" - ")
|
||||
.append(command.getDescriptionString(player))
|
||||
.append("\n\t")
|
||||
.append(command.getUsageString(player, args.toArray(new String[0])));
|
||||
|
||||
Command annotation = command.getClass().getAnnotation(Command.class);
|
||||
if (annotation.aliases().length > 0) {
|
||||
builder.append("\n\t").append(translate(player, "commands.help.aliases"));
|
||||
for (String alias : annotation.aliases()) {
|
||||
builder.append(alias).append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
builder.append("\n\t").append(translate(player, "commands.help.tip_need_permission"));
|
||||
if (!annotation.permission().isEmpty()) {
|
||||
builder.append(annotation.permission());
|
||||
} else {
|
||||
builder.append(translate(player, "commands.help.tip_need_no_permission"));
|
||||
}
|
||||
|
||||
if (!annotation.permissionTargeted().isEmpty()) {
|
||||
String permissionTargeted = annotation.permissionTargeted();
|
||||
builder
|
||||
.append(" ")
|
||||
.append(translate(player, "commands.help.tip_permission_targeted", permissionTargeted));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player player, Player targetPlayer, List<String> args) {
|
||||
Account account = (player == null) ? null : player.getAccount();
|
||||
var commandMap = CommandMap.getInstance();
|
||||
List<String> commands = new ArrayList<>();
|
||||
List<String> commands_no_permission = new ArrayList<>();
|
||||
if (args.isEmpty()) {
|
||||
commandMap
|
||||
.getHandlers()
|
||||
.forEach(
|
||||
(key, command) -> {
|
||||
Command annotation = command.getClass().getAnnotation(Command.class);
|
||||
if (player == null || account.hasPermission(annotation.permission())) {
|
||||
commands.add(createCommand(player, command, args));
|
||||
} else if (SHOW_COMMANDS_WITHOUT_PERMISSIONS) {
|
||||
commands_no_permission.add(createCommand(player, command, args));
|
||||
}
|
||||
});
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.help.available_commands");
|
||||
} else {
|
||||
String command_str = args.remove(0).toLowerCase();
|
||||
CommandHandler command = commandMap.getHandler(command_str);
|
||||
if (command == null) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.command_exist_error");
|
||||
CommandHandler.sendMessage(player, "Command: " + command_str);
|
||||
return;
|
||||
} else {
|
||||
Command annotation = command.getClass().getAnnotation(Command.class);
|
||||
if (player == null || account.hasPermission(annotation.permission())) {
|
||||
commands.add(createCommand(player, command, args));
|
||||
} else {
|
||||
commands_no_permission.add(createCommand(player, command, args));
|
||||
}
|
||||
}
|
||||
}
|
||||
final String suf = "\n\t" + translate(player, "commands.help.warn_player_has_no_permission");
|
||||
commands.forEach(s -> CommandHandler.sendMessage(player, s));
|
||||
commands_no_permission.forEach(s -> CommandHandler.sendMessage(player, s + suf));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.command.CommandMap;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "help",
|
||||
usage = {"[<command>]"},
|
||||
targetRequirement = Command.TargetRequirement.NONE)
|
||||
public final class HelpCommand implements CommandHandler {
|
||||
private final boolean SHOW_COMMANDS_WITHOUT_PERMISSIONS =
|
||||
false; // TODO: Make this into a server config key
|
||||
|
||||
private String createCommand(Player player, CommandHandler command, List<String> args) {
|
||||
StringBuilder builder =
|
||||
new StringBuilder(command.getLabel())
|
||||
.append(" - ")
|
||||
.append(command.getDescriptionString(player))
|
||||
.append("\n\t")
|
||||
.append(command.getUsageString(player, args.toArray(new String[0])));
|
||||
|
||||
Command annotation = command.getClass().getAnnotation(Command.class);
|
||||
if (annotation.aliases().length > 0) {
|
||||
builder.append("\n\t").append(translate(player, "commands.help.aliases"));
|
||||
for (String alias : annotation.aliases()) {
|
||||
builder.append(alias).append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
builder.append("\n\t").append(translate(player, "commands.help.tip_need_permission"));
|
||||
if (!annotation.permission().isEmpty()) {
|
||||
builder.append(annotation.permission());
|
||||
} else {
|
||||
builder.append(translate(player, "commands.help.tip_need_no_permission"));
|
||||
}
|
||||
|
||||
if (!annotation.permissionTargeted().isEmpty()) {
|
||||
String permissionTargeted = annotation.permissionTargeted();
|
||||
builder
|
||||
.append(" ")
|
||||
.append(translate(player, "commands.help.tip_permission_targeted", permissionTargeted));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player player, Player targetPlayer, List<String> args) {
|
||||
Account account = (player == null) ? null : player.getAccount();
|
||||
var commandMap = CommandMap.getInstance();
|
||||
List<String> commands = new ArrayList<>();
|
||||
List<String> commands_no_permission = new ArrayList<>();
|
||||
if (args.isEmpty()) {
|
||||
commandMap
|
||||
.getHandlers()
|
||||
.forEach(
|
||||
(key, command) -> {
|
||||
Command annotation = command.getClass().getAnnotation(Command.class);
|
||||
if (player == null || account.hasPermission(annotation.permission())) {
|
||||
commands.add(createCommand(player, command, args));
|
||||
} else if (SHOW_COMMANDS_WITHOUT_PERMISSIONS) {
|
||||
commands_no_permission.add(createCommand(player, command, args));
|
||||
}
|
||||
});
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.help.available_commands");
|
||||
} else {
|
||||
String command_str = args.remove(0).toLowerCase();
|
||||
CommandHandler command = commandMap.getHandler(command_str);
|
||||
if (command == null) {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.generic.command_exist_error");
|
||||
CommandHandler.sendMessage(player, "Command: " + command_str);
|
||||
return;
|
||||
} else {
|
||||
Command annotation = command.getClass().getAnnotation(Command.class);
|
||||
if (player == null || account.hasPermission(annotation.permission())) {
|
||||
commands.add(createCommand(player, command, args));
|
||||
} else {
|
||||
commands_no_permission.add(createCommand(player, command, args));
|
||||
}
|
||||
}
|
||||
}
|
||||
final String suf = "\n\t" + translate(player, "commands.help.warn_player_has_no_permission");
|
||||
commands.forEach(s -> CommandHandler.sendMessage(player, s));
|
||||
commands_no_permission.forEach(s -> CommandHandler.sendMessage(player, s + suf));
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,34 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "kick",
|
||||
aliases = {"restart"},
|
||||
permissionTargeted = "server.kick")
|
||||
public final class KickCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (sender != null) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender,
|
||||
"commands.kick.player_kick_player",
|
||||
sender.getUid(),
|
||||
sender.getAccount().getUsername(),
|
||||
targetPlayer.getUid(),
|
||||
targetPlayer.getAccount().getUsername());
|
||||
} else {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender,
|
||||
"commands.kick.server_kick_player",
|
||||
targetPlayer.getUid(),
|
||||
targetPlayer.getAccount().getUsername());
|
||||
}
|
||||
|
||||
targetPlayer.getSession().close();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "kick",
|
||||
aliases = {"restart"},
|
||||
permissionTargeted = "server.kick")
|
||||
public final class KickCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (sender != null) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender,
|
||||
"commands.kick.player_kick_player",
|
||||
sender.getUid(),
|
||||
sender.getAccount().getUsername(),
|
||||
targetPlayer.getUid(),
|
||||
targetPlayer.getAccount().getUsername());
|
||||
} else {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender,
|
||||
"commands.kick.server_kick_player",
|
||||
targetPlayer.getUid(),
|
||||
targetPlayer.getAccount().getUsername());
|
||||
}
|
||||
|
||||
targetPlayer.getSession().close();
|
||||
}
|
||||
}
|
||||
|
@ -1,54 +1,54 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "killall",
|
||||
usage = {"[<sceneId>]"},
|
||||
permission = "server.killall",
|
||||
permissionTargeted = "server.killall.others")
|
||||
public final class KillAllCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Scene scene = targetPlayer.getScene();
|
||||
try {
|
||||
switch (args.size()) {
|
||||
case 0: // *No args*
|
||||
break;
|
||||
case 1: // [sceneId]
|
||||
scene = targetPlayer.getWorld().getSceneById(Integer.parseInt(args.get(0)));
|
||||
break;
|
||||
default:
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error"));
|
||||
}
|
||||
if (scene == null) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.killall.scene_not_found_in_player_world"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Separate into list to avoid concurrency issue
|
||||
final Scene sceneF = scene;
|
||||
List<GameEntity> toKill =
|
||||
sceneF.getEntities().values().stream()
|
||||
.filter(entity -> entity instanceof EntityMonster)
|
||||
.toList();
|
||||
toKill.forEach(entity -> sceneF.killEntity(entity, 0));
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(sender, "commands.killall.kill_monsters_in_scene", toKill.size(), scene.getId()));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "killall",
|
||||
usage = {"[<sceneId>]"},
|
||||
permission = "server.killall",
|
||||
permissionTargeted = "server.killall.others")
|
||||
public final class KillAllCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Scene scene = targetPlayer.getScene();
|
||||
try {
|
||||
switch (args.size()) {
|
||||
case 0: // *No args*
|
||||
break;
|
||||
case 1: // [sceneId]
|
||||
scene = targetPlayer.getWorld().getSceneById(Integer.parseInt(args.get(0)));
|
||||
break;
|
||||
default:
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error"));
|
||||
}
|
||||
if (scene == null) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.killall.scene_not_found_in_player_world"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Separate into list to avoid concurrency issue
|
||||
final Scene sceneF = scene;
|
||||
List<GameEntity> toKill =
|
||||
sceneF.getEntities().values().stream()
|
||||
.filter(entity -> entity instanceof EntityMonster)
|
||||
.toList();
|
||||
toKill.forEach(entity -> sceneF.killEntity(entity, 0));
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(sender, "commands.killall.kill_monsters_in_scene", toKill.size(), scene.getId()));
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +1,41 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.game.props.LifeState;
|
||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "killCharacter",
|
||||
aliases = {"suicide", "kill"},
|
||||
permission = "player.killcharacter",
|
||||
permissionTargeted = "player.killcharacter.others")
|
||||
public final class KillCharacterCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
|
||||
// Packets
|
||||
entity
|
||||
.getWorld()
|
||||
.broadcastPacket(
|
||||
new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
|
||||
entity
|
||||
.getWorld()
|
||||
.broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
|
||||
// remove
|
||||
targetPlayer.getScene().removeEntity(entity);
|
||||
entity.onDeath(0);
|
||||
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.killCharacter.success", targetPlayer.getNickname()));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.game.props.LifeState;
|
||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "killCharacter",
|
||||
aliases = {"suicide", "kill"},
|
||||
permission = "player.killcharacter",
|
||||
permissionTargeted = "player.killcharacter.others")
|
||||
public final class KillCharacterCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
|
||||
// Packets
|
||||
entity
|
||||
.getWorld()
|
||||
.broadcastPacket(
|
||||
new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
|
||||
entity
|
||||
.getWorld()
|
||||
.broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
|
||||
// remove
|
||||
targetPlayer.getScene().removeEntity(entity);
|
||||
entity.onDeath(0);
|
||||
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.killCharacter.success", targetPlayer.getNickname()));
|
||||
}
|
||||
}
|
||||
|
@ -1,58 +1,58 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@Command(
|
||||
label = "language",
|
||||
usage = {"[<language code>]"},
|
||||
aliases = {"lang"},
|
||||
targetRequirement = Command.TargetRequirement.NONE)
|
||||
public final class LanguageCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.isEmpty()) {
|
||||
String curLangCode = null;
|
||||
if (sender != null) {
|
||||
curLangCode = Utils.getLanguageCode(sender.getAccount().getLocale());
|
||||
} else {
|
||||
curLangCode = Grasscutter.getLanguage().getLanguageCode();
|
||||
}
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.language.current_language", curLangCode));
|
||||
return;
|
||||
}
|
||||
|
||||
String langCode = args.get(0);
|
||||
|
||||
var languageInst = Grasscutter.getLanguage(langCode);
|
||||
var actualLangCode = languageInst.getLanguageCode();
|
||||
var locale = Locale.forLanguageTag(actualLangCode);
|
||||
if (sender != null) {
|
||||
var account = sender.getAccount();
|
||||
account.setLocale(locale);
|
||||
account.save();
|
||||
} else {
|
||||
Grasscutter.setLanguage(languageInst);
|
||||
var config = Grasscutter.getConfig();
|
||||
config.language.language = locale;
|
||||
Grasscutter.saveConfig(config);
|
||||
}
|
||||
|
||||
if (!langCode.equals(actualLangCode)) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.language.language_not_found", langCode));
|
||||
}
|
||||
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.language.language_changed", actualLangCode));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@Command(
|
||||
label = "language",
|
||||
usage = {"[<language code>]"},
|
||||
aliases = {"lang"},
|
||||
targetRequirement = Command.TargetRequirement.NONE)
|
||||
public final class LanguageCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.isEmpty()) {
|
||||
String curLangCode = null;
|
||||
if (sender != null) {
|
||||
curLangCode = Utils.getLanguageCode(sender.getAccount().getLocale());
|
||||
} else {
|
||||
curLangCode = Grasscutter.getLanguage().getLanguageCode();
|
||||
}
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.language.current_language", curLangCode));
|
||||
return;
|
||||
}
|
||||
|
||||
String langCode = args.get(0);
|
||||
|
||||
var languageInst = Grasscutter.getLanguage(langCode);
|
||||
var actualLangCode = languageInst.getLanguageCode();
|
||||
var locale = Locale.forLanguageTag(actualLangCode);
|
||||
if (sender != null) {
|
||||
var account = sender.getAccount();
|
||||
account.setLocale(locale);
|
||||
account.save();
|
||||
} else {
|
||||
Grasscutter.setLanguage(languageInst);
|
||||
var config = Grasscutter.getConfig();
|
||||
config.language.language = locale;
|
||||
Grasscutter.saveConfig(config);
|
||||
}
|
||||
|
||||
if (!langCode.equals(actualLangCode)) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.language.language_not_found", langCode));
|
||||
}
|
||||
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.language.language_changed", actualLangCode));
|
||||
}
|
||||
}
|
||||
|
@ -1,56 +1,56 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Command(
|
||||
label = "list",
|
||||
aliases = {"players"},
|
||||
usage = {"[<UID>]"},
|
||||
targetRequirement = Command.TargetRequirement.NONE)
|
||||
public final class ListCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Map<Integer, Player> playersMap = Grasscutter.getGameServer().getPlayers();
|
||||
boolean needUID = false;
|
||||
|
||||
if (args.size() > 0) {
|
||||
needUID = args.get(0).equals("uid");
|
||||
}
|
||||
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.list.success", playersMap.size()));
|
||||
|
||||
if (playersMap.size() != 0) {
|
||||
StringBuilder playerSet = new StringBuilder();
|
||||
boolean finalNeedUID = needUID;
|
||||
|
||||
playersMap
|
||||
.values()
|
||||
.forEach(
|
||||
player -> {
|
||||
playerSet.append(player.getNickname());
|
||||
|
||||
if (finalNeedUID) {
|
||||
if (sender != null) {
|
||||
playerSet.append(" <color=green>(").append(player.getUid()).append(")</color>");
|
||||
} else {
|
||||
playerSet.append(" (").append(player.getUid()).append(")");
|
||||
}
|
||||
}
|
||||
|
||||
playerSet.append(", ");
|
||||
});
|
||||
|
||||
String players = playerSet.toString();
|
||||
CommandHandler.sendMessage(sender, players.substring(0, players.length() - 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Command(
|
||||
label = "list",
|
||||
aliases = {"players"},
|
||||
usage = {"[<UID>]"},
|
||||
targetRequirement = Command.TargetRequirement.NONE)
|
||||
public final class ListCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Map<Integer, Player> playersMap = Grasscutter.getGameServer().getPlayers();
|
||||
boolean needUID = false;
|
||||
|
||||
if (args.size() > 0) {
|
||||
needUID = args.get(0).equals("uid");
|
||||
}
|
||||
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.list.success", playersMap.size()));
|
||||
|
||||
if (playersMap.size() != 0) {
|
||||
StringBuilder playerSet = new StringBuilder();
|
||||
boolean finalNeedUID = needUID;
|
||||
|
||||
playersMap
|
||||
.values()
|
||||
.forEach(
|
||||
player -> {
|
||||
playerSet.append(player.getNickname());
|
||||
|
||||
if (finalNeedUID) {
|
||||
if (sender != null) {
|
||||
playerSet.append(" <color=green>(").append(player.getUid()).append(")</color>");
|
||||
} else {
|
||||
playerSet.append(" (").append(player.getUid()).append(")");
|
||||
}
|
||||
}
|
||||
|
||||
playerSet.append(", ");
|
||||
});
|
||||
|
||||
String players = playerSet.toString();
|
||||
CommandHandler.sendMessage(sender, players.substring(0, players.length() - 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,74 +1,74 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.Command.TargetRequirement;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "permission",
|
||||
usage = {"add <permission>", "remove <permission>", "clear", "list"},
|
||||
permission = "permission",
|
||||
targetRequirement = TargetRequirement.PLAYER)
|
||||
public final class PermissionCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.isEmpty() || args.size() > 2) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Grasscutter.getPermissionHandler().EnablePermissionCommand()) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.permission_error");
|
||||
return;
|
||||
}
|
||||
|
||||
String action = args.get(0);
|
||||
String permission = "";
|
||||
if (args.size() > 1) {
|
||||
permission = args.get(1);
|
||||
}
|
||||
|
||||
Account account = targetPlayer.getAccount();
|
||||
if (account == null) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.account_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
default:
|
||||
sendUsageMessage(sender);
|
||||
break;
|
||||
case "add":
|
||||
if (permission.isEmpty()) {
|
||||
sendUsageMessage(sender);
|
||||
} else if (account.addPermission(permission)) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.add"));
|
||||
} else
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.has_error"));
|
||||
break;
|
||||
case "remove":
|
||||
if (account.removePermission(permission)) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.remove"));
|
||||
} else
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.permission.not_have_error"));
|
||||
break;
|
||||
case "clear":
|
||||
account.clearPermission();
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.remove"));
|
||||
break;
|
||||
case "list":
|
||||
CommandHandler.sendMessage(sender, String.join("\n", account.getPermissions()));
|
||||
break;
|
||||
}
|
||||
|
||||
account.save();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.Command.TargetRequirement;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "permission",
|
||||
usage = {"add <permission>", "remove <permission>", "clear", "list"},
|
||||
permission = "permission",
|
||||
targetRequirement = TargetRequirement.PLAYER)
|
||||
public final class PermissionCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.isEmpty() || args.size() > 2) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Grasscutter.getPermissionHandler().EnablePermissionCommand()) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.permission_error");
|
||||
return;
|
||||
}
|
||||
|
||||
String action = args.get(0);
|
||||
String permission = "";
|
||||
if (args.size() > 1) {
|
||||
permission = args.get(1);
|
||||
}
|
||||
|
||||
Account account = targetPlayer.getAccount();
|
||||
if (account == null) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.account_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
default:
|
||||
sendUsageMessage(sender);
|
||||
break;
|
||||
case "add":
|
||||
if (permission.isEmpty()) {
|
||||
sendUsageMessage(sender);
|
||||
} else if (account.addPermission(permission)) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.add"));
|
||||
} else
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.has_error"));
|
||||
break;
|
||||
case "remove":
|
||||
if (account.removePermission(permission)) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.remove"));
|
||||
} else
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.permission.not_have_error"));
|
||||
break;
|
||||
case "clear":
|
||||
account.clearPermission();
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.remove"));
|
||||
break;
|
||||
case "list":
|
||||
CommandHandler.sendMessage(sender, String.join("\n", account.getPermissions()));
|
||||
break;
|
||||
}
|
||||
|
||||
account.save();
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,29 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "position",
|
||||
aliases = {"pos"})
|
||||
public final class PositionCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Position pos = targetPlayer.getPosition();
|
||||
Position rot = targetPlayer.getRotation();
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender,
|
||||
"commands.position.success",
|
||||
pos.getX(),
|
||||
pos.getY(),
|
||||
pos.getZ(),
|
||||
rot.getX(),
|
||||
rot.getY(),
|
||||
rot.getZ(),
|
||||
targetPlayer.getSceneId());
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "position",
|
||||
aliases = {"pos"})
|
||||
public final class PositionCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Position pos = targetPlayer.getPosition();
|
||||
Position rot = targetPlayer.getRotation();
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender,
|
||||
"commands.position.success",
|
||||
pos.getX(),
|
||||
pos.getY(),
|
||||
pos.getZ(),
|
||||
rot.getX(),
|
||||
rot.getY(),
|
||||
rot.getZ(),
|
||||
targetPlayer.getSceneId());
|
||||
}
|
||||
}
|
||||
|
@ -1,43 +1,43 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "resetConst",
|
||||
aliases = {"resetconstellation"},
|
||||
usage = "[all]",
|
||||
permission = "player.resetconstellation",
|
||||
permissionTargeted = "player.resetconstellation.others")
|
||||
public final class ResetConstCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() > 0 && args.get(0).equalsIgnoreCase("all")) {
|
||||
targetPlayer.getAvatars().forEach(this::resetConstellation);
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.resetConst.reset_all"));
|
||||
} else {
|
||||
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Avatar avatar = entity.getAvatar();
|
||||
this.resetConstellation(avatar);
|
||||
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(sender, "commands.resetConst.success", avatar.getAvatarData().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
private void resetConstellation(Avatar avatar) {
|
||||
avatar.forceConstellationLevel(-1);
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "resetConst",
|
||||
aliases = {"resetconstellation"},
|
||||
usage = "[all]",
|
||||
permission = "player.resetconstellation",
|
||||
permissionTargeted = "player.resetconstellation.others")
|
||||
public final class ResetConstCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() > 0 && args.get(0).equalsIgnoreCase("all")) {
|
||||
targetPlayer.getAvatars().forEach(this::resetConstellation);
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.resetConst.reset_all"));
|
||||
} else {
|
||||
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Avatar avatar = entity.getAvatar();
|
||||
this.resetConstellation(avatar);
|
||||
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(sender, "commands.resetConst.success", avatar.getAvatarData().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
private void resetConstellation(Avatar avatar) {
|
||||
avatar.forceConstellationLevel(-1);
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,23 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "resetShopLimit",
|
||||
aliases = {"resetshop"},
|
||||
permission = "server.resetshop",
|
||||
permissionTargeted = "server.resetshop.others")
|
||||
public final class ResetShopLimitCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
targetPlayer.getShopLimit().forEach(x -> x.setNextRefreshTime(0));
|
||||
targetPlayer.save();
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.resetShopLimit.success"));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "resetShopLimit",
|
||||
aliases = {"resetshop"},
|
||||
permission = "server.resetshop",
|
||||
permissionTargeted = "server.resetshop.others")
|
||||
public final class ResetShopLimitCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
targetPlayer.getShopLimit().forEach(x -> x.setNextRefreshTime(0));
|
||||
targetPlayer.save();
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.resetShopLimit.success"));
|
||||
}
|
||||
}
|
||||
|
@ -1,241 +1,241 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.mail.Mail;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Command(
|
||||
label = "sendMail",
|
||||
usage = {"(<userId>|all) [<templateId>]", "help"},
|
||||
permission = "server.sendmail",
|
||||
targetRequirement = Command.TargetRequirement.NONE)
|
||||
public final class SendMailCommand implements CommandHandler {
|
||||
|
||||
// TODO: You should be able to do /sendmail and then just send subsequent messages until you
|
||||
// finish
|
||||
// However, due to the current nature of the command system, I don't think this is possible
|
||||
// without rewriting
|
||||
// the command system (again). For now this will do
|
||||
|
||||
// Key = User that is constructing the mail.
|
||||
private static final HashMap<Integer, MailBuilder> mailBeingConstructed =
|
||||
new HashMap<Integer, MailBuilder>();
|
||||
|
||||
// Yes this is awful and I hate it.
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
int senderId;
|
||||
if (sender != null) {
|
||||
senderId = sender.getUid();
|
||||
} else {
|
||||
senderId = -1;
|
||||
}
|
||||
|
||||
if (!mailBeingConstructed.containsKey(senderId)) {
|
||||
switch (args.size()) {
|
||||
case 1 -> {
|
||||
MailBuilder mailBuilder;
|
||||
switch (args.get(0).toLowerCase()) {
|
||||
case "help" -> {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
case "all" -> mailBuilder = new MailBuilder(true, new Mail());
|
||||
default -> {
|
||||
if (DatabaseHelper.getPlayerByUid(Integer.parseInt(args.get(0))) != null) {
|
||||
mailBuilder = new MailBuilder(Integer.parseInt(args.get(0)), new Mail());
|
||||
} else {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.user_not_exist", args.get(0)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
mailBeingConstructed.put(senderId, mailBuilder);
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.start_composition"));
|
||||
}
|
||||
case 2 -> CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.templates"));
|
||||
default -> CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.invalid_arguments"));
|
||||
}
|
||||
} else {
|
||||
MailBuilder mailBuilder = mailBeingConstructed.get(senderId);
|
||||
|
||||
if (args.size() >= 1) {
|
||||
switch (args.get(0).toLowerCase()) {
|
||||
case "stop" -> {
|
||||
mailBeingConstructed.remove(senderId);
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send_cancel"));
|
||||
}
|
||||
case "finish" -> {
|
||||
if (mailBuilder.constructionStage == 3) {
|
||||
if (!mailBuilder.sendToAll) {
|
||||
Grasscutter.getGameServer()
|
||||
.getPlayerByUid(mailBuilder.recipient, true)
|
||||
.sendMail(mailBuilder.mail);
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(sender, "commands.sendMail.send_done", mailBuilder.recipient));
|
||||
} else {
|
||||
DatabaseHelper.getByGameClass(Player.class)
|
||||
.forEach(
|
||||
player -> {
|
||||
var onlinePlayer =
|
||||
Grasscutter.getGameServer().getPlayerByUid(player.getUid(), false);
|
||||
Objects.requireNonNullElse(onlinePlayer, player)
|
||||
.sendMail(mailBuilder.mail);
|
||||
});
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.send_all_done"));
|
||||
}
|
||||
mailBeingConstructed.remove(senderId);
|
||||
} else {
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(
|
||||
sender,
|
||||
"commands.sendMail.not_composition_end",
|
||||
getConstructionArgs(mailBuilder.constructionStage, sender)));
|
||||
}
|
||||
}
|
||||
case "help" -> {
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(
|
||||
sender,
|
||||
"commands.sendMail.please_use",
|
||||
getConstructionArgs(mailBuilder.constructionStage, sender)));
|
||||
}
|
||||
default -> {
|
||||
switch (mailBuilder.constructionStage) {
|
||||
case 0 -> {
|
||||
String title = String.join(" ", args.subList(0, args.size()));
|
||||
mailBuilder.mail.mailContent.title = title;
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.set_title", title));
|
||||
mailBuilder.constructionStage++;
|
||||
}
|
||||
case 1 -> {
|
||||
String contents = String.join(" ", args.subList(0, args.size()));
|
||||
mailBuilder.mail.mailContent.content = contents;
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.set_contents", contents));
|
||||
mailBuilder.constructionStage++;
|
||||
}
|
||||
case 2 -> {
|
||||
String msgSender = String.join(" ", args.subList(0, args.size()));
|
||||
mailBuilder.mail.mailContent.sender = msgSender;
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.set_message_sender", msgSender));
|
||||
mailBuilder.constructionStage++;
|
||||
}
|
||||
case 3 -> {
|
||||
int item;
|
||||
int lvl = 1;
|
||||
int amount = 1;
|
||||
int refinement = 0;
|
||||
switch (args.size()) {
|
||||
case 4: // <itemId|itemName> [amount] [level] [refinement] // TODO: this requires
|
||||
// Mail support but there's no harm leaving it here for now
|
||||
try {
|
||||
refinement = Integer.parseInt(args.get(3));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.generic.invalid.itemRefinement"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 3: // <itemId|itemName> [amount] [level]
|
||||
try {
|
||||
lvl = Integer.parseInt(args.get(2));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.generic.invalid.itemLevel"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 2: // <itemId|itemName> [amount]
|
||||
try {
|
||||
amount = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.generic.invalid.amount"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 1: // <itemId|itemName>
|
||||
try {
|
||||
item = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
// TODO: Parse from item name using GM Handbook.
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.generic.invalid.itemId"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default: // *No args*
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.sendMail.give_usage");
|
||||
return;
|
||||
}
|
||||
mailBuilder.mail.itemList.add(new Mail.MailItem(item, amount, lvl));
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.send", amount, item, lvl));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(
|
||||
sender,
|
||||
"commands.sendMail.invalid_arguments_please_use",
|
||||
getConstructionArgs(mailBuilder.constructionStage, sender)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getConstructionArgs(int stage, Player sender) {
|
||||
return switch (stage) {
|
||||
case 0 -> translate(sender, "commands.sendMail.title");
|
||||
case 1 -> translate(sender, "commands.sendMail.message");
|
||||
case 2 -> translate(sender, "commands.sendMail.sender");
|
||||
case 3 -> translate(sender, "commands.sendMail.arguments");
|
||||
default -> translate(sender, "commands.sendMail.error", stage);
|
||||
};
|
||||
}
|
||||
|
||||
public static class MailBuilder {
|
||||
public int recipient;
|
||||
public boolean sendToAll;
|
||||
public int constructionStage;
|
||||
public Mail mail;
|
||||
|
||||
public MailBuilder(int recipient, Mail mail) {
|
||||
this.recipient = recipient;
|
||||
this.sendToAll = false;
|
||||
this.constructionStage = 0;
|
||||
this.mail = mail;
|
||||
}
|
||||
|
||||
public MailBuilder(boolean sendToAll, Mail mail) {
|
||||
if (sendToAll) {
|
||||
this.recipient = 0;
|
||||
this.sendToAll = true;
|
||||
this.constructionStage = 0;
|
||||
this.mail = mail;
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Please use MailBuilder(int, mail) when not sending to all");
|
||||
Thread.dumpStack();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.mail.Mail;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Command(
|
||||
label = "sendMail",
|
||||
usage = {"(<userId>|all) [<templateId>]", "help"},
|
||||
permission = "server.sendmail",
|
||||
targetRequirement = Command.TargetRequirement.NONE)
|
||||
public final class SendMailCommand implements CommandHandler {
|
||||
|
||||
// TODO: You should be able to do /sendmail and then just send subsequent messages until you
|
||||
// finish
|
||||
// However, due to the current nature of the command system, I don't think this is possible
|
||||
// without rewriting
|
||||
// the command system (again). For now this will do
|
||||
|
||||
// Key = User that is constructing the mail.
|
||||
private static final HashMap<Integer, MailBuilder> mailBeingConstructed =
|
||||
new HashMap<Integer, MailBuilder>();
|
||||
|
||||
// Yes this is awful and I hate it.
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
int senderId;
|
||||
if (sender != null) {
|
||||
senderId = sender.getUid();
|
||||
} else {
|
||||
senderId = -1;
|
||||
}
|
||||
|
||||
if (!mailBeingConstructed.containsKey(senderId)) {
|
||||
switch (args.size()) {
|
||||
case 1 -> {
|
||||
MailBuilder mailBuilder;
|
||||
switch (args.get(0).toLowerCase()) {
|
||||
case "help" -> {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
case "all" -> mailBuilder = new MailBuilder(true, new Mail());
|
||||
default -> {
|
||||
if (DatabaseHelper.getPlayerByUid(Integer.parseInt(args.get(0))) != null) {
|
||||
mailBuilder = new MailBuilder(Integer.parseInt(args.get(0)), new Mail());
|
||||
} else {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.user_not_exist", args.get(0)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
mailBeingConstructed.put(senderId, mailBuilder);
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.start_composition"));
|
||||
}
|
||||
case 2 -> CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.templates"));
|
||||
default -> CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.invalid_arguments"));
|
||||
}
|
||||
} else {
|
||||
MailBuilder mailBuilder = mailBeingConstructed.get(senderId);
|
||||
|
||||
if (args.size() >= 1) {
|
||||
switch (args.get(0).toLowerCase()) {
|
||||
case "stop" -> {
|
||||
mailBeingConstructed.remove(senderId);
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send_cancel"));
|
||||
}
|
||||
case "finish" -> {
|
||||
if (mailBuilder.constructionStage == 3) {
|
||||
if (!mailBuilder.sendToAll) {
|
||||
Grasscutter.getGameServer()
|
||||
.getPlayerByUid(mailBuilder.recipient, true)
|
||||
.sendMail(mailBuilder.mail);
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(sender, "commands.sendMail.send_done", mailBuilder.recipient));
|
||||
} else {
|
||||
DatabaseHelper.getByGameClass(Player.class)
|
||||
.forEach(
|
||||
player -> {
|
||||
var onlinePlayer =
|
||||
Grasscutter.getGameServer().getPlayerByUid(player.getUid(), false);
|
||||
Objects.requireNonNullElse(onlinePlayer, player)
|
||||
.sendMail(mailBuilder.mail);
|
||||
});
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.send_all_done"));
|
||||
}
|
||||
mailBeingConstructed.remove(senderId);
|
||||
} else {
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(
|
||||
sender,
|
||||
"commands.sendMail.not_composition_end",
|
||||
getConstructionArgs(mailBuilder.constructionStage, sender)));
|
||||
}
|
||||
}
|
||||
case "help" -> {
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(
|
||||
sender,
|
||||
"commands.sendMail.please_use",
|
||||
getConstructionArgs(mailBuilder.constructionStage, sender)));
|
||||
}
|
||||
default -> {
|
||||
switch (mailBuilder.constructionStage) {
|
||||
case 0 -> {
|
||||
String title = String.join(" ", args.subList(0, args.size()));
|
||||
mailBuilder.mail.mailContent.title = title;
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.set_title", title));
|
||||
mailBuilder.constructionStage++;
|
||||
}
|
||||
case 1 -> {
|
||||
String contents = String.join(" ", args.subList(0, args.size()));
|
||||
mailBuilder.mail.mailContent.content = contents;
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.set_contents", contents));
|
||||
mailBuilder.constructionStage++;
|
||||
}
|
||||
case 2 -> {
|
||||
String msgSender = String.join(" ", args.subList(0, args.size()));
|
||||
mailBuilder.mail.mailContent.sender = msgSender;
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.set_message_sender", msgSender));
|
||||
mailBuilder.constructionStage++;
|
||||
}
|
||||
case 3 -> {
|
||||
int item;
|
||||
int lvl = 1;
|
||||
int amount = 1;
|
||||
int refinement = 0;
|
||||
switch (args.size()) {
|
||||
case 4: // <itemId|itemName> [amount] [level] [refinement] // TODO: this requires
|
||||
// Mail support but there's no harm leaving it here for now
|
||||
try {
|
||||
refinement = Integer.parseInt(args.get(3));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.generic.invalid.itemRefinement"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 3: // <itemId|itemName> [amount] [level]
|
||||
try {
|
||||
lvl = Integer.parseInt(args.get(2));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.generic.invalid.itemLevel"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 2: // <itemId|itemName> [amount]
|
||||
try {
|
||||
amount = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.generic.invalid.amount"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 1: // <itemId|itemName>
|
||||
try {
|
||||
item = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
// TODO: Parse from item name using GM Handbook.
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.generic.invalid.itemId"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default: // *No args*
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.sendMail.give_usage");
|
||||
return;
|
||||
}
|
||||
mailBuilder.mail.itemList.add(new Mail.MailItem(item, amount, lvl));
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.sendMail.send", amount, item, lvl));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(
|
||||
sender,
|
||||
"commands.sendMail.invalid_arguments_please_use",
|
||||
getConstructionArgs(mailBuilder.constructionStage, sender)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getConstructionArgs(int stage, Player sender) {
|
||||
return switch (stage) {
|
||||
case 0 -> translate(sender, "commands.sendMail.title");
|
||||
case 1 -> translate(sender, "commands.sendMail.message");
|
||||
case 2 -> translate(sender, "commands.sendMail.sender");
|
||||
case 3 -> translate(sender, "commands.sendMail.arguments");
|
||||
default -> translate(sender, "commands.sendMail.error", stage);
|
||||
};
|
||||
}
|
||||
|
||||
public static class MailBuilder {
|
||||
public int recipient;
|
||||
public boolean sendToAll;
|
||||
public int constructionStage;
|
||||
public Mail mail;
|
||||
|
||||
public MailBuilder(int recipient, Mail mail) {
|
||||
this.recipient = recipient;
|
||||
this.sendToAll = false;
|
||||
this.constructionStage = 0;
|
||||
this.mail = mail;
|
||||
}
|
||||
|
||||
public MailBuilder(boolean sendToAll, Mail mail) {
|
||||
if (sendToAll) {
|
||||
this.recipient = 0;
|
||||
this.sendToAll = true;
|
||||
this.constructionStage = 0;
|
||||
this.mail = mail;
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Please use MailBuilder(int, mail) when not sending to all");
|
||||
Thread.dumpStack();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +1,37 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.Command.TargetRequirement;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "sendMessage",
|
||||
aliases = {"say", "sendservmsg", "sendservermessage", "b", "broadcast"},
|
||||
usage = {"<message>"},
|
||||
permission = "server.sendmessage",
|
||||
permissionTargeted = "server.sendmessage.others",
|
||||
targetRequirement = TargetRequirement.NONE)
|
||||
public final class SendMessageCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() == 0) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
String message = String.join(" ", args);
|
||||
|
||||
if (targetPlayer == null) {
|
||||
for (Player p : Grasscutter.getGameServer().getPlayers().values()) {
|
||||
CommandHandler.sendMessage(p, message);
|
||||
}
|
||||
} else {
|
||||
CommandHandler.sendMessage(targetPlayer, message);
|
||||
}
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.sendMessage.success");
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.Command.TargetRequirement;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "sendMessage",
|
||||
aliases = {"say", "sendservmsg", "sendservermessage", "b", "broadcast"},
|
||||
usage = {"<message>"},
|
||||
permission = "server.sendmessage",
|
||||
permissionTargeted = "server.sendmessage.others",
|
||||
targetRequirement = TargetRequirement.NONE)
|
||||
public final class SendMessageCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() == 0) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
String message = String.join(" ", args);
|
||||
|
||||
if (targetPlayer == null) {
|
||||
for (Player p : Grasscutter.getGameServer().getPlayers().values()) {
|
||||
CommandHandler.sendMessage(p, message);
|
||||
}
|
||||
} else {
|
||||
CommandHandler.sendMessage(targetPlayer, message);
|
||||
}
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.sendMessage.success");
|
||||
}
|
||||
}
|
||||
|
@ -1,92 +1,92 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "setConst",
|
||||
aliases = {"setconstellation"},
|
||||
usage = {"<constellation level> [all]"},
|
||||
permission = "player.setconstellation",
|
||||
permissionTargeted = "player.setconstellation.others")
|
||||
public final class SetConstCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 1) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
int constLevel = Integer.parseInt(args.get(0));
|
||||
// Check if level is out of range
|
||||
if (constLevel < -1 || constLevel > 6) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.setConst.range_error");
|
||||
return;
|
||||
}
|
||||
// If it's either empty or anything else other than "all" just do normal setConstellation
|
||||
if (args.size() == 1) {
|
||||
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
|
||||
if (entity == null) return;
|
||||
Avatar avatar = entity.getAvatar();
|
||||
this.setConstellation(targetPlayer, avatar, constLevel);
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.setConst.success", avatar.getAvatarData().getName(), constLevel);
|
||||
return;
|
||||
}
|
||||
// Check if there's an additional argument which is "all", if it does then go
|
||||
// setAllConstellation
|
||||
if (args.size() > 1 && args.get(1).equalsIgnoreCase("all")) {
|
||||
this.setAllConstellation(targetPlayer, constLevel);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.setConst.successall", constLevel);
|
||||
} else sendUsageMessage(sender);
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.setConst.level_error");
|
||||
}
|
||||
}
|
||||
|
||||
private void setConstellation(Player player, Avatar avatar, int constLevel) {
|
||||
int currentConstLevel = avatar.getCoreProudSkillLevel();
|
||||
avatar.forceConstellationLevel(constLevel);
|
||||
|
||||
// force player to reload scene when necessary
|
||||
if (constLevel < currentConstLevel) {
|
||||
this.reloadScene(player);
|
||||
}
|
||||
|
||||
// ensure that all changes are visible to the player
|
||||
avatar.recalcConstellations();
|
||||
avatar.recalcStats(true);
|
||||
avatar.save();
|
||||
}
|
||||
|
||||
private void setAllConstellation(Player player, int constLevel) {
|
||||
player
|
||||
.getAvatars()
|
||||
.forEach(
|
||||
avatar -> {
|
||||
avatar.forceConstellationLevel(constLevel);
|
||||
avatar.recalcConstellations();
|
||||
avatar.recalcStats(true);
|
||||
avatar.save();
|
||||
});
|
||||
// Just reload scene once, shorter than having to check for each constLevel < currentConstLevel
|
||||
this.reloadScene(player);
|
||||
}
|
||||
|
||||
private void reloadScene(Player player) {
|
||||
World world = player.getWorld();
|
||||
Scene scene = player.getScene();
|
||||
Position pos = player.getPosition();
|
||||
world.transferPlayerToScene(player, 1, pos);
|
||||
world.transferPlayerToScene(player, scene.getId(), pos);
|
||||
scene.broadcastPacket(new PacketSceneEntityAppearNotify(player));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "setConst",
|
||||
aliases = {"setconstellation"},
|
||||
usage = {"<constellation level> [all]"},
|
||||
permission = "player.setconstellation",
|
||||
permissionTargeted = "player.setconstellation.others")
|
||||
public final class SetConstCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 1) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
int constLevel = Integer.parseInt(args.get(0));
|
||||
// Check if level is out of range
|
||||
if (constLevel < -1 || constLevel > 6) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.setConst.range_error");
|
||||
return;
|
||||
}
|
||||
// If it's either empty or anything else other than "all" just do normal setConstellation
|
||||
if (args.size() == 1) {
|
||||
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
|
||||
if (entity == null) return;
|
||||
Avatar avatar = entity.getAvatar();
|
||||
this.setConstellation(targetPlayer, avatar, constLevel);
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.setConst.success", avatar.getAvatarData().getName(), constLevel);
|
||||
return;
|
||||
}
|
||||
// Check if there's an additional argument which is "all", if it does then go
|
||||
// setAllConstellation
|
||||
if (args.size() > 1 && args.get(1).equalsIgnoreCase("all")) {
|
||||
this.setAllConstellation(targetPlayer, constLevel);
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.setConst.successall", constLevel);
|
||||
} else sendUsageMessage(sender);
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.setConst.level_error");
|
||||
}
|
||||
}
|
||||
|
||||
private void setConstellation(Player player, Avatar avatar, int constLevel) {
|
||||
int currentConstLevel = avatar.getCoreProudSkillLevel();
|
||||
avatar.forceConstellationLevel(constLevel);
|
||||
|
||||
// force player to reload scene when necessary
|
||||
if (constLevel < currentConstLevel) {
|
||||
this.reloadScene(player);
|
||||
}
|
||||
|
||||
// ensure that all changes are visible to the player
|
||||
avatar.recalcConstellations();
|
||||
avatar.recalcStats(true);
|
||||
avatar.save();
|
||||
}
|
||||
|
||||
private void setAllConstellation(Player player, int constLevel) {
|
||||
player
|
||||
.getAvatars()
|
||||
.forEach(
|
||||
avatar -> {
|
||||
avatar.forceConstellationLevel(constLevel);
|
||||
avatar.recalcConstellations();
|
||||
avatar.recalcStats(true);
|
||||
avatar.save();
|
||||
});
|
||||
// Just reload scene once, shorter than having to check for each constLevel < currentConstLevel
|
||||
this.reloadScene(player);
|
||||
}
|
||||
|
||||
private void reloadScene(Player player) {
|
||||
World world = player.getWorld();
|
||||
Scene scene = player.getScene();
|
||||
Position pos = player.getPosition();
|
||||
world.transferPlayerToScene(player, 1, pos);
|
||||
world.transferPlayerToScene(player, scene.getId(), pos);
|
||||
scene.broadcastPacket(new PacketSceneEntityAppearNotify(player));
|
||||
}
|
||||
}
|
||||
|
@ -1,50 +1,50 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "setFetterLevel",
|
||||
usage = {"<level>"},
|
||||
aliases = {"setfetterlvl", "setfriendship"},
|
||||
permission = "player.setfetterlevel",
|
||||
permissionTargeted = "player.setfetterlevel.others")
|
||||
public final class SetFetterLevelCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() != 1) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int fetterLevel = Integer.parseInt(args.get(0));
|
||||
if (fetterLevel < 0 || fetterLevel > 10) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.setFetterLevel.range_error"));
|
||||
return;
|
||||
}
|
||||
Avatar avatar = targetPlayer.getTeamManager().getCurrentAvatarEntity().getAvatar();
|
||||
|
||||
avatar.setFetterLevel(fetterLevel);
|
||||
if (fetterLevel != 10) {
|
||||
avatar.setFetterExp(GameData.getAvatarFetterLevelDataMap().get(fetterLevel).getExp());
|
||||
}
|
||||
avatar.save();
|
||||
|
||||
targetPlayer.sendPacket(new PacketAvatarFetterDataNotify(avatar));
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.setFetterLevel.success", fetterLevel));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.setFetterLevel.level_error"));
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "setFetterLevel",
|
||||
usage = {"<level>"},
|
||||
aliases = {"setfetterlvl", "setfriendship"},
|
||||
permission = "player.setfetterlevel",
|
||||
permissionTargeted = "player.setfetterlevel.others")
|
||||
public final class SetFetterLevelCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() != 1) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int fetterLevel = Integer.parseInt(args.get(0));
|
||||
if (fetterLevel < 0 || fetterLevel > 10) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.setFetterLevel.range_error"));
|
||||
return;
|
||||
}
|
||||
Avatar avatar = targetPlayer.getTeamManager().getCurrentAvatarEntity().getAvatar();
|
||||
|
||||
avatar.setFetterLevel(fetterLevel);
|
||||
if (fetterLevel != 10) {
|
||||
avatar.setFetterExp(GameData.getAvatarFetterLevelDataMap().get(fetterLevel).getExp());
|
||||
}
|
||||
avatar.save();
|
||||
|
||||
targetPlayer.sendPacket(new PacketAvatarFetterDataNotify(avatar));
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.setFetterLevel.success", fetterLevel));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.setFetterLevel.level_error"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,27 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "stop",
|
||||
aliases = {"shutdown"},
|
||||
permission = "server.stop",
|
||||
targetRequirement = Command.TargetRequirement.NONE)
|
||||
public final class StopCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
CommandHandler.sendMessage(null, translate("commands.stop.success"));
|
||||
for (Player p : Grasscutter.getGameServer().getPlayers().values()) {
|
||||
CommandHandler.sendMessage(p, translate(p, "commands.stop.success"));
|
||||
}
|
||||
|
||||
System.exit(1000);
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "stop",
|
||||
aliases = {"shutdown"},
|
||||
permission = "server.stop",
|
||||
targetRequirement = Command.TargetRequirement.NONE)
|
||||
public final class StopCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
CommandHandler.sendMessage(null, translate("commands.stop.success"));
|
||||
for (Player p : Grasscutter.getGameServer().getPlayers().values()) {
|
||||
CommandHandler.sendMessage(p, translate(p, "commands.stop.success"));
|
||||
}
|
||||
|
||||
System.exit(1000);
|
||||
}
|
||||
}
|
||||
|
@ -1,268 +1,268 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketChangeMpTeamAvatarRsp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "team",
|
||||
usage = {"add <avatarId,...>", "(remove|set) [index|first|last|index-index,...]"},
|
||||
permission = "player.team",
|
||||
permissionTargeted = "player.team.others")
|
||||
public final class TeamCommand implements CommandHandler {
|
||||
private static final int BASE_AVATARID = 10000000;
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.isEmpty()) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args.get(0)) {
|
||||
case "add":
|
||||
if (!addCommand(sender, targetPlayer, args)) return;
|
||||
break;
|
||||
|
||||
case "remove":
|
||||
if (!removeCommand(sender, targetPlayer, args)) return;
|
||||
break;
|
||||
|
||||
case "set":
|
||||
if (!setCommand(sender, targetPlayer, args)) return;
|
||||
break;
|
||||
|
||||
default:
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
targetPlayer
|
||||
.getTeamManager()
|
||||
.updateTeamEntities(
|
||||
new PacketChangeMpTeamAvatarRsp(
|
||||
targetPlayer, targetPlayer.getTeamManager().getCurrentTeamInfo()));
|
||||
}
|
||||
|
||||
private boolean addCommand(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
sendUsageMessage(sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = -1;
|
||||
if (args.size() > 2) {
|
||||
try {
|
||||
index = Integer.parseInt(args.get(2)) - 1;
|
||||
if (index < 0) index = 0;
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_index");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var avatarIds = args.get(1).split(",");
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
|
||||
if (currentTeamAvatars.size() + avatarIds.length > GAME_OPTIONS.avatarLimits.singlePlayerTeam) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.add_too_much", GAME_OPTIONS.avatarLimits.singlePlayerTeam);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var avatarId : avatarIds) {
|
||||
int id = Integer.parseInt(avatarId);
|
||||
if (!addAvatar(sender, targetPlayer, id, index))
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.failed_to_add_avatar", avatarId);
|
||||
if (index > 0) ++index;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean removeCommand(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
sendUsageMessage(sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
var avatarCount = currentTeamAvatars.size();
|
||||
|
||||
var metaIndexList = args.get(1).split(",");
|
||||
var indexes = new HashSet<Integer>();
|
||||
var ignoreList = new ArrayList<Integer>();
|
||||
for (var metaIndex : metaIndexList) {
|
||||
// step 1: parse metaIndex to indexes
|
||||
var subIndexes = transformToIndexes(metaIndex, avatarCount);
|
||||
if (subIndexes == null) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.failed_to_parse_index", metaIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
// step 2: get all of the avatar id through indexes
|
||||
for (var avatarIndex : subIndexes) {
|
||||
try {
|
||||
indexes.add(currentTeamAvatars.get(avatarIndex - 1));
|
||||
} catch (Exception e) {
|
||||
ignoreList.add(avatarIndex);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 3: check if user remove all of the avatar
|
||||
if (indexes.size() >= avatarCount) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_too_much");
|
||||
return false;
|
||||
}
|
||||
|
||||
// step 4: hint user for ignore index
|
||||
if (!ignoreList.isEmpty()) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.ignore_index", ignoreList);
|
||||
}
|
||||
|
||||
// step 5: remove
|
||||
currentTeamAvatars.removeAll(indexes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setCommand(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 3) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
sendUsageMessage(sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
|
||||
int index;
|
||||
try {
|
||||
index = Integer.parseInt(args.get(1)) - 1;
|
||||
if (index < 0) index = 0;
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.failed_to_parse_index", args.get(1));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index + 1 > currentTeamAvatars.size()) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.index_out_of_range");
|
||||
return false;
|
||||
}
|
||||
|
||||
int avatarId;
|
||||
try {
|
||||
avatarId = Integer.parseInt(args.get(2));
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.failed_parse_avatar_id", args.get(2));
|
||||
return false;
|
||||
}
|
||||
if (avatarId < BASE_AVATARID) {
|
||||
avatarId += BASE_AVATARID;
|
||||
}
|
||||
|
||||
if (currentTeamAvatars.contains(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.avatar_already_in_team", avatarId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
|
||||
return false;
|
||||
}
|
||||
|
||||
currentTeamAvatars.set(index, avatarId);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean addAvatar(Player sender, Player targetPlayer, int avatarId, int index) {
|
||||
if (avatarId < BASE_AVATARID) {
|
||||
avatarId += BASE_AVATARID;
|
||||
}
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
if (currentTeamAvatars.contains(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.avatar_already_in_team", avatarId);
|
||||
return false;
|
||||
}
|
||||
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
|
||||
return false;
|
||||
}
|
||||
if (index < 0) {
|
||||
currentTeamAvatars.add(avatarId);
|
||||
} else {
|
||||
currentTeamAvatars.add(index, avatarId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<Integer> transformToIndexes(String metaIndexes, int listLength) {
|
||||
// step 1: check if metaIndexes is a special constants
|
||||
if (metaIndexes.equals("first")) {
|
||||
return List.of(1);
|
||||
} else if (metaIndexes.equals("last")) {
|
||||
return List.of(listLength);
|
||||
}
|
||||
|
||||
// step 2: check if metaIndexes is a range
|
||||
if (metaIndexes.contains("-")) {
|
||||
var range = metaIndexes.split("-");
|
||||
if (range.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int min, max;
|
||||
try {
|
||||
min =
|
||||
switch (range[0]) {
|
||||
case "first" -> 1;
|
||||
case "last" -> listLength;
|
||||
default -> Integer.parseInt(range[0]);
|
||||
};
|
||||
|
||||
max =
|
||||
switch (range[1]) {
|
||||
case "first" -> 1;
|
||||
case "last" -> listLength;
|
||||
default -> Integer.parseInt(range[1]);
|
||||
};
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (min > max) {
|
||||
min ^= max;
|
||||
max ^= min;
|
||||
min ^= max;
|
||||
}
|
||||
|
||||
var indexes = new ArrayList<Integer>();
|
||||
for (int i = min; i <= max; ++i) {
|
||||
indexes.add(i);
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
|
||||
// step 3: index is a value, simply return
|
||||
try {
|
||||
int index = Integer.parseInt(metaIndexes);
|
||||
return List.of(index);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketChangeMpTeamAvatarRsp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "team",
|
||||
usage = {"add <avatarId,...>", "(remove|set) [index|first|last|index-index,...]"},
|
||||
permission = "player.team",
|
||||
permissionTargeted = "player.team.others")
|
||||
public final class TeamCommand implements CommandHandler {
|
||||
private static final int BASE_AVATARID = 10000000;
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.isEmpty()) {
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args.get(0)) {
|
||||
case "add":
|
||||
if (!addCommand(sender, targetPlayer, args)) return;
|
||||
break;
|
||||
|
||||
case "remove":
|
||||
if (!removeCommand(sender, targetPlayer, args)) return;
|
||||
break;
|
||||
|
||||
case "set":
|
||||
if (!setCommand(sender, targetPlayer, args)) return;
|
||||
break;
|
||||
|
||||
default:
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
targetPlayer
|
||||
.getTeamManager()
|
||||
.updateTeamEntities(
|
||||
new PacketChangeMpTeamAvatarRsp(
|
||||
targetPlayer, targetPlayer.getTeamManager().getCurrentTeamInfo()));
|
||||
}
|
||||
|
||||
private boolean addCommand(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
sendUsageMessage(sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = -1;
|
||||
if (args.size() > 2) {
|
||||
try {
|
||||
index = Integer.parseInt(args.get(2)) - 1;
|
||||
if (index < 0) index = 0;
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_index");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var avatarIds = args.get(1).split(",");
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
|
||||
if (currentTeamAvatars.size() + avatarIds.length > GAME_OPTIONS.avatarLimits.singlePlayerTeam) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.add_too_much", GAME_OPTIONS.avatarLimits.singlePlayerTeam);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var avatarId : avatarIds) {
|
||||
int id = Integer.parseInt(avatarId);
|
||||
if (!addAvatar(sender, targetPlayer, id, index))
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.failed_to_add_avatar", avatarId);
|
||||
if (index > 0) ++index;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean removeCommand(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
sendUsageMessage(sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
var avatarCount = currentTeamAvatars.size();
|
||||
|
||||
var metaIndexList = args.get(1).split(",");
|
||||
var indexes = new HashSet<Integer>();
|
||||
var ignoreList = new ArrayList<Integer>();
|
||||
for (var metaIndex : metaIndexList) {
|
||||
// step 1: parse metaIndex to indexes
|
||||
var subIndexes = transformToIndexes(metaIndex, avatarCount);
|
||||
if (subIndexes == null) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.failed_to_parse_index", metaIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
// step 2: get all of the avatar id through indexes
|
||||
for (var avatarIndex : subIndexes) {
|
||||
try {
|
||||
indexes.add(currentTeamAvatars.get(avatarIndex - 1));
|
||||
} catch (Exception e) {
|
||||
ignoreList.add(avatarIndex);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 3: check if user remove all of the avatar
|
||||
if (indexes.size() >= avatarCount) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_too_much");
|
||||
return false;
|
||||
}
|
||||
|
||||
// step 4: hint user for ignore index
|
||||
if (!ignoreList.isEmpty()) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.ignore_index", ignoreList);
|
||||
}
|
||||
|
||||
// step 5: remove
|
||||
currentTeamAvatars.removeAll(indexes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setCommand(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 3) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
sendUsageMessage(sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
|
||||
int index;
|
||||
try {
|
||||
index = Integer.parseInt(args.get(1)) - 1;
|
||||
if (index < 0) index = 0;
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.failed_to_parse_index", args.get(1));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index + 1 > currentTeamAvatars.size()) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.index_out_of_range");
|
||||
return false;
|
||||
}
|
||||
|
||||
int avatarId;
|
||||
try {
|
||||
avatarId = Integer.parseInt(args.get(2));
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.failed_parse_avatar_id", args.get(2));
|
||||
return false;
|
||||
}
|
||||
if (avatarId < BASE_AVATARID) {
|
||||
avatarId += BASE_AVATARID;
|
||||
}
|
||||
|
||||
if (currentTeamAvatars.contains(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.avatar_already_in_team", avatarId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
|
||||
return false;
|
||||
}
|
||||
|
||||
currentTeamAvatars.set(index, avatarId);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean addAvatar(Player sender, Player targetPlayer, int avatarId, int index) {
|
||||
if (avatarId < BASE_AVATARID) {
|
||||
avatarId += BASE_AVATARID;
|
||||
}
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
if (currentTeamAvatars.contains(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.team.avatar_already_in_team", avatarId);
|
||||
return false;
|
||||
}
|
||||
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
|
||||
return false;
|
||||
}
|
||||
if (index < 0) {
|
||||
currentTeamAvatars.add(avatarId);
|
||||
} else {
|
||||
currentTeamAvatars.add(index, avatarId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<Integer> transformToIndexes(String metaIndexes, int listLength) {
|
||||
// step 1: check if metaIndexes is a special constants
|
||||
if (metaIndexes.equals("first")) {
|
||||
return List.of(1);
|
||||
} else if (metaIndexes.equals("last")) {
|
||||
return List.of(listLength);
|
||||
}
|
||||
|
||||
// step 2: check if metaIndexes is a range
|
||||
if (metaIndexes.contains("-")) {
|
||||
var range = metaIndexes.split("-");
|
||||
if (range.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int min, max;
|
||||
try {
|
||||
min =
|
||||
switch (range[0]) {
|
||||
case "first" -> 1;
|
||||
case "last" -> listLength;
|
||||
default -> Integer.parseInt(range[0]);
|
||||
};
|
||||
|
||||
max =
|
||||
switch (range[1]) {
|
||||
case "first" -> 1;
|
||||
case "last" -> listLength;
|
||||
default -> Integer.parseInt(range[1]);
|
||||
};
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (min > max) {
|
||||
min ^= max;
|
||||
max ^= min;
|
||||
min ^= max;
|
||||
}
|
||||
|
||||
var indexes = new ArrayList<Integer>();
|
||||
for (int i = min; i <= max; ++i) {
|
||||
indexes.add(i);
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
|
||||
// step 3: index is a value, simply return
|
||||
try {
|
||||
int index = Integer.parseInt(metaIndexes);
|
||||
return List.of(index);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +1,36 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "teleportAll",
|
||||
aliases = {"tpall"},
|
||||
permission = "player.tpall",
|
||||
permissionTargeted = "player.tpall.others")
|
||||
public final class TeleportAllCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (!targetPlayer.getWorld().isMultiplayer()) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.error"));
|
||||
return;
|
||||
}
|
||||
|
||||
for (Player player : targetPlayer.getWorld().getPlayers()) {
|
||||
if (player.equals(targetPlayer)) continue;
|
||||
|
||||
player
|
||||
.getWorld()
|
||||
.transferPlayerToScene(
|
||||
player, targetPlayer.getSceneId(), TeleportType.COMMAND, targetPlayer.getPosition());
|
||||
}
|
||||
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.success"));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "teleportAll",
|
||||
aliases = {"tpall"},
|
||||
permission = "player.tpall",
|
||||
permissionTargeted = "player.tpall.others")
|
||||
public final class TeleportAllCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (!targetPlayer.getWorld().isMultiplayer()) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.error"));
|
||||
return;
|
||||
}
|
||||
|
||||
for (Player player : targetPlayer.getWorld().getPlayers()) {
|
||||
if (player.equals(targetPlayer)) continue;
|
||||
|
||||
player
|
||||
.getWorld()
|
||||
.transferPlayerToScene(
|
||||
player, targetPlayer.getSceneId(), TeleportType.COMMAND, targetPlayer.getPosition());
|
||||
}
|
||||
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.success"));
|
||||
}
|
||||
}
|
||||
|
@ -1,78 +1,78 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "teleport",
|
||||
aliases = {"tp"},
|
||||
usage = {"<x> <y> <z> [sceneId]"},
|
||||
permission = "player.teleport",
|
||||
permissionTargeted = "player.teleport.others")
|
||||
public final class TeleportCommand implements CommandHandler {
|
||||
|
||||
private float parseRelative(
|
||||
String input, Float current) { // TODO: Maybe this will be useful elsewhere later
|
||||
if (input.contains("~")) { // Relative
|
||||
if (!input.equals("~")) { // Relative with offset
|
||||
current += Float.parseFloat(input.replace("~", ""));
|
||||
} // Else no offset, no modification
|
||||
} else { // Absolute
|
||||
current = Float.parseFloat(input);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Position pos = targetPlayer.getPosition();
|
||||
float x = pos.getX();
|
||||
float y = pos.getY();
|
||||
float z = pos.getZ();
|
||||
int sceneId = targetPlayer.getSceneId();
|
||||
|
||||
switch (args.size()) {
|
||||
case 4:
|
||||
try {
|
||||
sceneId = Integer.parseInt(args.get(3));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.execution.argument_error"));
|
||||
} // Fallthrough
|
||||
case 3:
|
||||
try {
|
||||
x = this.parseRelative(args.get(0), x);
|
||||
y = this.parseRelative(args.get(1), y);
|
||||
z = this.parseRelative(args.get(2), z);
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.teleport.invalid_position"));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
Position target_pos = new Position(x, y, z);
|
||||
boolean result =
|
||||
targetPlayer
|
||||
.getWorld()
|
||||
.transferPlayerToScene(targetPlayer, sceneId, TeleportType.COMMAND, target_pos);
|
||||
|
||||
if (!result) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.exists_error"));
|
||||
} else {
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(
|
||||
sender, "commands.teleport.success", targetPlayer.getNickname(), x, y, z, sceneId));
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "teleport",
|
||||
aliases = {"tp"},
|
||||
usage = {"<x> <y> <z> [sceneId]"},
|
||||
permission = "player.teleport",
|
||||
permissionTargeted = "player.teleport.others")
|
||||
public final class TeleportCommand implements CommandHandler {
|
||||
|
||||
private float parseRelative(
|
||||
String input, Float current) { // TODO: Maybe this will be useful elsewhere later
|
||||
if (input.contains("~")) { // Relative
|
||||
if (!input.equals("~")) { // Relative with offset
|
||||
current += Float.parseFloat(input.replace("~", ""));
|
||||
} // Else no offset, no modification
|
||||
} else { // Absolute
|
||||
current = Float.parseFloat(input);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Position pos = targetPlayer.getPosition();
|
||||
float x = pos.getX();
|
||||
float y = pos.getY();
|
||||
float z = pos.getZ();
|
||||
int sceneId = targetPlayer.getSceneId();
|
||||
|
||||
switch (args.size()) {
|
||||
case 4:
|
||||
try {
|
||||
sceneId = Integer.parseInt(args.get(3));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.execution.argument_error"));
|
||||
} // Fallthrough
|
||||
case 3:
|
||||
try {
|
||||
x = this.parseRelative(args.get(0), x);
|
||||
y = this.parseRelative(args.get(1), y);
|
||||
z = this.parseRelative(args.get(2), z);
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.teleport.invalid_position"));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
Position target_pos = new Position(x, y, z);
|
||||
boolean result =
|
||||
targetPlayer
|
||||
.getWorld()
|
||||
.transferPlayerToScene(targetPlayer, sceneId, TeleportType.COMMAND, target_pos);
|
||||
|
||||
if (!result) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.exists_error"));
|
||||
} else {
|
||||
CommandHandler.sendMessage(
|
||||
sender,
|
||||
translate(
|
||||
sender, "commands.teleport.success", targetPlayer.getNickname(), x, y, z, sceneId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,39 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "unban",
|
||||
permission = "server.ban",
|
||||
targetRequirement = Command.TargetRequirement.PLAYER)
|
||||
public final class UnBanCommand implements CommandHandler {
|
||||
|
||||
private boolean unBanAccount(Player targetPlayer) {
|
||||
Account account = targetPlayer.getAccount();
|
||||
|
||||
if (account == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
account.setBanReason(null);
|
||||
account.setBanEndTime(0);
|
||||
account.setBanStartTime(0);
|
||||
account.setBanned(false);
|
||||
account.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (unBanAccount(targetPlayer)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.unban.success");
|
||||
} else {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.unban.failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "unban",
|
||||
permission = "server.ban",
|
||||
targetRequirement = Command.TargetRequirement.PLAYER)
|
||||
public final class UnBanCommand implements CommandHandler {
|
||||
|
||||
private boolean unBanAccount(Player targetPlayer) {
|
||||
Account account = targetPlayer.getAccount();
|
||||
|
||||
if (account == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
account.setBanReason(null);
|
||||
account.setBanEndTime(0);
|
||||
account.setBanStartTime(0);
|
||||
account.setBanned(false);
|
||||
account.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (unBanAccount(targetPlayer)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.unban.success");
|
||||
} else {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.unban.failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,43 +1,43 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.player.PlayerProgressManager;
|
||||
import emu.grasscutter.server.packet.send.PacketOpenStateChangeNotify;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Command(
|
||||
label = "unlockall",
|
||||
usage = {""},
|
||||
permission = "player.unlockall",
|
||||
permissionTargeted = "player.unlockall.others")
|
||||
public final class UnlockAllCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Map<Integer, Integer> changed = new HashMap<>();
|
||||
|
||||
for (var state : GameData.getOpenStateList()) {
|
||||
// Don't unlock blacklisted open states.
|
||||
if (PlayerProgressManager.BLACKLIST_OPEN_STATES.contains(state.getId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (targetPlayer.getProgressManager().getOpenState(state.getId()) == 0) {
|
||||
targetPlayer.getOpenStates().put(state.getId(), 1);
|
||||
changed.put(state.getId(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
targetPlayer.sendPacket(new PacketOpenStateChangeNotify(changed));
|
||||
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.unlockall.success", targetPlayer.getNickname()));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.player.PlayerProgressManager;
|
||||
import emu.grasscutter.server.packet.send.PacketOpenStateChangeNotify;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Command(
|
||||
label = "unlockall",
|
||||
usage = {""},
|
||||
permission = "player.unlockall",
|
||||
permissionTargeted = "player.unlockall.others")
|
||||
public final class UnlockAllCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Map<Integer, Integer> changed = new HashMap<>();
|
||||
|
||||
for (var state : GameData.getOpenStateList()) {
|
||||
// Don't unlock blacklisted open states.
|
||||
if (PlayerProgressManager.BLACKLIST_OPEN_STATES.contains(state.getId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (targetPlayer.getProgressManager().getOpenState(state.getId()) == 0) {
|
||||
targetPlayer.getOpenStates().put(state.getId(), 1);
|
||||
changed.put(state.getId(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
targetPlayer.sendPacket(new PacketOpenStateChangeNotify(changed));
|
||||
|
||||
CommandHandler.sendMessage(
|
||||
sender, translate(sender, "commands.unlockall.success", targetPlayer.getNickname()));
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +1,52 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ClimateType;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "weather",
|
||||
aliases = {"w"},
|
||||
usage = {"weather [<weatherId>] [<climateType>]"},
|
||||
permission = "player.weather",
|
||||
permissionTargeted = "player.weather.others")
|
||||
public final class WeatherCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
int weatherId = targetPlayer.getWeatherId();
|
||||
ClimateType climate =
|
||||
ClimateType
|
||||
.CLIMATE_NONE; // Sending ClimateType.CLIMATE_NONE to Scene.setWeather will use the
|
||||
// default climate for that weather
|
||||
|
||||
if (args.isEmpty()) {
|
||||
climate = targetPlayer.getClimate();
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.weather.status", weatherId, climate.getShortName());
|
||||
return;
|
||||
}
|
||||
|
||||
for (String arg : args) {
|
||||
ClimateType c = ClimateType.getTypeByShortName(arg.toLowerCase());
|
||||
if (c != ClimateType.CLIMATE_NONE) {
|
||||
climate = c;
|
||||
} else {
|
||||
try {
|
||||
weatherId = Integer.parseInt(arg);
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.id");
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
targetPlayer.setWeather(weatherId, climate);
|
||||
climate = targetPlayer.getClimate(); // Might be different to what we set
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.weather.success", weatherId, climate.getShortName());
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ClimateType;
|
||||
import java.util.List;
|
||||
|
||||
@Command(
|
||||
label = "weather",
|
||||
aliases = {"w"},
|
||||
usage = {"weather [<weatherId>] [<climateType>]"},
|
||||
permission = "player.weather",
|
||||
permissionTargeted = "player.weather.others")
|
||||
public final class WeatherCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
int weatherId = targetPlayer.getWeatherId();
|
||||
ClimateType climate =
|
||||
ClimateType
|
||||
.CLIMATE_NONE; // Sending ClimateType.CLIMATE_NONE to Scene.setWeather will use the
|
||||
// default climate for that weather
|
||||
|
||||
if (args.isEmpty()) {
|
||||
climate = targetPlayer.getClimate();
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.weather.status", weatherId, climate.getShortName());
|
||||
return;
|
||||
}
|
||||
|
||||
for (String arg : args) {
|
||||
ClimateType c = ClimateType.getTypeByShortName(arg.toLowerCase());
|
||||
if (c != ClimateType.CLIMATE_NONE) {
|
||||
climate = c;
|
||||
} else {
|
||||
try {
|
||||
weatherId = Integer.parseInt(arg);
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.id");
|
||||
sendUsageMessage(sender);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
targetPlayer.setWeather(weatherId, climate);
|
||||
climate = targetPlayer.getClimate(); // Might be different to what we set
|
||||
CommandHandler.sendTranslatedMessage(
|
||||
sender, "commands.weather.success", weatherId, climate.getShortName());
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
package emu.grasscutter.data;
|
||||
|
||||
public abstract class GameResource {
|
||||
|
||||
public int getId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void onLoad() {}
|
||||
}
|
||||
package emu.grasscutter.data;
|
||||
|
||||
public abstract class GameResource {
|
||||
|
||||
public int getId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void onLoad() {}
|
||||
}
|
||||
|
@ -1,40 +1,40 @@
|
||||
package emu.grasscutter.data;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ResourceType {
|
||||
|
||||
/** Names of the file that this Resource loads from */
|
||||
String[] name();
|
||||
|
||||
/**
|
||||
* Load priority - dictates which order to load this resource, with "highest" being loaded first
|
||||
*/
|
||||
LoadPriority loadPriority() default LoadPriority.NORMAL;
|
||||
|
||||
enum LoadPriority {
|
||||
HIGHEST(4),
|
||||
HIGH(3),
|
||||
NORMAL(2),
|
||||
LOW(1),
|
||||
LOWEST(0);
|
||||
|
||||
private final int value;
|
||||
|
||||
LoadPriority(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static List<LoadPriority> getInOrder() {
|
||||
return Stream.of(LoadPriority.values()).sorted((x, y) -> y.value() - x.value()).toList();
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ResourceType {
|
||||
|
||||
/** Names of the file that this Resource loads from */
|
||||
String[] name();
|
||||
|
||||
/**
|
||||
* Load priority - dictates which order to load this resource, with "highest" being loaded first
|
||||
*/
|
||||
LoadPriority loadPriority() default LoadPriority.NORMAL;
|
||||
|
||||
enum LoadPriority {
|
||||
HIGHEST(4),
|
||||
HIGH(3),
|
||||
NORMAL(2),
|
||||
LOW(1),
|
||||
LOWEST(0);
|
||||
|
||||
private final int value;
|
||||
|
||||
LoadPriority(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static List<LoadPriority> getInOrder() {
|
||||
return Stream.of(LoadPriority.values()).sorted((x, y) -> y.value() - x.value()).toList();
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,21 @@
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
public class AbilityEmbryoEntry {
|
||||
private String name;
|
||||
private String[] abilities;
|
||||
|
||||
public AbilityEmbryoEntry() {}
|
||||
|
||||
public AbilityEmbryoEntry(String avatarName, String[] array) {
|
||||
this.name = avatarName;
|
||||
this.abilities = array;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String[] getAbilities() {
|
||||
return abilities;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
public class AbilityEmbryoEntry {
|
||||
private String name;
|
||||
private String[] abilities;
|
||||
|
||||
public AbilityEmbryoEntry() {}
|
||||
|
||||
public AbilityEmbryoEntry(String avatarName, String[] array) {
|
||||
this.name = avatarName;
|
||||
this.abilities = array;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String[] getAbilities() {
|
||||
return abilities;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class ConfigGadget {
|
||||
// There are more values that can be added that might be useful in the json
|
||||
@Nullable ConfigGadgetCombat combat;
|
||||
}
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class ConfigGadget {
|
||||
// There are more values that can be added that might be useful in the json
|
||||
@Nullable ConfigGadgetCombat combat;
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class ConfigGadgetCombatProperty {
|
||||
float HP;
|
||||
boolean isLockHP;
|
||||
boolean isInvincible;
|
||||
boolean isGhostToAllied;
|
||||
float attack;
|
||||
float defence;
|
||||
float weight;
|
||||
boolean useCreatorProperty;
|
||||
}
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class ConfigGadgetCombatProperty {
|
||||
float HP;
|
||||
boolean isLockHP;
|
||||
boolean isInvincible;
|
||||
boolean isGhostToAllied;
|
||||
float attack;
|
||||
float defence;
|
||||
float weight;
|
||||
boolean useCreatorProperty;
|
||||
}
|
||||
|
@ -1,61 +1,61 @@
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class HomeworldDefaultSaveData {
|
||||
|
||||
@SerializedName(value = "KFHBFNPDJBE", alternate = "PKACPHDGGEI")
|
||||
List<HomeBlock> homeBlockLists;
|
||||
|
||||
@SerializedName(value = "IJNPADKGNKE", alternate = "MINCKHBNING")
|
||||
Position bornPos;
|
||||
|
||||
@SerializedName("IPIIGEMFLHK")
|
||||
Position bornRot;
|
||||
|
||||
@SerializedName("HHOLBNPIHEM")
|
||||
Position djinPos;
|
||||
|
||||
@SerializedName("KNHCJKHCOAN")
|
||||
HomeFurniture mainhouse;
|
||||
|
||||
@SerializedName("NIHOJFEKFPG")
|
||||
List<HomeFurniture> doorLists;
|
||||
|
||||
@SerializedName("EPGELGEFJFK")
|
||||
List<HomeFurniture> stairLists;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public static class HomeBlock {
|
||||
|
||||
@SerializedName(value = "FGIJCELCGFI", alternate = "PGDPDIDJEEL")
|
||||
int blockId;
|
||||
|
||||
@SerializedName("BEAPOFELABD")
|
||||
List<HomeFurniture> furnitures;
|
||||
|
||||
@SerializedName("MLIODLGDFHJ")
|
||||
List<HomeFurniture> persistentFurnitures;
|
||||
}
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public static class HomeFurniture {
|
||||
|
||||
@SerializedName(value = "ENHNGKJBJAB", alternate = "KMAAJJHPNBA")
|
||||
int id;
|
||||
|
||||
@SerializedName(value = "NGIEEIOLPPO", alternate = "JFKAHNCPDME")
|
||||
Position pos;
|
||||
// @SerializedName(value = "HEOCEHKEBFM", alternate = "LKCKOOGFDBM")
|
||||
Position rot;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class HomeworldDefaultSaveData {
|
||||
|
||||
@SerializedName(value = "KFHBFNPDJBE", alternate = "PKACPHDGGEI")
|
||||
List<HomeBlock> homeBlockLists;
|
||||
|
||||
@SerializedName(value = "IJNPADKGNKE", alternate = "MINCKHBNING")
|
||||
Position bornPos;
|
||||
|
||||
@SerializedName("IPIIGEMFLHK")
|
||||
Position bornRot;
|
||||
|
||||
@SerializedName("HHOLBNPIHEM")
|
||||
Position djinPos;
|
||||
|
||||
@SerializedName("KNHCJKHCOAN")
|
||||
HomeFurniture mainhouse;
|
||||
|
||||
@SerializedName("NIHOJFEKFPG")
|
||||
List<HomeFurniture> doorLists;
|
||||
|
||||
@SerializedName("EPGELGEFJFK")
|
||||
List<HomeFurniture> stairLists;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public static class HomeBlock {
|
||||
|
||||
@SerializedName(value = "FGIJCELCGFI", alternate = "PGDPDIDJEEL")
|
||||
int blockId;
|
||||
|
||||
@SerializedName("BEAPOFELABD")
|
||||
List<HomeFurniture> furnitures;
|
||||
|
||||
@SerializedName("MLIODLGDFHJ")
|
||||
List<HomeFurniture> persistentFurnitures;
|
||||
}
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public static class HomeFurniture {
|
||||
|
||||
@SerializedName(value = "ENHNGKJBJAB", alternate = "KMAAJJHPNBA")
|
||||
int id;
|
||||
|
||||
@SerializedName(value = "NGIEEIOLPPO", alternate = "JFKAHNCPDME")
|
||||
Position pos;
|
||||
// @SerializedName(value = "HEOCEHKEBFM", alternate = "LKCKOOGFDBM")
|
||||
Position rot;
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,24 @@
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import com.github.davidmoten.rtreemulti.RTree;
|
||||
import com.github.davidmoten.rtreemulti.geometry.Geometry;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class SceneNpcBornData {
|
||||
int sceneId;
|
||||
List<SceneNpcBornEntry> bornPosList;
|
||||
|
||||
/** Spatial Index For NPC */
|
||||
transient RTree<SceneNpcBornEntry, Geometry> index;
|
||||
|
||||
/** npc groups */
|
||||
transient Map<Integer, SceneGroup> groups = new ConcurrentHashMap<>();
|
||||
}
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import com.github.davidmoten.rtreemulti.RTree;
|
||||
import com.github.davidmoten.rtreemulti.geometry.Geometry;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class SceneNpcBornData {
|
||||
int sceneId;
|
||||
List<SceneNpcBornEntry> bornPosList;
|
||||
|
||||
/** Spatial Index For NPC */
|
||||
transient RTree<SceneNpcBornEntry, Geometry> index;
|
||||
|
||||
/** npc groups */
|
||||
transient Map<Integer, SceneGroup> groups = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
@ -1,42 +1,42 @@
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class SceneNpcBornEntry {
|
||||
@SerializedName(
|
||||
value = "id",
|
||||
alternate = {"_id", "ID"})
|
||||
int id;
|
||||
|
||||
@SerializedName(
|
||||
value = "configId",
|
||||
alternate = {"_configId"})
|
||||
int configId;
|
||||
|
||||
@SerializedName(
|
||||
value = "pos",
|
||||
alternate = {"_pos"})
|
||||
Position pos;
|
||||
|
||||
@SerializedName(
|
||||
value = "rot",
|
||||
alternate = {"_rot"})
|
||||
Position rot;
|
||||
|
||||
@SerializedName(
|
||||
value = "groupId",
|
||||
alternate = {"_groupId"})
|
||||
int groupId;
|
||||
|
||||
@SerializedName(
|
||||
value = "suiteIdList",
|
||||
alternate = {"_suiteIdList"})
|
||||
List<Integer> suiteIdList;
|
||||
}
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class SceneNpcBornEntry {
|
||||
@SerializedName(
|
||||
value = "id",
|
||||
alternate = {"_id", "ID"})
|
||||
int id;
|
||||
|
||||
@SerializedName(
|
||||
value = "configId",
|
||||
alternate = {"_configId"})
|
||||
int configId;
|
||||
|
||||
@SerializedName(
|
||||
value = "pos",
|
||||
alternate = {"_pos"})
|
||||
Position pos;
|
||||
|
||||
@SerializedName(
|
||||
value = "rot",
|
||||
alternate = {"_rot"})
|
||||
Position rot;
|
||||
|
||||
@SerializedName(
|
||||
value = "groupId",
|
||||
alternate = {"_groupId"})
|
||||
int groupId;
|
||||
|
||||
@SerializedName(
|
||||
value = "suiteIdList",
|
||||
alternate = {"_suiteIdList"})
|
||||
List<Integer> suiteIdList;
|
||||
}
|
||||
|
@ -1,24 +1,24 @@
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import emu.grasscutter.data.common.PointData;
|
||||
import lombok.Getter;
|
||||
|
||||
public class ScenePointEntry {
|
||||
@Getter private final int sceneId;
|
||||
@Getter private final PointData pointData;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public ScenePointEntry(String name, PointData pointData) {
|
||||
this.sceneId = Integer.parseInt(name.split("_")[0]);
|
||||
this.pointData = pointData;
|
||||
}
|
||||
|
||||
public ScenePointEntry(int sceneId, PointData pointData) {
|
||||
this.sceneId = sceneId;
|
||||
this.pointData = pointData;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.sceneId + "_" + this.pointData.getId();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import emu.grasscutter.data.common.PointData;
|
||||
import lombok.Getter;
|
||||
|
||||
public class ScenePointEntry {
|
||||
@Getter private final int sceneId;
|
||||
@Getter private final PointData pointData;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public ScenePointEntry(String name, PointData pointData) {
|
||||
this.sceneId = Integer.parseInt(name.split("_")[0]);
|
||||
this.pointData = pointData;
|
||||
}
|
||||
|
||||
public ScenePointEntry(int sceneId, PointData pointData) {
|
||||
this.sceneId = sceneId;
|
||||
this.pointData = pointData;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.sceneId + "_" + this.pointData.getId();
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ScriptSceneData {
|
||||
Map<String, ScriptObject> scriptObjectList;
|
||||
|
||||
@Data
|
||||
public static class ScriptObject {
|
||||
// private SceneGroup groups;
|
||||
@SerializedName("dummy_points")
|
||||
private Map<String, List<Float>> dummyPoints;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ScriptSceneData {
|
||||
Map<String, ScriptObject> scriptObjectList;
|
||||
|
||||
@Data
|
||||
public static class ScriptObject {
|
||||
// private SceneGroup groups;
|
||||
@SerializedName("dummy_points")
|
||||
private Map<String, List<Float>> dummyPoints;
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
public class CurveInfo {
|
||||
private String type;
|
||||
private String arith;
|
||||
private float value;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getArith() {
|
||||
return arith;
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
public class CurveInfo {
|
||||
private String type;
|
||||
private String arith;
|
||||
private float value;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getArith() {
|
||||
return arith;
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
@ -1,105 +1,105 @@
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
import it.unimi.dsi.fastutil.floats.FloatArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2FloatArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2FloatMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import lombok.val;
|
||||
|
||||
public class DynamicFloat {
|
||||
public static DynamicFloat ZERO = new DynamicFloat(0f);
|
||||
private List<StackOp> ops;
|
||||
private boolean dynamic = false;
|
||||
private float constant = 0f;
|
||||
|
||||
public DynamicFloat(float constant) {
|
||||
this.constant = constant;
|
||||
}
|
||||
|
||||
public DynamicFloat(String key) {
|
||||
this.dynamic = true;
|
||||
this.ops = List.of(new StackOp(key));
|
||||
}
|
||||
|
||||
public DynamicFloat(boolean b) {
|
||||
this.dynamic = true;
|
||||
this.ops = List.of(new StackOp(String.valueOf(b)));
|
||||
}
|
||||
|
||||
public DynamicFloat(List<StackOp> ops) {
|
||||
this.dynamic = true;
|
||||
this.ops = ops;
|
||||
}
|
||||
|
||||
public String toString(boolean nextBoolean) {
|
||||
String key = String.valueOf(nextBoolean);
|
||||
this.ops = List.of(new StackOp(key));
|
||||
return ops.toString();
|
||||
}
|
||||
|
||||
public float get() {
|
||||
return this.get(new Object2FloatArrayMap<String>());
|
||||
}
|
||||
|
||||
public float get(Object2FloatMap<String> props) {
|
||||
if (!dynamic) return constant;
|
||||
|
||||
val fl = new FloatArrayList();
|
||||
for (var op : this.ops) {
|
||||
switch (op.op) {
|
||||
case CONSTANT -> fl.push(op.fValue);
|
||||
case KEY -> fl.push(props.getOrDefault(op.sValue, 0f));
|
||||
case ADD -> fl.push(fl.popFloat() + fl.popFloat());
|
||||
case SUB -> fl.push(
|
||||
-fl.popFloat() + fl.popFloat()); // [f0, f1, f2] -> [f0, f1-f2] (opposite of RPN order)
|
||||
case MUL -> fl.push(fl.popFloat() * fl.popFloat());
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
return fl.popFloat(); // well-formed data will always have only one value left at this point
|
||||
}
|
||||
|
||||
public static class StackOp {
|
||||
public Op op;
|
||||
|
||||
public float fValue;
|
||||
public String sValue;
|
||||
public boolean bValue;
|
||||
|
||||
public StackOp(String s) {
|
||||
switch (s.toUpperCase()) {
|
||||
case "ADD" -> this.op = Op.ADD;
|
||||
case "SUB" -> this.op = Op.SUB;
|
||||
case "MUL" -> this.op = Op.MUL;
|
||||
case "DIV" -> this.op = Op.DIV;
|
||||
default -> {
|
||||
this.op = Op.KEY;
|
||||
this.sValue = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public StackOp(boolean b) {
|
||||
this.op = Op.NEXBOOLEAN;
|
||||
this.bValue = Boolean.parseBoolean(String.valueOf(b));
|
||||
}
|
||||
|
||||
public StackOp(float f) {
|
||||
this.op = Op.CONSTANT;
|
||||
this.fValue = f;
|
||||
}
|
||||
|
||||
enum Op {
|
||||
CONSTANT,
|
||||
KEY,
|
||||
ADD,
|
||||
SUB,
|
||||
MUL,
|
||||
DIV,
|
||||
NEXBOOLEAN
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
import it.unimi.dsi.fastutil.floats.FloatArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2FloatArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2FloatMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import lombok.val;
|
||||
|
||||
public class DynamicFloat {
|
||||
public static DynamicFloat ZERO = new DynamicFloat(0f);
|
||||
private List<StackOp> ops;
|
||||
private boolean dynamic = false;
|
||||
private float constant = 0f;
|
||||
|
||||
public DynamicFloat(float constant) {
|
||||
this.constant = constant;
|
||||
}
|
||||
|
||||
public DynamicFloat(String key) {
|
||||
this.dynamic = true;
|
||||
this.ops = List.of(new StackOp(key));
|
||||
}
|
||||
|
||||
public DynamicFloat(boolean b) {
|
||||
this.dynamic = true;
|
||||
this.ops = List.of(new StackOp(String.valueOf(b)));
|
||||
}
|
||||
|
||||
public DynamicFloat(List<StackOp> ops) {
|
||||
this.dynamic = true;
|
||||
this.ops = ops;
|
||||
}
|
||||
|
||||
public String toString(boolean nextBoolean) {
|
||||
String key = String.valueOf(nextBoolean);
|
||||
this.ops = List.of(new StackOp(key));
|
||||
return ops.toString();
|
||||
}
|
||||
|
||||
public float get() {
|
||||
return this.get(new Object2FloatArrayMap<String>());
|
||||
}
|
||||
|
||||
public float get(Object2FloatMap<String> props) {
|
||||
if (!dynamic) return constant;
|
||||
|
||||
val fl = new FloatArrayList();
|
||||
for (var op : this.ops) {
|
||||
switch (op.op) {
|
||||
case CONSTANT -> fl.push(op.fValue);
|
||||
case KEY -> fl.push(props.getOrDefault(op.sValue, 0f));
|
||||
case ADD -> fl.push(fl.popFloat() + fl.popFloat());
|
||||
case SUB -> fl.push(
|
||||
-fl.popFloat() + fl.popFloat()); // [f0, f1, f2] -> [f0, f1-f2] (opposite of RPN order)
|
||||
case MUL -> fl.push(fl.popFloat() * fl.popFloat());
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
return fl.popFloat(); // well-formed data will always have only one value left at this point
|
||||
}
|
||||
|
||||
public static class StackOp {
|
||||
public Op op;
|
||||
|
||||
public float fValue;
|
||||
public String sValue;
|
||||
public boolean bValue;
|
||||
|
||||
public StackOp(String s) {
|
||||
switch (s.toUpperCase()) {
|
||||
case "ADD" -> this.op = Op.ADD;
|
||||
case "SUB" -> this.op = Op.SUB;
|
||||
case "MUL" -> this.op = Op.MUL;
|
||||
case "DIV" -> this.op = Op.DIV;
|
||||
default -> {
|
||||
this.op = Op.KEY;
|
||||
this.sValue = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public StackOp(boolean b) {
|
||||
this.op = Op.NEXBOOLEAN;
|
||||
this.bValue = Boolean.parseBoolean(String.valueOf(b));
|
||||
}
|
||||
|
||||
public StackOp(float f) {
|
||||
this.op = Op.CONSTANT;
|
||||
this.fValue = f;
|
||||
}
|
||||
|
||||
enum Op {
|
||||
CONSTANT,
|
||||
KEY,
|
||||
ADD,
|
||||
SUB,
|
||||
MUL,
|
||||
DIV,
|
||||
NEXBOOLEAN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,25 @@
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
|
||||
public class FightPropData {
|
||||
private String propType;
|
||||
private FightProperty prop;
|
||||
private float value;
|
||||
|
||||
public String getPropType() {
|
||||
return propType;
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public FightProperty getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void onLoad() {
|
||||
this.prop = FightProperty.getPropByName(propType);
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
|
||||
public class FightPropData {
|
||||
private String propType;
|
||||
private FightProperty prop;
|
||||
private float value;
|
||||
|
||||
public String getPropType() {
|
||||
return propType;
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public FightProperty getProp() {
|
||||
return prop;
|
||||
}
|
||||
|
||||
public void onLoad() {
|
||||
this.prop = FightProperty.getPropByName(propType);
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,39 @@
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
// Used in excels
|
||||
public class ItemParamData {
|
||||
@SerializedName(
|
||||
value = "id",
|
||||
alternate = {"itemId"})
|
||||
private int id;
|
||||
|
||||
@SerializedName(
|
||||
value = "count",
|
||||
alternate = {"itemCount"})
|
||||
private int count;
|
||||
|
||||
public ItemParamData() {}
|
||||
|
||||
public ItemParamData(int id, int count) {
|
||||
this.id = id;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getItemCount() {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
// Used in excels
|
||||
public class ItemParamData {
|
||||
@SerializedName(
|
||||
value = "id",
|
||||
alternate = {"itemId"})
|
||||
private int id;
|
||||
|
||||
@SerializedName(
|
||||
value = "count",
|
||||
alternate = {"itemCount"})
|
||||
private int count;
|
||||
|
||||
public ItemParamData() {}
|
||||
|
||||
public ItemParamData(int id, int count) {
|
||||
this.id = id;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getItemCount() {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,26 @@
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
public class ItemParamStringData {
|
||||
private int id;
|
||||
private String count;
|
||||
|
||||
public ItemParamStringData() {}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public ItemParamData toItemParamData() {
|
||||
if (count.contains(";")) {
|
||||
String[] split = count.split(";");
|
||||
count = count.split(";")[split.length - 1];
|
||||
} else if (count.contains(".")) {
|
||||
return new ItemParamData(id, (int) Math.ceil(Double.parseDouble(count)));
|
||||
}
|
||||
return new ItemParamData(id, Integer.parseInt(count));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
public class ItemParamStringData {
|
||||
private int id;
|
||||
private String count;
|
||||
|
||||
public ItemParamStringData() {}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public ItemParamData toItemParamData() {
|
||||
if (count.contains(";")) {
|
||||
String[] split = count.split(";");
|
||||
count = count.split(";")[split.length - 1];
|
||||
} else if (count.contains(".")) {
|
||||
return new ItemParamData(id, (int) Math.ceil(Double.parseDouble(count)));
|
||||
}
|
||||
return new ItemParamData(id, Integer.parseInt(count));
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,24 @@
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OpenCondData {
|
||||
private String condType;
|
||||
private List<Integer> paramList;
|
||||
|
||||
public String getCondType() {
|
||||
return condType;
|
||||
}
|
||||
|
||||
public void setCondType(String condType) {
|
||||
condType = condType;
|
||||
}
|
||||
|
||||
public List<Integer> getParamList() {
|
||||
return paramList;
|
||||
}
|
||||
|
||||
public void setParamList(List<Integer> paramList) {
|
||||
paramList = paramList;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OpenCondData {
|
||||
private String condType;
|
||||
private List<Integer> paramList;
|
||||
|
||||
public String getCondType() {
|
||||
return condType;
|
||||
}
|
||||
|
||||
public void setCondType(String condType) {
|
||||
condType = condType;
|
||||
}
|
||||
|
||||
public List<Integer> getParamList() {
|
||||
return paramList;
|
||||
}
|
||||
|
||||
public void setParamList(List<Integer> paramList) {
|
||||
paramList = paramList;
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
public class PropGrowCurve {
|
||||
private String type;
|
||||
private String growCurve;
|
||||
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public String getGrowCurve() {
|
||||
return this.growCurve;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
public class PropGrowCurve {
|
||||
private String type;
|
||||
private String growCurve;
|
||||
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public String getGrowCurve() {
|
||||
return this.growCurve;
|
||||
}
|
||||
}
|
||||
|
@ -1,74 +1,74 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.game.props.BattlePassMissionRefreshType;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.MissionStatus;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = {"BattlePassMissionExcelConfigData.json"})
|
||||
@Getter
|
||||
public class BattlePassMissionData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
private int id;
|
||||
|
||||
private int addPoint;
|
||||
private int scheduleId;
|
||||
private int progress;
|
||||
private TriggerConfig triggerConfig;
|
||||
private BattlePassMissionRefreshType refreshType;
|
||||
|
||||
private transient Set<Integer> mainParams;
|
||||
|
||||
public WatcherTriggerType getTriggerType() {
|
||||
return this.getTriggerConfig().getTriggerType();
|
||||
}
|
||||
|
||||
public boolean isCycleRefresh() {
|
||||
return getRefreshType() == null
|
||||
|| getRefreshType()
|
||||
== BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE;
|
||||
}
|
||||
|
||||
public boolean isValidRefreshType() {
|
||||
return getRefreshType() == null
|
||||
|| getRefreshType()
|
||||
== BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE
|
||||
|| getScheduleId() == 2701;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
if (this.getTriggerConfig() != null) {
|
||||
var params = getTriggerConfig().getParamList()[0];
|
||||
if ((params != null) && !params.isEmpty()) {
|
||||
this.mainParams =
|
||||
Arrays.stream(params.split("[:;,]")).map(Integer::parseInt).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission toProto() {
|
||||
var protoBuilder =
|
||||
emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.newBuilder();
|
||||
|
||||
protoBuilder
|
||||
.setMissionId(getId())
|
||||
.setTotalProgress(this.getProgress())
|
||||
.setRewardBattlePassPoint(this.getAddPoint())
|
||||
.setMissionStatus(MissionStatus.MISSION_STATUS_UNFINISHED)
|
||||
.setMissionType(this.getRefreshType() == null ? 0 : this.getRefreshType().getValue());
|
||||
|
||||
return protoBuilder.build();
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class TriggerConfig {
|
||||
private WatcherTriggerType triggerType;
|
||||
private String[] paramList;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.game.props.BattlePassMissionRefreshType;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.MissionStatus;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = {"BattlePassMissionExcelConfigData.json"})
|
||||
@Getter
|
||||
public class BattlePassMissionData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
private int id;
|
||||
|
||||
private int addPoint;
|
||||
private int scheduleId;
|
||||
private int progress;
|
||||
private TriggerConfig triggerConfig;
|
||||
private BattlePassMissionRefreshType refreshType;
|
||||
|
||||
private transient Set<Integer> mainParams;
|
||||
|
||||
public WatcherTriggerType getTriggerType() {
|
||||
return this.getTriggerConfig().getTriggerType();
|
||||
}
|
||||
|
||||
public boolean isCycleRefresh() {
|
||||
return getRefreshType() == null
|
||||
|| getRefreshType()
|
||||
== BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE;
|
||||
}
|
||||
|
||||
public boolean isValidRefreshType() {
|
||||
return getRefreshType() == null
|
||||
|| getRefreshType()
|
||||
== BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE
|
||||
|| getScheduleId() == 2701;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
if (this.getTriggerConfig() != null) {
|
||||
var params = getTriggerConfig().getParamList()[0];
|
||||
if ((params != null) && !params.isEmpty()) {
|
||||
this.mainParams =
|
||||
Arrays.stream(params.split("[:;,]")).map(Integer::parseInt).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission toProto() {
|
||||
var protoBuilder =
|
||||
emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.newBuilder();
|
||||
|
||||
protoBuilder
|
||||
.setMissionId(getId())
|
||||
.setTotalProgress(this.getProgress())
|
||||
.setRewardBattlePassPoint(this.getAddPoint())
|
||||
.setMissionStatus(MissionStatus.MISSION_STATUS_UNFINISHED)
|
||||
.setMissionType(this.getRefreshType() == null ? 0 : this.getRefreshType().getValue());
|
||||
|
||||
return protoBuilder.build();
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class TriggerConfig {
|
||||
private WatcherTriggerType triggerType;
|
||||
private String[] paramList;
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,25 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = "BattlePassRewardExcelConfigData.json")
|
||||
@Getter
|
||||
public class BattlePassRewardData extends GameResource {
|
||||
private int indexId;
|
||||
private int level;
|
||||
private List<Integer> freeRewardIdList;
|
||||
private List<Integer> paidRewardIdList;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
// Reward ID is a combination of index and level.
|
||||
// We do this to get a unique ID.
|
||||
return this.indexId * 100 + this.level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = "BattlePassRewardExcelConfigData.json")
|
||||
@Getter
|
||||
public class BattlePassRewardData extends GameResource {
|
||||
private int indexId;
|
||||
private int level;
|
||||
private List<Integer> freeRewardIdList;
|
||||
private List<Integer> paidRewardIdList;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
// Reward ID is a combination of index and level.
|
||||
// We do this to get a unique ID.
|
||||
return this.indexId * 100 + this.level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {}
|
||||
}
|
||||
|
@ -1,46 +1,46 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = "BlossomRefreshExcelConfigData.json")
|
||||
@Getter
|
||||
public class BlossomRefreshExcelConfigData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
private int id;
|
||||
// Map details
|
||||
private long nameTextMapHash;
|
||||
private long descTextMapHash;
|
||||
private String icon;
|
||||
private String clientShowType; // BLOSSOM_SHOWTYPE_CHALLENGE, BLOSSOM_SHOWTYPE_NPCTALK
|
||||
|
||||
// Refresh details
|
||||
private String refreshType; // Leyline blossoms, magical ore outcrops
|
||||
private int
|
||||
refreshCount; // Number of entries to spawn at refresh (1 for each leyline type for each city,
|
||||
// 4 for magical ore for each city)
|
||||
private String refreshTime; // Server time-of-day to refresh at
|
||||
private RefreshCond[] refreshCondVec; // AR requirements etc.
|
||||
|
||||
private int cityId;
|
||||
private int blossomChestId; // 1 for mora, 2 for exp
|
||||
private Drop[] dropVec;
|
||||
|
||||
// Unknown details
|
||||
// @Getter private int reviseLevel;
|
||||
// @Getter private int campUpdateNeedCount; // Always 1 if specified
|
||||
|
||||
@Getter
|
||||
public static class Drop {
|
||||
int dropId;
|
||||
int previewReward;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class RefreshCond {
|
||||
String type;
|
||||
List<Integer> param;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = "BlossomRefreshExcelConfigData.json")
|
||||
@Getter
|
||||
public class BlossomRefreshExcelConfigData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
private int id;
|
||||
// Map details
|
||||
private long nameTextMapHash;
|
||||
private long descTextMapHash;
|
||||
private String icon;
|
||||
private String clientShowType; // BLOSSOM_SHOWTYPE_CHALLENGE, BLOSSOM_SHOWTYPE_NPCTALK
|
||||
|
||||
// Refresh details
|
||||
private String refreshType; // Leyline blossoms, magical ore outcrops
|
||||
private int
|
||||
refreshCount; // Number of entries to spawn at refresh (1 for each leyline type for each city,
|
||||
// 4 for magical ore for each city)
|
||||
private String refreshTime; // Server time-of-day to refresh at
|
||||
private RefreshCond[] refreshCondVec; // AR requirements etc.
|
||||
|
||||
private int cityId;
|
||||
private int blossomChestId; // 1 for mora, 2 for exp
|
||||
private Drop[] dropVec;
|
||||
|
||||
// Unknown details
|
||||
// @Getter private int reviseLevel;
|
||||
// @Getter private int campUpdateNeedCount; // Always 1 if specified
|
||||
|
||||
@Getter
|
||||
public static class Drop {
|
||||
int dropId;
|
||||
int previewReward;
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class RefreshCond {
|
||||
String type;
|
||||
List<Integer> param;
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,28 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.game.props.ServerBuffType;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = "BuffExcelConfigData.json")
|
||||
@Getter
|
||||
public class BuffData extends GameResource {
|
||||
private int groupId;
|
||||
private int serverBuffId;
|
||||
private float time;
|
||||
private boolean isPersistent;
|
||||
private ServerBuffType serverBuffType;
|
||||
private String abilityName;
|
||||
private String modifierName;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.serverBuffId;
|
||||
}
|
||||
|
||||
public void onLoad() {
|
||||
this.serverBuffType =
|
||||
this.serverBuffType != null ? this.serverBuffType : ServerBuffType.SERVER_BUFF_NONE;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.game.props.ServerBuffType;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = "BuffExcelConfigData.json")
|
||||
@Getter
|
||||
public class BuffData extends GameResource {
|
||||
private int groupId;
|
||||
private int serverBuffId;
|
||||
private float time;
|
||||
private boolean isPersistent;
|
||||
private ServerBuffType serverBuffType;
|
||||
private String abilityName;
|
||||
private String modifierName;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.serverBuffId;
|
||||
}
|
||||
|
||||
public void onLoad() {
|
||||
this.serverBuffType =
|
||||
this.serverBuffType != null ? this.serverBuffType : ServerBuffType.SERVER_BUFF_NONE;
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,24 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@ResourceType(name = "CityConfigData.json", loadPriority = ResourceType.LoadPriority.HIGH)
|
||||
@Getter
|
||||
@Setter
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class CityData extends GameResource {
|
||||
int cityId;
|
||||
int sceneId;
|
||||
List<Integer> areaIdVec;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.cityId;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@ResourceType(name = "CityConfigData.json", loadPriority = ResourceType.LoadPriority.HIGH)
|
||||
@Getter
|
||||
@Setter
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class CityData extends GameResource {
|
||||
int cityId;
|
||||
int sceneId;
|
||||
List<Integer> areaIdVec;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.cityId;
|
||||
}
|
||||
}
|
||||
|
@ -1,82 +1,82 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ResourceType(name = "CombineExcelConfigData.json")
|
||||
public class CombineData extends GameResource {
|
||||
|
||||
private int combineId;
|
||||
private int playerLevel;
|
||||
private boolean isDefaultShow;
|
||||
private int combineType;
|
||||
private int subCombineType;
|
||||
private int resultItemId;
|
||||
private int resultItemCount;
|
||||
private int scoinCost;
|
||||
private List<ItemParamData> randomItems;
|
||||
private List<ItemParamData> materialItems;
|
||||
private String recipeType;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.combineId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
super.onLoad();
|
||||
// clean data
|
||||
randomItems =
|
||||
randomItems.stream().filter(item -> item.getId() > 0).collect(Collectors.toList());
|
||||
materialItems =
|
||||
materialItems.stream().filter(item -> item.getId() > 0).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public int getCombineId() {
|
||||
return combineId;
|
||||
}
|
||||
|
||||
public int getPlayerLevel() {
|
||||
return playerLevel;
|
||||
}
|
||||
|
||||
public boolean isDefaultShow() {
|
||||
return isDefaultShow;
|
||||
}
|
||||
|
||||
public int getCombineType() {
|
||||
return combineType;
|
||||
}
|
||||
|
||||
public int getSubCombineType() {
|
||||
return subCombineType;
|
||||
}
|
||||
|
||||
public int getResultItemId() {
|
||||
return resultItemId;
|
||||
}
|
||||
|
||||
public int getResultItemCount() {
|
||||
return resultItemCount;
|
||||
}
|
||||
|
||||
public int getScoinCost() {
|
||||
return scoinCost;
|
||||
}
|
||||
|
||||
public List<ItemParamData> getRandomItems() {
|
||||
return randomItems;
|
||||
}
|
||||
|
||||
public List<ItemParamData> getMaterialItems() {
|
||||
return materialItems;
|
||||
}
|
||||
|
||||
public String getRecipeType() {
|
||||
return recipeType;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ResourceType(name = "CombineExcelConfigData.json")
|
||||
public class CombineData extends GameResource {
|
||||
|
||||
private int combineId;
|
||||
private int playerLevel;
|
||||
private boolean isDefaultShow;
|
||||
private int combineType;
|
||||
private int subCombineType;
|
||||
private int resultItemId;
|
||||
private int resultItemCount;
|
||||
private int scoinCost;
|
||||
private List<ItemParamData> randomItems;
|
||||
private List<ItemParamData> materialItems;
|
||||
private String recipeType;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.combineId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
super.onLoad();
|
||||
// clean data
|
||||
randomItems =
|
||||
randomItems.stream().filter(item -> item.getId() > 0).collect(Collectors.toList());
|
||||
materialItems =
|
||||
materialItems.stream().filter(item -> item.getId() > 0).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public int getCombineId() {
|
||||
return combineId;
|
||||
}
|
||||
|
||||
public int getPlayerLevel() {
|
||||
return playerLevel;
|
||||
}
|
||||
|
||||
public boolean isDefaultShow() {
|
||||
return isDefaultShow;
|
||||
}
|
||||
|
||||
public int getCombineType() {
|
||||
return combineType;
|
||||
}
|
||||
|
||||
public int getSubCombineType() {
|
||||
return subCombineType;
|
||||
}
|
||||
|
||||
public int getResultItemId() {
|
||||
return resultItemId;
|
||||
}
|
||||
|
||||
public int getResultItemCount() {
|
||||
return resultItemCount;
|
||||
}
|
||||
|
||||
public int getScoinCost() {
|
||||
return scoinCost;
|
||||
}
|
||||
|
||||
public List<ItemParamData> getRandomItems() {
|
||||
return randomItems;
|
||||
}
|
||||
|
||||
public List<ItemParamData> getMaterialItems() {
|
||||
return materialItems;
|
||||
}
|
||||
|
||||
public String getRecipeType() {
|
||||
return recipeType;
|
||||
}
|
||||
}
|
||||
|
@ -1,43 +1,43 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
|
||||
@ResourceType(
|
||||
name = {"CookBonusExcelConfigData.json"},
|
||||
loadPriority = LoadPriority.LOW)
|
||||
public class CookBonusData extends GameResource {
|
||||
private int avatarId;
|
||||
private int recipeId;
|
||||
private int[] paramVec;
|
||||
private int[] complexParamVec;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.avatarId;
|
||||
}
|
||||
|
||||
public int getAvatarId() {
|
||||
return avatarId;
|
||||
}
|
||||
|
||||
public int getRecipeId() {
|
||||
return recipeId;
|
||||
}
|
||||
|
||||
public int[] getParamVec() {
|
||||
return paramVec;
|
||||
}
|
||||
|
||||
public int[] getComplexParamVec() {
|
||||
return complexParamVec;
|
||||
}
|
||||
|
||||
public int getReplacementItemId() {
|
||||
return this.paramVec[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
|
||||
@ResourceType(
|
||||
name = {"CookBonusExcelConfigData.json"},
|
||||
loadPriority = LoadPriority.LOW)
|
||||
public class CookBonusData extends GameResource {
|
||||
private int avatarId;
|
||||
private int recipeId;
|
||||
private int[] paramVec;
|
||||
private int[] complexParamVec;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.avatarId;
|
||||
}
|
||||
|
||||
public int getAvatarId() {
|
||||
return avatarId;
|
||||
}
|
||||
|
||||
public int getRecipeId() {
|
||||
return recipeId;
|
||||
}
|
||||
|
||||
public int[] getParamVec() {
|
||||
return paramVec;
|
||||
}
|
||||
|
||||
public int[] getComplexParamVec() {
|
||||
return complexParamVec;
|
||||
}
|
||||
|
||||
public int getReplacementItemId() {
|
||||
return this.paramVec[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {}
|
||||
}
|
||||
|
@ -1,24 +1,24 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(
|
||||
name = {"CookRecipeExcelConfigData.json"},
|
||||
loadPriority = LoadPriority.LOW)
|
||||
@Getter
|
||||
public class CookRecipeData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
private int id;
|
||||
|
||||
private int rankLevel;
|
||||
private boolean isDefaultUnlocked;
|
||||
private int maxProficiency;
|
||||
|
||||
private List<ItemParamData> qualityOutputVec;
|
||||
private List<ItemParamData> inputVec;
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(
|
||||
name = {"CookRecipeExcelConfigData.json"},
|
||||
loadPriority = LoadPriority.LOW)
|
||||
@Getter
|
||||
public class CookRecipeData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
private int id;
|
||||
|
||||
private int rankLevel;
|
||||
private boolean isDefaultUnlocked;
|
||||
private int maxProficiency;
|
||||
|
||||
private List<ItemParamData> qualityOutputVec;
|
||||
private List<ItemParamData> inputVec;
|
||||
}
|
||||
|
@ -1,36 +1,36 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.List;
|
||||
|
||||
@ResourceType(
|
||||
name = "EnvAnimalGatherExcelConfigData.json",
|
||||
loadPriority = ResourceType.LoadPriority.LOW)
|
||||
public class EnvAnimalGatherConfigData extends GameResource {
|
||||
private int animalId;
|
||||
private String entityType;
|
||||
private List<ItemParamData> gatherItemId;
|
||||
private String excludeWeathers;
|
||||
private int aliveTime;
|
||||
private int escapeTime;
|
||||
private int escapeRadius;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return animalId;
|
||||
}
|
||||
|
||||
public int getAnimalId() {
|
||||
return animalId;
|
||||
}
|
||||
|
||||
public String getEntityType() {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
public ItemParamData getGatherItem() {
|
||||
return gatherItemId.size() > 0 ? gatherItemId.get(0) : null;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.List;
|
||||
|
||||
@ResourceType(
|
||||
name = "EnvAnimalGatherExcelConfigData.json",
|
||||
loadPriority = ResourceType.LoadPriority.LOW)
|
||||
public class EnvAnimalGatherConfigData extends GameResource {
|
||||
private int animalId;
|
||||
private String entityType;
|
||||
private List<ItemParamData> gatherItemId;
|
||||
private String excludeWeathers;
|
||||
private int aliveTime;
|
||||
private int escapeTime;
|
||||
private int escapeRadius;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return animalId;
|
||||
}
|
||||
|
||||
public int getAnimalId() {
|
||||
return animalId;
|
||||
}
|
||||
|
||||
public String getEntityType() {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
public ItemParamData getGatherItem() {
|
||||
return gatherItemId.size() > 0 ? gatherItemId.get(0) : null;
|
||||
}
|
||||
}
|
||||
|
@ -1,59 +1,59 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.FightPropData;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@ResourceType(name = "EquipAffixExcelConfigData.json")
|
||||
public class EquipAffixData extends GameResource {
|
||||
|
||||
private int affixId;
|
||||
private int id;
|
||||
private int level;
|
||||
private long nameTextMapHash;
|
||||
private String openConfig;
|
||||
private FightPropData[] addProps;
|
||||
private float[] paramList;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return affixId;
|
||||
}
|
||||
|
||||
public int getMainId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public long getNameTextMapHash() {
|
||||
return nameTextMapHash;
|
||||
}
|
||||
|
||||
public String getOpenConfig() {
|
||||
return openConfig;
|
||||
}
|
||||
|
||||
public FightPropData[] getAddProps() {
|
||||
return addProps;
|
||||
}
|
||||
|
||||
public float[] getParamList() {
|
||||
return paramList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
ArrayList<FightPropData> parsed = new ArrayList<FightPropData>(getAddProps().length);
|
||||
for (FightPropData prop : getAddProps()) {
|
||||
if (prop.getPropType() != null && prop.getValue() != 0f) {
|
||||
prop.onLoad();
|
||||
parsed.add(prop);
|
||||
}
|
||||
}
|
||||
this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.FightPropData;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@ResourceType(name = "EquipAffixExcelConfigData.json")
|
||||
public class EquipAffixData extends GameResource {
|
||||
|
||||
private int affixId;
|
||||
private int id;
|
||||
private int level;
|
||||
private long nameTextMapHash;
|
||||
private String openConfig;
|
||||
private FightPropData[] addProps;
|
||||
private float[] paramList;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return affixId;
|
||||
}
|
||||
|
||||
public int getMainId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public long getNameTextMapHash() {
|
||||
return nameTextMapHash;
|
||||
}
|
||||
|
||||
public String getOpenConfig() {
|
||||
return openConfig;
|
||||
}
|
||||
|
||||
public FightPropData[] getAddProps() {
|
||||
return addProps;
|
||||
}
|
||||
|
||||
public float[] getParamList() {
|
||||
return paramList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
ArrayList<FightPropData> parsed = new ArrayList<FightPropData>(getAddProps().length);
|
||||
for (FightPropData prop : getAddProps()) {
|
||||
if (prop.getPropType() != null && prop.getValue() != 0f) {
|
||||
prop.onLoad();
|
||||
parsed.add(prop);
|
||||
}
|
||||
}
|
||||
this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,23 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
|
||||
@ResourceType(name = "FetterCharacterCardExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
|
||||
public class FetterCharacterCardData extends GameResource {
|
||||
private int avatarId;
|
||||
private int rewardId;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return avatarId;
|
||||
}
|
||||
|
||||
public int getRewardId() {
|
||||
return rewardId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
|
||||
@ResourceType(name = "FetterCharacterCardExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
|
||||
public class FetterCharacterCardData extends GameResource {
|
||||
private int avatarId;
|
||||
private int rewardId;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return avatarId;
|
||||
}
|
||||
|
||||
public int getRewardId() {
|
||||
return rewardId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {}
|
||||
}
|
||||
|
@ -1,38 +1,38 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
import emu.grasscutter.data.common.OpenCondData;
|
||||
import java.util.List;
|
||||
|
||||
@ResourceType(
|
||||
name = {
|
||||
"FetterInfoExcelConfigData.json",
|
||||
"FettersExcelConfigData.json",
|
||||
"FetterStoryExcelConfigData.json",
|
||||
"PhotographExpressionExcelConfigData.json",
|
||||
"PhotographPosenameExcelConfigData.json"
|
||||
},
|
||||
loadPriority = LoadPriority.HIGHEST)
|
||||
public class FetterData extends GameResource {
|
||||
private int avatarId;
|
||||
private int fetterId;
|
||||
private List<OpenCondData> openCond;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return fetterId;
|
||||
}
|
||||
|
||||
public int getAvatarId() {
|
||||
return avatarId;
|
||||
}
|
||||
|
||||
public List<OpenCondData> getOpenConds() {
|
||||
return openCond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
import emu.grasscutter.data.common.OpenCondData;
|
||||
import java.util.List;
|
||||
|
||||
@ResourceType(
|
||||
name = {
|
||||
"FetterInfoExcelConfigData.json",
|
||||
"FettersExcelConfigData.json",
|
||||
"FetterStoryExcelConfigData.json",
|
||||
"PhotographExpressionExcelConfigData.json",
|
||||
"PhotographPosenameExcelConfigData.json"
|
||||
},
|
||||
loadPriority = LoadPriority.HIGHEST)
|
||||
public class FetterData extends GameResource {
|
||||
private int avatarId;
|
||||
private int fetterId;
|
||||
private List<OpenCondData> openCond;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return fetterId;
|
||||
}
|
||||
|
||||
public int getAvatarId() {
|
||||
return avatarId;
|
||||
}
|
||||
|
||||
public List<OpenCondData> getOpenConds() {
|
||||
return openCond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {}
|
||||
}
|
||||
|
@ -1,29 +1,29 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(
|
||||
name = {"ForgeExcelConfigData.json"},
|
||||
loadPriority = LoadPriority.HIGHEST)
|
||||
@Getter
|
||||
public class ForgeData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
private int id;
|
||||
|
||||
private int playerLevel;
|
||||
private int forgeType;
|
||||
private int showItemId;
|
||||
private int resultItemId;
|
||||
private int resultItemCount;
|
||||
private int forgeTime;
|
||||
private int queueNum;
|
||||
private int scoinCost;
|
||||
private int priority;
|
||||
private int forgePoint;
|
||||
private List<ItemParamData> materialItems;
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(
|
||||
name = {"ForgeExcelConfigData.json"},
|
||||
loadPriority = LoadPriority.HIGHEST)
|
||||
@Getter
|
||||
public class ForgeData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
private int id;
|
||||
|
||||
private int playerLevel;
|
||||
private int forgeType;
|
||||
private int showItemId;
|
||||
private int resultItemId;
|
||||
private int resultItemCount;
|
||||
private int forgeTime;
|
||||
private int queueNum;
|
||||
private int scoinCost;
|
||||
private int priority;
|
||||
private int forgePoint;
|
||||
private List<ItemParamData> materialItems;
|
||||
}
|
||||
|
@ -1,34 +1,34 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Getter
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@ResourceType(name = {"FurnitureMakeExcelConfigData.json"})
|
||||
public class FurnitureMakeConfigData extends GameResource {
|
||||
|
||||
int configID;
|
||||
int furnitureItemID;
|
||||
int count;
|
||||
int exp;
|
||||
List<ItemParamData> materialItems;
|
||||
int makeTime;
|
||||
int maxAccelerateTime;
|
||||
int quickFetchMaterialNum;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return configID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.materialItems = materialItems.stream().filter(x -> x.getId() > 0).toList();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Getter
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@ResourceType(name = {"FurnitureMakeExcelConfigData.json"})
|
||||
public class FurnitureMakeConfigData extends GameResource {
|
||||
|
||||
int configID;
|
||||
int furnitureItemID;
|
||||
int count;
|
||||
int exp;
|
||||
List<ItemParamData> materialItems;
|
||||
int makeTime;
|
||||
int maxAccelerateTime;
|
||||
int quickFetchMaterialNum;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return configID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.materialItems = materialItems.stream().filter(x -> x.getId() > 0).toList();
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +1,47 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
|
||||
@ResourceType(name = "GatherExcelConfigData.json")
|
||||
public class GatherData extends GameResource {
|
||||
private int pointType;
|
||||
private int id;
|
||||
private int gadgetId;
|
||||
private int itemId;
|
||||
private int cd; // Probably hours
|
||||
private boolean isForbidGuest;
|
||||
private boolean initDisableInteract;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.pointType;
|
||||
}
|
||||
|
||||
public int getGatherId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getGadgetId() {
|
||||
return gadgetId;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public int getCd() {
|
||||
return cd;
|
||||
}
|
||||
|
||||
public boolean isForbidGuest() {
|
||||
return isForbidGuest;
|
||||
}
|
||||
|
||||
public boolean initDisableInteract() {
|
||||
return initDisableInteract;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
|
||||
@ResourceType(name = "GatherExcelConfigData.json")
|
||||
public class GatherData extends GameResource {
|
||||
private int pointType;
|
||||
private int id;
|
||||
private int gadgetId;
|
||||
private int itemId;
|
||||
private int cd; // Probably hours
|
||||
private boolean isForbidGuest;
|
||||
private boolean initDisableInteract;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.pointType;
|
||||
}
|
||||
|
||||
public int getGatherId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getGadgetId() {
|
||||
return gadgetId;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public int getCd() {
|
||||
return cd;
|
||||
}
|
||||
|
||||
public boolean isForbidGuest() {
|
||||
return isForbidGuest;
|
||||
}
|
||||
|
||||
public boolean initDisableInteract() {
|
||||
return initDisableInteract;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {}
|
||||
}
|
||||
|
@ -1,31 +1,31 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Getter
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@ResourceType(name = {"HomeWorldBgmExcelConfigData.json"})
|
||||
public class HomeWorldBgmData extends GameResource {
|
||||
@SerializedName(value = "homeBgmId", alternate = "MJJENLEBKEF")
|
||||
private int homeBgmId;
|
||||
|
||||
private boolean isDefaultUnlock;
|
||||
private boolean NBIDHGOOCKD;
|
||||
private boolean JJMNJMCCOKP;
|
||||
private int cityId;
|
||||
private int sortOrder;
|
||||
private String GEGHMJBJMGB;
|
||||
|
||||
@SerializedName(value = "bgmNameTextMapHash", alternate = "LMLNBMJFFML")
|
||||
private long bgmNameTextMapHash;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.homeBgmId;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Getter
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@ResourceType(name = {"HomeWorldBgmExcelConfigData.json"})
|
||||
public class HomeWorldBgmData extends GameResource {
|
||||
@SerializedName(value = "homeBgmId", alternate = "MJJENLEBKEF")
|
||||
private int homeBgmId;
|
||||
|
||||
private boolean isDefaultUnlock;
|
||||
private boolean NBIDHGOOCKD;
|
||||
private boolean JJMNJMCCOKP;
|
||||
private int cityId;
|
||||
private int sortOrder;
|
||||
private String GEGHMJBJMGB;
|
||||
|
||||
@SerializedName(value = "bgmNameTextMapHash", alternate = "LMLNBMJFFML")
|
||||
private long bgmNameTextMapHash;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.homeBgmId;
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,31 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Getter
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@ResourceType(name = {"HomeworldLevelExcelConfigData.json"})
|
||||
public class HomeWorldLevelData extends GameResource {
|
||||
|
||||
int level;
|
||||
int exp;
|
||||
int homeCoinStoreLimit;
|
||||
int homeFetterExpStoreLimit;
|
||||
int rewardId;
|
||||
int furnitureMakeSlotCount;
|
||||
int outdoorUnlockBlockCount;
|
||||
int freeUnlockModuleCount;
|
||||
int deployNpcCount;
|
||||
int limitShopGoodsCount;
|
||||
List<String> levelFuncs;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Getter
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@ResourceType(name = {"HomeworldLevelExcelConfigData.json"})
|
||||
public class HomeWorldLevelData extends GameResource {
|
||||
|
||||
int level;
|
||||
int exp;
|
||||
int homeCoinStoreLimit;
|
||||
int homeFetterExpStoreLimit;
|
||||
int rewardId;
|
||||
int furnitureMakeSlotCount;
|
||||
int outdoorUnlockBlockCount;
|
||||
int freeUnlockModuleCount;
|
||||
int deployNpcCount;
|
||||
int limitShopGoodsCount;
|
||||
List<String> levelFuncs;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,33 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@ResourceType(
|
||||
name = "InvestigationMonsterConfigData.json",
|
||||
loadPriority = ResourceType.LoadPriority.LOW)
|
||||
@Getter
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class InvestigationMonsterData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
int id;
|
||||
|
||||
int cityId;
|
||||
List<Integer> monsterIdList;
|
||||
List<Integer> groupIdList;
|
||||
int rewardPreviewId;
|
||||
String mapMarkCreateType;
|
||||
String monsterCategory;
|
||||
|
||||
CityData cityData;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.cityData = GameData.getCityDataMap().get(cityId);
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@ResourceType(
|
||||
name = "InvestigationMonsterConfigData.json",
|
||||
loadPriority = ResourceType.LoadPriority.LOW)
|
||||
@Getter
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class InvestigationMonsterData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
int id;
|
||||
|
||||
int cityId;
|
||||
List<Integer> monsterIdList;
|
||||
List<Integer> groupIdList;
|
||||
int rewardPreviewId;
|
||||
String mapMarkCreateType;
|
||||
String monsterCategory;
|
||||
|
||||
CityData cityData;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.cityData = GameData.getCityDataMap().get(cityId);
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@ResourceType(name = "MusicGameBasicConfigData.json")
|
||||
@Getter
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class MusicGameBasicData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
int id;
|
||||
|
||||
int musicID;
|
||||
int musicLevel;
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@ResourceType(name = "MusicGameBasicConfigData.json")
|
||||
@Getter
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class MusicGameBasicData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
int id;
|
||||
|
||||
int musicID;
|
||||
int musicLevel;
|
||||
}
|
||||
|
@ -1,23 +1,23 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@ResourceType(name = "PersonalLineExcelConfigData.json")
|
||||
@Getter
|
||||
@Setter // TODO: remove setters next API break
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class PersonalLineData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
int id;
|
||||
|
||||
int avatarID;
|
||||
List<Integer> preQuestId;
|
||||
int startQuestId;
|
||||
int chapterId;
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import java.util.List;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@ResourceType(name = "PersonalLineExcelConfigData.json")
|
||||
@Getter
|
||||
@Setter // TODO: remove setters next API break
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class PersonalLineData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
int id;
|
||||
|
||||
int avatarID;
|
||||
List<Integer> preQuestId;
|
||||
int startQuestId;
|
||||
int chapterId;
|
||||
}
|
||||
|
@ -1,57 +1,57 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.FightPropData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = "ProudSkillExcelConfigData.json")
|
||||
public class ProudSkillData extends GameResource {
|
||||
private int proudSkillId;
|
||||
@Getter private int proudSkillGroupId;
|
||||
@Getter private int level;
|
||||
@Getter private int coinCost;
|
||||
@Getter private int breakLevel;
|
||||
@Getter private int proudSkillType;
|
||||
@Getter private String openConfig;
|
||||
@Getter private List<ItemParamData> costItems;
|
||||
@Getter private List<String> filterConds;
|
||||
@Getter private List<String> lifeEffectParams;
|
||||
@Getter private FightPropData[] addProps;
|
||||
@Getter private float[] paramList;
|
||||
@Getter private long[] paramDescList;
|
||||
@Getter private long nameTextMapHash;
|
||||
@Transient private Iterable<ItemParamData> totalCostItems;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return proudSkillId;
|
||||
}
|
||||
|
||||
public Iterable<ItemParamData> getTotalCostItems() {
|
||||
if (this.totalCostItems == null) {
|
||||
ArrayList<ItemParamData> total =
|
||||
(this.costItems != null) ? new ArrayList<>(this.costItems) : new ArrayList<>(1);
|
||||
if (this.coinCost > 0) total.add(new ItemParamData(202, this.coinCost));
|
||||
this.totalCostItems = total;
|
||||
}
|
||||
return this.totalCostItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Fight props
|
||||
ArrayList<FightPropData> parsed = new ArrayList<FightPropData>(getAddProps().length);
|
||||
for (FightPropData prop : getAddProps()) {
|
||||
if (prop.getPropType() != null && prop.getValue() != 0f) {
|
||||
prop.onLoad();
|
||||
parsed.add(prop);
|
||||
}
|
||||
}
|
||||
this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.FightPropData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = "ProudSkillExcelConfigData.json")
|
||||
public class ProudSkillData extends GameResource {
|
||||
private int proudSkillId;
|
||||
@Getter private int proudSkillGroupId;
|
||||
@Getter private int level;
|
||||
@Getter private int coinCost;
|
||||
@Getter private int breakLevel;
|
||||
@Getter private int proudSkillType;
|
||||
@Getter private String openConfig;
|
||||
@Getter private List<ItemParamData> costItems;
|
||||
@Getter private List<String> filterConds;
|
||||
@Getter private List<String> lifeEffectParams;
|
||||
@Getter private FightPropData[] addProps;
|
||||
@Getter private float[] paramList;
|
||||
@Getter private long[] paramDescList;
|
||||
@Getter private long nameTextMapHash;
|
||||
@Transient private Iterable<ItemParamData> totalCostItems;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return proudSkillId;
|
||||
}
|
||||
|
||||
public Iterable<ItemParamData> getTotalCostItems() {
|
||||
if (this.totalCostItems == null) {
|
||||
ArrayList<ItemParamData> total =
|
||||
(this.costItems != null) ? new ArrayList<>(this.costItems) : new ArrayList<>(1);
|
||||
if (this.coinCost > 0) total.add(new ItemParamData(202, this.coinCost));
|
||||
this.totalCostItems = total;
|
||||
}
|
||||
return this.totalCostItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Fight props
|
||||
ArrayList<FightPropData> parsed = new ArrayList<FightPropData>(getAddProps().length);
|
||||
for (FightPropData prop : getAddProps()) {
|
||||
if (prop.getPropType() != null && prop.getValue() != 0f) {
|
||||
prop.onLoad();
|
||||
parsed.add(prop);
|
||||
}
|
||||
}
|
||||
this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,26 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.List;
|
||||
|
||||
@ResourceType(name = "RewardExcelConfigData.json")
|
||||
public class RewardData extends GameResource {
|
||||
public int rewardId;
|
||||
public List<ItemParamData> rewardItemList;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return rewardId;
|
||||
}
|
||||
|
||||
public List<ItemParamData> getRewardItemList() {
|
||||
return rewardItemList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
rewardItemList = rewardItemList.stream().filter(i -> i.getId() > 0).toList();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import java.util.List;
|
||||
|
||||
@ResourceType(name = "RewardExcelConfigData.json")
|
||||
public class RewardData extends GameResource {
|
||||
public int rewardId;
|
||||
public List<ItemParamData> rewardItemList;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return rewardId;
|
||||
}
|
||||
|
||||
public List<ItemParamData> getRewardItemList() {
|
||||
return rewardItemList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
rewardItemList = rewardItemList.stream().filter(i -> i.getId() > 0).toList();
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,35 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.common.ItemParamStringData;
|
||||
import java.util.Arrays;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH)
|
||||
public class RewardPreviewData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
private int id;
|
||||
|
||||
private ItemParamStringData[] previewItems;
|
||||
private ItemParamData[] previewItemsArray;
|
||||
|
||||
public ItemParamData[] getPreviewItems() {
|
||||
return previewItemsArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
if (this.previewItems != null && this.previewItems.length > 0) {
|
||||
this.previewItemsArray =
|
||||
Arrays.stream(this.previewItems)
|
||||
.filter(d -> d.getId() > 0 && d.getCount() != null && !d.getCount().isEmpty())
|
||||
.map(ItemParamStringData::toItemParamData)
|
||||
.toArray(size -> new ItemParamData[size]);
|
||||
} else {
|
||||
this.previewItemsArray = new ItemParamData[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.common.ItemParamStringData;
|
||||
import java.util.Arrays;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH)
|
||||
public class RewardPreviewData extends GameResource {
|
||||
@Getter(onMethod_ = @Override)
|
||||
private int id;
|
||||
|
||||
private ItemParamStringData[] previewItems;
|
||||
private ItemParamData[] previewItemsArray;
|
||||
|
||||
public ItemParamData[] getPreviewItems() {
|
||||
return previewItemsArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
if (this.previewItems != null && this.previewItems.length > 0) {
|
||||
this.previewItemsArray =
|
||||
Arrays.stream(this.previewItems)
|
||||
.filter(d -> d.getId() > 0 && d.getCount() != null && !d.getCount().isEmpty())
|
||||
.map(ItemParamStringData::toItemParamData)
|
||||
.toArray(size -> new ItemParamData[size]);
|
||||
} else {
|
||||
this.previewItemsArray = new ItemParamData[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,112 +1,112 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.game.shop.ShopInfo;
|
||||
import java.util.List;
|
||||
|
||||
@ResourceType(name = "ShopGoodsExcelConfigData.json")
|
||||
public class ShopGoodsData extends GameResource {
|
||||
private int goodsId;
|
||||
private int shopType;
|
||||
private int itemId;
|
||||
|
||||
private int itemCount;
|
||||
|
||||
private int costScoin;
|
||||
private int costHcoin;
|
||||
private int costMcoin;
|
||||
|
||||
private List<ItemParamData> costItems;
|
||||
private int minPlayerLevel;
|
||||
private int maxPlayerLevel;
|
||||
|
||||
private int buyLimit;
|
||||
|
||||
@SerializedName(
|
||||
value = "subTabId",
|
||||
alternate = {"secondarySheetId"})
|
||||
private int subTabId;
|
||||
|
||||
private String refreshType;
|
||||
private transient ShopInfo.ShopRefreshType refreshTypeEnum;
|
||||
|
||||
private int refreshParam;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
if (this.refreshType == null) this.refreshTypeEnum = ShopInfo.ShopRefreshType.NONE;
|
||||
else {
|
||||
this.refreshTypeEnum =
|
||||
switch (this.refreshType) {
|
||||
case "SHOP_REFRESH_DAILY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_DAILY;
|
||||
case "SHOP_REFRESH_WEEKLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_WEEKLY;
|
||||
case "SHOP_REFRESH_MONTHLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_MONTHLY;
|
||||
default -> ShopInfo.ShopRefreshType.NONE;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return getGoodsId();
|
||||
}
|
||||
|
||||
public int getGoodsId() {
|
||||
return goodsId;
|
||||
}
|
||||
|
||||
public int getShopType() {
|
||||
return shopType;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public int getItemCount() {
|
||||
return itemCount;
|
||||
}
|
||||
|
||||
public int getCostScoin() {
|
||||
return costScoin;
|
||||
}
|
||||
|
||||
public int getCostHcoin() {
|
||||
return costHcoin;
|
||||
}
|
||||
|
||||
public int getCostMcoin() {
|
||||
return costMcoin;
|
||||
}
|
||||
|
||||
public List<ItemParamData> getCostItems() {
|
||||
return costItems;
|
||||
}
|
||||
|
||||
public int getMinPlayerLevel() {
|
||||
return minPlayerLevel;
|
||||
}
|
||||
|
||||
public int getMaxPlayerLevel() {
|
||||
return maxPlayerLevel;
|
||||
}
|
||||
|
||||
public int getBuyLimit() {
|
||||
return buyLimit;
|
||||
}
|
||||
|
||||
public int getSubTabId() {
|
||||
return subTabId;
|
||||
}
|
||||
|
||||
public ShopInfo.ShopRefreshType getRefreshType() {
|
||||
return refreshTypeEnum;
|
||||
}
|
||||
|
||||
public int getRefreshParam() {
|
||||
return refreshParam;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.game.shop.ShopInfo;
|
||||
import java.util.List;
|
||||
|
||||
@ResourceType(name = "ShopGoodsExcelConfigData.json")
|
||||
public class ShopGoodsData extends GameResource {
|
||||
private int goodsId;
|
||||
private int shopType;
|
||||
private int itemId;
|
||||
|
||||
private int itemCount;
|
||||
|
||||
private int costScoin;
|
||||
private int costHcoin;
|
||||
private int costMcoin;
|
||||
|
||||
private List<ItemParamData> costItems;
|
||||
private int minPlayerLevel;
|
||||
private int maxPlayerLevel;
|
||||
|
||||
private int buyLimit;
|
||||
|
||||
@SerializedName(
|
||||
value = "subTabId",
|
||||
alternate = {"secondarySheetId"})
|
||||
private int subTabId;
|
||||
|
||||
private String refreshType;
|
||||
private transient ShopInfo.ShopRefreshType refreshTypeEnum;
|
||||
|
||||
private int refreshParam;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
if (this.refreshType == null) this.refreshTypeEnum = ShopInfo.ShopRefreshType.NONE;
|
||||
else {
|
||||
this.refreshTypeEnum =
|
||||
switch (this.refreshType) {
|
||||
case "SHOP_REFRESH_DAILY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_DAILY;
|
||||
case "SHOP_REFRESH_WEEKLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_WEEKLY;
|
||||
case "SHOP_REFRESH_MONTHLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_MONTHLY;
|
||||
default -> ShopInfo.ShopRefreshType.NONE;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return getGoodsId();
|
||||
}
|
||||
|
||||
public int getGoodsId() {
|
||||
return goodsId;
|
||||
}
|
||||
|
||||
public int getShopType() {
|
||||
return shopType;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public int getItemCount() {
|
||||
return itemCount;
|
||||
}
|
||||
|
||||
public int getCostScoin() {
|
||||
return costScoin;
|
||||
}
|
||||
|
||||
public int getCostHcoin() {
|
||||
return costHcoin;
|
||||
}
|
||||
|
||||
public int getCostMcoin() {
|
||||
return costMcoin;
|
||||
}
|
||||
|
||||
public List<ItemParamData> getCostItems() {
|
||||
return costItems;
|
||||
}
|
||||
|
||||
public int getMinPlayerLevel() {
|
||||
return minPlayerLevel;
|
||||
}
|
||||
|
||||
public int getMaxPlayerLevel() {
|
||||
return maxPlayerLevel;
|
||||
}
|
||||
|
||||
public int getBuyLimit() {
|
||||
return buyLimit;
|
||||
}
|
||||
|
||||
public int getSubTabId() {
|
||||
return subTabId;
|
||||
}
|
||||
|
||||
public ShopInfo.ShopRefreshType getRefreshType() {
|
||||
return refreshTypeEnum;
|
||||
}
|
||||
|
||||
public int getRefreshParam() {
|
||||
return refreshParam;
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = "TriggerExcelConfigData.json")
|
||||
@Getter
|
||||
public class TriggerExcelConfigData extends GameResource {
|
||||
@Getter private int id;
|
||||
private int sceneId;
|
||||
private int groupId;
|
||||
private String triggerName;
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@ResourceType(name = "TriggerExcelConfigData.json")
|
||||
@Getter
|
||||
public class TriggerExcelConfigData extends GameResource {
|
||||
@Getter private int id;
|
||||
private int sceneId;
|
||||
private int groupId;
|
||||
private String triggerName;
|
||||
}
|
||||
|
@ -1,22 +1,22 @@
|
||||
package emu.grasscutter.database;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
|
||||
@Entity(value = "counters", useDiscriminator = false)
|
||||
public class DatabaseCounter {
|
||||
@Id private String id;
|
||||
private int count;
|
||||
|
||||
public DatabaseCounter() {}
|
||||
|
||||
public DatabaseCounter(String id) {
|
||||
this.id = id;
|
||||
this.count = 10000;
|
||||
}
|
||||
|
||||
public int getNextId() {
|
||||
int id = ++count;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.database;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
|
||||
@Entity(value = "counters", useDiscriminator = false)
|
||||
public class DatabaseCounter {
|
||||
@Id private String id;
|
||||
private int count;
|
||||
|
||||
public DatabaseCounter() {}
|
||||
|
||||
public DatabaseCounter(String id) {
|
||||
this.id = id;
|
||||
this.count = 10000;
|
||||
}
|
||||
|
||||
public int getNextId() {
|
||||
int id = ++count;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
@ -1,126 +1,126 @@
|
||||
package emu.grasscutter.database;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.DATABASE;
|
||||
import static emu.grasscutter.config.Configuration.SERVER;
|
||||
|
||||
import com.mongodb.MongoCommandException;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.MongoIterable;
|
||||
import dev.morphia.Datastore;
|
||||
import dev.morphia.Morphia;
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.mapping.Mapper;
|
||||
import dev.morphia.mapping.MapperOptions;
|
||||
import dev.morphia.query.experimental.filters.Filters;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.Grasscutter.ServerRunMode;
|
||||
import emu.grasscutter.game.Account;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
public final class DatabaseManager {
|
||||
private static Datastore gameDatastore;
|
||||
private static Datastore dispatchDatastore;
|
||||
|
||||
public static Datastore getGameDatastore() {
|
||||
return gameDatastore;
|
||||
}
|
||||
|
||||
public static MongoDatabase getGameDatabase() {
|
||||
return getGameDatastore().getDatabase();
|
||||
}
|
||||
|
||||
// Yes. I very dislike this method. However, this will be good for now.
|
||||
// TODO: Add dispatch routes for player account management
|
||||
public static Datastore getAccountDatastore() {
|
||||
if (SERVER.runMode == ServerRunMode.GAME_ONLY) {
|
||||
return dispatchDatastore;
|
||||
} else {
|
||||
return gameDatastore;
|
||||
}
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
// Initialize
|
||||
MongoClient gameMongoClient = MongoClients.create(DATABASE.game.connectionUri);
|
||||
|
||||
// Set mapper options.
|
||||
MapperOptions mapperOptions =
|
||||
MapperOptions.builder().storeEmpties(true).storeNulls(false).build();
|
||||
|
||||
// Create data store.
|
||||
gameDatastore =
|
||||
Morphia.createDatastore(gameMongoClient, DATABASE.game.collection, mapperOptions);
|
||||
|
||||
// Map classes.
|
||||
Class<?>[] entities =
|
||||
new Reflections(Grasscutter.class.getPackageName())
|
||||
.getTypesAnnotatedWith(Entity.class).stream()
|
||||
.filter(
|
||||
cls -> {
|
||||
Entity e = cls.getAnnotation(Entity.class);
|
||||
return e != null && !e.value().equals(Mapper.IGNORED_FIELDNAME);
|
||||
})
|
||||
.toArray(Class<?>[]::new);
|
||||
|
||||
gameDatastore.getMapper().map(entities);
|
||||
|
||||
// Ensure indexes for the game datastore
|
||||
ensureIndexes(gameDatastore);
|
||||
|
||||
if (SERVER.runMode == ServerRunMode.GAME_ONLY) {
|
||||
MongoClient dispatchMongoClient = MongoClients.create(DATABASE.server.connectionUri);
|
||||
|
||||
dispatchDatastore =
|
||||
Morphia.createDatastore(dispatchMongoClient, DATABASE.server.collection, mapperOptions);
|
||||
dispatchDatastore.getMapper().map(new Class<?>[] {DatabaseCounter.class, Account.class});
|
||||
|
||||
// Ensure indexes for dispatch datastore
|
||||
ensureIndexes(dispatchDatastore);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the database indexes exist and rebuilds them if there is an error with them
|
||||
*
|
||||
* @param datastore The datastore to ensure indexes on
|
||||
*/
|
||||
private static void ensureIndexes(Datastore datastore) {
|
||||
try {
|
||||
datastore.ensureIndexes();
|
||||
} catch (MongoCommandException e) {
|
||||
Grasscutter.getLogger().info("Mongo index error: ", e);
|
||||
// Duplicate index error
|
||||
if (e.getCode() == 85) {
|
||||
// Drop all indexes and re add them
|
||||
MongoIterable<String> collections = datastore.getDatabase().listCollectionNames();
|
||||
for (String name : collections) {
|
||||
datastore.getDatabase().getCollection(name).dropIndexes();
|
||||
}
|
||||
// Add back indexes
|
||||
datastore.ensureIndexes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized int getNextId(Class<?> c) {
|
||||
DatabaseCounter counter =
|
||||
getGameDatastore()
|
||||
.find(DatabaseCounter.class)
|
||||
.filter(Filters.eq("_id", c.getSimpleName()))
|
||||
.first();
|
||||
if (counter == null) {
|
||||
counter = new DatabaseCounter(c.getSimpleName());
|
||||
}
|
||||
try {
|
||||
return counter.getNextId();
|
||||
} finally {
|
||||
getGameDatastore().save(counter);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized int getNextId(Object o) {
|
||||
return getNextId(o.getClass());
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.database;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.DATABASE;
|
||||
import static emu.grasscutter.config.Configuration.SERVER;
|
||||
|
||||
import com.mongodb.MongoCommandException;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.MongoIterable;
|
||||
import dev.morphia.Datastore;
|
||||
import dev.morphia.Morphia;
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.mapping.Mapper;
|
||||
import dev.morphia.mapping.MapperOptions;
|
||||
import dev.morphia.query.experimental.filters.Filters;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.Grasscutter.ServerRunMode;
|
||||
import emu.grasscutter.game.Account;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
public final class DatabaseManager {
|
||||
private static Datastore gameDatastore;
|
||||
private static Datastore dispatchDatastore;
|
||||
|
||||
public static Datastore getGameDatastore() {
|
||||
return gameDatastore;
|
||||
}
|
||||
|
||||
public static MongoDatabase getGameDatabase() {
|
||||
return getGameDatastore().getDatabase();
|
||||
}
|
||||
|
||||
// Yes. I very dislike this method. However, this will be good for now.
|
||||
// TODO: Add dispatch routes for player account management
|
||||
public static Datastore getAccountDatastore() {
|
||||
if (SERVER.runMode == ServerRunMode.GAME_ONLY) {
|
||||
return dispatchDatastore;
|
||||
} else {
|
||||
return gameDatastore;
|
||||
}
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
// Initialize
|
||||
MongoClient gameMongoClient = MongoClients.create(DATABASE.game.connectionUri);
|
||||
|
||||
// Set mapper options.
|
||||
MapperOptions mapperOptions =
|
||||
MapperOptions.builder().storeEmpties(true).storeNulls(false).build();
|
||||
|
||||
// Create data store.
|
||||
gameDatastore =
|
||||
Morphia.createDatastore(gameMongoClient, DATABASE.game.collection, mapperOptions);
|
||||
|
||||
// Map classes.
|
||||
Class<?>[] entities =
|
||||
new Reflections(Grasscutter.class.getPackageName())
|
||||
.getTypesAnnotatedWith(Entity.class).stream()
|
||||
.filter(
|
||||
cls -> {
|
||||
Entity e = cls.getAnnotation(Entity.class);
|
||||
return e != null && !e.value().equals(Mapper.IGNORED_FIELDNAME);
|
||||
})
|
||||
.toArray(Class<?>[]::new);
|
||||
|
||||
gameDatastore.getMapper().map(entities);
|
||||
|
||||
// Ensure indexes for the game datastore
|
||||
ensureIndexes(gameDatastore);
|
||||
|
||||
if (SERVER.runMode == ServerRunMode.GAME_ONLY) {
|
||||
MongoClient dispatchMongoClient = MongoClients.create(DATABASE.server.connectionUri);
|
||||
|
||||
dispatchDatastore =
|
||||
Morphia.createDatastore(dispatchMongoClient, DATABASE.server.collection, mapperOptions);
|
||||
dispatchDatastore.getMapper().map(new Class<?>[] {DatabaseCounter.class, Account.class});
|
||||
|
||||
// Ensure indexes for dispatch datastore
|
||||
ensureIndexes(dispatchDatastore);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the database indexes exist and rebuilds them if there is an error with them
|
||||
*
|
||||
* @param datastore The datastore to ensure indexes on
|
||||
*/
|
||||
private static void ensureIndexes(Datastore datastore) {
|
||||
try {
|
||||
datastore.ensureIndexes();
|
||||
} catch (MongoCommandException e) {
|
||||
Grasscutter.getLogger().info("Mongo index error: ", e);
|
||||
// Duplicate index error
|
||||
if (e.getCode() == 85) {
|
||||
// Drop all indexes and re add them
|
||||
MongoIterable<String> collections = datastore.getDatabase().listCollectionNames();
|
||||
for (String name : collections) {
|
||||
datastore.getDatabase().getCollection(name).dropIndexes();
|
||||
}
|
||||
// Add back indexes
|
||||
datastore.ensureIndexes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized int getNextId(Class<?> c) {
|
||||
DatabaseCounter counter =
|
||||
getGameDatastore()
|
||||
.find(DatabaseCounter.class)
|
||||
.filter(Filters.eq("_id", c.getSimpleName()))
|
||||
.first();
|
||||
if (counter == null) {
|
||||
counter = new DatabaseCounter(c.getSimpleName());
|
||||
}
|
||||
try {
|
||||
return counter.getNextId();
|
||||
} finally {
|
||||
getGameDatastore().save(counter);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized int getNextId(Object o) {
|
||||
return getNextId(o.getClass());
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,31 @@
|
||||
package emu.grasscutter.game;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
public class CoopRequest {
|
||||
private final Player requester;
|
||||
private final long requestTime;
|
||||
private final long expireTime;
|
||||
|
||||
public CoopRequest(Player requester) {
|
||||
this.requester = requester;
|
||||
this.requestTime = System.currentTimeMillis();
|
||||
this.expireTime = this.requestTime + 10000;
|
||||
}
|
||||
|
||||
public Player getRequester() {
|
||||
return requester;
|
||||
}
|
||||
|
||||
public long getRequestTime() {
|
||||
return requestTime;
|
||||
}
|
||||
|
||||
public long getExpireTime() {
|
||||
return expireTime;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return System.currentTimeMillis() > getExpireTime();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
public class CoopRequest {
|
||||
private final Player requester;
|
||||
private final long requestTime;
|
||||
private final long expireTime;
|
||||
|
||||
public CoopRequest(Player requester) {
|
||||
this.requester = requester;
|
||||
this.requestTime = System.currentTimeMillis();
|
||||
this.expireTime = this.requestTime + 10000;
|
||||
}
|
||||
|
||||
public Player getRequester() {
|
||||
return requester;
|
||||
}
|
||||
|
||||
public long getRequestTime() {
|
||||
return requestTime;
|
||||
}
|
||||
|
||||
public long getExpireTime() {
|
||||
return expireTime;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return System.currentTimeMillis() > getExpireTime();
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +1,47 @@
|
||||
package emu.grasscutter.game.achievement;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class AchievementControlReturns {
|
||||
private final Return ret;
|
||||
private final int changedAchievementStatusNum;
|
||||
|
||||
private AchievementControlReturns(Return ret) {
|
||||
this(ret, 0);
|
||||
}
|
||||
|
||||
private AchievementControlReturns(Return ret, int changedAchievementStatusNum) {
|
||||
this.ret = ret;
|
||||
this.changedAchievementStatusNum = changedAchievementStatusNum;
|
||||
}
|
||||
|
||||
public static AchievementControlReturns success(int changedAchievementStatusNum) {
|
||||
return new AchievementControlReturns(Return.SUCCESS, changedAchievementStatusNum);
|
||||
}
|
||||
|
||||
public static AchievementControlReturns achievementNotFound() {
|
||||
return new AchievementControlReturns(Return.ACHIEVEMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
public static AchievementControlReturns alreadyAchieved() {
|
||||
return new AchievementControlReturns(Return.ALREADY_ACHIEVED);
|
||||
}
|
||||
|
||||
public static AchievementControlReturns notYetAchieved() {
|
||||
return new AchievementControlReturns(Return.NOT_YET_ACHIEVED);
|
||||
}
|
||||
|
||||
public enum Return {
|
||||
SUCCESS("commands.achievement.success."),
|
||||
ACHIEVEMENT_NOT_FOUND("commands.achievement.fail.achievement_not_found"),
|
||||
ALREADY_ACHIEVED("commands.achievement.fail.already_achieved"),
|
||||
NOT_YET_ACHIEVED("commands.achievement.fail.not_yet_achieved");
|
||||
|
||||
@Getter private final String key;
|
||||
|
||||
Return(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.achievement;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class AchievementControlReturns {
|
||||
private final Return ret;
|
||||
private final int changedAchievementStatusNum;
|
||||
|
||||
private AchievementControlReturns(Return ret) {
|
||||
this(ret, 0);
|
||||
}
|
||||
|
||||
private AchievementControlReturns(Return ret, int changedAchievementStatusNum) {
|
||||
this.ret = ret;
|
||||
this.changedAchievementStatusNum = changedAchievementStatusNum;
|
||||
}
|
||||
|
||||
public static AchievementControlReturns success(int changedAchievementStatusNum) {
|
||||
return new AchievementControlReturns(Return.SUCCESS, changedAchievementStatusNum);
|
||||
}
|
||||
|
||||
public static AchievementControlReturns achievementNotFound() {
|
||||
return new AchievementControlReturns(Return.ACHIEVEMENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
public static AchievementControlReturns alreadyAchieved() {
|
||||
return new AchievementControlReturns(Return.ALREADY_ACHIEVED);
|
||||
}
|
||||
|
||||
public static AchievementControlReturns notYetAchieved() {
|
||||
return new AchievementControlReturns(Return.NOT_YET_ACHIEVED);
|
||||
}
|
||||
|
||||
public enum Return {
|
||||
SUCCESS("commands.achievement.success."),
|
||||
ACHIEVEMENT_NOT_FOUND("commands.achievement.fail.achievement_not_found"),
|
||||
ALREADY_ACHIEVED("commands.achievement.fail.already_achieved"),
|
||||
NOT_YET_ACHIEVED("commands.achievement.fail.not_yet_achieved");
|
||||
|
||||
@Getter private final String key;
|
||||
|
||||
Return(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
package emu.grasscutter.game.activity;
|
||||
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ActivityWatcherType {
|
||||
WatcherTriggerType value();
|
||||
}
|
||||
package emu.grasscutter.game.activity;
|
||||
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ActivityWatcherType {
|
||||
WatcherTriggerType value();
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
package emu.grasscutter.game.activity;
|
||||
|
||||
import emu.grasscutter.game.props.ActivityType;
|
||||
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
|
||||
|
||||
@GameActivity(ActivityType.NONE)
|
||||
public class DefaultActivityHandler extends ActivityHandler {
|
||||
@Override
|
||||
public void onProtoBuild(
|
||||
PlayerActivityData playerActivityData,
|
||||
ActivityInfoOuterClass.ActivityInfo.Builder activityInfo) {}
|
||||
|
||||
@Override
|
||||
public void onInitPlayerActivityData(PlayerActivityData playerActivityData) {}
|
||||
}
|
||||
package emu.grasscutter.game.activity;
|
||||
|
||||
import emu.grasscutter.game.props.ActivityType;
|
||||
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
|
||||
|
||||
@GameActivity(ActivityType.NONE)
|
||||
public class DefaultActivityHandler extends ActivityHandler {
|
||||
@Override
|
||||
public void onProtoBuild(
|
||||
PlayerActivityData playerActivityData,
|
||||
ActivityInfoOuterClass.ActivityInfo.Builder activityInfo) {}
|
||||
|
||||
@Override
|
||||
public void onInitPlayerActivityData(PlayerActivityData playerActivityData) {}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package emu.grasscutter.game.activity;
|
||||
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
|
||||
@ActivityWatcherType(WatcherTriggerType.TRIGGER_NONE)
|
||||
public class DefaultWatcher extends ActivityWatcher {
|
||||
@Override
|
||||
protected boolean isMeet(String... param) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity;
|
||||
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
|
||||
@ActivityWatcherType(WatcherTriggerType.TRIGGER_NONE)
|
||||
public class DefaultWatcher extends ActivityWatcher {
|
||||
@Override
|
||||
protected boolean isMeet(String... param) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
package emu.grasscutter.game.activity;
|
||||
|
||||
import emu.grasscutter.game.props.ActivityType;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface GameActivity {
|
||||
ActivityType value();
|
||||
}
|
||||
package emu.grasscutter.game.activity;
|
||||
|
||||
import emu.grasscutter.game.props.ActivityType;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface GameActivity {
|
||||
ActivityType value();
|
||||
}
|
||||
|
@ -1,105 +1,105 @@
|
||||
package emu.grasscutter.game.activity.musicgame;
|
||||
|
||||
import emu.grasscutter.game.activity.ActivityHandler;
|
||||
import emu.grasscutter.game.activity.GameActivity;
|
||||
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||
import emu.grasscutter.game.props.ActivityType;
|
||||
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.MusicGameActivityDetailInfoOuterClass;
|
||||
import emu.grasscutter.utils.JsonUtils;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@GameActivity(ActivityType.NEW_ACTIVITY_MUSIC_GAME)
|
||||
public class MusicGameActivityHandler extends ActivityHandler {
|
||||
|
||||
@Override
|
||||
public void onInitPlayerActivityData(PlayerActivityData playerActivityData) {
|
||||
var musicGamePlayerData = MusicGamePlayerData.create();
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProtoBuild(
|
||||
PlayerActivityData playerActivityData,
|
||||
ActivityInfoOuterClass.ActivityInfo.Builder activityInfo) {
|
||||
MusicGamePlayerData musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
|
||||
|
||||
activityInfo.setMusicGameInfo(
|
||||
MusicGameActivityDetailInfoOuterClass.MusicGameActivityDetailInfo.newBuilder()
|
||||
.putAllMusicGameRecordMap(
|
||||
musicGamePlayerData.getMusicGameRecord().values().stream()
|
||||
.collect(
|
||||
Collectors.toMap(
|
||||
MusicGamePlayerData.MusicGameRecord::getMusicId,
|
||||
MusicGamePlayerData.MusicGameRecord::toProto)))
|
||||
//
|
||||
// .addAllPersonCustomBeatmap(musicGamePlayerData.getPersonalCustomBeatmapRecord().values().stream()
|
||||
// .map(MusicGamePlayerData.CustomBeatmapRecord::toPersonalBriefProto)
|
||||
// .map(UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder::build)
|
||||
// .toList())
|
||||
//
|
||||
//
|
||||
// .addAllOthersCustomBeatmap(musicGamePlayerData.getOthersCustomBeatmapRecord().values().stream()
|
||||
// .map(MusicGamePlayerData.CustomBeatmapRecord::toOthersBriefProto)
|
||||
// .map(UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder::build)
|
||||
// .toList())
|
||||
.build());
|
||||
}
|
||||
|
||||
public MusicGamePlayerData getMusicGamePlayerData(PlayerActivityData playerActivityData) {
|
||||
if (playerActivityData.getDetail() == null || playerActivityData.getDetail().isBlank()) {
|
||||
onInitPlayerActivityData(playerActivityData);
|
||||
playerActivityData.save();
|
||||
}
|
||||
|
||||
return JsonUtils.decode(playerActivityData.getDetail(), MusicGamePlayerData.class);
|
||||
}
|
||||
|
||||
public boolean setMusicGameRecord(
|
||||
PlayerActivityData playerActivityData, MusicGamePlayerData.MusicGameRecord newRecord) {
|
||||
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
|
||||
var saveRecord = musicGamePlayerData.getMusicGameRecord().get(newRecord.getMusicId());
|
||||
|
||||
saveRecord.setMaxCombo(Math.max(newRecord.getMaxCombo(), saveRecord.getMaxCombo()));
|
||||
saveRecord.setMaxScore(Math.max(newRecord.getMaxScore(), saveRecord.getMaxScore()));
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
playerActivityData.save();
|
||||
|
||||
return newRecord.getMaxScore() > saveRecord.getMaxScore();
|
||||
}
|
||||
|
||||
public void setMusicGameCustomBeatmapRecord(
|
||||
PlayerActivityData playerActivityData, MusicGamePlayerData.CustomBeatmapRecord newRecord) {
|
||||
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
|
||||
musicGamePlayerData.getOthersCustomBeatmapRecord().put(newRecord.getMusicShareId(), newRecord);
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
playerActivityData.save();
|
||||
}
|
||||
|
||||
public void addPersonalBeatmap(
|
||||
PlayerActivityData playerActivityData, MusicGameBeatmap musicGameBeatmap) {
|
||||
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
|
||||
musicGamePlayerData
|
||||
.getPersonalCustomBeatmapRecord()
|
||||
.put(
|
||||
musicGameBeatmap.getMusicShareId(),
|
||||
MusicGamePlayerData.CustomBeatmapRecord.of()
|
||||
.musicShareId(musicGameBeatmap.getMusicShareId())
|
||||
.build());
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
playerActivityData.save();
|
||||
}
|
||||
|
||||
public void removePersonalBeatmap(
|
||||
PlayerActivityData playerActivityData, MusicGameBeatmap musicGameBeatmap) {
|
||||
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
|
||||
musicGamePlayerData.getPersonalCustomBeatmapRecord().remove(musicGameBeatmap.getMusicShareId());
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
playerActivityData.save();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity.musicgame;
|
||||
|
||||
import emu.grasscutter.game.activity.ActivityHandler;
|
||||
import emu.grasscutter.game.activity.GameActivity;
|
||||
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||
import emu.grasscutter.game.props.ActivityType;
|
||||
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.MusicGameActivityDetailInfoOuterClass;
|
||||
import emu.grasscutter.utils.JsonUtils;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@GameActivity(ActivityType.NEW_ACTIVITY_MUSIC_GAME)
|
||||
public class MusicGameActivityHandler extends ActivityHandler {
|
||||
|
||||
@Override
|
||||
public void onInitPlayerActivityData(PlayerActivityData playerActivityData) {
|
||||
var musicGamePlayerData = MusicGamePlayerData.create();
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProtoBuild(
|
||||
PlayerActivityData playerActivityData,
|
||||
ActivityInfoOuterClass.ActivityInfo.Builder activityInfo) {
|
||||
MusicGamePlayerData musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
|
||||
|
||||
activityInfo.setMusicGameInfo(
|
||||
MusicGameActivityDetailInfoOuterClass.MusicGameActivityDetailInfo.newBuilder()
|
||||
.putAllMusicGameRecordMap(
|
||||
musicGamePlayerData.getMusicGameRecord().values().stream()
|
||||
.collect(
|
||||
Collectors.toMap(
|
||||
MusicGamePlayerData.MusicGameRecord::getMusicId,
|
||||
MusicGamePlayerData.MusicGameRecord::toProto)))
|
||||
//
|
||||
// .addAllPersonCustomBeatmap(musicGamePlayerData.getPersonalCustomBeatmapRecord().values().stream()
|
||||
// .map(MusicGamePlayerData.CustomBeatmapRecord::toPersonalBriefProto)
|
||||
// .map(UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder::build)
|
||||
// .toList())
|
||||
//
|
||||
//
|
||||
// .addAllOthersCustomBeatmap(musicGamePlayerData.getOthersCustomBeatmapRecord().values().stream()
|
||||
// .map(MusicGamePlayerData.CustomBeatmapRecord::toOthersBriefProto)
|
||||
// .map(UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder::build)
|
||||
// .toList())
|
||||
.build());
|
||||
}
|
||||
|
||||
public MusicGamePlayerData getMusicGamePlayerData(PlayerActivityData playerActivityData) {
|
||||
if (playerActivityData.getDetail() == null || playerActivityData.getDetail().isBlank()) {
|
||||
onInitPlayerActivityData(playerActivityData);
|
||||
playerActivityData.save();
|
||||
}
|
||||
|
||||
return JsonUtils.decode(playerActivityData.getDetail(), MusicGamePlayerData.class);
|
||||
}
|
||||
|
||||
public boolean setMusicGameRecord(
|
||||
PlayerActivityData playerActivityData, MusicGamePlayerData.MusicGameRecord newRecord) {
|
||||
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
|
||||
var saveRecord = musicGamePlayerData.getMusicGameRecord().get(newRecord.getMusicId());
|
||||
|
||||
saveRecord.setMaxCombo(Math.max(newRecord.getMaxCombo(), saveRecord.getMaxCombo()));
|
||||
saveRecord.setMaxScore(Math.max(newRecord.getMaxScore(), saveRecord.getMaxScore()));
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
playerActivityData.save();
|
||||
|
||||
return newRecord.getMaxScore() > saveRecord.getMaxScore();
|
||||
}
|
||||
|
||||
public void setMusicGameCustomBeatmapRecord(
|
||||
PlayerActivityData playerActivityData, MusicGamePlayerData.CustomBeatmapRecord newRecord) {
|
||||
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
|
||||
musicGamePlayerData.getOthersCustomBeatmapRecord().put(newRecord.getMusicShareId(), newRecord);
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
playerActivityData.save();
|
||||
}
|
||||
|
||||
public void addPersonalBeatmap(
|
||||
PlayerActivityData playerActivityData, MusicGameBeatmap musicGameBeatmap) {
|
||||
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
|
||||
musicGamePlayerData
|
||||
.getPersonalCustomBeatmapRecord()
|
||||
.put(
|
||||
musicGameBeatmap.getMusicShareId(),
|
||||
MusicGamePlayerData.CustomBeatmapRecord.of()
|
||||
.musicShareId(musicGameBeatmap.getMusicShareId())
|
||||
.build());
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
playerActivityData.save();
|
||||
}
|
||||
|
||||
public void removePersonalBeatmap(
|
||||
PlayerActivityData playerActivityData, MusicGameBeatmap musicGameBeatmap) {
|
||||
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
|
||||
musicGamePlayerData.getPersonalCustomBeatmapRecord().remove(musicGameBeatmap.getMusicShareId());
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
playerActivityData.save();
|
||||
}
|
||||
}
|
||||
|
@ -1,97 +1,97 @@
|
||||
package emu.grasscutter.game.activity.musicgame;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.net.proto.UgcMusicBriefInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.UgcMusicNoteOuterClass;
|
||||
import emu.grasscutter.net.proto.UgcMusicRecordOuterClass;
|
||||
import emu.grasscutter.net.proto.UgcMusicTrackOuterClass;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Entity("music_game_beatmaps")
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public class MusicGameBeatmap {
|
||||
|
||||
@Id long musicShareId;
|
||||
int authorUid;
|
||||
int musicId;
|
||||
int musicNoteCount;
|
||||
int savePosition;
|
||||
int maxScore;
|
||||
int createTime;
|
||||
|
||||
List<List<BeatmapNote>> beatmap;
|
||||
|
||||
public static MusicGameBeatmap getByShareId(long musicShareId) {
|
||||
return DatabaseHelper.getMusicGameBeatmap(musicShareId);
|
||||
}
|
||||
|
||||
public static List<List<BeatmapNote>> parse(
|
||||
List<UgcMusicTrackOuterClass.UgcMusicTrack> beatmapItemListList) {
|
||||
return beatmapItemListList.stream()
|
||||
.map(item -> item.getMusicNoteListList().stream().map(BeatmapNote::parse).toList())
|
||||
.toList();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
if (musicShareId == 0) {
|
||||
musicShareId = new Random().nextLong(100000000000000L, 999999999999999L);
|
||||
}
|
||||
DatabaseHelper.saveMusicGameBeatmap(this);
|
||||
}
|
||||
|
||||
public UgcMusicRecordOuterClass.UgcMusicRecord toProto() {
|
||||
return UgcMusicRecordOuterClass.UgcMusicRecord.newBuilder()
|
||||
.setMusicId(musicId)
|
||||
.addAllMusicTrackList(beatmap.stream().map(this::musicBeatmapListToProto).toList())
|
||||
.build();
|
||||
}
|
||||
|
||||
public UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder toBriefProto() {
|
||||
var player = DatabaseHelper.getPlayerByUid(authorUid);
|
||||
|
||||
return UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.newBuilder()
|
||||
.setMusicId(musicId)
|
||||
// .setMusicNoteCount(musicNoteCount)
|
||||
.setUgcGuid(musicShareId)
|
||||
.setMaxScore(maxScore)
|
||||
// .setShareTime(createTime)
|
||||
.setCreatorNickname(player.getNickname())
|
||||
.setVersion(1);
|
||||
}
|
||||
|
||||
private UgcMusicTrackOuterClass.UgcMusicTrack musicBeatmapListToProto(
|
||||
List<BeatmapNote> beatmapNoteList) {
|
||||
return UgcMusicTrackOuterClass.UgcMusicTrack.newBuilder()
|
||||
.addAllMusicNoteList(beatmapNoteList.stream().map(BeatmapNote::toProto).toList())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
@Entity
|
||||
public static class BeatmapNote {
|
||||
int startTime;
|
||||
int endTime;
|
||||
|
||||
public static BeatmapNote parse(UgcMusicNoteOuterClass.UgcMusicNote note) {
|
||||
return BeatmapNote.of().startTime(note.getStartTime()).endTime(note.getEndTime()).build();
|
||||
}
|
||||
|
||||
public UgcMusicNoteOuterClass.UgcMusicNote toProto() {
|
||||
return UgcMusicNoteOuterClass.UgcMusicNote.newBuilder()
|
||||
.setStartTime(startTime)
|
||||
.setEndTime(endTime)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity.musicgame;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.net.proto.UgcMusicBriefInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.UgcMusicNoteOuterClass;
|
||||
import emu.grasscutter.net.proto.UgcMusicRecordOuterClass;
|
||||
import emu.grasscutter.net.proto.UgcMusicTrackOuterClass;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Entity("music_game_beatmaps")
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public class MusicGameBeatmap {
|
||||
|
||||
@Id long musicShareId;
|
||||
int authorUid;
|
||||
int musicId;
|
||||
int musicNoteCount;
|
||||
int savePosition;
|
||||
int maxScore;
|
||||
int createTime;
|
||||
|
||||
List<List<BeatmapNote>> beatmap;
|
||||
|
||||
public static MusicGameBeatmap getByShareId(long musicShareId) {
|
||||
return DatabaseHelper.getMusicGameBeatmap(musicShareId);
|
||||
}
|
||||
|
||||
public static List<List<BeatmapNote>> parse(
|
||||
List<UgcMusicTrackOuterClass.UgcMusicTrack> beatmapItemListList) {
|
||||
return beatmapItemListList.stream()
|
||||
.map(item -> item.getMusicNoteListList().stream().map(BeatmapNote::parse).toList())
|
||||
.toList();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
if (musicShareId == 0) {
|
||||
musicShareId = new Random().nextLong(100000000000000L, 999999999999999L);
|
||||
}
|
||||
DatabaseHelper.saveMusicGameBeatmap(this);
|
||||
}
|
||||
|
||||
public UgcMusicRecordOuterClass.UgcMusicRecord toProto() {
|
||||
return UgcMusicRecordOuterClass.UgcMusicRecord.newBuilder()
|
||||
.setMusicId(musicId)
|
||||
.addAllMusicTrackList(beatmap.stream().map(this::musicBeatmapListToProto).toList())
|
||||
.build();
|
||||
}
|
||||
|
||||
public UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder toBriefProto() {
|
||||
var player = DatabaseHelper.getPlayerByUid(authorUid);
|
||||
|
||||
return UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.newBuilder()
|
||||
.setMusicId(musicId)
|
||||
// .setMusicNoteCount(musicNoteCount)
|
||||
.setUgcGuid(musicShareId)
|
||||
.setMaxScore(maxScore)
|
||||
// .setShareTime(createTime)
|
||||
.setCreatorNickname(player.getNickname())
|
||||
.setVersion(1);
|
||||
}
|
||||
|
||||
private UgcMusicTrackOuterClass.UgcMusicTrack musicBeatmapListToProto(
|
||||
List<BeatmapNote> beatmapNoteList) {
|
||||
return UgcMusicTrackOuterClass.UgcMusicTrack.newBuilder()
|
||||
.addAllMusicNoteList(beatmapNoteList.stream().map(BeatmapNote::toProto).toList())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
@Entity
|
||||
public static class BeatmapNote {
|
||||
int startTime;
|
||||
int endTime;
|
||||
|
||||
public static BeatmapNote parse(UgcMusicNoteOuterClass.UgcMusicNote note) {
|
||||
return BeatmapNote.of().startTime(note.getStartTime()).endTime(note.getEndTime()).build();
|
||||
}
|
||||
|
||||
public UgcMusicNoteOuterClass.UgcMusicNote toProto() {
|
||||
return UgcMusicNoteOuterClass.UgcMusicNote.newBuilder()
|
||||
.setStartTime(startTime)
|
||||
.setEndTime(endTime)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,86 +1,86 @@
|
||||
package emu.grasscutter.game.activity.musicgame;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.MusicGameBasicData;
|
||||
import emu.grasscutter.net.proto.MusicGameRecordOuterClass;
|
||||
import emu.grasscutter.net.proto.UgcMusicBriefInfoOuterClass;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public class MusicGamePlayerData {
|
||||
Map<Integer, MusicGameRecord> musicGameRecord;
|
||||
Map<Long, CustomBeatmapRecord> personalCustomBeatmapRecord;
|
||||
Map<Long, CustomBeatmapRecord> othersCustomBeatmapRecord;
|
||||
|
||||
public static MusicGamePlayerData create() {
|
||||
return MusicGamePlayerData.of()
|
||||
.musicGameRecord(
|
||||
GameData.getMusicGameBasicDataMap().values().stream()
|
||||
.collect(
|
||||
Collectors.toMap(
|
||||
MusicGameBasicData::getId, MusicGamePlayerData.MusicGameRecord::create)))
|
||||
.personalCustomBeatmapRecord(new HashMap<>())
|
||||
.othersCustomBeatmapRecord(new HashMap<>())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public static class MusicGameRecord {
|
||||
int musicId;
|
||||
int maxCombo;
|
||||
int maxScore;
|
||||
|
||||
public static MusicGameRecord create(MusicGameBasicData musicGameBasicData) {
|
||||
return MusicGameRecord.of().musicId(musicGameBasicData.getId()).build();
|
||||
}
|
||||
|
||||
public MusicGameRecordOuterClass.MusicGameRecord toProto() {
|
||||
return MusicGameRecordOuterClass.MusicGameRecord.newBuilder()
|
||||
.setIsUnlock(true)
|
||||
.setMaxCombo(maxCombo)
|
||||
.setMaxScore(maxScore)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public static class CustomBeatmapRecord {
|
||||
long musicShareId;
|
||||
int score;
|
||||
boolean settle;
|
||||
|
||||
public UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder toPersonalBriefProto() {
|
||||
var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId);
|
||||
|
||||
return UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.newBuilder()
|
||||
// .setCanShare(true)
|
||||
// .setCreateTime(musicGameBeatmap.getCreateTime())
|
||||
.setMusicId(musicGameBeatmap.getMusicId())
|
||||
.setMaxScore(musicGameBeatmap.getMaxScore())
|
||||
// .setPosition(musicGameBeatmap.getSavePosition())
|
||||
// .setMusicNoteCount(musicGameBeatmap.getMusicNoteCount())
|
||||
.setUgcGuid(musicShareId);
|
||||
}
|
||||
|
||||
public UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder toOthersBriefProto() {
|
||||
var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId);
|
||||
|
||||
return musicGameBeatmap.toBriefProto()
|
||||
// .setScore(score)
|
||||
// .setSettle(settle)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity.musicgame;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.MusicGameBasicData;
|
||||
import emu.grasscutter.net.proto.MusicGameRecordOuterClass;
|
||||
import emu.grasscutter.net.proto.UgcMusicBriefInfoOuterClass;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public class MusicGamePlayerData {
|
||||
Map<Integer, MusicGameRecord> musicGameRecord;
|
||||
Map<Long, CustomBeatmapRecord> personalCustomBeatmapRecord;
|
||||
Map<Long, CustomBeatmapRecord> othersCustomBeatmapRecord;
|
||||
|
||||
public static MusicGamePlayerData create() {
|
||||
return MusicGamePlayerData.of()
|
||||
.musicGameRecord(
|
||||
GameData.getMusicGameBasicDataMap().values().stream()
|
||||
.collect(
|
||||
Collectors.toMap(
|
||||
MusicGameBasicData::getId, MusicGamePlayerData.MusicGameRecord::create)))
|
||||
.personalCustomBeatmapRecord(new HashMap<>())
|
||||
.othersCustomBeatmapRecord(new HashMap<>())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public static class MusicGameRecord {
|
||||
int musicId;
|
||||
int maxCombo;
|
||||
int maxScore;
|
||||
|
||||
public static MusicGameRecord create(MusicGameBasicData musicGameBasicData) {
|
||||
return MusicGameRecord.of().musicId(musicGameBasicData.getId()).build();
|
||||
}
|
||||
|
||||
public MusicGameRecordOuterClass.MusicGameRecord toProto() {
|
||||
return MusicGameRecordOuterClass.MusicGameRecord.newBuilder()
|
||||
.setIsUnlock(true)
|
||||
.setMaxCombo(maxCombo)
|
||||
.setMaxScore(maxScore)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public static class CustomBeatmapRecord {
|
||||
long musicShareId;
|
||||
int score;
|
||||
boolean settle;
|
||||
|
||||
public UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder toPersonalBriefProto() {
|
||||
var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId);
|
||||
|
||||
return UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.newBuilder()
|
||||
// .setCanShare(true)
|
||||
// .setCreateTime(musicGameBeatmap.getCreateTime())
|
||||
.setMusicId(musicGameBeatmap.getMusicId())
|
||||
.setMaxScore(musicGameBeatmap.getMaxScore())
|
||||
// .setPosition(musicGameBeatmap.getSavePosition())
|
||||
// .setMusicNoteCount(musicGameBeatmap.getMusicNoteCount())
|
||||
.setUgcGuid(musicShareId);
|
||||
}
|
||||
|
||||
public UgcMusicBriefInfoOuterClass.UgcMusicBriefInfo.Builder toOthersBriefProto() {
|
||||
var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId);
|
||||
|
||||
return musicGameBeatmap.toBriefProto()
|
||||
// .setScore(score)
|
||||
// .setSettle(settle)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,23 @@
|
||||
package emu.grasscutter.game.activity.musicgame;
|
||||
|
||||
import emu.grasscutter.game.activity.ActivityWatcher;
|
||||
import emu.grasscutter.game.activity.ActivityWatcherType;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
|
||||
@ActivityWatcherType(WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE)
|
||||
public class MusicGameScoreTrigger extends ActivityWatcher {
|
||||
@Override
|
||||
protected boolean isMeet(String... param) {
|
||||
if (param.length != 2) {
|
||||
return false;
|
||||
}
|
||||
var paramList = getActivityWatcherData().getTriggerConfig().getParamList();
|
||||
if (!paramList.get(0).equals(param[0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var score = Integer.parseInt(param[1]);
|
||||
var target = Integer.parseInt(paramList.get(1));
|
||||
return score >= target;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity.musicgame;
|
||||
|
||||
import emu.grasscutter.game.activity.ActivityWatcher;
|
||||
import emu.grasscutter.game.activity.ActivityWatcherType;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
|
||||
@ActivityWatcherType(WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE)
|
||||
public class MusicGameScoreTrigger extends ActivityWatcher {
|
||||
@Override
|
||||
protected boolean isMeet(String... param) {
|
||||
if (param.length != 2) {
|
||||
return false;
|
||||
}
|
||||
var paramList = getActivityWatcherData().getTriggerConfig().getParamList();
|
||||
if (!paramList.get(0).equals(param[0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var score = Integer.parseInt(param[1]);
|
||||
var target = Integer.parseInt(paramList.get(1));
|
||||
return score >= target;
|
||||
}
|
||||
}
|
||||
|
@ -1,415 +1,415 @@
|
||||
package emu.grasscutter.game.battlepass;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.BattlePassRewardData;
|
||||
import emu.grasscutter.data.excels.ItemData;
|
||||
import emu.grasscutter.data.excels.RewardData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.MaterialType;
|
||||
import emu.grasscutter.game.player.BasePlayerDataManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.BattlePassMissionRefreshType;
|
||||
import emu.grasscutter.game.props.BattlePassMissionStatus;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.net.proto.BattlePassCycleOuterClass.BattlePassCycle;
|
||||
import emu.grasscutter.net.proto.BattlePassRewardTakeOptionOuterClass.BattlePassRewardTakeOption;
|
||||
import emu.grasscutter.net.proto.BattlePassScheduleOuterClass.BattlePassSchedule;
|
||||
import emu.grasscutter.net.proto.BattlePassUnlockStatusOuterClass.BattlePassUnlockStatus;
|
||||
import emu.grasscutter.server.packet.send.PacketBattlePassCurScheduleUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketTakeBattlePassRewardRsp;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
@Entity(value = "battlepass", useDiscriminator = false)
|
||||
public class BattlePassManager extends BasePlayerDataManager {
|
||||
@Id @Getter private ObjectId id;
|
||||
|
||||
@Indexed private int ownerUid;
|
||||
@Getter private int point;
|
||||
@Getter private int cyclePoints; // Weekly maximum cap
|
||||
@Getter private int level;
|
||||
|
||||
@Getter private boolean viewed;
|
||||
private boolean paid;
|
||||
|
||||
private Map<Integer, BattlePassMission> missions;
|
||||
private Map<Integer, BattlePassReward> takenRewards;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public BattlePassManager() {}
|
||||
|
||||
public BattlePassManager(Player player) {
|
||||
super(player);
|
||||
this.ownerUid = player.getUid();
|
||||
}
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
this.ownerUid = player.getUid();
|
||||
}
|
||||
|
||||
public void updateViewed() {
|
||||
this.viewed = true;
|
||||
}
|
||||
|
||||
public boolean setLevel(int level) {
|
||||
if (level >= 0 && level <= GameConstants.BATTLE_PASS_MAX_LEVEL) {
|
||||
this.level = level;
|
||||
this.point = 0;
|
||||
this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.player));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addPoints(int points) {
|
||||
this.addPointsDirectly(points, false);
|
||||
|
||||
this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(player));
|
||||
this.save();
|
||||
}
|
||||
|
||||
public void addPointsDirectly(int points, boolean isWeekly) {
|
||||
int amount = points;
|
||||
|
||||
if (isWeekly) {
|
||||
amount = Math.min(amount, GameConstants.BATTLE_PASS_POINT_PER_WEEK - this.cyclePoints);
|
||||
}
|
||||
|
||||
if (amount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.point += amount;
|
||||
this.cyclePoints += amount;
|
||||
|
||||
if (this.point >= GameConstants.BATTLE_PASS_POINT_PER_LEVEL
|
||||
&& this.getLevel() < GameConstants.BATTLE_PASS_MAX_LEVEL) {
|
||||
int levelups = Math.floorDiv(this.point, GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
|
||||
|
||||
// Make sure player cant go above max BP level
|
||||
levelups = Math.min(levelups, GameConstants.BATTLE_PASS_MAX_LEVEL - levelups);
|
||||
|
||||
// Set new points after level up
|
||||
this.point = this.point - (levelups * GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
|
||||
this.level += levelups;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<Integer, BattlePassMission> getMissions() {
|
||||
if (this.missions == null) this.missions = new HashMap<>();
|
||||
return this.missions;
|
||||
}
|
||||
|
||||
// Will return a new empty mission if the mission id is not found
|
||||
public BattlePassMission loadMissionById(int id) {
|
||||
return getMissions().computeIfAbsent(id, i -> new BattlePassMission(i));
|
||||
}
|
||||
|
||||
public boolean hasMission(int id) {
|
||||
return getMissions().containsKey(id);
|
||||
}
|
||||
|
||||
public boolean isPaid() {
|
||||
// ToDo: Change this when we actually support unlocking "paid" BP.
|
||||
return true;
|
||||
}
|
||||
|
||||
public Map<Integer, BattlePassReward> getTakenRewards() {
|
||||
if (this.takenRewards == null) this.takenRewards = new HashMap<>();
|
||||
return this.takenRewards;
|
||||
}
|
||||
|
||||
// Mission triggers
|
||||
public void triggerMission(WatcherTriggerType triggerType) {
|
||||
getPlayer().getServer().getBattlePassSystem().triggerMission(getPlayer(), triggerType);
|
||||
}
|
||||
|
||||
public void triggerMission(WatcherTriggerType triggerType, int param, int progress) {
|
||||
getPlayer()
|
||||
.getServer()
|
||||
.getBattlePassSystem()
|
||||
.triggerMission(getPlayer(), triggerType, param, progress);
|
||||
}
|
||||
|
||||
// Handlers
|
||||
public void takeMissionPoint(List<Integer> missionIdList) {
|
||||
// Obvious exploit check
|
||||
if (missionIdList.size() > GameData.getBattlePassMissionDataMap().size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<BattlePassMission> updatedMissions = new ArrayList<>(missionIdList.size());
|
||||
|
||||
for (int id : missionIdList) {
|
||||
// Skip if we dont have this mission
|
||||
if (!this.hasMission(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BattlePassMission mission = this.loadMissionById(id);
|
||||
|
||||
if (mission.getData() == null) {
|
||||
this.getMissions().remove(mission.getId());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Take reward
|
||||
if (mission.getStatus() == BattlePassMissionStatus.MISSION_STATUS_FINISHED) {
|
||||
this.addPointsDirectly(mission.getData().getAddPoint(), mission.getData().isCycleRefresh());
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_POINT_TAKEN);
|
||||
|
||||
updatedMissions.add(mission);
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedMissions.size() > 0) {
|
||||
// Save to db
|
||||
this.save();
|
||||
|
||||
// Packet
|
||||
getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(updatedMissions));
|
||||
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
|
||||
}
|
||||
}
|
||||
|
||||
private void takeRewardsFromSelectChest(
|
||||
ItemData rewardItemData, int index, ItemParamData entry, List<GameItem> rewardItems) {
|
||||
// Sanity checks.
|
||||
if (rewardItemData.getItemUse().size() < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get possible item choices.
|
||||
String[] choices = rewardItemData.getItemUse().get(0).getUseParam()[0].split(",");
|
||||
if (choices.length < index) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get data for the selected item.
|
||||
// This depends on the type of chest.
|
||||
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
|
||||
// data.
|
||||
if (rewardItemData.getItemUse().get(0).getUseOp() == ItemUseOp.ITEM_USE_ADD_SELECT_ITEM) {
|
||||
GameItem rewardItem =
|
||||
new GameItem(GameData.getItemDataMap().get(chosenId), entry.getItemCount());
|
||||
rewardItems.add(rewardItem);
|
||||
}
|
||||
// For ITEM_USE_GRANT_SELECT_REWARD chests, we have to again look up reward data.
|
||||
else if (rewardItemData.getItemUse().get(0).getUseOp()
|
||||
== ItemUseOp.ITEM_USE_GRANT_SELECT_REWARD) {
|
||||
RewardData selectedReward = GameData.getRewardDataMap().get(chosenId);
|
||||
|
||||
for (var r : selectedReward.getRewardItemList()) {
|
||||
GameItem rewardItem =
|
||||
new GameItem(GameData.getItemDataMap().get(r.getItemId()), r.getItemCount());
|
||||
rewardItems.add(rewardItem);
|
||||
}
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Invalid chest type for BP reward.");
|
||||
}
|
||||
}
|
||||
|
||||
public void takeReward(List<BattlePassRewardTakeOption> takeOptionList) {
|
||||
List<BattlePassRewardTakeOption> rewardList = new ArrayList<>();
|
||||
|
||||
for (BattlePassRewardTakeOption option : takeOptionList) {
|
||||
// Duplicate check
|
||||
if (option.getTag().getRewardId() == 0
|
||||
|| getTakenRewards().containsKey(option.getTag().getRewardId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Level check
|
||||
if (option.getTag().getLevel() > this.getLevel()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BattlePassRewardData rewardData =
|
||||
GameData.getBattlePassRewardDataMap()
|
||||
.get(GameConstants.BATTLE_PASS_CURRENT_INDEX * 100 + option.getTag().getLevel());
|
||||
|
||||
// Sanity check with excel data
|
||||
if (rewardData.getFreeRewardIdList().contains(option.getTag().getRewardId())) {
|
||||
rewardList.add(option);
|
||||
} else if (this.isPaid()
|
||||
&& rewardData.getPaidRewardIdList().contains(option.getTag().getRewardId())) {
|
||||
rewardList.add(option);
|
||||
} else {
|
||||
Grasscutter.getLogger().info("Not in rewards list: {}", option.getTag().getRewardId());
|
||||
}
|
||||
}
|
||||
|
||||
// Get rewards
|
||||
List<GameItem> rewardItems = null;
|
||||
|
||||
if (rewardList.size() > 0) {
|
||||
|
||||
rewardItems = new ArrayList<>();
|
||||
|
||||
for (var option : rewardList) {
|
||||
var tag = option.getTag();
|
||||
int index = option.getOptionIdx();
|
||||
|
||||
// Make sure we have reward data.
|
||||
RewardData reward = GameData.getRewardDataMap().get(tag.getRewardId());
|
||||
if (reward == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add reward items.
|
||||
for (var entry : reward.getRewardItemList()) {
|
||||
ItemData rewardItemData = GameData.getItemDataMap().get(entry.getItemId());
|
||||
|
||||
// Some rewards are chests where the user can select the item they want.
|
||||
if (rewardItemData.getMaterialType() == MaterialType.MATERIAL_SELECTABLE_CHEST) {
|
||||
this.takeRewardsFromSelectChest(rewardItemData, index, entry, rewardItems);
|
||||
}
|
||||
// All other rewards directly give us the right item.
|
||||
else {
|
||||
GameItem rewardItem = new GameItem(rewardItemData, entry.getItemCount());
|
||||
rewardItems.add(rewardItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the reward and set as taken.
|
||||
BattlePassReward bpReward =
|
||||
new BattlePassReward(
|
||||
tag.getLevel(),
|
||||
tag.getRewardId(),
|
||||
tag.getUnlockStatus() == BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID);
|
||||
this.getTakenRewards().put(bpReward.getRewardId(), bpReward);
|
||||
}
|
||||
|
||||
// Save to db
|
||||
this.save();
|
||||
|
||||
// Add items and send battle pass schedule packet
|
||||
getPlayer().getInventory().addItems(rewardItems);
|
||||
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
|
||||
}
|
||||
|
||||
getPlayer().sendPacket(new PacketTakeBattlePassRewardRsp(takeOptionList, rewardItems));
|
||||
}
|
||||
|
||||
public int buyLevels(int buyLevel) {
|
||||
int boughtLevels = Math.min(buyLevel, GameConstants.BATTLE_PASS_MAX_LEVEL - buyLevel);
|
||||
|
||||
if (boughtLevels > 0) {
|
||||
int price = GameConstants.BATTLE_PASS_LEVEL_PRICE * boughtLevels;
|
||||
|
||||
if (getPlayer().getPrimogems() < price) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
this.level += boughtLevels;
|
||||
this.save();
|
||||
|
||||
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
|
||||
}
|
||||
|
||||
return boughtLevels;
|
||||
}
|
||||
|
||||
public void resetDailyMissions() {
|
||||
var resetMissions = new ArrayList<BattlePassMission>();
|
||||
|
||||
for (var mission : this.missions.values()) {
|
||||
if (mission.getData().getRefreshType() == null
|
||||
|| mission.getData().getRefreshType()
|
||||
== BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_DAILY) {
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED);
|
||||
mission.setProgress(0);
|
||||
|
||||
resetMissions.add(mission);
|
||||
}
|
||||
}
|
||||
|
||||
this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
|
||||
this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
|
||||
}
|
||||
|
||||
public void resetWeeklyMissions() {
|
||||
var resetMissions = new ArrayList<BattlePassMission>();
|
||||
|
||||
for (var mission : this.missions.values()) {
|
||||
if (mission.getData().getRefreshType()
|
||||
== BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE) {
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED);
|
||||
mission.setProgress(0);
|
||||
|
||||
resetMissions.add(mission);
|
||||
}
|
||||
}
|
||||
|
||||
this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
|
||||
this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
|
||||
}
|
||||
|
||||
//
|
||||
public BattlePassSchedule getScheduleProto() {
|
||||
var currentDate = LocalDate.now();
|
||||
var nextSundayDate =
|
||||
(currentDate.getDayOfWeek() == DayOfWeek.SUNDAY)
|
||||
? currentDate
|
||||
: LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
|
||||
var nextSundayTime =
|
||||
LocalDateTime.of(
|
||||
nextSundayDate.getYear(),
|
||||
nextSundayDate.getMonthValue(),
|
||||
nextSundayDate.getDayOfMonth(),
|
||||
23,
|
||||
59,
|
||||
59);
|
||||
|
||||
BattlePassSchedule.Builder schedule =
|
||||
BattlePassSchedule.newBuilder()
|
||||
.setScheduleId(2700)
|
||||
.setLevel(this.getLevel())
|
||||
.setPoint(this.getPoint())
|
||||
.setBeginTime(0)
|
||||
.setEndTime(2059483200)
|
||||
.setIsViewed(this.isViewed())
|
||||
.setUnlockStatus(
|
||||
this.isPaid()
|
||||
? BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID
|
||||
: BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_FREE)
|
||||
.setPaidPlatformFlags(2) // Not bought on Playstation.
|
||||
.setCurCyclePoints(this.getCyclePoints())
|
||||
.setCurCycle(
|
||||
BattlePassCycle.newBuilder()
|
||||
.setBeginTime(0)
|
||||
.setEndTime((int) nextSundayTime.atZone(ZoneId.systemDefault()).toEpochSecond())
|
||||
.setCycleIdx(3));
|
||||
|
||||
for (BattlePassReward reward : getTakenRewards().values()) {
|
||||
schedule.addRewardTakenList(reward.toProto());
|
||||
}
|
||||
|
||||
return schedule.build();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
DatabaseHelper.saveBattlePass(this);
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.battlepass;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.BattlePassRewardData;
|
||||
import emu.grasscutter.data.excels.ItemData;
|
||||
import emu.grasscutter.data.excels.RewardData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.MaterialType;
|
||||
import emu.grasscutter.game.player.BasePlayerDataManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.BattlePassMissionRefreshType;
|
||||
import emu.grasscutter.game.props.BattlePassMissionStatus;
|
||||
import emu.grasscutter.game.props.ItemUseOp;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.net.proto.BattlePassCycleOuterClass.BattlePassCycle;
|
||||
import emu.grasscutter.net.proto.BattlePassRewardTakeOptionOuterClass.BattlePassRewardTakeOption;
|
||||
import emu.grasscutter.net.proto.BattlePassScheduleOuterClass.BattlePassSchedule;
|
||||
import emu.grasscutter.net.proto.BattlePassUnlockStatusOuterClass.BattlePassUnlockStatus;
|
||||
import emu.grasscutter.server.packet.send.PacketBattlePassCurScheduleUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketTakeBattlePassRewardRsp;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
@Entity(value = "battlepass", useDiscriminator = false)
|
||||
public class BattlePassManager extends BasePlayerDataManager {
|
||||
@Id @Getter private ObjectId id;
|
||||
|
||||
@Indexed private int ownerUid;
|
||||
@Getter private int point;
|
||||
@Getter private int cyclePoints; // Weekly maximum cap
|
||||
@Getter private int level;
|
||||
|
||||
@Getter private boolean viewed;
|
||||
private boolean paid;
|
||||
|
||||
private Map<Integer, BattlePassMission> missions;
|
||||
private Map<Integer, BattlePassReward> takenRewards;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public BattlePassManager() {}
|
||||
|
||||
public BattlePassManager(Player player) {
|
||||
super(player);
|
||||
this.ownerUid = player.getUid();
|
||||
}
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
this.ownerUid = player.getUid();
|
||||
}
|
||||
|
||||
public void updateViewed() {
|
||||
this.viewed = true;
|
||||
}
|
||||
|
||||
public boolean setLevel(int level) {
|
||||
if (level >= 0 && level <= GameConstants.BATTLE_PASS_MAX_LEVEL) {
|
||||
this.level = level;
|
||||
this.point = 0;
|
||||
this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.player));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addPoints(int points) {
|
||||
this.addPointsDirectly(points, false);
|
||||
|
||||
this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(player));
|
||||
this.save();
|
||||
}
|
||||
|
||||
public void addPointsDirectly(int points, boolean isWeekly) {
|
||||
int amount = points;
|
||||
|
||||
if (isWeekly) {
|
||||
amount = Math.min(amount, GameConstants.BATTLE_PASS_POINT_PER_WEEK - this.cyclePoints);
|
||||
}
|
||||
|
||||
if (amount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.point += amount;
|
||||
this.cyclePoints += amount;
|
||||
|
||||
if (this.point >= GameConstants.BATTLE_PASS_POINT_PER_LEVEL
|
||||
&& this.getLevel() < GameConstants.BATTLE_PASS_MAX_LEVEL) {
|
||||
int levelups = Math.floorDiv(this.point, GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
|
||||
|
||||
// Make sure player cant go above max BP level
|
||||
levelups = Math.min(levelups, GameConstants.BATTLE_PASS_MAX_LEVEL - levelups);
|
||||
|
||||
// Set new points after level up
|
||||
this.point = this.point - (levelups * GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
|
||||
this.level += levelups;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<Integer, BattlePassMission> getMissions() {
|
||||
if (this.missions == null) this.missions = new HashMap<>();
|
||||
return this.missions;
|
||||
}
|
||||
|
||||
// Will return a new empty mission if the mission id is not found
|
||||
public BattlePassMission loadMissionById(int id) {
|
||||
return getMissions().computeIfAbsent(id, i -> new BattlePassMission(i));
|
||||
}
|
||||
|
||||
public boolean hasMission(int id) {
|
||||
return getMissions().containsKey(id);
|
||||
}
|
||||
|
||||
public boolean isPaid() {
|
||||
// ToDo: Change this when we actually support unlocking "paid" BP.
|
||||
return true;
|
||||
}
|
||||
|
||||
public Map<Integer, BattlePassReward> getTakenRewards() {
|
||||
if (this.takenRewards == null) this.takenRewards = new HashMap<>();
|
||||
return this.takenRewards;
|
||||
}
|
||||
|
||||
// Mission triggers
|
||||
public void triggerMission(WatcherTriggerType triggerType) {
|
||||
getPlayer().getServer().getBattlePassSystem().triggerMission(getPlayer(), triggerType);
|
||||
}
|
||||
|
||||
public void triggerMission(WatcherTriggerType triggerType, int param, int progress) {
|
||||
getPlayer()
|
||||
.getServer()
|
||||
.getBattlePassSystem()
|
||||
.triggerMission(getPlayer(), triggerType, param, progress);
|
||||
}
|
||||
|
||||
// Handlers
|
||||
public void takeMissionPoint(List<Integer> missionIdList) {
|
||||
// Obvious exploit check
|
||||
if (missionIdList.size() > GameData.getBattlePassMissionDataMap().size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<BattlePassMission> updatedMissions = new ArrayList<>(missionIdList.size());
|
||||
|
||||
for (int id : missionIdList) {
|
||||
// Skip if we dont have this mission
|
||||
if (!this.hasMission(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BattlePassMission mission = this.loadMissionById(id);
|
||||
|
||||
if (mission.getData() == null) {
|
||||
this.getMissions().remove(mission.getId());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Take reward
|
||||
if (mission.getStatus() == BattlePassMissionStatus.MISSION_STATUS_FINISHED) {
|
||||
this.addPointsDirectly(mission.getData().getAddPoint(), mission.getData().isCycleRefresh());
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_POINT_TAKEN);
|
||||
|
||||
updatedMissions.add(mission);
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedMissions.size() > 0) {
|
||||
// Save to db
|
||||
this.save();
|
||||
|
||||
// Packet
|
||||
getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(updatedMissions));
|
||||
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
|
||||
}
|
||||
}
|
||||
|
||||
private void takeRewardsFromSelectChest(
|
||||
ItemData rewardItemData, int index, ItemParamData entry, List<GameItem> rewardItems) {
|
||||
// Sanity checks.
|
||||
if (rewardItemData.getItemUse().size() < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get possible item choices.
|
||||
String[] choices = rewardItemData.getItemUse().get(0).getUseParam()[0].split(",");
|
||||
if (choices.length < index) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get data for the selected item.
|
||||
// This depends on the type of chest.
|
||||
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
|
||||
// data.
|
||||
if (rewardItemData.getItemUse().get(0).getUseOp() == ItemUseOp.ITEM_USE_ADD_SELECT_ITEM) {
|
||||
GameItem rewardItem =
|
||||
new GameItem(GameData.getItemDataMap().get(chosenId), entry.getItemCount());
|
||||
rewardItems.add(rewardItem);
|
||||
}
|
||||
// For ITEM_USE_GRANT_SELECT_REWARD chests, we have to again look up reward data.
|
||||
else if (rewardItemData.getItemUse().get(0).getUseOp()
|
||||
== ItemUseOp.ITEM_USE_GRANT_SELECT_REWARD) {
|
||||
RewardData selectedReward = GameData.getRewardDataMap().get(chosenId);
|
||||
|
||||
for (var r : selectedReward.getRewardItemList()) {
|
||||
GameItem rewardItem =
|
||||
new GameItem(GameData.getItemDataMap().get(r.getItemId()), r.getItemCount());
|
||||
rewardItems.add(rewardItem);
|
||||
}
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Invalid chest type for BP reward.");
|
||||
}
|
||||
}
|
||||
|
||||
public void takeReward(List<BattlePassRewardTakeOption> takeOptionList) {
|
||||
List<BattlePassRewardTakeOption> rewardList = new ArrayList<>();
|
||||
|
||||
for (BattlePassRewardTakeOption option : takeOptionList) {
|
||||
// Duplicate check
|
||||
if (option.getTag().getRewardId() == 0
|
||||
|| getTakenRewards().containsKey(option.getTag().getRewardId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Level check
|
||||
if (option.getTag().getLevel() > this.getLevel()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BattlePassRewardData rewardData =
|
||||
GameData.getBattlePassRewardDataMap()
|
||||
.get(GameConstants.BATTLE_PASS_CURRENT_INDEX * 100 + option.getTag().getLevel());
|
||||
|
||||
// Sanity check with excel data
|
||||
if (rewardData.getFreeRewardIdList().contains(option.getTag().getRewardId())) {
|
||||
rewardList.add(option);
|
||||
} else if (this.isPaid()
|
||||
&& rewardData.getPaidRewardIdList().contains(option.getTag().getRewardId())) {
|
||||
rewardList.add(option);
|
||||
} else {
|
||||
Grasscutter.getLogger().info("Not in rewards list: {}", option.getTag().getRewardId());
|
||||
}
|
||||
}
|
||||
|
||||
// Get rewards
|
||||
List<GameItem> rewardItems = null;
|
||||
|
||||
if (rewardList.size() > 0) {
|
||||
|
||||
rewardItems = new ArrayList<>();
|
||||
|
||||
for (var option : rewardList) {
|
||||
var tag = option.getTag();
|
||||
int index = option.getOptionIdx();
|
||||
|
||||
// Make sure we have reward data.
|
||||
RewardData reward = GameData.getRewardDataMap().get(tag.getRewardId());
|
||||
if (reward == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add reward items.
|
||||
for (var entry : reward.getRewardItemList()) {
|
||||
ItemData rewardItemData = GameData.getItemDataMap().get(entry.getItemId());
|
||||
|
||||
// Some rewards are chests where the user can select the item they want.
|
||||
if (rewardItemData.getMaterialType() == MaterialType.MATERIAL_SELECTABLE_CHEST) {
|
||||
this.takeRewardsFromSelectChest(rewardItemData, index, entry, rewardItems);
|
||||
}
|
||||
// All other rewards directly give us the right item.
|
||||
else {
|
||||
GameItem rewardItem = new GameItem(rewardItemData, entry.getItemCount());
|
||||
rewardItems.add(rewardItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the reward and set as taken.
|
||||
BattlePassReward bpReward =
|
||||
new BattlePassReward(
|
||||
tag.getLevel(),
|
||||
tag.getRewardId(),
|
||||
tag.getUnlockStatus() == BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID);
|
||||
this.getTakenRewards().put(bpReward.getRewardId(), bpReward);
|
||||
}
|
||||
|
||||
// Save to db
|
||||
this.save();
|
||||
|
||||
// Add items and send battle pass schedule packet
|
||||
getPlayer().getInventory().addItems(rewardItems);
|
||||
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
|
||||
}
|
||||
|
||||
getPlayer().sendPacket(new PacketTakeBattlePassRewardRsp(takeOptionList, rewardItems));
|
||||
}
|
||||
|
||||
public int buyLevels(int buyLevel) {
|
||||
int boughtLevels = Math.min(buyLevel, GameConstants.BATTLE_PASS_MAX_LEVEL - buyLevel);
|
||||
|
||||
if (boughtLevels > 0) {
|
||||
int price = GameConstants.BATTLE_PASS_LEVEL_PRICE * boughtLevels;
|
||||
|
||||
if (getPlayer().getPrimogems() < price) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
this.level += boughtLevels;
|
||||
this.save();
|
||||
|
||||
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
|
||||
}
|
||||
|
||||
return boughtLevels;
|
||||
}
|
||||
|
||||
public void resetDailyMissions() {
|
||||
var resetMissions = new ArrayList<BattlePassMission>();
|
||||
|
||||
for (var mission : this.missions.values()) {
|
||||
if (mission.getData().getRefreshType() == null
|
||||
|| mission.getData().getRefreshType()
|
||||
== BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_DAILY) {
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED);
|
||||
mission.setProgress(0);
|
||||
|
||||
resetMissions.add(mission);
|
||||
}
|
||||
}
|
||||
|
||||
this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
|
||||
this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
|
||||
}
|
||||
|
||||
public void resetWeeklyMissions() {
|
||||
var resetMissions = new ArrayList<BattlePassMission>();
|
||||
|
||||
for (var mission : this.missions.values()) {
|
||||
if (mission.getData().getRefreshType()
|
||||
== BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE) {
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED);
|
||||
mission.setProgress(0);
|
||||
|
||||
resetMissions.add(mission);
|
||||
}
|
||||
}
|
||||
|
||||
this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
|
||||
this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
|
||||
}
|
||||
|
||||
//
|
||||
public BattlePassSchedule getScheduleProto() {
|
||||
var currentDate = LocalDate.now();
|
||||
var nextSundayDate =
|
||||
(currentDate.getDayOfWeek() == DayOfWeek.SUNDAY)
|
||||
? currentDate
|
||||
: LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
|
||||
var nextSundayTime =
|
||||
LocalDateTime.of(
|
||||
nextSundayDate.getYear(),
|
||||
nextSundayDate.getMonthValue(),
|
||||
nextSundayDate.getDayOfMonth(),
|
||||
23,
|
||||
59,
|
||||
59);
|
||||
|
||||
BattlePassSchedule.Builder schedule =
|
||||
BattlePassSchedule.newBuilder()
|
||||
.setScheduleId(2700)
|
||||
.setLevel(this.getLevel())
|
||||
.setPoint(this.getPoint())
|
||||
.setBeginTime(0)
|
||||
.setEndTime(2059483200)
|
||||
.setIsViewed(this.isViewed())
|
||||
.setUnlockStatus(
|
||||
this.isPaid()
|
||||
? BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID
|
||||
: BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_FREE)
|
||||
.setPaidPlatformFlags(2) // Not bought on Playstation.
|
||||
.setCurCyclePoints(this.getCyclePoints())
|
||||
.setCurCycle(
|
||||
BattlePassCycle.newBuilder()
|
||||
.setBeginTime(0)
|
||||
.setEndTime((int) nextSundayTime.atZone(ZoneId.systemDefault()).toEpochSecond())
|
||||
.setCycleIdx(3));
|
||||
|
||||
for (BattlePassReward reward : getTakenRewards().values()) {
|
||||
schedule.addRewardTakenList(reward.toProto());
|
||||
}
|
||||
|
||||
return schedule.build();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
DatabaseHelper.saveBattlePass(this);
|
||||
}
|
||||
}
|
||||
|
@ -1,75 +1,75 @@
|
||||
package emu.grasscutter.game.battlepass;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.BattlePassMissionData;
|
||||
import emu.grasscutter.game.props.BattlePassMissionStatus;
|
||||
|
||||
@Entity
|
||||
public class BattlePassMission {
|
||||
private int id;
|
||||
private int progress;
|
||||
private BattlePassMissionStatus status;
|
||||
|
||||
@Transient private BattlePassMissionData data;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public BattlePassMission() {}
|
||||
|
||||
public BattlePassMission(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public BattlePassMissionData getData() {
|
||||
if (this.data == null) {
|
||||
this.data = GameData.getBattlePassMissionDataMap().get(getId());
|
||||
}
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public int getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
public void setProgress(int value) {
|
||||
this.progress = value;
|
||||
}
|
||||
|
||||
public void addProgress(int addProgress, int maxProgress) {
|
||||
this.progress = Math.min(addProgress + this.progress, maxProgress);
|
||||
}
|
||||
|
||||
public BattlePassMissionStatus getStatus() {
|
||||
if (status == null) status = BattlePassMissionStatus.MISSION_STATUS_UNFINISHED;
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(BattlePassMissionStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public boolean isFinshed() {
|
||||
return getStatus().getValue() >= 2;
|
||||
}
|
||||
|
||||
public emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission toProto() {
|
||||
var protoBuilder =
|
||||
emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.newBuilder();
|
||||
|
||||
protoBuilder
|
||||
.setMissionId(getId())
|
||||
.setCurProgress(getProgress())
|
||||
.setTotalProgress(getData().getProgress())
|
||||
.setRewardBattlePassPoint(getData().getAddPoint())
|
||||
.setMissionStatus(getStatus().getMissionStatus())
|
||||
.setMissionType(
|
||||
getData().getRefreshType() == null ? 0 : getData().getRefreshType().getValue());
|
||||
|
||||
return protoBuilder.build();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.battlepass;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.BattlePassMissionData;
|
||||
import emu.grasscutter.game.props.BattlePassMissionStatus;
|
||||
|
||||
@Entity
|
||||
public class BattlePassMission {
|
||||
private int id;
|
||||
private int progress;
|
||||
private BattlePassMissionStatus status;
|
||||
|
||||
@Transient private BattlePassMissionData data;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public BattlePassMission() {}
|
||||
|
||||
public BattlePassMission(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public BattlePassMissionData getData() {
|
||||
if (this.data == null) {
|
||||
this.data = GameData.getBattlePassMissionDataMap().get(getId());
|
||||
}
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public int getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
public void setProgress(int value) {
|
||||
this.progress = value;
|
||||
}
|
||||
|
||||
public void addProgress(int addProgress, int maxProgress) {
|
||||
this.progress = Math.min(addProgress + this.progress, maxProgress);
|
||||
}
|
||||
|
||||
public BattlePassMissionStatus getStatus() {
|
||||
if (status == null) status = BattlePassMissionStatus.MISSION_STATUS_UNFINISHED;
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(BattlePassMissionStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public boolean isFinshed() {
|
||||
return getStatus().getValue() >= 2;
|
||||
}
|
||||
|
||||
public emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission toProto() {
|
||||
var protoBuilder =
|
||||
emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.newBuilder();
|
||||
|
||||
protoBuilder
|
||||
.setMissionId(getId())
|
||||
.setCurProgress(getProgress())
|
||||
.setTotalProgress(getData().getProgress())
|
||||
.setRewardBattlePassPoint(getData().getAddPoint())
|
||||
.setMissionStatus(getStatus().getMissionStatus())
|
||||
.setMissionType(
|
||||
getData().getRefreshType() == null ? 0 : getData().getRefreshType().getValue());
|
||||
|
||||
return protoBuilder.build();
|
||||
}
|
||||
}
|
||||
|
@ -1,51 +1,51 @@
|
||||
package emu.grasscutter.game.battlepass;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.data.excels.BattlePassMissionData;
|
||||
import emu.grasscutter.net.proto.BattlePassRewardTagOuterClass.BattlePassRewardTag;
|
||||
import emu.grasscutter.net.proto.BattlePassUnlockStatusOuterClass.BattlePassUnlockStatus;
|
||||
|
||||
@Entity
|
||||
public class BattlePassReward {
|
||||
private int level;
|
||||
private int rewardId;
|
||||
private boolean paid;
|
||||
|
||||
@Transient private BattlePassMissionData data;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public BattlePassReward() {}
|
||||
|
||||
public BattlePassReward(int level, int rewardId, boolean paid) {
|
||||
this.level = level;
|
||||
this.rewardId = rewardId;
|
||||
this.paid = paid;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public int getRewardId() {
|
||||
return rewardId;
|
||||
}
|
||||
|
||||
public boolean isPaid() {
|
||||
return paid;
|
||||
}
|
||||
|
||||
public BattlePassRewardTag toProto() {
|
||||
var protoBuilder = BattlePassRewardTag.newBuilder();
|
||||
|
||||
protoBuilder
|
||||
.setLevel(this.getLevel())
|
||||
.setRewardId(this.getRewardId())
|
||||
.setUnlockStatus(
|
||||
this.isPaid()
|
||||
? BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID
|
||||
: BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_FREE);
|
||||
|
||||
return protoBuilder.build();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.battlepass;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.data.excels.BattlePassMissionData;
|
||||
import emu.grasscutter.net.proto.BattlePassRewardTagOuterClass.BattlePassRewardTag;
|
||||
import emu.grasscutter.net.proto.BattlePassUnlockStatusOuterClass.BattlePassUnlockStatus;
|
||||
|
||||
@Entity
|
||||
public class BattlePassReward {
|
||||
private int level;
|
||||
private int rewardId;
|
||||
private boolean paid;
|
||||
|
||||
@Transient private BattlePassMissionData data;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public BattlePassReward() {}
|
||||
|
||||
public BattlePassReward(int level, int rewardId, boolean paid) {
|
||||
this.level = level;
|
||||
this.rewardId = rewardId;
|
||||
this.paid = paid;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public int getRewardId() {
|
||||
return rewardId;
|
||||
}
|
||||
|
||||
public boolean isPaid() {
|
||||
return paid;
|
||||
}
|
||||
|
||||
public BattlePassRewardTag toProto() {
|
||||
var protoBuilder = BattlePassRewardTag.newBuilder();
|
||||
|
||||
protoBuilder
|
||||
.setLevel(this.getLevel())
|
||||
.setRewardId(this.getRewardId())
|
||||
.setUnlockStatus(
|
||||
this.isPaid()
|
||||
? BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID
|
||||
: BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_FREE);
|
||||
|
||||
return protoBuilder.build();
|
||||
}
|
||||
}
|
||||
|
@ -1,80 +1,80 @@
|
||||
package emu.grasscutter.game.battlepass;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.BattlePassMissionData;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.BattlePassMissionStatus;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.server.game.BaseGameSystem;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BattlePassSystem extends BaseGameSystem {
|
||||
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
|
||||
// player
|
||||
public BattlePassSystem(GameServer server) {
|
||||
super(server);
|
||||
|
||||
this.cachedTriggers = new HashMap<>();
|
||||
|
||||
for (BattlePassMissionData missionData : GameData.getBattlePassMissionDataMap().values()) {
|
||||
if (missionData.isValidRefreshType()) {
|
||||
List<BattlePassMissionData> triggerList =
|
||||
getTriggers().computeIfAbsent(missionData.getTriggerType(), e -> new ArrayList<>());
|
||||
triggerList.add(missionData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
private Map<WatcherTriggerType, List<BattlePassMissionData>> getTriggers() {
|
||||
return cachedTriggers;
|
||||
}
|
||||
|
||||
public void triggerMission(Player player, WatcherTriggerType triggerType) {
|
||||
triggerMission(player, triggerType, 0, 1);
|
||||
}
|
||||
|
||||
public void triggerMission(
|
||||
Player player, WatcherTriggerType triggerType, int param, int progress) {
|
||||
List<BattlePassMissionData> triggerList = getTriggers().get(triggerType);
|
||||
|
||||
if (triggerList == null || triggerList.isEmpty()) return;
|
||||
|
||||
for (BattlePassMissionData data : triggerList) {
|
||||
// Skip params check if param == 0
|
||||
if (param != 0) {
|
||||
if (!data.getMainParams().contains(param)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Get mission from player, if it doesnt exist, then we make one
|
||||
BattlePassMission mission = player.getBattlePassManager().loadMissionById(data.getId());
|
||||
|
||||
if (mission.isFinshed()) continue;
|
||||
|
||||
// Add progress
|
||||
mission.addProgress(progress, data.getProgress());
|
||||
|
||||
if (mission.getProgress() >= data.getProgress()) {
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_FINISHED);
|
||||
}
|
||||
|
||||
// Save to db
|
||||
player.getBattlePassManager().save();
|
||||
|
||||
// Packet
|
||||
player.sendPacket(new PacketBattlePassMissionUpdateNotify(mission));
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.battlepass;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.BattlePassMissionData;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.BattlePassMissionStatus;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.server.game.BaseGameSystem;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BattlePassSystem extends BaseGameSystem {
|
||||
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
|
||||
// player
|
||||
public BattlePassSystem(GameServer server) {
|
||||
super(server);
|
||||
|
||||
this.cachedTriggers = new HashMap<>();
|
||||
|
||||
for (BattlePassMissionData missionData : GameData.getBattlePassMissionDataMap().values()) {
|
||||
if (missionData.isValidRefreshType()) {
|
||||
List<BattlePassMissionData> triggerList =
|
||||
getTriggers().computeIfAbsent(missionData.getTriggerType(), e -> new ArrayList<>());
|
||||
triggerList.add(missionData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
private Map<WatcherTriggerType, List<BattlePassMissionData>> getTriggers() {
|
||||
return cachedTriggers;
|
||||
}
|
||||
|
||||
public void triggerMission(Player player, WatcherTriggerType triggerType) {
|
||||
triggerMission(player, triggerType, 0, 1);
|
||||
}
|
||||
|
||||
public void triggerMission(
|
||||
Player player, WatcherTriggerType triggerType, int param, int progress) {
|
||||
List<BattlePassMissionData> triggerList = getTriggers().get(triggerType);
|
||||
|
||||
if (triggerList == null || triggerList.isEmpty()) return;
|
||||
|
||||
for (BattlePassMissionData data : triggerList) {
|
||||
// Skip params check if param == 0
|
||||
if (param != 0) {
|
||||
if (!data.getMainParams().contains(param)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Get mission from player, if it doesnt exist, then we make one
|
||||
BattlePassMission mission = player.getBattlePassManager().loadMissionById(data.getId());
|
||||
|
||||
if (mission.isFinshed()) continue;
|
||||
|
||||
// Add progress
|
||||
mission.addProgress(progress, data.getProgress());
|
||||
|
||||
if (mission.getProgress() >= data.getProgress()) {
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_FINISHED);
|
||||
}
|
||||
|
||||
// Save to db
|
||||
player.getBattlePassManager().save();
|
||||
|
||||
// Packet
|
||||
player.sendPacket(new PacketBattlePassMissionUpdateNotify(mission));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,215 +1,215 @@
|
||||
package emu.grasscutter.game.chat;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.GAME_INFO;
|
||||
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.command.CommandMap;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.packet.send.PacketPlayerChatNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketPrivateChatNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketPullPrivateChatRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketPullRecentChatRsp;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ChatSystem implements ChatSystemHandler {
|
||||
static final String PREFIXES = "[/!]";
|
||||
static final Pattern RE_PREFIXES = Pattern.compile(PREFIXES);
|
||||
static final Pattern RE_COMMANDS = Pattern.compile("\n" + PREFIXES);
|
||||
|
||||
// We store the chat history for ongoing sessions in the form
|
||||
// user id -> chat partner id -> [messages]
|
||||
private final Map<Integer, Map<Integer, List<ChatInfo>>> history = new HashMap<>();
|
||||
|
||||
private final GameServer server;
|
||||
|
||||
public ChatSystem(GameServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
private boolean tryInvokeCommand(Player sender, Player target, String rawMessage) {
|
||||
if (!RE_PREFIXES.matcher(rawMessage.substring(0, 1)).matches()) return false;
|
||||
for (String line : rawMessage.substring(1).split("\n[/!]"))
|
||||
CommandMap.getInstance().invoke(sender, target, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
/********************
|
||||
* Chat history handling
|
||||
********************/
|
||||
private void putInHistory(int uid, int partnerId, ChatInfo info) {
|
||||
this.history
|
||||
.computeIfAbsent(uid, x -> new HashMap<>())
|
||||
.computeIfAbsent(partnerId, x -> new ArrayList<>())
|
||||
.add(info);
|
||||
}
|
||||
|
||||
public void clearHistoryOnLogout(Player player) {
|
||||
this.history.remove(player.getUid());
|
||||
}
|
||||
|
||||
public void handlePullPrivateChatReq(Player player, int partnerId) {
|
||||
var chatHistory =
|
||||
this.history
|
||||
.computeIfAbsent(player.getUid(), x -> new HashMap<>())
|
||||
.computeIfAbsent(partnerId, x -> new ArrayList<>());
|
||||
player.sendPacket(new PacketPullPrivateChatRsp(chatHistory));
|
||||
}
|
||||
|
||||
public void handlePullRecentChatReq(Player player) {
|
||||
// If this user has no chat history yet, create it by sending the server welcome messages.
|
||||
if (!this.history
|
||||
.computeIfAbsent(player.getUid(), x -> new HashMap<>())
|
||||
.containsKey(GameConstants.SERVER_CONSOLE_UID)) {
|
||||
this.sendServerWelcomeMessages(player);
|
||||
}
|
||||
|
||||
// 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
|
||||
// chat partner
|
||||
// for every given player and return the last messages exchanged with that partner.
|
||||
int historyLength =
|
||||
this.history.get(player.getUid()).get(GameConstants.SERVER_CONSOLE_UID).size();
|
||||
var messages =
|
||||
this.history
|
||||
.get(player.getUid())
|
||||
.get(GameConstants.SERVER_CONSOLE_UID)
|
||||
.subList(Math.max(historyLength - 3, 0), historyLength);
|
||||
player.sendPacket(new PacketPullRecentChatRsp(messages));
|
||||
}
|
||||
|
||||
/********************
|
||||
* Sending messages
|
||||
********************/
|
||||
public void sendPrivateMessageFromServer(int targetUid, String message) {
|
||||
// Sanity checks.
|
||||
if (message == null || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create chat packet and put in history.
|
||||
var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, message);
|
||||
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
|
||||
|
||||
// Send.
|
||||
target.sendPacket(packet);
|
||||
}
|
||||
|
||||
public void sendPrivateMessageFromServer(int targetUid, int emote) {
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create chat packet and put in history.
|
||||
var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, emote);
|
||||
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
|
||||
|
||||
// Send.
|
||||
target.sendPacket(packet);
|
||||
}
|
||||
|
||||
public void sendPrivateMessage(Player player, int targetUid, String message) {
|
||||
// Sanity checks.
|
||||
if (message == null || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
|
||||
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create chat packet.
|
||||
var packet = new PacketPrivateChatNotify(player.getUid(), targetUid, message);
|
||||
|
||||
// Send and put in history.
|
||||
player.sendPacket(packet);
|
||||
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
|
||||
|
||||
// Check if command
|
||||
boolean isCommand = tryInvokeCommand(player, target, message);
|
||||
|
||||
if ((target != null) && (!isCommand)) {
|
||||
target.sendPacket(packet);
|
||||
putInHistory(targetUid, player.getUid(), packet.getChatInfo());
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPrivateMessage(Player player, int targetUid, int emote) {
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
|
||||
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create chat packet.
|
||||
var packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), emote);
|
||||
|
||||
// Send and put is history.
|
||||
player.sendPacket(packet);
|
||||
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
|
||||
|
||||
if (target != null) {
|
||||
target.sendPacket(packet);
|
||||
putInHistory(targetUid, player.getUid(), packet.getChatInfo());
|
||||
}
|
||||
}
|
||||
|
||||
public void sendTeamMessage(Player player, int channel, String message) {
|
||||
// Sanity checks
|
||||
if (message == null || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if command
|
||||
if (tryInvokeCommand(player, null, message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create and send chat packet
|
||||
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, message));
|
||||
}
|
||||
|
||||
public void sendTeamMessage(Player player, int channel, int icon) {
|
||||
// Create and send chat packet
|
||||
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, icon));
|
||||
}
|
||||
|
||||
/********************
|
||||
* Welcome messages
|
||||
********************/
|
||||
private void sendServerWelcomeMessages(Player player) {
|
||||
var joinOptions = GAME_INFO.joinOptions;
|
||||
|
||||
if (joinOptions.welcomeEmotes != null && joinOptions.welcomeEmotes.length > 0) {
|
||||
this.sendPrivateMessageFromServer(
|
||||
player.getUid(),
|
||||
joinOptions.welcomeEmotes[Utils.randomRange(0, joinOptions.welcomeEmotes.length - 1)]);
|
||||
}
|
||||
|
||||
if (joinOptions.welcomeMessage != null && joinOptions.welcomeMessage.length() > 0) {
|
||||
this.sendPrivateMessageFromServer(player.getUid(), joinOptions.welcomeMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.chat;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.GAME_INFO;
|
||||
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.command.CommandMap;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.packet.send.PacketPlayerChatNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketPrivateChatNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketPullPrivateChatRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketPullRecentChatRsp;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ChatSystem implements ChatSystemHandler {
|
||||
static final String PREFIXES = "[/!]";
|
||||
static final Pattern RE_PREFIXES = Pattern.compile(PREFIXES);
|
||||
static final Pattern RE_COMMANDS = Pattern.compile("\n" + PREFIXES);
|
||||
|
||||
// We store the chat history for ongoing sessions in the form
|
||||
// user id -> chat partner id -> [messages]
|
||||
private final Map<Integer, Map<Integer, List<ChatInfo>>> history = new HashMap<>();
|
||||
|
||||
private final GameServer server;
|
||||
|
||||
public ChatSystem(GameServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
private boolean tryInvokeCommand(Player sender, Player target, String rawMessage) {
|
||||
if (!RE_PREFIXES.matcher(rawMessage.substring(0, 1)).matches()) return false;
|
||||
for (String line : rawMessage.substring(1).split("\n[/!]"))
|
||||
CommandMap.getInstance().invoke(sender, target, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
/********************
|
||||
* Chat history handling
|
||||
********************/
|
||||
private void putInHistory(int uid, int partnerId, ChatInfo info) {
|
||||
this.history
|
||||
.computeIfAbsent(uid, x -> new HashMap<>())
|
||||
.computeIfAbsent(partnerId, x -> new ArrayList<>())
|
||||
.add(info);
|
||||
}
|
||||
|
||||
public void clearHistoryOnLogout(Player player) {
|
||||
this.history.remove(player.getUid());
|
||||
}
|
||||
|
||||
public void handlePullPrivateChatReq(Player player, int partnerId) {
|
||||
var chatHistory =
|
||||
this.history
|
||||
.computeIfAbsent(player.getUid(), x -> new HashMap<>())
|
||||
.computeIfAbsent(partnerId, x -> new ArrayList<>());
|
||||
player.sendPacket(new PacketPullPrivateChatRsp(chatHistory));
|
||||
}
|
||||
|
||||
public void handlePullRecentChatReq(Player player) {
|
||||
// If this user has no chat history yet, create it by sending the server welcome messages.
|
||||
if (!this.history
|
||||
.computeIfAbsent(player.getUid(), x -> new HashMap<>())
|
||||
.containsKey(GameConstants.SERVER_CONSOLE_UID)) {
|
||||
this.sendServerWelcomeMessages(player);
|
||||
}
|
||||
|
||||
// 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
|
||||
// chat partner
|
||||
// for every given player and return the last messages exchanged with that partner.
|
||||
int historyLength =
|
||||
this.history.get(player.getUid()).get(GameConstants.SERVER_CONSOLE_UID).size();
|
||||
var messages =
|
||||
this.history
|
||||
.get(player.getUid())
|
||||
.get(GameConstants.SERVER_CONSOLE_UID)
|
||||
.subList(Math.max(historyLength - 3, 0), historyLength);
|
||||
player.sendPacket(new PacketPullRecentChatRsp(messages));
|
||||
}
|
||||
|
||||
/********************
|
||||
* Sending messages
|
||||
********************/
|
||||
public void sendPrivateMessageFromServer(int targetUid, String message) {
|
||||
// Sanity checks.
|
||||
if (message == null || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create chat packet and put in history.
|
||||
var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, message);
|
||||
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
|
||||
|
||||
// Send.
|
||||
target.sendPacket(packet);
|
||||
}
|
||||
|
||||
public void sendPrivateMessageFromServer(int targetUid, int emote) {
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create chat packet and put in history.
|
||||
var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, emote);
|
||||
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
|
||||
|
||||
// Send.
|
||||
target.sendPacket(packet);
|
||||
}
|
||||
|
||||
public void sendPrivateMessage(Player player, int targetUid, String message) {
|
||||
// Sanity checks.
|
||||
if (message == null || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
|
||||
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create chat packet.
|
||||
var packet = new PacketPrivateChatNotify(player.getUid(), targetUid, message);
|
||||
|
||||
// Send and put in history.
|
||||
player.sendPacket(packet);
|
||||
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
|
||||
|
||||
// Check if command
|
||||
boolean isCommand = tryInvokeCommand(player, target, message);
|
||||
|
||||
if ((target != null) && (!isCommand)) {
|
||||
target.sendPacket(packet);
|
||||
putInHistory(targetUid, player.getUid(), packet.getChatInfo());
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPrivateMessage(Player player, int targetUid, int emote) {
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
|
||||
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create chat packet.
|
||||
var packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), emote);
|
||||
|
||||
// Send and put is history.
|
||||
player.sendPacket(packet);
|
||||
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
|
||||
|
||||
if (target != null) {
|
||||
target.sendPacket(packet);
|
||||
putInHistory(targetUid, player.getUid(), packet.getChatInfo());
|
||||
}
|
||||
}
|
||||
|
||||
public void sendTeamMessage(Player player, int channel, String message) {
|
||||
// Sanity checks
|
||||
if (message == null || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if command
|
||||
if (tryInvokeCommand(player, null, message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create and send chat packet
|
||||
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, message));
|
||||
}
|
||||
|
||||
public void sendTeamMessage(Player player, int channel, int icon) {
|
||||
// Create and send chat packet
|
||||
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, icon));
|
||||
}
|
||||
|
||||
/********************
|
||||
* Welcome messages
|
||||
********************/
|
||||
private void sendServerWelcomeMessages(Player player) {
|
||||
var joinOptions = GAME_INFO.joinOptions;
|
||||
|
||||
if (joinOptions.welcomeEmotes != null && joinOptions.welcomeEmotes.length > 0) {
|
||||
this.sendPrivateMessageFromServer(
|
||||
player.getUid(),
|
||||
joinOptions.welcomeEmotes[Utils.randomRange(0, joinOptions.welcomeEmotes.length - 1)]);
|
||||
}
|
||||
|
||||
if (joinOptions.welcomeMessage != null && joinOptions.welcomeMessage.length() > 0) {
|
||||
this.sendPrivateMessageFromServer(player.getUid(), joinOptions.welcomeMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,26 @@
|
||||
package emu.grasscutter.game.chat;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
|
||||
public interface ChatSystemHandler {
|
||||
GameServer getServer();
|
||||
|
||||
void sendPrivateMessage(Player player, int targetUid, String message);
|
||||
|
||||
void sendPrivateMessage(Player player, int targetUid, int emote);
|
||||
|
||||
void sendTeamMessage(Player player, int channel, String message);
|
||||
|
||||
void sendTeamMessage(Player player, int channel, int icon);
|
||||
|
||||
void sendPrivateMessageFromServer(int targetUid, String message);
|
||||
|
||||
void sendPrivateMessageFromServer(int targetUid, int emote);
|
||||
|
||||
void handlePullPrivateChatReq(Player player, int targetUid);
|
||||
|
||||
void clearHistoryOnLogout(Player player);
|
||||
|
||||
void handlePullRecentChatReq(Player player);
|
||||
}
|
||||
package emu.grasscutter.game.chat;
|
||||
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
|
||||
public interface ChatSystemHandler {
|
||||
GameServer getServer();
|
||||
|
||||
void sendPrivateMessage(Player player, int targetUid, String message);
|
||||
|
||||
void sendPrivateMessage(Player player, int targetUid, int emote);
|
||||
|
||||
void sendTeamMessage(Player player, int channel, String message);
|
||||
|
||||
void sendTeamMessage(Player player, int channel, int icon);
|
||||
|
||||
void sendPrivateMessageFromServer(int targetUid, String message);
|
||||
|
||||
void sendPrivateMessageFromServer(int targetUid, int emote);
|
||||
|
||||
void handlePullPrivateChatReq(Player player, int targetUid);
|
||||
|
||||
void clearHistoryOnLogout(Player player);
|
||||
|
||||
void handlePullRecentChatReq(Player player);
|
||||
}
|
||||
|
@ -1,143 +1,143 @@
|
||||
package emu.grasscutter.game.combine;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.DataLoader;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.CombineData;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||
import emu.grasscutter.server.game.BaseGameSystem;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.packet.send.PacketCombineFormulaDataNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketCombineRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketReliquaryDecomposeRsp;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CombineManger extends BaseGameSystem {
|
||||
private static final Int2ObjectMap<List<Integer>> reliquaryDecomposeData =
|
||||
new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public CombineManger(GameServer server) {
|
||||
super(server);
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
// Read the data we need for strongbox.
|
||||
try {
|
||||
DataLoader.loadList("ReliquaryDecompose.json", ReliquaryDecomposeEntry.class)
|
||||
.forEach(
|
||||
entry -> {
|
||||
reliquaryDecomposeData.put(entry.getConfigId(), entry.getItems());
|
||||
});
|
||||
Grasscutter.getLogger()
|
||||
.debug("Loaded {} reliquary decompose entries.", reliquaryDecomposeData.size());
|
||||
} catch (Exception ex) {
|
||||
Grasscutter.getLogger().error("Unable to load reliquary decompose data.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean unlockCombineDiagram(Player player, int combineId) {
|
||||
if (!player.getUnlockedCombines().add(combineId)) {
|
||||
return false; // Already unlocked
|
||||
}
|
||||
// Tell the client that this diagram is now unlocked and add the unlocked item to the player.
|
||||
player.sendPacket(new PacketCombineFormulaDataNotify(combineId));
|
||||
return true;
|
||||
}
|
||||
|
||||
public CombineResult combineItem(Player player, int cid, int count) {
|
||||
// check config exist
|
||||
if (!GameData.getCombineDataMap().containsKey(cid)) {
|
||||
player.getWorld().getHost().sendPacket(new PacketCombineRsp());
|
||||
return null;
|
||||
}
|
||||
|
||||
CombineData combineData = GameData.getCombineDataMap().get(cid);
|
||||
|
||||
if (combineData.getPlayerLevel() > player.getLevel()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// consume items
|
||||
List<ItemParamData> material = new ArrayList<>(combineData.getMaterialItems());
|
||||
material.add(new ItemParamData(202, combineData.getScoinCost()));
|
||||
|
||||
boolean success = player.getInventory().payItems(material, count, ActionReason.Combine);
|
||||
|
||||
// abort if not enough material
|
||||
if (!success) {
|
||||
player.sendPacket(
|
||||
new PacketCombineRsp(RetcodeOuterClass.Retcode.RET_ITEM_COMBINE_COUNT_NOT_ENOUGH_VALUE));
|
||||
}
|
||||
|
||||
// make the result
|
||||
player
|
||||
.getInventory()
|
||||
.addItem(combineData.getResultItemId(), combineData.getResultItemCount() * count);
|
||||
|
||||
CombineResult result = new CombineResult();
|
||||
result.setMaterial(List.of());
|
||||
result.setResult(
|
||||
List.of(
|
||||
new ItemParamData(
|
||||
combineData.getResultItemId(), combineData.getResultItemCount() * count)));
|
||||
// TODO lucky characters
|
||||
result.setExtra(List.of());
|
||||
result.setBack(List.of());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized void decomposeReliquaries(
|
||||
Player player, int configId, int count, List<Long> input) {
|
||||
// Check if the configId is legal.
|
||||
List<Integer> possibleDrops = reliquaryDecomposeData.get(configId);
|
||||
if (possibleDrops == null) {
|
||||
player.sendPacket(
|
||||
new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the number of input items matches the output count.
|
||||
if (input.size() != count * 3) {
|
||||
player.sendPacket(
|
||||
new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if all the input reliquaries actually are in the player's inventory.
|
||||
for (long guid : input) {
|
||||
if (player.getInventory().getItemByGuid(guid) == null) {
|
||||
player.sendPacket(
|
||||
new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the input reliquaries.
|
||||
for (long guid : input) {
|
||||
player.getInventory().removeItem(guid);
|
||||
}
|
||||
|
||||
// Generate outoput reliquaries.
|
||||
List<Long> resultItems = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
int itemId = Utils.drawRandomListElement(possibleDrops);
|
||||
GameItem newReliquary = new GameItem(itemId, 1);
|
||||
|
||||
player.getInventory().addItem(newReliquary);
|
||||
resultItems.add(newReliquary.getGuid());
|
||||
}
|
||||
|
||||
// Send packet.
|
||||
player.sendPacket(new PacketReliquaryDecomposeRsp(resultItems));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.combine;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.DataLoader;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.CombineData;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||
import emu.grasscutter.server.game.BaseGameSystem;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.packet.send.PacketCombineFormulaDataNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketCombineRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketReliquaryDecomposeRsp;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CombineManger extends BaseGameSystem {
|
||||
private static final Int2ObjectMap<List<Integer>> reliquaryDecomposeData =
|
||||
new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public CombineManger(GameServer server) {
|
||||
super(server);
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
// Read the data we need for strongbox.
|
||||
try {
|
||||
DataLoader.loadList("ReliquaryDecompose.json", ReliquaryDecomposeEntry.class)
|
||||
.forEach(
|
||||
entry -> {
|
||||
reliquaryDecomposeData.put(entry.getConfigId(), entry.getItems());
|
||||
});
|
||||
Grasscutter.getLogger()
|
||||
.debug("Loaded {} reliquary decompose entries.", reliquaryDecomposeData.size());
|
||||
} catch (Exception ex) {
|
||||
Grasscutter.getLogger().error("Unable to load reliquary decompose data.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean unlockCombineDiagram(Player player, int combineId) {
|
||||
if (!player.getUnlockedCombines().add(combineId)) {
|
||||
return false; // Already unlocked
|
||||
}
|
||||
// Tell the client that this diagram is now unlocked and add the unlocked item to the player.
|
||||
player.sendPacket(new PacketCombineFormulaDataNotify(combineId));
|
||||
return true;
|
||||
}
|
||||
|
||||
public CombineResult combineItem(Player player, int cid, int count) {
|
||||
// check config exist
|
||||
if (!GameData.getCombineDataMap().containsKey(cid)) {
|
||||
player.getWorld().getHost().sendPacket(new PacketCombineRsp());
|
||||
return null;
|
||||
}
|
||||
|
||||
CombineData combineData = GameData.getCombineDataMap().get(cid);
|
||||
|
||||
if (combineData.getPlayerLevel() > player.getLevel()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// consume items
|
||||
List<ItemParamData> material = new ArrayList<>(combineData.getMaterialItems());
|
||||
material.add(new ItemParamData(202, combineData.getScoinCost()));
|
||||
|
||||
boolean success = player.getInventory().payItems(material, count, ActionReason.Combine);
|
||||
|
||||
// abort if not enough material
|
||||
if (!success) {
|
||||
player.sendPacket(
|
||||
new PacketCombineRsp(RetcodeOuterClass.Retcode.RET_ITEM_COMBINE_COUNT_NOT_ENOUGH_VALUE));
|
||||
}
|
||||
|
||||
// make the result
|
||||
player
|
||||
.getInventory()
|
||||
.addItem(combineData.getResultItemId(), combineData.getResultItemCount() * count);
|
||||
|
||||
CombineResult result = new CombineResult();
|
||||
result.setMaterial(List.of());
|
||||
result.setResult(
|
||||
List.of(
|
||||
new ItemParamData(
|
||||
combineData.getResultItemId(), combineData.getResultItemCount() * count)));
|
||||
// TODO lucky characters
|
||||
result.setExtra(List.of());
|
||||
result.setBack(List.of());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized void decomposeReliquaries(
|
||||
Player player, int configId, int count, List<Long> input) {
|
||||
// Check if the configId is legal.
|
||||
List<Integer> possibleDrops = reliquaryDecomposeData.get(configId);
|
||||
if (possibleDrops == null) {
|
||||
player.sendPacket(
|
||||
new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the number of input items matches the output count.
|
||||
if (input.size() != count * 3) {
|
||||
player.sendPacket(
|
||||
new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if all the input reliquaries actually are in the player's inventory.
|
||||
for (long guid : input) {
|
||||
if (player.getInventory().getItemByGuid(guid) == null) {
|
||||
player.sendPacket(
|
||||
new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the input reliquaries.
|
||||
for (long guid : input) {
|
||||
player.getInventory().removeItem(guid);
|
||||
}
|
||||
|
||||
// Generate outoput reliquaries.
|
||||
List<Long> resultItems = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
int itemId = Utils.drawRandomListElement(possibleDrops);
|
||||
GameItem newReliquary = new GameItem(itemId, 1);
|
||||
|
||||
player.getInventory().addItem(newReliquary);
|
||||
resultItems.add(newReliquary.getGuid());
|
||||
}
|
||||
|
||||
// Send packet.
|
||||
player.sendPacket(new PacketReliquaryDecomposeRsp(resultItems));
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user