Fix whitespace [skip actions]

This commit is contained in:
github-actions 2022-07-21 07:21:22 +00:00 committed by Melledy
parent 510d564bcb
commit ae2d1fe438
166 changed files with 12928 additions and 12928 deletions

View File

@ -6,29 +6,29 @@ import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils;
public final class GameConstants {
public static String VERSION = "2.8.0";
public static String VERSION = "2.8.0";
public static final int MAX_TEAMS = 4;
public static final int MAIN_CHARACTER_MALE = 10000005;
public static final int MAIN_CHARACTER_FEMALE = 10000007;
public static final Position START_POSITION = new Position(2747, 194, -1719);
public static final int MAX_TEAMS = 4;
public static final int MAIN_CHARACTER_MALE = 10000005;
public static final int MAIN_CHARACTER_FEMALE = 10000007;
public static final Position START_POSITION = new Position(2747, 194, -1719);
public static final int MAX_FRIENDS = 45;
public static final int MAX_FRIEND_REQUESTS = 50;
public static final int MAX_FRIENDS = 45;
public static final int MAX_FRIEND_REQUESTS = 50;
public static final int SERVER_CONSOLE_UID = 99; // The UID of the server console's "player".
public static final int SERVER_CONSOLE_UID = 99; // The UID of the server console's "player".
public static final int BATTLE_PASS_MAX_LEVEL = 50;
public static final int BATTLE_PASS_POINT_PER_LEVEL = 1000;
public static final int BATTLE_PASS_POINT_PER_WEEK = 10000;
public static final int BATTLE_PASS_LEVEL_PRICE = 150;
public static final int BATTLE_PASS_CURRENT_INDEX = 2;
public static final int BATTLE_PASS_MAX_LEVEL = 50;
public static final int BATTLE_PASS_POINT_PER_LEVEL = 1000;
public static final int BATTLE_PASS_POINT_PER_WEEK = 10000;
public static final int BATTLE_PASS_LEVEL_PRICE = 150;
public static final int BATTLE_PASS_CURRENT_INDEX = 2;
// Default entity ability hashes.
public static final String[] DEFAULT_ABILITY_STRINGS = {
"Avatar_DefaultAbility_VisionReplaceDieInvincible", "Avatar_DefaultAbility_AvartarInShaderChange", "Avatar_SprintBS_Invincible",
"Avatar_Freeze_Duration_Reducer", "Avatar_Attack_ReviveEnergy", "Avatar_Component_Initializer", "Avatar_FallAnthem_Achievement_Listener"
};
public static final int[] DEFAULT_ABILITY_HASHES = Arrays.stream(DEFAULT_ABILITY_STRINGS).mapToInt(Utils::abilityHash).toArray();
public static final int DEFAULT_ABILITY_NAME = Utils.abilityHash("Default");
// Default entity ability hashes.
public static final String[] DEFAULT_ABILITY_STRINGS = {
"Avatar_DefaultAbility_VisionReplaceDieInvincible", "Avatar_DefaultAbility_AvartarInShaderChange", "Avatar_SprintBS_Invincible",
"Avatar_Freeze_Duration_Reducer", "Avatar_Attack_ReviveEnergy", "Avatar_Component_Initializer", "Avatar_FallAnthem_Achievement_Listener"
};
public static final int[] DEFAULT_ABILITY_HASHES = Arrays.stream(DEFAULT_ABILITY_STRINGS).mapToInt(Utils::abilityHash).toArray();
public static final int DEFAULT_ABILITY_NAME = Utils.abilityHash("Default");
}

View File

@ -217,7 +217,7 @@ public final class Grasscutter {
*/
private static void onShutdown() {
// Disable all plugins.
if(pluginManager != null)
if (pluginManager != null)
pluginManager.disablePlugins();
}

View File

@ -21,7 +21,7 @@ public final class DefaultAuthentication implements AuthenticationSystem {
private OAuthAuthenticator oAuthAuthenticator = new OAuthAuthentication();
public DefaultAuthentication() {
if(ACCOUNT.EXPERIMENTAL_RealPassword) {
if (ACCOUNT.EXPERIMENTAL_RealPassword) {
passwordAuthenticator = new ExperimentalPasswordAuthenticator();
} else {
passwordAuthenticator = new PasswordAuthenticator();

View File

@ -167,7 +167,7 @@ public final class CommandMap {
CommandHandler.sendTranslatedMessage(player, "commands.execution.clear_target");
return true;
}
// Sets default targetPlayer to the UID provided.
try {
int uid = Integer.parseInt(targetUid);
@ -237,7 +237,7 @@ public final class CommandMap {
Command annotation = this.annotations.get(label);
// Resolve targetPlayer
try{
try {
targetPlayer = getTargetPlayer(playerId, player, targetPlayer, args);
} catch (IllegalArgumentException e) {
return;

View File

@ -39,8 +39,8 @@ public final class AccountCommand implements CommandHandler {
int uid = 0;
String password = "";
if(Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) {
if(args.size() < 3) {
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) {
if (args.size() < 3) {
CommandHandler.sendMessage(null, "EXPERIMENTAL_RealPassword requires a password argument");
CommandHandler.sendMessage(null, "Usage: account create <username> <password> [uid]");
@ -53,7 +53,7 @@ public final class AccountCommand implements CommandHandler {
uid = Integer.parseInt(args.get(3));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(null, translate(sender, "commands.account.invalid"));
if(Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) {
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) {
CommandHandler.sendMessage(null, "EXPERIMENTAL_RealPassword requires argument 2 to be a password, not a uid");
CommandHandler.sendMessage(null, "Usage: account create <username> <password> [uid]");
}
@ -76,7 +76,7 @@ public final class AccountCommand implements CommandHandler {
CommandHandler.sendMessage(null, translate(sender, "commands.account.exists"));
return;
} else {
if(Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) {
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) {
account.setPassword(BCrypt.withDefaults().hashToString(12, password.toCharArray()));
}
account.addPermission("*");
@ -93,7 +93,7 @@ public final class AccountCommand implements CommandHandler {
CommandHandler.sendMessage(null, translate(sender, "commands.account.no_account"));
return;
}
// Get the player for the account.
// If that player is currently online, we kick them before proceeding with the deletion.
Player player = Grasscutter.getGameServer().getPlayerByAccountId(toDelete.getId());
@ -106,12 +106,12 @@ public final class AccountCommand implements CommandHandler {
CommandHandler.sendMessage(null, translate(sender, "commands.account.delete"));
return;
case "resetpass":
if(Configuration.ACCOUNT.EXPERIMENTAL_RealPassword != true) {
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword != true) {
CommandHandler.sendMessage(null, "resetpass requires EXPERIMENTAL_RealPassword to be true.");
return;
}
if(args.size() != 3) {
if (args.size() != 3) {
CommandHandler.sendMessage(null, "Invalid Args");
CommandHandler.sendMessage(null, "Usage: account resetpass <username> <password>");
return;

View File

@ -28,7 +28,7 @@ public final class AnnounceCommand implements CommandHandler {
return;
}
switch (args.get(0)){
switch (args.get(0)) {
case "tpl":
if (args.size() < 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.announce.command_usage");
@ -37,7 +37,7 @@ public final class AnnounceCommand implements CommandHandler {
var templateId = Integer.parseInt(args.get(1));
var tpl = manager.getAnnounceConfigItemMap().get(templateId);
if(tpl == null){
if (tpl == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.announce.not_found", templateId));
return;
}

View File

@ -38,7 +38,7 @@ public final class CoopCommand implements CommandHandler {
CommandHandler.sendMessage(sender, translate(sender, "commands.coop.usage"));
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);

View File

@ -21,10 +21,10 @@ public final class EnterDungeonCommand implements CommandHandler {
try {
int dungeonId = Integer.parseInt(args.get(0));
if (dungeonId == targetPlayer.getSceneId()) {
CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.in_dungeon_error"));
return;
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) {

View File

@ -257,7 +257,7 @@ public final class GiveCommand implements CommandHandler {
if (avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_MALE) {
avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(504));
}
else if(avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_FEMALE) {
else if (avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_FEMALE) {
avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(704));
}
@ -352,11 +352,11 @@ public final class GiveCommand implements CommandHandler {
return affixes;
}
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);
} catch (NumberFormatException ignored) {
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);
} catch (NumberFormatException ignored) {
// If the argument was not an integer, we try to determine
// the append prop ID from the given text + artifact information.
// A substat string has the format `substat_tier`, with the
@ -378,8 +378,8 @@ public final class GiveCommand implements CommandHandler {
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 {
// Get the main stat from the arguments.
@ -476,11 +476,11 @@ public final class GiveCommand implements CommandHandler {
10000-10008, 11411, 11506-11508, 12505, 12506, 12508, 12509,
13503, 13506, 14411, 14503, 14505, 14508, 15504-15506
""");
private static final SparseSet illegalRelicIds = new SparseSet("""
20001, 23300-23340, 23383-23385, 78310-78554, 99310-99554
""");
private static final SparseSet illegalItemIds = new SparseSet("""
100086, 100087, 100100-101000, 101106-101110, 101306, 101500-104000,
105001, 105004, 106000-107000, 107011, 108000, 109000-110000,

View File

@ -22,13 +22,13 @@ public final class HelpCommand implements CommandHandler {
}
}
builder.append("\n\t").append(translate(player, "commands.help.tip_need_permission"));
if(annotation.permission().isEmpty() || annotation.permission().isBlank()) {
if (annotation.permission().isEmpty() || annotation.permission().isBlank()) {
builder.append(translate(player, "commands.help.tip_need_no_permission"));
} else {
builder.append(annotation.permission());
}
if(!annotation.permissionTargeted().isEmpty() && !annotation.permissionTargeted().isBlank()) {
if (!annotation.permissionTargeted().isEmpty() && !annotation.permissionTargeted().isBlank()) {
String permissionTargeted = annotation.permissionTargeted();
builder.append(" ").append(translate(player, "commands.help.tip_permission_targeted", permissionTargeted));
}

View File

@ -15,13 +15,13 @@ public final class ReloadCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
CommandHandler.sendMessage(sender, translate(sender, "commands.reload.reload_start"));
Grasscutter.loadConfig();
Grasscutter.loadLanguage();
Grasscutter.getGameServer().getGachaSystem().load();
Grasscutter.getGameServer().getDropSystem().load();
Grasscutter.getGameServer().getShopSystem().load();
CommandHandler.sendMessage(sender, translate(sender, "commands.reload.reload_done"));
}
}

View File

@ -27,7 +27,7 @@ public final class SendMailCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
int senderId;
if(sender != null) {
if (sender != null) {
senderId = sender.getUid();
} else {
senderId = -1;
@ -162,7 +162,7 @@ public final class SendMailCommand implements CommandHandler {
}
private String getConstructionArgs(int stage, Player sender) {
return switch(stage) {
return switch (stage) {
case 0 -> translate(sender, "commands.sendMail.title");
case 1 -> translate(sender, "commands.sendMail.message");
case 2 -> translate(sender, "commands.sendMail.sender");

View File

@ -1,205 +1,205 @@
package emu.grasscutter.command.commands;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.tower.TowerLevelRecord;
@Command(label = "setprop", usage = "setprop|prop <prop> <value>", aliases = {"prop"}, permission = "player.setprop", permissionTargeted = "player.setprop.others", description = "commands.setProp.description")
public final class SetPropCommand implements CommandHandler {
static enum PseudoProp {
NONE,
WORLD_LEVEL,
TOWER_LEVEL,
BP_LEVEL,
GOD_MODE,
NO_STAMINA,
UNLIMITED_ENERGY
}
static class Prop {
String name;
PlayerProperty prop;
PseudoProp pseudoProp;
public Prop(PlayerProperty prop) {
this(prop.toString(), prop, PseudoProp.NONE);
}
public Prop(String name) {
this(name, PlayerProperty.PROP_NONE, PseudoProp.NONE);
}
public Prop(String name, PseudoProp pseudoProp) {
this(name, PlayerProperty.PROP_NONE, pseudoProp);
}
public Prop(String name, PlayerProperty prop) {
this(name, prop, PseudoProp.NONE);
}
public Prop(String name, PlayerProperty prop, PseudoProp pseudoProp) {
this.name = name;
this.prop = prop;
this.pseudoProp = pseudoProp;
}
}
Map<String, Prop> props;
public SetPropCommand() {
this.props = new HashMap<>();
// Full PlayerProperty enum that won't be advertised but can be used by devs
for (PlayerProperty prop : PlayerProperty.values()) {
String name = prop.toString().substring(5); // PROP_EXP -> EXP
String key = name.toLowerCase(); // EXP -> exp
this.props.put(key, new Prop(name, prop));
}
// Add special props
Prop worldlevel = new Prop("World Level", PlayerProperty.PROP_PLAYER_WORLD_LEVEL, PseudoProp.WORLD_LEVEL);
this.props.put("worldlevel", worldlevel);
this.props.put("wl", worldlevel);
Prop abyss = new Prop("Tower Level", PseudoProp.TOWER_LEVEL);
this.props.put("abyss", abyss);
this.props.put("abyssfloor", abyss);
this.props.put("ut", abyss);
this.props.put("tower", abyss);
this.props.put("towerlevel", abyss);
this.props.put("unlocktower", abyss);
Prop bplevel = new Prop("BP Level", PseudoProp.BP_LEVEL);
this.props.put("bplevel", bplevel);
this.props.put("bp", bplevel);
this.props.put("battlepass", bplevel);
Prop godmode = new Prop("godmode", PseudoProp.GOD_MODE);
this.props.put("godmode", godmode);
this.props.put("god", godmode);
Prop nostamina = new Prop("nostamina", PseudoProp.NO_STAMINA);
this.props.put("nostamina", nostamina);
this.props.put("nostam", nostamina);
this.props.put("ns", nostamina);
Prop unlimitedenergy = new Prop("unlimitedenergy", PseudoProp.UNLIMITED_ENERGY);
this.props.put("unlimitedenergy", unlimitedenergy);
this.props.put("ue", unlimitedenergy);
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.setProp.usage");
return;
}
String propStr = args.get(0).toLowerCase();
String valueStr = args.get(1).toLowerCase();
int value;
if (!props.containsKey(propStr)) {
CommandHandler.sendTranslatedMessage(sender, "commands.setProp.usage");
return;
}
try {
value = switch(valueStr.toLowerCase()) {
case "on", "true" -> 1;
case "off", "false" -> 0;
case "toggle" -> -1;
default -> Integer.parseInt(valueStr);
};
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error");
return;
}
boolean success = false;
Prop prop = props.get(propStr);
success = switch (prop.pseudoProp) {
case WORLD_LEVEL -> targetPlayer.setWorldLevel(value);
case BP_LEVEL -> targetPlayer.getBattlePassManager().setLevel(value);
case TOWER_LEVEL -> this.setTowerLevel(sender, targetPlayer, value);
case GOD_MODE, NO_STAMINA, UNLIMITED_ENERGY -> this.setBool(sender, targetPlayer, prop.pseudoProp, value);
default -> targetPlayer.setProperty(prop.prop, value);
};
if (success) {
if (targetPlayer == sender) {
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_to", prop.name, valueStr);
} else {
String uidStr = targetPlayer.getAccount().getId();
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_for_to", prop.name, uidStr, valueStr);
}
} else {
if (prop.prop != PlayerProperty.PROP_NONE) { // PseudoProps need to do their own error messages
String min = Integer.toString(targetPlayer.getPropertyMin(prop.prop));
String max = Integer.toString(targetPlayer.getPropertyMax(prop.prop));
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.value_between", prop.name, min, max);
}
}
}
private boolean setTowerLevel(Player sender, Player targetPlayer, int topFloor) {
List<Integer> floorIds = targetPlayer.getServer().getTowerSystem().getAllFloors();
if (topFloor < 0 || topFloor > floorIds.size()) {
String min = Integer.toString(0);
String max = Integer.toString(floorIds.size());
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.value_between", "Tower Level", min, max);
return false;
}
Map<Integer, TowerLevelRecord> recordMap = targetPlayer.getTowerManager().getRecordMap();
// Add records for each unlocked floor
for (int floor : floorIds.subList(0, topFloor)) {
if (!recordMap.containsKey(floor)) {
recordMap.put(floor, new TowerLevelRecord(floor));
}
}
// Remove records for each floor past our target
for (int floor : floorIds.subList(topFloor, floorIds.size())) {
if (recordMap.containsKey(floor)) {
recordMap.remove(floor);
}
}
// Six stars required on Floor 8 to unlock Floor 9+
if (topFloor > 8) {
recordMap.get(floorIds.get(7)).setLevelStars(0, 6); // levelIds seem to start at 1 for Floor 1 Chamber 1, so this doesn't get shown at all
}
return true;
}
private boolean setBool(Player sender, Player targetPlayer, PseudoProp pseudoProp, int value) {
boolean enabled = switch (pseudoProp) {
case GOD_MODE -> targetPlayer.inGodmode();
case NO_STAMINA -> targetPlayer.getUnlimitedStamina();
case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().getEnergyUsage();
default -> false;
};
enabled = switch (value) {
case -1 -> !enabled;
case 0 -> false;
default -> true;
};
switch (pseudoProp) {
case GOD_MODE:
targetPlayer.setGodmode(enabled);
break;
case NO_STAMINA:
targetPlayer.setUnlimitedStamina(enabled);
break;
case UNLIMITED_ENERGY:
targetPlayer.getEnergyManager().setEnergyUsage(!enabled);
break;
default:
return false;
}
return true;
}
}
package emu.grasscutter.command.commands;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.tower.TowerLevelRecord;
@Command(label = "setprop", usage = "setprop|prop <prop> <value>", aliases = {"prop"}, permission = "player.setprop", permissionTargeted = "player.setprop.others", description = "commands.setProp.description")
public final class SetPropCommand implements CommandHandler {
static enum PseudoProp {
NONE,
WORLD_LEVEL,
TOWER_LEVEL,
BP_LEVEL,
GOD_MODE,
NO_STAMINA,
UNLIMITED_ENERGY
}
static class Prop {
String name;
PlayerProperty prop;
PseudoProp pseudoProp;
public Prop(PlayerProperty prop) {
this(prop.toString(), prop, PseudoProp.NONE);
}
public Prop(String name) {
this(name, PlayerProperty.PROP_NONE, PseudoProp.NONE);
}
public Prop(String name, PseudoProp pseudoProp) {
this(name, PlayerProperty.PROP_NONE, pseudoProp);
}
public Prop(String name, PlayerProperty prop) {
this(name, prop, PseudoProp.NONE);
}
public Prop(String name, PlayerProperty prop, PseudoProp pseudoProp) {
this.name = name;
this.prop = prop;
this.pseudoProp = pseudoProp;
}
}
Map<String, Prop> props;
public SetPropCommand() {
this.props = new HashMap<>();
// Full PlayerProperty enum that won't be advertised but can be used by devs
for (PlayerProperty prop : PlayerProperty.values()) {
String name = prop.toString().substring(5); // PROP_EXP -> EXP
String key = name.toLowerCase(); // EXP -> exp
this.props.put(key, new Prop(name, prop));
}
// Add special props
Prop worldlevel = new Prop("World Level", PlayerProperty.PROP_PLAYER_WORLD_LEVEL, PseudoProp.WORLD_LEVEL);
this.props.put("worldlevel", worldlevel);
this.props.put("wl", worldlevel);
Prop abyss = new Prop("Tower Level", PseudoProp.TOWER_LEVEL);
this.props.put("abyss", abyss);
this.props.put("abyssfloor", abyss);
this.props.put("ut", abyss);
this.props.put("tower", abyss);
this.props.put("towerlevel", abyss);
this.props.put("unlocktower", abyss);
Prop bplevel = new Prop("BP Level", PseudoProp.BP_LEVEL);
this.props.put("bplevel", bplevel);
this.props.put("bp", bplevel);
this.props.put("battlepass", bplevel);
Prop godmode = new Prop("godmode", PseudoProp.GOD_MODE);
this.props.put("godmode", godmode);
this.props.put("god", godmode);
Prop nostamina = new Prop("nostamina", PseudoProp.NO_STAMINA);
this.props.put("nostamina", nostamina);
this.props.put("nostam", nostamina);
this.props.put("ns", nostamina);
Prop unlimitedenergy = new Prop("unlimitedenergy", PseudoProp.UNLIMITED_ENERGY);
this.props.put("unlimitedenergy", unlimitedenergy);
this.props.put("ue", unlimitedenergy);
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.setProp.usage");
return;
}
String propStr = args.get(0).toLowerCase();
String valueStr = args.get(1).toLowerCase();
int value;
if (!props.containsKey(propStr)) {
CommandHandler.sendTranslatedMessage(sender, "commands.setProp.usage");
return;
}
try {
value = switch (valueStr.toLowerCase()) {
case "on", "true" -> 1;
case "off", "false" -> 0;
case "toggle" -> -1;
default -> Integer.parseInt(valueStr);
};
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error");
return;
}
boolean success = false;
Prop prop = props.get(propStr);
success = switch (prop.pseudoProp) {
case WORLD_LEVEL -> targetPlayer.setWorldLevel(value);
case BP_LEVEL -> targetPlayer.getBattlePassManager().setLevel(value);
case TOWER_LEVEL -> this.setTowerLevel(sender, targetPlayer, value);
case GOD_MODE, NO_STAMINA, UNLIMITED_ENERGY -> this.setBool(sender, targetPlayer, prop.pseudoProp, value);
default -> targetPlayer.setProperty(prop.prop, value);
};
if (success) {
if (targetPlayer == sender) {
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_to", prop.name, valueStr);
} else {
String uidStr = targetPlayer.getAccount().getId();
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_for_to", prop.name, uidStr, valueStr);
}
} else {
if (prop.prop != PlayerProperty.PROP_NONE) { // PseudoProps need to do their own error messages
String min = Integer.toString(targetPlayer.getPropertyMin(prop.prop));
String max = Integer.toString(targetPlayer.getPropertyMax(prop.prop));
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.value_between", prop.name, min, max);
}
}
}
private boolean setTowerLevel(Player sender, Player targetPlayer, int topFloor) {
List<Integer> floorIds = targetPlayer.getServer().getTowerSystem().getAllFloors();
if (topFloor < 0 || topFloor > floorIds.size()) {
String min = Integer.toString(0);
String max = Integer.toString(floorIds.size());
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.value_between", "Tower Level", min, max);
return false;
}
Map<Integer, TowerLevelRecord> recordMap = targetPlayer.getTowerManager().getRecordMap();
// Add records for each unlocked floor
for (int floor : floorIds.subList(0, topFloor)) {
if (!recordMap.containsKey(floor)) {
recordMap.put(floor, new TowerLevelRecord(floor));
}
}
// Remove records for each floor past our target
for (int floor : floorIds.subList(topFloor, floorIds.size())) {
if (recordMap.containsKey(floor)) {
recordMap.remove(floor);
}
}
// Six stars required on Floor 8 to unlock Floor 9+
if (topFloor > 8) {
recordMap.get(floorIds.get(7)).setLevelStars(0, 6); // levelIds seem to start at 1 for Floor 1 Chamber 1, so this doesn't get shown at all
}
return true;
}
private boolean setBool(Player sender, Player targetPlayer, PseudoProp pseudoProp, int value) {
boolean enabled = switch (pseudoProp) {
case GOD_MODE -> targetPlayer.inGodmode();
case NO_STAMINA -> targetPlayer.getUnlimitedStamina();
case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().getEnergyUsage();
default -> false;
};
enabled = switch (value) {
case -1 -> !enabled;
case 0 -> false;
default -> true;
};
switch (pseudoProp) {
case GOD_MODE:
targetPlayer.setGodmode(enabled);
break;
case NO_STAMINA:
targetPlayer.setUnlimitedStamina(enabled);
break;
case UNLIMITED_ENERGY:
targetPlayer.getEnergyManager().setEnergyUsage(!enabled);
break;
default:
return false;
}
return true;
}
}

View File

@ -31,7 +31,7 @@ public final class SpawnCommand implements CommandHandler {
int id = 0; // This is just to shut up the linter, it's not a real default
int amount = 1;
int level = 1;
float x = 0, y = 0, z = 0;
float x = 0, y = 0, z = 0;
switch (args.size()) {
case 6:
try {
@ -64,7 +64,7 @@ public final class SpawnCommand implements CommandHandler {
CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.usage"));
return;
}
MonsterData monsterData = GameData.getMonsterDataMap().get(id);
GadgetData gadgetData = GameData.getGadgetDataMap().get(id);
ItemData itemData = GameData.getItemDataMap().get(id);
@ -72,21 +72,21 @@ public final class SpawnCommand implements CommandHandler {
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.entityId"));
return;
}
Scene scene = targetPlayer.getScene();
if (scene.getEntities().size() + amount > GAME_OPTIONS.sceneEntityLimit) {
amount = Math.max(Math.min(GAME_OPTIONS.sceneEntityLimit - scene.getEntities().size(), amount), 0);
CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.limit_reached", amount));
if (amount <= 0) {
return;
}
amount = Math.max(Math.min(GAME_OPTIONS.sceneEntityLimit - scene.getEntities().size(), amount), 0);
CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.limit_reached", amount));
if (amount <= 0) {
return;
}
}
double maxRadius = Math.sqrt(amount * 0.2 / Math.PI);
for (int i = 0; i < amount; i++) {
Position pos = GetRandomPositionInCircle(targetPlayer.getPosition(), maxRadius).addY(3);
if(x != 0 && y != 0 && z != 0) {
if (x != 0 && y != 0 && z != 0) {
pos = GetRandomPositionInCircle(new Position(x, y, z), maxRadius).addY(3);
}
GameEntity entity = null;
@ -120,7 +120,7 @@ public final class SpawnCommand implements CommandHandler {
CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.success", Integer.toString(amount), Integer.toString(id)));
}
private Position GetRandomPositionInCircle(Position origin, double radius){
private Position GetRandomPositionInCircle(Position origin, double radius) {
Position target = origin.clone();
double angle = Math.random() * 360;
double r = Math.sqrt(Math.random() * radius * radius);

View File

@ -1,255 +1,255 @@
package emu.grasscutter.command.commands;
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.List;
import static emu.grasscutter.config.Configuration.*;
import java.util.ArrayList;
import java.util.HashSet;
@Command(label = "team", usage = "team <add|remove|set> [avatarId,...] [index|first|last|index-index,...]",
permission = "player.team", permissionTargeted = "player.team.others", description = "commands.team.description")
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()) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.usage");
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");
CommandHandler.sendTranslatedMessage(sender, "commands.team.usage");
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");
CommandHandler.sendTranslatedMessage(sender, "commands.team.add_usage");
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);
var success = addAvatar(sender, targetPlayer, id, index);
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");
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_usage");
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");
CommandHandler.sendTranslatedMessage(sender, "commands.team.set_usage");
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 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.List;
import static emu.grasscutter.config.Configuration.*;
import java.util.ArrayList;
import java.util.HashSet;
@Command(label = "team", usage = "team <add|remove|set> [avatarId,...] [index|first|last|index-index,...]",
permission = "player.team", permissionTargeted = "player.team.others", description = "commands.team.description")
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()) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.usage");
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");
CommandHandler.sendTranslatedMessage(sender, "commands.team.usage");
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");
CommandHandler.sendTranslatedMessage(sender, "commands.team.add_usage");
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);
var success = addAvatar(sender, targetPlayer, id, index);
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");
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_usage");
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");
CommandHandler.sendTranslatedMessage(sender, "commands.team.set_usage");
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;
}
}
}

View File

@ -19,7 +19,7 @@ public final class TeleportAllCommand implements CommandHandler {
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.error"));
return;
}
for (Player player : targetPlayer.getWorld().getPlayers()) {
if (player.equals(targetPlayer))
continue;
@ -27,7 +27,7 @@ public final class TeleportAllCommand implements CommandHandler {
player.getWorld().transferPlayerToScene(player, targetPlayer.getSceneId(), pos);
}
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.success"));
}
}

View File

@ -58,8 +58,8 @@ public final class TeleportCommand implements CommandHandler {
if (!result) {
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.exists_error"));
} else {
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.success",
targetPlayer.getNickname(), Float.toString(x), Float.toString(y),
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.success",
targetPlayer.getNickname(), Float.toString(x), Float.toString(y),
Float.toString(z), Integer.toString(sceneId))
);
}

View File

@ -28,7 +28,7 @@ public class ConfigContainer {
try { // Check if the server is using a legacy config.
JsonObject configObject = Grasscutter.getGsonFactory()
.fromJson(new FileReader(Grasscutter.configFile), JsonObject.class);
if(!configObject.has("version")) {
if (!configObject.has("version")) {
Grasscutter.getLogger().info("Updating legacy ..");
Grasscutter.saveConfig(null);
}
@ -37,7 +37,7 @@ public class ConfigContainer {
var existing = config.version;
var latest = version();
if(existing == latest)
if (existing == latest)
return;
// Create a new configuration instance.
@ -135,20 +135,20 @@ public class ConfigContainer {
public static class Game {
public String bindAddress = "0.0.0.0";
public int bindPort = 22102;
/* This is the address used in the default region. */
public String accessAddress = "127.0.0.1";
/* This is the port used in the default region. */
public int accessPort = 0;
/* Entities within a certain range will be loaded for the player */
public int loadEntitiesForPlayerRange = 100;
public boolean enableScriptInBigWorld = false;
public boolean enableConsole = true;
/* Controls whether packets should be logged in console or not */
public ServerDebugMode logPackets = ServerDebugMode.NONE;
public GameOptions gameOptions = new GameOptions();
public JoinOptions joinOptions = new JoinOptions();
public ConsoleAccount serverAccount = new ConsoleAccount();
@ -160,7 +160,7 @@ public class ConfigContainer {
public Region[] regions = {};
public String defaultName = "Grasscutter";
public ServerDebugMode logRequests = ServerDebugMode.NONE;
}
@ -276,4 +276,4 @@ public class ConfigContainer {
public String Ip = "127.0.0.1";
public int Port = 22102;
}
}
}

View File

@ -9,19 +9,19 @@ import static emu.grasscutter.Grasscutter.config;
/**
* A data container for the server's configuration.
*
*
* Use `import static emu.grasscutter.Configuration.*;`
* to import all configuration constants.
*/
public final class Configuration extends ConfigContainer {
/*
* Constants
*/
// 'c' is short for 'config' and makes code look 'cleaner'.
public static final ConfigContainer c = config;
public static final Locale LANGUAGE = config.language.language;
public static final Locale FALLBACK_LANGUAGE = config.language.fallback;
public static final String DOCUMENT_LANGUAGE = config.language.document;
@ -30,22 +30,22 @@ public final class Configuration extends ConfigContainer {
private static final String PLUGINS_FOLDER = config.folderStructure.plugins;
private static final String SCRIPTS_FOLDER = config.folderStructure.scripts;
private static final String PACKETS_FOLDER = config.folderStructure.packets;
public static final Server SERVER = config.server;
public static final Database DATABASE = config.databaseInfo;
public static final Account ACCOUNT = config.account;
public static final HTTP HTTP_INFO = config.server.http;
public static final Game GAME_INFO = config.server.game;
public static final Dispatch DISPATCH_INFO = config.server.dispatch;
public static final Encryption HTTP_ENCRYPTION = config.server.http.encryption;
public static final Policies HTTP_POLICIES = config.server.http.policies;
public static final Files HTTP_STATIC_FILES = config.server.http.files;
public static final GameOptions GAME_OPTIONS = config.server.game.gameOptions;
public static final GameOptions.InventoryLimits INVENTORY_LIMITS = config.server.game.gameOptions.inventoryLimits;
/*
* Utilities
*/
@ -56,11 +56,11 @@ public final class Configuration extends ConfigContainer {
public static String DATA(String path) {
return Paths.get(DATA_FOLDER, path).toString();
}
public static String RESOURCE(String path) {
return Paths.get(RESOURCES_FOLDER, path).toString();
}
public static String PLUGIN() {
return PLUGINS_FOLDER;
}

View File

@ -29,7 +29,7 @@ public class DataLoader {
public static InputStream load(String resourcePath) throws FileNotFoundException {
return load(resourcePath, true);
}
/**
* Creates an input stream reader for a data file. If the file isn't found within the /data directory then it will fallback to the default within the jar resources
*

View File

@ -22,47 +22,47 @@ public class GameDepot {
private static Int2ObjectMap<List<ReliquaryMainPropData>> relicMainPropDepot = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<List<ReliquaryAffixData>> relicAffixDepot = new Int2ObjectOpenHashMap<>();
private static Map<String, AvatarConfig> playerAbilities = new HashMap<>();
private static Map<String, AvatarConfig> playerAbilities = new HashMap<>();
private static HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> spawnLists = new HashMap<>();
public static void load() {
for (ReliquaryMainPropData data : GameData.getReliquaryMainPropDataMap().values()) {
if (data.getWeight() <= 0 || data.getPropDepotId() <= 0) {
continue;
}
for (ReliquaryMainPropData data : GameData.getReliquaryMainPropDataMap().values()) {
if (data.getWeight() <= 0 || data.getPropDepotId() <= 0) {
continue;
}
List<ReliquaryMainPropData> list = relicMainPropDepot.computeIfAbsent(data.getPropDepotId(), k -> new ArrayList<>());
list.add(data);
WeightedList<ReliquaryMainPropData> weightedList = relicRandomMainPropDepot.computeIfAbsent(data.getPropDepotId(), k -> new WeightedList<>());
weightedList.add(data.getWeight(), data);
}
for (ReliquaryAffixData data : GameData.getReliquaryAffixDataMap().values()) {
if (data.getWeight() <= 0 || data.getDepotId() <= 0) {
continue;
}
List<ReliquaryAffixData> list = relicAffixDepot.computeIfAbsent(data.getDepotId(), k -> new ArrayList<>());
list.add(data);
}
// Let the server owner know if theyre missing weights
if (relicMainPropDepot.size() == 0 || relicAffixDepot.size() == 0) {
Grasscutter.getLogger().error("Relic properties are missing weights! Please check your ReliquaryMainPropExcelConfigData or ReliquaryAffixExcelConfigData files in your ExcelBinOutput folder.");
}
}
}
for (ReliquaryAffixData data : GameData.getReliquaryAffixDataMap().values()) {
if (data.getWeight() <= 0 || data.getDepotId() <= 0) {
continue;
}
List<ReliquaryAffixData> list = relicAffixDepot.computeIfAbsent(data.getDepotId(), k -> new ArrayList<>());
list.add(data);
}
// Let the server owner know if theyre missing weights
if (relicMainPropDepot.size() == 0 || relicAffixDepot.size() == 0) {
Grasscutter.getLogger().error("Relic properties are missing weights! Please check your ReliquaryMainPropExcelConfigData or ReliquaryAffixExcelConfigData files in your ExcelBinOutput folder.");
}
}
public static ReliquaryMainPropData getRandomRelicMainProp(int depot) {
public static ReliquaryMainPropData getRandomRelicMainProp(int depot) {
WeightedList<ReliquaryMainPropData> depotList = relicRandomMainPropDepot.get(depot);
if (depotList == null) {
return null;
}
return depotList.next();
}
if (depotList == null) {
return null;
}
return depotList.next();
}
public static List<ReliquaryMainPropData> getRelicMainPropList(int depot) {
return relicMainPropDepot.get(depot);
}
public static List<ReliquaryAffixData> getRelicAffixList(int depot) {
return relicAffixDepot.get(depot);
}
return relicAffixDepot.get(depot);
}
public static HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> getSpawnLists() {
return spawnLists;
@ -72,9 +72,9 @@ public class GameDepot {
spawnLists.putAll(data);
}
public static void setPlayerAbilities(Map<String, AvatarConfig> playerAbilities) {
GameDepot.playerAbilities = playerAbilities;
}
public static void setPlayerAbilities(Map<String, AvatarConfig> playerAbilities) {
GameDepot.playerAbilities = playerAbilities;
}
public static Map<String, AvatarConfig> getPlayerAbilities() {
return playerAbilities;

View File

@ -34,281 +34,281 @@ import static emu.grasscutter.utils.Language.translate;
public class ResourceLoader {
private static final List<String> loadedResources = new ArrayList<>();
private static final List<String> loadedResources = new ArrayList<>();
public static List<Class<?>> getResourceDefClasses() {
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
public static List<Class<?>> getResourceDefClasses() {
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
List<Class<?>> classList = new ArrayList<>(classes.size());
classes.forEach(o -> {
Class<?> c = (Class<?>) o;
if (c.getAnnotation(ResourceType.class) != null) {
classList.add(c);
}
});
List<Class<?>> classList = new ArrayList<>(classes.size());
classes.forEach(o -> {
Class<?> c = (Class<?>) o;
if (c.getAnnotation(ResourceType.class) != null) {
classList.add(c);
}
});
classList.sort((a, b) -> b.getAnnotation(ResourceType.class).loadPriority().value() - a.getAnnotation(ResourceType.class).loadPriority().value());
classList.sort((a, b) -> b.getAnnotation(ResourceType.class).loadPriority().value() - a.getAnnotation(ResourceType.class).loadPriority().value());
return classList;
}
return classList;
}
public static void loadAll() {
public static void loadAll() {
Grasscutter.getLogger().info(translate("messages.status.resources.loading"));
// Load ability lists
loadAbilityEmbryos();
loadOpenConfig();
loadAbilityModifiers();
// Load resources
loadResources();
// Process into depots
GameDepot.load();
// Load spawn data and quests
loadSpawnData();
loadQuests();
// Load scene points - must be done AFTER resources are loaded
loadScenePoints();
// Load default home layout
loadHomeworldDefaultSaveData();
loadNpcBornData();
// Load ability lists
loadAbilityEmbryos();
loadOpenConfig();
loadAbilityModifiers();
// Load resources
loadResources();
// Process into depots
GameDepot.load();
// Load spawn data and quests
loadSpawnData();
loadQuests();
// Load scene points - must be done AFTER resources are loaded
loadScenePoints();
// Load default home layout
loadHomeworldDefaultSaveData();
loadNpcBornData();
Grasscutter.getLogger().info(translate("messages.status.resources.finish"));
}
}
public static void loadResources() {
loadResources(false);
}
public static void loadResources() {
loadResources(false);
}
public static void loadResources(boolean doReload) {
for (Class<?> resourceDefinition : getResourceDefClasses()) {
ResourceType type = resourceDefinition.getAnnotation(ResourceType.class);
public static void loadResources(boolean doReload) {
for (Class<?> resourceDefinition : getResourceDefClasses()) {
ResourceType type = resourceDefinition.getAnnotation(ResourceType.class);
if (type == null) {
continue;
}
if (type == null) {
continue;
}
@SuppressWarnings("rawtypes")
Int2ObjectMap map = GameData.getMapByResourceDef(resourceDefinition);
@SuppressWarnings("rawtypes")
Int2ObjectMap map = GameData.getMapByResourceDef(resourceDefinition);
if (map == null) {
continue;
}
if (map == null) {
continue;
}
try {
loadFromResource(resourceDefinition, type, map, doReload);
} catch (Exception e) {
Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e);
}
}
}
try {
loadFromResource(resourceDefinition, type, map, doReload);
} catch (Exception e) {
Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e);
}
}
}
@SuppressWarnings("rawtypes")
protected static void loadFromResource(Class<?> c, ResourceType type, Int2ObjectMap map, boolean doReload) throws Exception {
if(!loadedResources.contains(c.getSimpleName()) || doReload) {
for (String name : type.name()) {
loadFromResource(c, name, map);
}
loadedResources.add(c.getSimpleName());
@SuppressWarnings("rawtypes")
protected static void loadFromResource(Class<?> c, ResourceType type, Int2ObjectMap map, boolean doReload) throws Exception {
if (!loadedResources.contains(c.getSimpleName()) || doReload) {
for (String name : type.name()) {
loadFromResource(c, name, map);
}
loadedResources.add(c.getSimpleName());
Grasscutter.getLogger().debug("Loaded " + map.size() + " " + c.getSimpleName() + "s.");
}
}
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
protected static void loadFromResource(Class<?> c, String fileName, Int2ObjectMap map) throws Exception {
try (FileReader fileReader = new FileReader(RESOURCE("ExcelBinOutput/" + fileName))) {
List list = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, c).getType());
@SuppressWarnings({"rawtypes", "unchecked"})
protected static void loadFromResource(Class<?> c, String fileName, Int2ObjectMap map) throws Exception {
try (FileReader fileReader = new FileReader(RESOURCE("ExcelBinOutput/" + fileName))) {
List list = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, c).getType());
for (Object o : list) {
GameResource res = (GameResource) o;
res.onLoad();
map.put(res.getId(), res);
}
}
}
for (Object o : list) {
GameResource res = (GameResource) o;
res.onLoad();
map.put(res.getId(), res);
}
}
}
private static void loadScenePoints() {
Pattern pattern = Pattern.compile("(?<=scene)(.*?)(?=_point.json)");
File folder = new File(RESOURCE("BinOutput/Scene/Point"));
private static void loadScenePoints() {
Pattern pattern = Pattern.compile("(?<=scene)(.*?)(?=_point.json)");
File folder = new File(RESOURCE("BinOutput/Scene/Point"));
if (!folder.isDirectory() || !folder.exists() || folder.listFiles() == null) {
Grasscutter.getLogger().error("Scene point files cannot be found, you cannot use teleport waypoints!");
return;
}
if (!folder.isDirectory() || !folder.exists() || folder.listFiles() == null) {
Grasscutter.getLogger().error("Scene point files cannot be found, you cannot use teleport waypoints!");
return;
}
List<ScenePointEntry> scenePointList = new ArrayList<>();
for (File file : Objects.requireNonNull(folder.listFiles())) {
ScenePointConfig config; Integer sceneId;
List<ScenePointEntry> scenePointList = new ArrayList<>();
for (File file : Objects.requireNonNull(folder.listFiles())) {
ScenePointConfig config; Integer sceneId;
Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {
sceneId = Integer.parseInt(matcher.group(1));
} else {
continue;
}
Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {
sceneId = Integer.parseInt(matcher.group(1));
} else {
continue;
}
try (FileReader fileReader = new FileReader(file)) {
config = Grasscutter.getGsonFactory().fromJson(fileReader, ScenePointConfig.class);
} catch (Exception e) {
e.printStackTrace();
continue;
}
try (FileReader fileReader = new FileReader(file)) {
config = Grasscutter.getGsonFactory().fromJson(fileReader, ScenePointConfig.class);
} catch (Exception e) {
e.printStackTrace();
continue;
}
if (config.points == null) {
continue;
}
if (config.points == null) {
continue;
}
for (Map.Entry<String, JsonElement> entry : config.points.entrySet()) {
PointData pointData = Grasscutter.getGsonFactory().fromJson(entry.getValue(), PointData.class);
pointData.setId(Integer.parseInt(entry.getKey()));
for (Map.Entry<String, JsonElement> entry : config.points.entrySet()) {
PointData pointData = Grasscutter.getGsonFactory().fromJson(entry.getValue(), PointData.class);
pointData.setId(Integer.parseInt(entry.getKey()));
ScenePointEntry sl = new ScenePointEntry(sceneId + "_" + entry.getKey(), pointData);
scenePointList.add(sl);
GameData.getScenePointIdList().add(pointData.getId());
ScenePointEntry sl = new ScenePointEntry(sceneId + "_" + entry.getKey(), pointData);
scenePointList.add(sl);
GameData.getScenePointIdList().add(pointData.getId());
pointData.updateDailyDungeon();
}
pointData.updateDailyDungeon();
}
for (ScenePointEntry entry : scenePointList) {
GameData.getScenePointEntries().put(entry.getName(), entry);
}
}
}
for (ScenePointEntry entry : scenePointList) {
GameData.getScenePointEntries().put(entry.getName(), entry);
}
}
}
private static void loadAbilityEmbryos() {
List<AbilityEmbryoEntry> embryoList = null;
private static void loadAbilityEmbryos() {
List<AbilityEmbryoEntry> embryoList = null;
// Read from cached file if exists
try (InputStream embryoCache = DataLoader.load("AbilityEmbryos.json", false)) {
embryoList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(embryoCache), TypeToken.getParameterized(Collection.class, AbilityEmbryoEntry.class).getType());
} catch (Exception ignored) {}
// Read from cached file if exists
try (InputStream embryoCache = DataLoader.load("AbilityEmbryos.json", false)) {
embryoList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(embryoCache), TypeToken.getParameterized(Collection.class, AbilityEmbryoEntry.class).getType());
} catch (Exception ignored) {}
if(embryoList == null) {
// Load from BinOutput
Pattern pattern = Pattern.compile("(?<=ConfigAvatar_)(.*?)(?=.json)");
if (embryoList == null) {
// Load from BinOutput
Pattern pattern = Pattern.compile("(?<=ConfigAvatar_)(.*?)(?=.json)");
embryoList = new LinkedList<>();
File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Avatar/")));
File[] files = folder.listFiles();
if(files == null) {
Grasscutter.getLogger().error("Error loading ability embryos: no files found in " + folder.getAbsolutePath());
return;
}
embryoList = new LinkedList<>();
File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Avatar/")));
File[] files = folder.listFiles();
if (files == null) {
Grasscutter.getLogger().error("Error loading ability embryos: no files found in " + folder.getAbsolutePath());
return;
}
for (File file : files) {
AvatarConfig config;
String avatarName;
for (File file : files) {
AvatarConfig config;
String avatarName;
Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {
avatarName = matcher.group(0);
} else {
continue;
}
Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {
avatarName = matcher.group(0);
} else {
continue;
}
try (FileReader fileReader = new FileReader(file)) {
config = Grasscutter.getGsonFactory().fromJson(fileReader, AvatarConfig.class);
} catch (Exception e) {
e.printStackTrace();
continue;
}
try (FileReader fileReader = new FileReader(file)) {
config = Grasscutter.getGsonFactory().fromJson(fileReader, AvatarConfig.class);
} catch (Exception e) {
e.printStackTrace();
continue;
}
if (config.abilities == null) {
continue;
}
if (config.abilities == null) {
continue;
}
int s = config.abilities.size();
AbilityEmbryoEntry al = new AbilityEmbryoEntry(avatarName, config.abilities.stream().map(Object::toString).toArray(size -> new String[s]));
embryoList.add(al);
}
int s = config.abilities.size();
AbilityEmbryoEntry al = new AbilityEmbryoEntry(avatarName, config.abilities.stream().map(Object::toString).toArray(size -> new String[s]));
embryoList.add(al);
}
File playerElementsFile = new File(Utils.toFilePath(RESOURCE("BinOutput/AbilityGroup/AbilityGroup_Other_PlayerElementAbility.json")));
File playerElementsFile = new File(Utils.toFilePath(RESOURCE("BinOutput/AbilityGroup/AbilityGroup_Other_PlayerElementAbility.json")));
if (playerElementsFile.exists()) {
try (FileReader fileReader = new FileReader(playerElementsFile)) {
GameDepot.setPlayerAbilities(Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken<Map<String, AvatarConfig>>(){}.getType()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
if (playerElementsFile.exists()) {
try (FileReader fileReader = new FileReader(playerElementsFile)) {
GameDepot.setPlayerAbilities(Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken<Map<String, AvatarConfig>>(){}.getType()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
if (embryoList == null || embryoList.isEmpty()) {
Grasscutter.getLogger().error("No embryos loaded!");
return;
}
if (embryoList == null || embryoList.isEmpty()) {
Grasscutter.getLogger().error("No embryos loaded!");
return;
}
for (AbilityEmbryoEntry entry : embryoList) {
GameData.getAbilityEmbryoInfo().put(entry.getName(), entry);
}
}
for (AbilityEmbryoEntry entry : embryoList) {
GameData.getAbilityEmbryoInfo().put(entry.getName(), entry);
}
}
private static void loadAbilityModifiers() {
// Load from BinOutput
File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Ability/Temp/AvatarAbilities/")));
File[] files = folder.listFiles();
if (files == null) {
Grasscutter.getLogger().error("Error loading ability modifiers: no files found in " + folder.getAbsolutePath());
return;
}
private static void loadAbilityModifiers() {
// Load from BinOutput
File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Ability/Temp/AvatarAbilities/")));
File[] files = folder.listFiles();
if (files == null) {
Grasscutter.getLogger().error("Error loading ability modifiers: no files found in " + folder.getAbsolutePath());
return;
}
for (File file : files) {
List<AbilityConfigData> abilityConfigList;
for (File file : files) {
List<AbilityConfigData> abilityConfigList;
try (FileReader fileReader = new FileReader(file)) {
abilityConfigList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, AbilityConfigData.class).getType());
} catch (Exception e) {
e.printStackTrace();
continue;
}
try (FileReader fileReader = new FileReader(file)) {
abilityConfigList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, AbilityConfigData.class).getType());
} catch (Exception e) {
e.printStackTrace();
continue;
}
for (AbilityConfigData data : abilityConfigList) {
if (data.Default.modifiers == null || data.Default.modifiers.size() == 0) {
continue;
}
for (AbilityConfigData data : abilityConfigList) {
if (data.Default.modifiers == null || data.Default.modifiers.size() == 0) {
continue;
}
AbilityModifierEntry modifierEntry = new AbilityModifierEntry(data.Default.abilityName);
AbilityModifierEntry modifierEntry = new AbilityModifierEntry(data.Default.abilityName);
for (Entry<String, AbilityModifier> entry : data.Default.modifiers.entrySet()) {
AbilityModifier modifier = entry.getValue();
for (Entry<String, AbilityModifier> entry : data.Default.modifiers.entrySet()) {
AbilityModifier modifier = entry.getValue();
// Stare.
if (modifier.onAdded != null) {
for (AbilityModifierAction action : modifier.onAdded) {
if (action.$type.contains("HealHP")) {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnAdded().add(action);
}
}
}
// Stare.
if (modifier.onAdded != null) {
for (AbilityModifierAction action : modifier.onAdded) {
if (action.$type.contains("HealHP")) {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnAdded().add(action);
}
}
}
if (modifier.onThinkInterval != null) {
for (AbilityModifierAction action : modifier.onThinkInterval) {
if (action.$type.contains("HealHP")) {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnThinkInterval().add(action);
}
}
}
if (modifier.onThinkInterval != null) {
for (AbilityModifierAction action : modifier.onThinkInterval) {
if (action.$type.contains("HealHP")) {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnThinkInterval().add(action);
}
}
}
if (modifier.onRemoved != null) {
for (AbilityModifierAction action : modifier.onRemoved) {
if (action.$type.contains("HealHP")) {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnRemoved().add(action);
}
}
}
}
if (modifier.onRemoved != null) {
for (AbilityModifierAction action : modifier.onRemoved) {
if (action.$type.contains("HealHP")) {
action.type = AbilityModifierActionType.HealHP;
modifierEntry.getOnRemoved().add(action);
}
}
}
}
GameData.getAbilityModifiers().put(modifierEntry.getName(), modifierEntry);
}
}
}
GameData.getAbilityModifiers().put(modifierEntry.getName(), modifierEntry);
}
}
}
private static void loadSpawnData() {
String[] spawnDataNames = {"Spawns.json", "GadgetSpawns.json"};
ArrayList<SpawnGroupEntry> spawnEntryMap = new ArrayList<>();
private static void loadSpawnData() {
String[] spawnDataNames = {"Spawns.json", "GadgetSpawns.json"};
ArrayList<SpawnGroupEntry> spawnEntryMap = new ArrayList<>();
for (String name : spawnDataNames) {
// Load spawn entries from file
@ -321,10 +321,10 @@ public class ResourceLoader {
} catch (Exception ignored) {}
}
if (spawnEntryMap.isEmpty()) {
Grasscutter.getLogger().error("No spawn data loaded!");
return;
}
if (spawnEntryMap.isEmpty()) {
Grasscutter.getLogger().error("No spawn data loaded!");
return;
}
HashMap<GridBlockId, ArrayList<SpawnDataEntry>> areaSort = new HashMap<>();
//key = sceneId,x,z , value = ArrayList<SpawnDataEntry>
@ -333,7 +333,7 @@ public class ResourceLoader {
s -> {
s.setGroup(entry);
GridBlockId point = s.getBlockId();
if(!areaSort.containsKey(point)) {
if (!areaSort.containsKey(point)) {
areaSort.put(point, new ArrayList<>());
}
areaSort.get(point).add(s);
@ -341,155 +341,155 @@ public class ResourceLoader {
);
}
GameDepot.addSpawnListById(areaSort);
}
}
private static void loadOpenConfig() {
// Read from cached file if exists
List<OpenConfigEntry> list = null;
private static void loadOpenConfig() {
// Read from cached file if exists
List<OpenConfigEntry> list = null;
try(InputStream openConfigCache = DataLoader.load("OpenConfig.json", false)) {
list = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(openConfigCache), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
} catch (Exception ignored) {}
try (InputStream openConfigCache = DataLoader.load("OpenConfig.json", false)) {
list = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(openConfigCache), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
} catch (Exception ignored) {}
if (list == null) {
Map<String, OpenConfigEntry> map = new TreeMap<>();
java.lang.reflect.Type type = new TypeToken<Map<String, OpenConfigData[]>>() {}.getType();
String[] folderNames = {"BinOutput/Talent/EquipTalents/", "BinOutput/Talent/AvatarTalents/"};
if (list == null) {
Map<String, OpenConfigEntry> map = new TreeMap<>();
java.lang.reflect.Type type = new TypeToken<Map<String, OpenConfigData[]>>() {}.getType();
String[] folderNames = {"BinOutput/Talent/EquipTalents/", "BinOutput/Talent/AvatarTalents/"};
for (String name : folderNames) {
File folder = new File(Utils.toFilePath(RESOURCE(name)));
File[] files = folder.listFiles();
if(files == null) {
Grasscutter.getLogger().error("Error loading open config: no files found in " + folder.getAbsolutePath()); return;
}
for (String name : folderNames) {
File folder = new File(Utils.toFilePath(RESOURCE(name)));
File[] files = folder.listFiles();
if (files == null) {
Grasscutter.getLogger().error("Error loading open config: no files found in " + folder.getAbsolutePath()); return;
}
for (File file : files) {
if (!file.getName().endsWith(".json")) {
continue;
}
for (File file : files) {
if (!file.getName().endsWith(".json")) {
continue;
}
Map<String, OpenConfigData[]> config;
Map<String, OpenConfigData[]> config;
try (FileReader fileReader = new FileReader(file)) {
config = Grasscutter.getGsonFactory().fromJson(fileReader, type);
} catch (Exception e) {
e.printStackTrace();
continue;
}
try (FileReader fileReader = new FileReader(file)) {
config = Grasscutter.getGsonFactory().fromJson(fileReader, type);
} catch (Exception e) {
e.printStackTrace();
continue;
}
for (Entry<String, OpenConfigData[]> e : config.entrySet()) {
OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), e.getValue());
map.put(entry.getName(), entry);
}
}
}
for (Entry<String, OpenConfigData[]> e : config.entrySet()) {
OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), e.getValue());
map.put(entry.getName(), entry);
}
}
}
list = new ArrayList<>(map.values());
}
list = new ArrayList<>(map.values());
}
if (list == null || list.isEmpty()) {
Grasscutter.getLogger().error("No openconfig entries loaded!");
return;
}
if (list == null || list.isEmpty()) {
Grasscutter.getLogger().error("No openconfig entries loaded!");
return;
}
for (OpenConfigEntry entry : list) {
GameData.getOpenConfigEntries().put(entry.getName(), entry);
}
}
for (OpenConfigEntry entry : list) {
GameData.getOpenConfigEntries().put(entry.getName(), entry);
}
}
private static void loadQuests() {
File folder = new File(RESOURCE("BinOutput/Quest/"));
private static void loadQuests() {
File folder = new File(RESOURCE("BinOutput/Quest/"));
if (!folder.exists()) {
return;
}
if (!folder.exists()) {
return;
}
for (File file : folder.listFiles()) {
MainQuestData mainQuest = null;
for (File file : folder.listFiles()) {
MainQuestData mainQuest = null;
try (FileReader fileReader = new FileReader(file)) {
mainQuest = Grasscutter.getGsonFactory().fromJson(fileReader, MainQuestData.class);
} catch (Exception e) {
e.printStackTrace();
continue;
}
try (FileReader fileReader = new FileReader(file)) {
mainQuest = Grasscutter.getGsonFactory().fromJson(fileReader, MainQuestData.class);
} catch (Exception e) {
e.printStackTrace();
continue;
}
GameData.getMainQuestDataMap().put(mainQuest.getId(), mainQuest);
}
GameData.getMainQuestDataMap().put(mainQuest.getId(), mainQuest);
}
Grasscutter.getLogger().debug("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas.");
}
Grasscutter.getLogger().debug("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas.");
}
@SneakyThrows
private static void loadHomeworldDefaultSaveData(){
var folder = Files.list(Path.of(RESOURCE("BinOutput/HomeworldDefaultSave"))).toList();
var pattern = Pattern.compile("scene(.*)_home_config.json");
@SneakyThrows
private static void loadHomeworldDefaultSaveData() {
var folder = Files.list(Path.of(RESOURCE("BinOutput/HomeworldDefaultSave"))).toList();
var pattern = Pattern.compile("scene(.*)_home_config.json");
for(var file : folder){
var matcher = pattern.matcher(file.getFileName().toString());
if(!matcher.find()){
continue;
}
var sceneId = matcher.group(1);
for (var file : folder) {
var matcher = pattern.matcher(file.getFileName().toString());
if (!matcher.find()) {
continue;
}
var sceneId = matcher.group(1);
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), HomeworldDefaultSaveData.class);
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), HomeworldDefaultSaveData.class);
GameData.getHomeworldDefaultSaveData().put(Integer.parseInt(sceneId), data);
}
GameData.getHomeworldDefaultSaveData().put(Integer.parseInt(sceneId), data);
}
Grasscutter.getLogger().debug("Loaded " + GameData.getHomeworldDefaultSaveData().size() + " HomeworldDefaultSaveDatas.");
}
Grasscutter.getLogger().debug("Loaded " + GameData.getHomeworldDefaultSaveData().size() + " HomeworldDefaultSaveDatas.");
}
@SneakyThrows
private static void loadNpcBornData(){
var folder = Files.list(Path.of(RESOURCE("BinOutput/Scene/SceneNpcBorn"))).toList();
@SneakyThrows
private static void loadNpcBornData() {
var folder = Files.list(Path.of(RESOURCE("BinOutput/Scene/SceneNpcBorn"))).toList();
for(var file : folder){
if(file.toFile().isDirectory()){
continue;
}
for (var file : folder) {
if (file.toFile().isDirectory()) {
continue;
}
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), SceneNpcBornData.class);
if(data.getBornPosList() == null || data.getBornPosList().size() == 0){
continue;
}
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), SceneNpcBornData.class);
if (data.getBornPosList() == null || data.getBornPosList().size() == 0) {
continue;
}
data.setIndex(SceneIndexManager.buildIndex(3, data.getBornPosList(), item -> item.getPos().toPoint()));
GameData.getSceneNpcBornData().put(data.getSceneId(), data);
}
data.setIndex(SceneIndexManager.buildIndex(3, data.getBornPosList(), item -> item.getPos().toPoint()));
GameData.getSceneNpcBornData().put(data.getSceneId(), data);
}
Grasscutter.getLogger().debug("Loaded " + GameData.getSceneNpcBornData().size() + " SceneNpcBornDatas.");
}
Grasscutter.getLogger().debug("Loaded " + GameData.getSceneNpcBornData().size() + " SceneNpcBornDatas.");
}
// BinOutput configs
// BinOutput configs
public static class AvatarConfig {
@SerializedName(value="abilities", alternate={"targetAbilities"})
public ArrayList<AvatarConfigAbility> abilities;
}
public static class AvatarConfig {
@SerializedName(value="abilities", alternate={"targetAbilities"})
public ArrayList<AvatarConfigAbility> abilities;
}
public static class AvatarConfigAbility {
public String abilityName;
public String toString() {
return abilityName;
}
}
public static class AvatarConfigAbility {
public String abilityName;
public String toString() {
return abilityName;
}
}
private static class OpenConfig {
public OpenConfigData[] data;
}
private static class OpenConfig {
public OpenConfigData[] data;
}
public static class OpenConfigData {
public String $type;
public String abilityName;
public static class OpenConfigData {
public String $type;
public String abilityName;
@SerializedName(value="talentIndex", alternate={"OJOFFKLNAHN"})
public int talentIndex;
@SerializedName(value="talentIndex", alternate={"OJOFFKLNAHN"})
public int talentIndex;
@SerializedName(value="skillID", alternate={"overtime"})
public int skillID;
@SerializedName(value="skillID", alternate={"overtime"})
public int skillID;
@SerializedName(value="pointDelta", alternate={"IGEBKIHPOIF"})
public int pointDelta;
}
@SerializedName(value="pointDelta", alternate={"IGEBKIHPOIF"})
public int pointDelta;
}
}

View File

@ -4,32 +4,32 @@ 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"})
@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;
}
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;
}
}

View File

@ -1,35 +1,35 @@
package emu.grasscutter.data.excels;
import java.util.List;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData;
@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 java.util.List;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData;
@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;
}
}

View File

@ -23,9 +23,9 @@ public class ForgeData extends GameResource {
private List<ItemParamData> materialItems;
@Override
public int getId() {
return this.id;
}
public int getId() {
return this.id;
}
public int getPlayerLevel() {
return playerLevel;

View File

@ -12,58 +12,58 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@ResourceType(name = "ReliquaryLevelExcelConfigData.json")
public class ReliquaryLevelData extends GameResource {
private int id;
private Int2FloatMap propMap;
private int rank;
private int level;
private int exp;
private List<RelicLevelProperty> addProps;
@Override
public int getId() {
return this.id;
}
public int getRank() {
return rank;
}
public int getLevel() {
return level;
}
public int getExp() {
return exp;
}
public float getPropValue(FightProperty prop) {
return getPropValue(prop.getId());
}
public float getPropValue(int id) {
return propMap.getOrDefault(id, 0f);
}
@Override
public void onLoad() {
this.id = (rank << 8) + this.getLevel();
this.propMap = new Int2FloatOpenHashMap();
for (RelicLevelProperty p : addProps) {
this.propMap.put(FightProperty.getPropByName(p.getPropType()).getId(), p.getValue());
}
}
public class RelicLevelProperty {
private String propType;
private float value;
public String getPropType() {
return propType;
}
public float getValue() {
return value;
}
}
private int id;
private Int2FloatMap propMap;
private int rank;
private int level;
private int exp;
private List<RelicLevelProperty> addProps;
@Override
public int getId() {
return this.id;
}
public int getRank() {
return rank;
}
public int getLevel() {
return level;
}
public int getExp() {
return exp;
}
public float getPropValue(FightProperty prop) {
return getPropValue(prop.getId());
}
public float getPropValue(int id) {
return propMap.getOrDefault(id, 0f);
}
@Override
public void onLoad() {
this.id = (rank << 8) + this.getLevel();
this.propMap = new Int2FloatOpenHashMap();
for (RelicLevelProperty p : addProps) {
this.propMap.put(FightProperty.getPropByName(p.getPropType()).getId(), p.getValue());
}
}
public class RelicLevelProperty {
private String propType;
private float value;
public String getPropType() {
return propType;
}
public float getValue() {
return value;
}
}
}

View File

@ -29,98 +29,98 @@ import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.quest.GameQuest;
public final class DatabaseManager {
private static Datastore gameDatastore;
private static Datastore dispatchDatastore;
private static Datastore gameDatastore;
private static Datastore dispatchDatastore;
private static final Class<?>[] mappedClasses = new Class<?>[] {
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class,
GachaRecord.class, Mail.class, GameMainQuest.class, GameHome.class, BattlePassManager.class,
private static final Class<?>[] mappedClasses = new Class<?>[] {
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class,
GachaRecord.class, Mail.class, GameMainQuest.class, GameHome.class, BattlePassManager.class,
PlayerActivityData.class, MusicGameBeatmap.class
};
};
public static Datastore getGameDatastore() {
return gameDatastore;
return gameDatastore;
}
public static MongoDatabase getGameDatabase() {
return getGameDatastore().getDatabase();
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.
gameDatastore.getMapper().map(mappedClasses);
// Ensure indexes
try {
gameDatastore.ensureIndexes();
} catch (MongoCommandException exception) {
Grasscutter.getLogger().info("Mongo index error: ", exception);
// Duplicate index error
if (exception.getCode() == 85) {
// Drop all indexes and re add them
MongoIterable<String> collections = gameDatastore.getDatabase().listCollectionNames();
for (String name : collections) {
gameDatastore.getDatabase().getCollection(name).dropIndexes();
}
// Add back indexes
gameDatastore.ensureIndexes();
}
}
// 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;
}
}
if(SERVER.runMode == ServerRunMode.GAME_ONLY) {
MongoClient dispatchMongoClient = MongoClients.create(DATABASE.server.connectionUri);
dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, DATABASE.server.collection);
public static void initialize() {
// Initialize
MongoClient gameMongoClient = MongoClients.create(DATABASE.game.connectionUri);
// Ensure indexes for dispatch server
try {
dispatchDatastore.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 = dispatchDatastore.getDatabase().listCollectionNames();
for (String name : collections) {
dispatchDatastore.getDatabase().getCollection(name).dropIndexes();
}
// Add back indexes
dispatchDatastore.ensureIndexes();
}
}
}
}
// 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.
gameDatastore.getMapper().map(mappedClasses);
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);
}
}
// Ensure indexes
try {
gameDatastore.ensureIndexes();
} catch (MongoCommandException exception) {
Grasscutter.getLogger().info("Mongo index error: ", exception);
// Duplicate index error
if (exception.getCode() == 85) {
// Drop all indexes and re add them
MongoIterable<String> collections = gameDatastore.getDatabase().listCollectionNames();
for (String name : collections) {
gameDatastore.getDatabase().getCollection(name).dropIndexes();
}
// Add back indexes
gameDatastore.ensureIndexes();
}
}
public static synchronized int getNextId(Object o) {
return getNextId(o.getClass());
}
}
if (SERVER.runMode == ServerRunMode.GAME_ONLY) {
MongoClient dispatchMongoClient = MongoClients.create(DATABASE.server.connectionUri);
dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, DATABASE.server.collection);
// Ensure indexes for dispatch server
try {
dispatchDatastore.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 = dispatchDatastore.getDatabase().listCollectionNames();
for (String name : collections) {
dispatchDatastore.getDatabase().getCollection(name).dropIndexes();
}
// Add back indexes
dispatchDatastore.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());
}
}

View File

@ -14,93 +14,93 @@ import org.bson.Document;
@Entity(value = "accounts", useDiscriminator = false)
public class Account {
@Id private String id;
@Indexed(options = @IndexOptions(unique = true))
@Collation(locale = "simple", caseLevel = true)
private String username;
private String password; // Unused for now
private int reservedPlayerId;
private String email;
private String token;
private String sessionKey; // Session token for dispatch server
private List<String> permissions;
@Id private String id;
@Indexed(options = @IndexOptions(unique = true))
@Collation(locale = "simple", caseLevel = true)
private String username;
private String password; // Unused for now
private int reservedPlayerId;
private String email;
private String token;
private String sessionKey; // Session token for dispatch server
private List<String> permissions;
private Locale locale;
private String banReason;
private int banEndTime;
private int banStartTime;
private boolean isBanned;
@Deprecated
public Account() {
this.permissions = new ArrayList<>();
private String banReason;
private int banEndTime;
private int banStartTime;
private boolean isBanned;
@Deprecated
public Account() {
this.permissions = new ArrayList<>();
this.locale = LANGUAGE;
}
}
public String getId() {
return id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void setPassword(String password) {
this.password = password;
}
public String getToken() {
return token;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public void setToken(String token) {
this.token = token;
}
public int getReservedPlayerUid() {
return this.reservedPlayerId;
}
public int getReservedPlayerUid() {
return this.reservedPlayerId;
}
public void setReservedPlayerUid(int playerId) {
this.reservedPlayerId = playerId;
}
public String getEmail() {
if(email != null && !email.isEmpty()) {
return email;
} else {
return "";
}
}
public void setReservedPlayerUid(int playerId) {
this.reservedPlayerId = playerId;
}
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
if (email != null && !email.isEmpty()) {
return email;
} else {
return "";
}
}
public String getSessionKey() {
return this.sessionKey;
}
public void setEmail(String email) {
this.email = email;
}
public String generateSessionKey() {
this.sessionKey = Utils.bytesToHex(Crypto.createSessionKey(32));
this.save();
return this.sessionKey;
}
public String getSessionKey() {
return this.sessionKey;
}
public String generateSessionKey() {
this.sessionKey = Utils.bytesToHex(Crypto.createSessionKey(32));
this.save();
return this.sessionKey;
}
public Locale getLocale() {
return locale;
@ -110,126 +110,126 @@ public class Account {
this.locale = locale;
}
public String getBanReason() {
return banReason;
}
public String getBanReason() {
return banReason;
}
public void setBanReason(String banReason) {
this.banReason = banReason;
}
public void setBanReason(String banReason) {
this.banReason = banReason;
}
public int getBanEndTime() {
return banEndTime;
}
public int getBanEndTime() {
return banEndTime;
}
public void setBanEndTime(int banEndTime) {
this.banEndTime = banEndTime;
}
public void setBanEndTime(int banEndTime) {
this.banEndTime = banEndTime;
}
public int getBanStartTime() {
return banStartTime;
}
public int getBanStartTime() {
return banStartTime;
}
public void setBanStartTime(int banStartTime) {
this.banStartTime = banStartTime;
}
public void setBanStartTime(int banStartTime) {
this.banStartTime = banStartTime;
}
public boolean isBanned() {
if (banEndTime > 0 && banEndTime < System.currentTimeMillis() / 1000) {
this.isBanned = false;
this.banEndTime = 0;
this.banStartTime = 0;
this.banReason = null;
save();
}
public boolean isBanned() {
if (banEndTime > 0 && banEndTime < System.currentTimeMillis() / 1000) {
this.isBanned = false;
this.banEndTime = 0;
this.banStartTime = 0;
this.banReason = null;
save();
}
return isBanned;
}
return isBanned;
}
public void setBanned(boolean isBanned) {
this.isBanned = isBanned;
}
public void setBanned(boolean isBanned) {
this.isBanned = isBanned;
}
/**
* The collection of a player's permissions.
*/
public List<String> getPermissions() {
return this.permissions;
}
public boolean addPermission(String permission) {
if(this.permissions.contains(permission)) return false;
this.permissions.add(permission); return true;
}
/**
* The collection of a player's permissions.
*/
public List<String> getPermissions() {
return this.permissions;
}
public static boolean permissionMatchesWildcard(String wildcard, String[] permissionParts) {
String[] wildcardParts = wildcard.split("\\.");
if (permissionParts.length < wildcardParts.length) { // A longer wildcard can never match a shorter permission
return false;
}
for (int i=0; i<wildcardParts.length; i++) {
switch (wildcardParts[i]) {
case "**": // Recursing match
return true;
case "*": // Match only one layer
if (i >= (permissionParts.length-1)) {
return true;
}
break;
default: // This layer isn't a wildcard, it needs to match exactly
if (!wildcardParts[i].equals(permissionParts[i])) {
return false;
}
}
}
// At this point the wildcard will have matched every layer, but if it is shorter then the permission then this is not a match at this point (no **).
return (wildcardParts.length == permissionParts.length);
}
public boolean addPermission(String permission) {
if (this.permissions.contains(permission)) return false;
this.permissions.add(permission); return true;
}
public boolean hasPermission(String permission) {
if(this.permissions.contains("*") && this.permissions.size() == 1) return true;
public static boolean permissionMatchesWildcard(String wildcard, String[] permissionParts) {
String[] wildcardParts = wildcard.split("\\.");
if (permissionParts.length < wildcardParts.length) { // A longer wildcard can never match a shorter permission
return false;
}
for (int i=0; i<wildcardParts.length; i++) {
switch (wildcardParts[i]) {
case "**": // Recursing match
return true;
case "*": // Match only one layer
if (i >= (permissionParts.length-1)) {
return true;
}
break;
default: // This layer isn't a wildcard, it needs to match exactly
if (!wildcardParts[i].equals(permissionParts[i])) {
return false;
}
}
}
// At this point the wildcard will have matched every layer, but if it is shorter then the permission then this is not a match at this point (no **).
return (wildcardParts.length == permissionParts.length);
}
// Add default permissions if it doesn't exist
List<String> permissions = Stream.of(this.permissions, Arrays.asList(ACCOUNT.defaultPermissions))
.flatMap(Collection::stream)
.distinct().toList();
public boolean hasPermission(String permission) {
if (this.permissions.contains("*") && this.permissions.size() == 1) return true;
if (permissions.contains(permission)) return true;
// Add default permissions if it doesn't exist
List<String> permissions = Stream.of(this.permissions, Arrays.asList(ACCOUNT.defaultPermissions))
.flatMap(Collection::stream)
.distinct().toList();
String[] permissionParts = permission.split("\\.");
for (String p : permissions) {
if (p.startsWith("-") && permissionMatchesWildcard(p.substring(1), permissionParts)) return false;
if (permissionMatchesWildcard(p, permissionParts)) return true;
}
if (permissions.contains(permission)) return true;
return permissions.contains("*");
}
String[] permissionParts = permission.split("\\.");
for (String p : permissions) {
if (p.startsWith("-") && permissionMatchesWildcard(p.substring(1), permissionParts)) return false;
if (permissionMatchesWildcard(p, permissionParts)) return true;
}
public boolean removePermission(String permission) {
return this.permissions.remove(permission);
}
return permissions.contains("*");
}
// TODO make unique
public String generateLoginToken() {
this.token = Utils.bytesToHex(Crypto.createSessionKey(32));
this.save();
return this.token;
}
public void save() {
DatabaseHelper.saveAccount(this);
}
public boolean removePermission(String permission) {
return this.permissions.remove(permission);
}
@PreLoad
public void onLoad(Document document) {
// Grant the superuser permissions to accounts created before the permissions update
if (!document.containsKey("permissions")) {
this.addPermission("*");
}
// TODO make unique
public String generateLoginToken() {
this.token = Utils.bytesToHex(Crypto.createSessionKey(32));
this.save();
return this.token;
}
public void save() {
DatabaseHelper.saveAccount(this);
}
@PreLoad
public void onLoad(Document document) {
// Grant the superuser permissions to accounts created before the permissions update
if (!document.containsKey("permissions")) {
this.addPermission("*");
}
// Set account default language as server default language
if (!document.containsKey("locale")) {
this.locale = LANGUAGE;
}
}
}
}

View File

@ -20,158 +20,158 @@ import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
public class AbilityManager extends BasePlayerManager {
HealAbilityManager healAbilityManager;
public AbilityManager(Player player) {
super(player);
this.healAbilityManager = new HealAbilityManager(player);
}
public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception {
public AbilityManager(Player player) {
super(player);
this.healAbilityManager = new HealAbilityManager(player);
}
public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception {
healAbilityManager.healHandler(invoke);
//Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray()));
switch (invoke.getArgumentType()) {
case ABILITY_INVOKE_ARGUMENT_META_OVERRIDE_PARAM:
handleOverrideParam(invoke);
break;
case ABILITY_INVOKE_ARGUMENT_META_REINIT_OVERRIDEMAP:
handleReinitOverrideMap(invoke);
break;
case ABILITY_INVOKE_ARGUMENT_META_MODIFIER_CHANGE:
handleModifierChange(invoke);
break;
case ABILITY_INVOKE_ARGUMENT_MIXIN_COST_STAMINA:
handleMixinCostStamina(invoke);
break;
case ABILITY_INVOKE_ARGUMENT_ACTION_GENERATE_ELEM_BALL:
handleGenerateElemBall(invoke);
break;
default:
break;
}
}
private void handleOverrideParam(AbilityInvokeEntry invoke) throws Exception {
GameEntity entity = player.getScene().getEntityById(invoke.getEntityId());
if (entity == null) {
return;
}
AbilityScalarValueEntry entry = AbilityScalarValueEntry.parseFrom(invoke.getAbilityData());
entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue());
}
private void handleReinitOverrideMap(AbilityInvokeEntry invoke) throws Exception {
GameEntity entity = player.getScene().getEntityById(invoke.getEntityId());
if (entity == null) {
return;
}
AbilityMetaReInitOverrideMap map = AbilityMetaReInitOverrideMap.parseFrom(invoke.getAbilityData());
for (AbilityScalarValueEntry entry : map.getOverrideMapList()) {
entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue());
}
}
private void handleModifierChange(AbilityInvokeEntry invoke) throws Exception {
// Sanity checks
GameEntity target = player.getScene().getEntityById(invoke.getEntityId());
if (target == null) {
return;
}
AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData());
if (data == null) {
return;
}
// Destroying rocks
if (target instanceof EntityGadget targetGadget && targetGadget.getContent() instanceof GadgetGatherObject gatherObject) {
if (data.getAction() == ModifierAction.REMOVED) {
gatherObject.dropItems(this.getPlayer());
return;
}
//Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray()));
switch (invoke.getArgumentType()) {
case ABILITY_INVOKE_ARGUMENT_META_OVERRIDE_PARAM:
handleOverrideParam(invoke);
break;
case ABILITY_INVOKE_ARGUMENT_META_REINIT_OVERRIDEMAP:
handleReinitOverrideMap(invoke);
break;
case ABILITY_INVOKE_ARGUMENT_META_MODIFIER_CHANGE:
handleModifierChange(invoke);
break;
case ABILITY_INVOKE_ARGUMENT_MIXIN_COST_STAMINA:
handleMixinCostStamina(invoke);
break;
case ABILITY_INVOKE_ARGUMENT_ACTION_GENERATE_ELEM_BALL:
handleGenerateElemBall(invoke);
break;
default:
break;
}
// Sanity checks
AbilityInvokeEntryHead head = invoke.getHead();
if (head == null) {
return;
}
GameEntity sourceEntity = player.getScene().getEntityById(data.getApplyEntityId());
if (sourceEntity == null) {
return;
}
// This is not how it works but we will keep it for now since healing abilities dont work properly anyways
if (data.getAction() == ModifierAction.ADDED && data.getParentAbilityName() != null) {
// Handle add modifier here
String modifierString = data.getParentAbilityName().getStr();
AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString);
if (modifier != null && modifier.getOnAdded().size() > 0) {
for (AbilityModifierAction action : modifier.getOnAdded()) {
invokeAction(action, target, sourceEntity);
}
}
// Add to meta modifier list
target.getMetaModifiers().put(head.getInstancedModifierId(), modifierString);
} else if (data.getAction() == ModifierAction.REMOVED) {
// Handle remove modifier
String modifierString = target.getMetaModifiers().get(head.getInstancedModifierId());
if (modifierString != null) {
// Get modifier and call on remove event
AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString);
if (modifier != null && modifier.getOnRemoved().size() > 0) {
for (AbilityModifierAction action : modifier.getOnRemoved()) {
invokeAction(action, target, sourceEntity);
}
}
// Remove from meta modifiers
target.getMetaModifiers().remove(head.getInstancedModifierId());
}
}
}
private void handleMixinCostStamina(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
AbilityMixinCostStamina costStamina = AbilityMixinCostStamina.parseFrom((invoke.getAbilityData()));
getPlayer().getStaminaManager().handleMixinCostStamina(costStamina.getIsSwim());
}
private void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
this.player.getEnergyManager().handleGenerateElemBall(invoke);
}
private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {
switch (action.type) {
case HealHP -> {
}
case LoseHP -> {
if (action.amountByTargetCurrentHPRatio == null) {
return;
}
float damageAmount = 0;
if (action.amount.isDynamic && action.amount.dynamicKey != null) {
damageAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
}
if (damageAmount > 0) {
target.damage(damageAmount);
}
}
}
}
}
private void handleOverrideParam(AbilityInvokeEntry invoke) throws Exception {
GameEntity entity = player.getScene().getEntityById(invoke.getEntityId());
if (entity == null) {
return;
}
AbilityScalarValueEntry entry = AbilityScalarValueEntry.parseFrom(invoke.getAbilityData());
entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue());
}
private void handleReinitOverrideMap(AbilityInvokeEntry invoke) throws Exception {
GameEntity entity = player.getScene().getEntityById(invoke.getEntityId());
if (entity == null) {
return;
}
AbilityMetaReInitOverrideMap map = AbilityMetaReInitOverrideMap.parseFrom(invoke.getAbilityData());
for (AbilityScalarValueEntry entry : map.getOverrideMapList()) {
entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue());
}
}
private void handleModifierChange(AbilityInvokeEntry invoke) throws Exception {
// Sanity checks
GameEntity target = player.getScene().getEntityById(invoke.getEntityId());
if (target == null) {
return;
}
AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData());
if (data == null) {
return;
}
// Destroying rocks
if (target instanceof EntityGadget targetGadget && targetGadget.getContent() instanceof GadgetGatherObject gatherObject) {
if (data.getAction() == ModifierAction.REMOVED) {
gatherObject.dropItems(this.getPlayer());
return;
}
}
// Sanity checks
AbilityInvokeEntryHead head = invoke.getHead();
if (head == null) {
return;
}
GameEntity sourceEntity = player.getScene().getEntityById(data.getApplyEntityId());
if (sourceEntity == null) {
return;
}
// This is not how it works but we will keep it for now since healing abilities dont work properly anyways
if (data.getAction() == ModifierAction.ADDED && data.getParentAbilityName() != null) {
// Handle add modifier here
String modifierString = data.getParentAbilityName().getStr();
AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString);
if (modifier != null && modifier.getOnAdded().size() > 0) {
for (AbilityModifierAction action : modifier.getOnAdded()) {
invokeAction(action, target, sourceEntity);
}
}
// Add to meta modifier list
target.getMetaModifiers().put(head.getInstancedModifierId(), modifierString);
} else if (data.getAction() == ModifierAction.REMOVED) {
// Handle remove modifier
String modifierString = target.getMetaModifiers().get(head.getInstancedModifierId());
if (modifierString != null) {
// Get modifier and call on remove event
AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString);
if (modifier != null && modifier.getOnRemoved().size() > 0) {
for (AbilityModifierAction action : modifier.getOnRemoved()) {
invokeAction(action, target, sourceEntity);
}
}
// Remove from meta modifiers
target.getMetaModifiers().remove(head.getInstancedModifierId());
}
}
}
private void handleMixinCostStamina(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
AbilityMixinCostStamina costStamina = AbilityMixinCostStamina.parseFrom((invoke.getAbilityData()));
getPlayer().getStaminaManager().handleMixinCostStamina(costStamina.getIsSwim());
}
private void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
this.player.getEnergyManager().handleGenerateElemBall(invoke);
}
private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {
switch (action.type) {
case HealHP -> {
}
case LoseHP -> {
if (action.amountByTargetCurrentHPRatio == null) {
return;
}
float damageAmount = 0;
if (action.amount.isDynamic && action.amount.dynamicKey != null) {
damageAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
}
if (damageAmount > 0) {
target.damage(damageAmount);
}
}
}
}
}

View File

@ -45,7 +45,7 @@ public class ActivityManager extends BasePlayerManager {
activityWatcherTypeMap.put(typeName.value(), ConstructorAccess.get(item));
});
try(Reader reader = DataLoader.loadReader("ActivityConfig.json")) {
try (Reader reader = DataLoader.loadReader("ActivityConfig.json")) {
List<ActivityConfigItem> activities = Grasscutter.getGsonFactory().fromJson(
reader,
TypeToken.getParameterized(List.class, ActivityConfigItem.class).getType());
@ -53,16 +53,16 @@ public class ActivityManager extends BasePlayerManager {
activities.forEach(item -> {
var activityData = GameData.getActivityDataMap().get(item.getActivityId());
if(activityData == null){
if (activityData == null) {
Grasscutter.getLogger().warn("activity {} not exist.", item.getActivityId());
return;
}
var activityHandlerType = activityHandlerTypeMap.get(ActivityType.getTypeByName(activityData.getActivityType()));
ActivityHandler activityHandler;
if(activityHandlerType != null) {
if (activityHandlerType != null) {
activityHandler = (ActivityHandler) activityHandlerType.newInstance();
}else{
}else {
activityHandler = new DefaultActivityHandler();
}
activityHandler.setActivityConfigItem(item);
@ -79,14 +79,14 @@ public class ActivityManager extends BasePlayerManager {
}
public ActivityManager(Player player){
public ActivityManager(Player player) {
super(player);
playerActivityDataMap = new ConcurrentHashMap<>();
// load data for player
activityConfigItemMap.values().forEach(item -> {
var data = PlayerActivityData.getByPlayer(player, item.getActivityId());
if(data == null){
if (data == null) {
data = item.getActivityHandler().initPlayerActivityData(player);
data.save();
}
@ -116,34 +116,34 @@ public class ActivityManager extends BasePlayerManager {
params));
}
public ActivityInfoOuterClass.ActivityInfo getInfoProtoByActivityId(int activityId){
public ActivityInfoOuterClass.ActivityInfo getInfoProtoByActivityId(int activityId) {
var activityHandler = activityConfigItemMap.get(activityId).getActivityHandler();
var activityData = playerActivityDataMap.get(activityId);
return activityHandler.toProto(activityData);
}
public Optional<ActivityHandler> getActivityHandler(ActivityType type){
public Optional<ActivityHandler> getActivityHandler(ActivityType type) {
return activityConfigItemMap.values().stream()
.map(ActivityConfigItem::getActivityHandler)
.filter(x -> type == x.getClass().getAnnotation(GameActivity.class).value())
.findFirst();
}
public <T extends ActivityHandler> Optional<T> getActivityHandlerAs(ActivityType type, Class<T> clazz){
public <T extends ActivityHandler> Optional<T> getActivityHandlerAs(ActivityType type, Class<T> clazz) {
return getActivityHandler(type).map(x -> (T)x);
}
public Optional<Integer> getActivityIdByActivityType(ActivityType type){
public Optional<Integer> getActivityIdByActivityType(ActivityType type) {
return getActivityHandler(type)
.map(ActivityHandler::getActivityConfigItem)
.map(ActivityConfigItem::getActivityId);
}
public Optional<PlayerActivityData> getPlayerActivityDataByActivityType(ActivityType type){
public Optional<PlayerActivityData> getPlayerActivityDataByActivityType(ActivityType type) {
return getActivityIdByActivityType(type)
.map(playerActivityDataMap::get);
}
public Optional<ActivityInfoOuterClass.ActivityInfo> getInfoProtoByActivityType(ActivityType type){
public Optional<ActivityInfoOuterClass.ActivityInfo> getInfoProtoByActivityType(ActivityType type) {
return getActivityIdByActivityType(type)
.map(this::getInfoProtoByActivityId);
}

View File

@ -41,8 +41,8 @@ public class MusicGameActivityHandler extends ActivityHandler {
.build());
}
public MusicGamePlayerData getMusicGamePlayerData(PlayerActivityData playerActivityData){
if(playerActivityData.getDetail() == null || playerActivityData.getDetail().isBlank()){
public MusicGamePlayerData getMusicGamePlayerData(PlayerActivityData playerActivityData) {
if (playerActivityData.getDetail() == null || playerActivityData.getDetail().isBlank()) {
onInitPlayerActivityData(playerActivityData);
playerActivityData.save();
}
@ -51,7 +51,7 @@ public class MusicGameActivityHandler extends ActivityHandler {
MusicGamePlayerData.class);
}
public boolean setMusicGameRecord(PlayerActivityData playerActivityData, MusicGamePlayerData.MusicGameRecord newRecord){
public boolean setMusicGameRecord(PlayerActivityData playerActivityData, MusicGamePlayerData.MusicGameRecord newRecord) {
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
var saveRecord = musicGamePlayerData.getMusicGameRecord().get(newRecord.getMusicId());
@ -63,7 +63,7 @@ public class MusicGameActivityHandler extends ActivityHandler {
return newRecord.getMaxScore() > saveRecord.getMaxScore();
}
public void setMusicGameCustomBeatmapRecord(PlayerActivityData playerActivityData, MusicGamePlayerData.CustomBeatmapRecord newRecord){
public void setMusicGameCustomBeatmapRecord(PlayerActivityData playerActivityData, MusicGamePlayerData.CustomBeatmapRecord newRecord) {
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
musicGamePlayerData.getOthersCustomBeatmapRecord().put(newRecord.getMusicShareId(), newRecord);

View File

@ -21,7 +21,7 @@ public class MusicGamePlayerData {
Map<Long, CustomBeatmapRecord> personalCustomBeatmapRecord;
Map<Long, CustomBeatmapRecord> othersCustomBeatmapRecord;
public static MusicGamePlayerData create(){
public static MusicGamePlayerData create() {
return MusicGamePlayerData.of()
.musicGameRecord(GameData.getMusicGameBasicDataMap().values().stream()
.collect(Collectors.toMap(MusicGameBasicData::getId, MusicGamePlayerData.MusicGameRecord::create)))
@ -38,13 +38,13 @@ public class MusicGamePlayerData {
int maxCombo;
int maxScore;
public static MusicGameRecord create(MusicGameBasicData musicGameBasicData){
public static MusicGameRecord create(MusicGameBasicData musicGameBasicData) {
return MusicGameRecord.of()
.musicId(musicGameBasicData.getId())
.build();
}
public MusicGameRecordOuterClass.MusicGameRecord toProto(){
public MusicGameRecordOuterClass.MusicGameRecord toProto() {
return MusicGameRecordOuterClass.MusicGameRecord.newBuilder()
.setIsUnlock(true)
.setMaxCombo(maxCombo)
@ -61,7 +61,7 @@ public class MusicGamePlayerData {
int score;
boolean settle;
public MusicBriefInfoOuterClass.MusicBriefInfo.Builder toPersonalBriefProto(){
public MusicBriefInfoOuterClass.MusicBriefInfo.Builder toPersonalBriefProto() {
var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId);
return MusicBriefInfoOuterClass.MusicBriefInfo.newBuilder()
@ -74,7 +74,7 @@ public class MusicGamePlayerData {
.setMusicShareId(musicShareId);
}
public MusicBriefInfoOuterClass.MusicBriefInfo.Builder toOthersBriefProto(){
public MusicBriefInfoOuterClass.MusicBriefInfo.Builder toOthersBriefProto() {
var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId);
return musicGameBeatmap.toBriefProto()

File diff suppressed because it is too large Load Diff

View File

@ -19,155 +19,155 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
public class AvatarStorage extends BasePlayerManager implements Iterable<Avatar> {
private final Int2ObjectMap<Avatar> avatars;
private final Long2ObjectMap<Avatar> avatarsGuid;
public AvatarStorage(Player player) {
super(player);
this.avatars = new Int2ObjectOpenHashMap<>();
this.avatarsGuid = new Long2ObjectOpenHashMap<>();
}
private final Int2ObjectMap<Avatar> avatars;
private final Long2ObjectMap<Avatar> avatarsGuid;
public Int2ObjectMap<Avatar> getAvatars() {
return avatars;
}
public int getAvatarCount() {
return this.avatars.size();
}
public Avatar getAvatarById(int id) {
return getAvatars().get(id);
}
public Avatar getAvatarByGuid(long id) {
return avatarsGuid.get(id);
}
public boolean hasAvatar(int id) {
return getAvatars().containsKey(id);
}
public boolean addAvatar(Avatar avatar) {
if (avatar.getAvatarData() == null || this.hasAvatar(avatar.getAvatarId())) {
return false;
}
// Set owner first
avatar.setOwner(getPlayer());
public AvatarStorage(Player player) {
super(player);
this.avatars = new Int2ObjectOpenHashMap<>();
this.avatarsGuid = new Long2ObjectOpenHashMap<>();
}
// Put into maps
this.avatars.put(avatar.getAvatarId(), avatar);
this.avatarsGuid.put(avatar.getGuid(), avatar);
public Int2ObjectMap<Avatar> getAvatars() {
return avatars;
}
avatar.save();
public int getAvatarCount() {
return this.avatars.size();
}
return true;
}
public void addStartingWeapon(Avatar avatar) {
// Make sure avatar owner is this player
if (avatar.getPlayer() != this.getPlayer()) {
return;
}
// Create weapon
GameItem weapon = new GameItem(avatar.getAvatarData().getInitialWeapon());
if (weapon.getItemData() != null) {
this.getPlayer().getInventory().addItem(weapon);
avatar.equipItem(weapon, true);
}
}
public boolean wearFlycloak(long avatarGuid, int flycloakId) {
Avatar avatar = this.getAvatarByGuid(avatarGuid);
public Avatar getAvatarById(int id) {
return getAvatars().get(id);
}
if (avatar == null || !getPlayer().getFlyCloakList().contains(flycloakId)) {
return false;
}
avatar.setFlyCloak(flycloakId);
avatar.save();
// Update
getPlayer().sendPacket(new PacketAvatarFlycloakChangeNotify(avatar));
public Avatar getAvatarByGuid(long id) {
return avatarsGuid.get(id);
}
return true;
}
public boolean changeCostume(long avatarGuid, int costumeId) {
Avatar avatar = this.getAvatarByGuid(avatarGuid);
public boolean hasAvatar(int id) {
return getAvatars().containsKey(id);
}
if (avatar == null) {
return false;
}
if (costumeId != 0 && !getPlayer().getCostumeList().contains(costumeId)) {
return false;
}
// TODO make sure avatar can wear costume
avatar.setCostume(costumeId);
avatar.save();
public boolean addAvatar(Avatar avatar) {
if (avatar.getAvatarData() == null || this.hasAvatar(avatar.getAvatarId())) {
return false;
}
// Update entity
EntityAvatar entity = avatar.getAsEntity();
if (entity == null) {
entity = new EntityAvatar(avatar);
getPlayer().sendPacket(new PacketAvatarChangeCostumeNotify(entity));
} else {
getPlayer().getScene().broadcastPacket(new PacketAvatarChangeCostumeNotify(entity));
}
// Done
return true;
}
public void loadFromDatabase() {
List<Avatar> avatars = DatabaseHelper.getAvatars(getPlayer());
for (Avatar avatar : avatars) {
// Should never happen
if (avatar.getObjectId() == null) {
continue;
}
AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId());
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId());
if (avatarData == null || skillDepot == null) {
continue;
}
// Set ownerships
avatar.setAvatarData(avatarData);
avatar.setSkillDepot(skillDepot);
avatar.setOwner(getPlayer());
// Force recalc of const boosted skills
avatar.recalcConstellations();
// Add to avatar storage
this.avatars.put(avatar.getAvatarId(), avatar);
this.avatarsGuid.put(avatar.getGuid(), avatar);
}
}
public void postLoad() {
for (Avatar avatar : this) {
// Weapon check
if (avatar.getWeapon() == null) {
this.addStartingWeapon(avatar);
}
// Recalc stats
avatar.recalcStats();
}
}
// Set owner first
avatar.setOwner(getPlayer());
@Override
public Iterator<Avatar> iterator() {
return getAvatars().values().iterator();
}
// Put into maps
this.avatars.put(avatar.getAvatarId(), avatar);
this.avatarsGuid.put(avatar.getGuid(), avatar);
avatar.save();
return true;
}
public void addStartingWeapon(Avatar avatar) {
// Make sure avatar owner is this player
if (avatar.getPlayer() != this.getPlayer()) {
return;
}
// Create weapon
GameItem weapon = new GameItem(avatar.getAvatarData().getInitialWeapon());
if (weapon.getItemData() != null) {
this.getPlayer().getInventory().addItem(weapon);
avatar.equipItem(weapon, true);
}
}
public boolean wearFlycloak(long avatarGuid, int flycloakId) {
Avatar avatar = this.getAvatarByGuid(avatarGuid);
if (avatar == null || !getPlayer().getFlyCloakList().contains(flycloakId)) {
return false;
}
avatar.setFlyCloak(flycloakId);
avatar.save();
// Update
getPlayer().sendPacket(new PacketAvatarFlycloakChangeNotify(avatar));
return true;
}
public boolean changeCostume(long avatarGuid, int costumeId) {
Avatar avatar = this.getAvatarByGuid(avatarGuid);
if (avatar == null) {
return false;
}
if (costumeId != 0 && !getPlayer().getCostumeList().contains(costumeId)) {
return false;
}
// TODO make sure avatar can wear costume
avatar.setCostume(costumeId);
avatar.save();
// Update entity
EntityAvatar entity = avatar.getAsEntity();
if (entity == null) {
entity = new EntityAvatar(avatar);
getPlayer().sendPacket(new PacketAvatarChangeCostumeNotify(entity));
} else {
getPlayer().getScene().broadcastPacket(new PacketAvatarChangeCostumeNotify(entity));
}
// Done
return true;
}
public void loadFromDatabase() {
List<Avatar> avatars = DatabaseHelper.getAvatars(getPlayer());
for (Avatar avatar : avatars) {
// Should never happen
if (avatar.getObjectId() == null) {
continue;
}
AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId());
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId());
if (avatarData == null || skillDepot == null) {
continue;
}
// Set ownerships
avatar.setAvatarData(avatarData);
avatar.setSkillDepot(skillDepot);
avatar.setOwner(getPlayer());
// Force recalc of const boosted skills
avatar.recalcConstellations();
// Add to avatar storage
this.avatars.put(avatar.getAvatarId(), avatar);
this.avatarsGuid.put(avatar.getGuid(), avatar);
}
}
public void postLoad() {
for (Avatar avatar : this) {
// Weapon check
if (avatar.getWeapon() == null) {
this.addStartingWeapon(avatar);
}
// Recalc stats
avatar.recalcStats();
}
}
@Override
public Iterator<Avatar> iterator() {
return getAvatars().values().iterator();
}
}

View File

@ -42,322 +42,322 @@ import lombok.Getter;
@Entity(value = "battlepass", useDiscriminator = false)
public class BattlePassManager extends BasePlayerDataManager {
@Id @Getter private ObjectId id;
@Indexed private int ownerUid;
@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);
}
public void setPlayer(Player player) {
this.player = player;
this.ownerUid = player.getUid();
this.player = player;
this.ownerUid = player.getUid();
}
public void updateViewed() {
this.viewed = true;
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 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){
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;
}
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;
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 Map<Integer, BattlePassMission> getMissions() {
if (this.missions == null) this.missions = new HashMap<>();
return this.missions;
}
public boolean isPaid() {
// ToDo: Change this when we actually support unlocking "paid" BP.
return true;
}
// 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 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()));
}
}
public boolean hasMission(int id) {
return getMissions().containsKey(id);
}
private void takeRewardsFromSelectChest(ItemData rewardItemData, int index, ItemParamData entry, List<GameItem> rewardItems) {
// Sanity checks.
if (rewardItemData.getItemUse().size() < 1) {
return;
}
public boolean isPaid() {
// ToDo: Change this when we actually support unlocking "paid" BP.
return true;
}
// Get possible item choices.
String[] choices = rewardItemData.getItemUse().get(0).getUseParam().get(0).split(",");
if (choices.length < index) {
return;
}
public Map<Integer, BattlePassReward> getTakenRewards() {
if (this.takenRewards == null) this.takenRewards = new HashMap<>();
return this.takenRewards;
}
// Get data for the selected item.
// This depends on the type of chest.
int chosenId = Integer.parseInt(choices[index - 1]);
// Mission triggers
public void triggerMission(WatcherTriggerType triggerType) {
getPlayer().getServer().getBattlePassSystem().triggerMission(getPlayer(), triggerType);
}
// 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().equals("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().equals("ITEM_USE_GRANT_SELECT_REWARD")) {
RewardData selectedReward = GameData.getRewardDataMap().get(chosenId);
public void triggerMission(WatcherTriggerType triggerType, int param, int progress) {
getPlayer().getServer().getBattlePassSystem().triggerMission(getPlayer(), triggerType, param, progress);
}
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) {
// Handlers
public void takeMissionPoint(List<Integer> missionIdList) {
// Obvious exploit check
if (missionIdList.size() > GameData.getBattlePassMissionDataMap().size()) {
return;
}
rewardItems = new ArrayList<>();
for (var option : rewardList) {
var tag = option.getTag();
int index = option.getOptionIdx();
List<BattlePassMission> updatedMissions = new ArrayList<>(missionIdList.size());
// 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());
for (int id : missionIdList) {
// Skip if we dont have this mission
if (!this.hasMission(id)) {
continue;
}
// 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);
}
}
BattlePassMission mission = this.loadMissionById(id);
// 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>();
if (mission.getData() == null) {
this.getMissions().remove(mission.getId());
continue;
}
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);
// Take reward
if (mission.getStatus() == BattlePassMissionStatus.MISSION_STATUS_FINISHED) {
this.addPointsDirectly(mission.getData().getAddPoint(), mission.getData().isCycleRefresh());
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_POINT_TAKEN);
resetMissions.add(mission);
}
}
updatedMissions.add(mission);
}
}
this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
}
public void resetWeeklyMissions() {
var resetMissions = new ArrayList<BattlePassMission>();
if (updatedMissions.size() > 0) {
// Save to db
this.save();
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);
// Packet
getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(updatedMissions));
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
}
}
resetMissions.add(mission);
}
}
private void takeRewardsFromSelectChest(ItemData rewardItemData, int index, ItemParamData entry, List<GameItem> rewardItems) {
// Sanity checks.
if (rewardItemData.getItemUse().size() < 1) {
return;
}
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()
// Get possible item choices.
String[] choices = rewardItemData.getItemUse().get(0).getUseParam().get(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().equals("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().equals("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())
@ -366,21 +366,21 @@ public class BattlePassManager extends BasePlayerDataManager {
.setIsViewed(this.isViewed())
.setUnlockStatus(this.isPaid() ? BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID : BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_FREE)
.setJPFMGBEBBBJ(2) // Not bought on Playstation.
.setCurCyclePoints(this.getCyclePoints())
.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();
}
.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);
DatabaseHelper.saveBattlePass(this);
}
}

View File

@ -16,64 +16,64 @@ import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify;
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);
}
}
}
private final Map<WatcherTriggerType, List<BattlePassMissionData>> cachedTriggers;
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);
}
// 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);
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;
this.cachedTriggers = new HashMap<>();
// 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));
}
}
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));
}
}
}

View File

@ -31,59 +31,59 @@ import java.util.List;
import com.google.gson.reflect.TypeToken;
public class CombineManger extends BaseGameSystem {
private final static Int2ObjectMap<List<Integer>> reliquaryDecomposeData = new Int2ObjectOpenHashMap<>();
private final static Int2ObjectMap<List<Integer>> reliquaryDecomposeData = new Int2ObjectOpenHashMap<>();
public CombineManger(GameServer server) {
super(server);
}
public static void initialize() {
// Read the data we need for strongbox.
try (Reader fileReader = DataLoader.loadReader("ReliquaryDecompose.json")) {
List<ReliquaryDecomposeEntry> decomposeEntries = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ReliquaryDecomposeEntry.class).getType());
// Read the data we need for strongbox.
try (Reader fileReader = DataLoader.loadReader("ReliquaryDecompose.json")) {
List<ReliquaryDecomposeEntry> decomposeEntries = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ReliquaryDecomposeEntry.class).getType());
for (ReliquaryDecomposeEntry entry : decomposeEntries) {
reliquaryDecomposeData.put(entry.getConfigId(), entry.getItems());
}
for (ReliquaryDecomposeEntry entry : decomposeEntries) {
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);
}
}
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, GameItem diagramItem) {
// Make sure this is actually a diagram.
if (!diagramItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COMBINE")) {
return false;
}
// Make sure this is actually a diagram.
if (!diagramItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COMBINE")) {
return false;
}
// Determine the combine item we should unlock.
int combineId = Integer.parseInt(diagramItem.getItemData().getItemUse().get(0).getUseParam().get(0));
// Determine the combine item we should unlock.
int combineId = Integer.parseInt(diagramItem.getItemData().getItemUse().get(0).getUseParam().get(0));
// Remove the diagram from the player's inventory.
// We need to do this here, before sending CombineFormulaDataNotify, or the the combine UI won't correctly
// update when unlocking the diagram.
player.getInventory().removeItem(diagramItem, 1);
// Remove the diagram from the player's inventory.
// We need to do this here, before sending CombineFormulaDataNotify, or the the combine UI won't correctly
// update when unlocking the diagram.
player.getInventory().removeItem(diagramItem, 1);
// Tell the client that this diagram is now unlocked and add the unlocked item to the player.
player.getUnlockedCombines().add(combineId);
player.sendPacket(new PacketCombineFormulaDataNotify(combineId));
// Tell the client that this diagram is now unlocked and add the unlocked item to the player.
player.getUnlockedCombines().add(combineId);
player.sendPacket(new PacketCombineFormulaDataNotify(combineId));
return true;
}
return true;
}
public CombineResult combineItem(Player player, int cid, int count){
public CombineResult combineItem(Player player, int cid, int count) {
// check config exist
if(!GameData.getCombineDataMap().containsKey(cid)){
if (!GameData.getCombineDataMap().containsKey(cid)) {
player.getWorld().getHost().sendPacket(new PacketCombineRsp());
return null;
}
CombineData combineData = GameData.getCombineDataMap().get(cid);
if(combineData.getPlayerLevel() > player.getLevel()){
if (combineData.getPlayerLevel() > player.getLevel()) {
return null;
}
@ -120,7 +120,7 @@ public class CombineManger extends BaseGameSystem {
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));
@ -144,7 +144,7 @@ public class CombineManger extends BaseGameSystem {
List<Long> resultItems = new ArrayList<>();
for (int i = 0; i < count; i++) {
int itemId = Utils.drawRandomListElement(possibleDrops);
GameItem newReliquary = new GameItem(itemId, 1);
GameItem newReliquary = new GameItem(itemId, 1);
player.getInventory().addItem(newReliquary);
resultItems.add(newReliquary.getGuid());

View File

@ -32,7 +32,7 @@ public class DropSystem extends BaseGameSystem {
this.dropData = new Int2ObjectOpenHashMap<>();
this.load();
}
public Int2ObjectMap<List<DropData>> getDropData() {
return dropData;
}
@ -41,7 +41,7 @@ public class DropSystem extends BaseGameSystem {
try (Reader fileReader = DataLoader.loadReader("Drop.json")) {
getDropData().clear();
List<DropInfo> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DropInfo.class).getType());
if(banners.size() > 0) {
if (banners.size() > 0) {
for (DropInfo di : banners) {
getDropData().put(di.getMonsterId(), di.getDropDataList());
}

View File

@ -20,95 +20,95 @@ import emu.grasscutter.utils.Position;
import java.util.List;
public class DungeonSystem extends BaseGameSystem {
private static final BasicDungeonSettleListener basicDungeonSettleObserver = new BasicDungeonSettleListener();
public DungeonSystem(GameServer server) {
super(server);
}
private static final BasicDungeonSettleListener basicDungeonSettleObserver = new BasicDungeonSettleListener();
public void getEntryInfo(Player player, int pointId) {
ScenePointEntry entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId);
if (entry == null) {
// Error
player.sendPacket(new PacketDungeonEntryInfoRsp());
return;
}
player.sendPacket(new PacketDungeonEntryInfoRsp(player, entry.getPointData()));
}
public DungeonSystem(GameServer server) {
super(server);
}
public boolean enterDungeon(Player player, int pointId, int dungeonId) {
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
if (data == null) {
return false;
}
Grasscutter.getLogger().info("{}({}) is trying to enter dungeon {}" ,player.getNickname(),player.getUid(),dungeonId);
int sceneId = data.getSceneId();
player.getScene().setPrevScene(sceneId);
if (player.getWorld().transferPlayerToScene(player, sceneId, data)) {
player.getScene().addDungeonSettleObserver(basicDungeonSettleObserver);
player.getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_ENTER_DUNGEON, data.getId());
}
player.getScene().setPrevScenePoint(pointId);
player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId));
return true;
}
public void getEntryInfo(Player player, int pointId) {
ScenePointEntry entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId);
/**
* used in tower dungeons handoff
*/
public boolean handoffDungeon(Player player, int dungeonId, List<DungeonSettleListener> dungeonSettleListeners) {
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
if (entry == null) {
// Error
player.sendPacket(new PacketDungeonEntryInfoRsp());
return;
}
if (data == null) {
return false;
}
Grasscutter.getLogger().info("{}({}) is trying to enter tower dungeon {}" ,player.getNickname(),player.getUid(),dungeonId);
player.sendPacket(new PacketDungeonEntryInfoRsp(player, entry.getPointData()));
}
if(player.getWorld().transferPlayerToScene(player, data.getSceneId(), data)){
dungeonSettleListeners.forEach(player.getScene()::addDungeonSettleObserver);
}
return true;
}
public boolean enterDungeon(Player player, int pointId, int dungeonId) {
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
public void exitDungeon(Player player) {
Scene scene = player.getScene();
if (scene==null || scene.getSceneType() != SceneType.SCENE_DUNGEON) {
return;
}
// Get previous scene
int prevScene = scene.getPrevScene() > 0 ? scene.getPrevScene() : 3;
// Get previous position
DungeonData dungeonData = scene.getDungeonData();
Position prevPos = new Position(GameConstants.START_POSITION);
if (dungeonData != null) {
ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, scene.getPrevScenePoint());
if (entry != null) {
prevPos.set(entry.getPointData().getTranPos());
}
}
// clean temp team if it has
player.getTeamManager().cleanTemporaryTeam();
player.getTowerManager().clearEntry();
if (data == null) {
return false;
}
Grasscutter.getLogger().info("{}({}) is trying to enter dungeon {}" ,player.getNickname(),player.getUid(),dungeonId);
// Transfer player back to world
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
}
public void updateDailyDungeons() {
for (ScenePointEntry entry : GameData.getScenePointEntries().values()) {
entry.getPointData().updateDailyDungeon();
}
}
int sceneId = data.getSceneId();
player.getScene().setPrevScene(sceneId);
if (player.getWorld().transferPlayerToScene(player, sceneId, data)) {
player.getScene().addDungeonSettleObserver(basicDungeonSettleObserver);
player.getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_ENTER_DUNGEON, data.getId());
}
player.getScene().setPrevScenePoint(pointId);
player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId));
return true;
}
/**
* used in tower dungeons handoff
*/
public boolean handoffDungeon(Player player, int dungeonId, List<DungeonSettleListener> dungeonSettleListeners) {
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
if (data == null) {
return false;
}
Grasscutter.getLogger().info("{}({}) is trying to enter tower dungeon {}" ,player.getNickname(),player.getUid(),dungeonId);
if (player.getWorld().transferPlayerToScene(player, data.getSceneId(), data)) {
dungeonSettleListeners.forEach(player.getScene()::addDungeonSettleObserver);
}
return true;
}
public void exitDungeon(Player player) {
Scene scene = player.getScene();
if (scene==null || scene.getSceneType() != SceneType.SCENE_DUNGEON) {
return;
}
// Get previous scene
int prevScene = scene.getPrevScene() > 0 ? scene.getPrevScene() : 3;
// Get previous position
DungeonData dungeonData = scene.getDungeonData();
Position prevPos = new Position(GameConstants.START_POSITION);
if (dungeonData != null) {
ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, scene.getPrevScenePoint());
if (entry != null) {
prevPos.set(entry.getPointData().getTranPos());
}
}
// clean temp team if it has
player.getTeamManager().cleanTemporaryTeam();
player.getTowerManager().clearEntry();
// Transfer player back to world
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
}
public void updateDailyDungeons() {
for (ScenePointEntry entry : GameData.getScenePointEntries().values()) {
entry.getPointData().updateDailyDungeon();
}
}
}

View File

@ -36,180 +36,180 @@ import com.google.gson.reflect.TypeToken;
public class DungeonChallenge extends WorldChallenge {
/**
* has more challenge
*/
private boolean stage;
private IntSet rewardedPlayers;
/**
* has more challenge
*/
private boolean stage;
private IntSet rewardedPlayers;
private final static Int2ObjectMap<List<DungeonDropEntry>> dungeonDropData = new Int2ObjectOpenHashMap<>();
private final static Int2ObjectMap<List<DungeonDropEntry>> dungeonDropData = new Int2ObjectOpenHashMap<>();
public static void initialize() {
// Read the data we need for dungeon rewards drops.
try (Reader fileReader = DataLoader.loadReader("DungeonDrop.json")) {
List<DungeonDrop> dungeonDropList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DungeonDrop.class).getType());
public static void initialize() {
// Read the data we need for dungeon rewards drops.
try (Reader fileReader = DataLoader.loadReader("DungeonDrop.json")) {
List<DungeonDrop> dungeonDropList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DungeonDrop.class).getType());
for (DungeonDrop entry : dungeonDropList) {
dungeonDropData.put(entry.getDungeonId(), entry.getDrops());
}
for (DungeonDrop entry : dungeonDropList) {
dungeonDropData.put(entry.getDungeonId(), entry.getDrops());
}
Grasscutter.getLogger().debug("Loaded {} dungeon drop data entries.", dungeonDropData.size());
}
catch (Exception ex) {
Grasscutter.getLogger().error("Unable to load dungeon drop data.", ex);
}
}
Grasscutter.getLogger().debug("Loaded {} dungeon drop data entries.", dungeonDropData.size());
}
catch (Exception ex) {
Grasscutter.getLogger().error("Unable to load dungeon drop data.", ex);
}
}
public DungeonChallenge(Scene scene, SceneGroup group,
int challengeId, int challengeIndex,
List<Integer> paramList,
int timeLimit, int goal,
List<ChallengeTrigger> challengeTriggers) {
super(scene, group, challengeId, challengeIndex, paramList, timeLimit, goal, challengeTriggers);
this.setRewardedPlayers(new IntOpenHashSet());
}
public DungeonChallenge(Scene scene, SceneGroup group,
int challengeId, int challengeIndex,
List<Integer> paramList,
int timeLimit, int goal,
List<ChallengeTrigger> challengeTriggers) {
super(scene, group, challengeId, challengeIndex, paramList, timeLimit, goal, challengeTriggers);
this.setRewardedPlayers(new IntOpenHashSet());
}
public boolean isStage() {
return stage;
}
public boolean isStage() {
return stage;
}
public void setStage(boolean stage) {
this.stage = stage;
}
public void setStage(boolean stage) {
this.stage = stage;
}
public IntSet getRewardedPlayers() {
return rewardedPlayers;
}
public IntSet getRewardedPlayers() {
return rewardedPlayers;
}
public void setRewardedPlayers(IntSet rewardedPlayers) {
this.rewardedPlayers = rewardedPlayers;
}
public void setRewardedPlayers(IntSet rewardedPlayers) {
this.rewardedPlayers = rewardedPlayers;
}
@Override
public void done() {
super.done();
if (this.isSuccess()) {
// Settle
settle();
}
}
@Override
public void done() {
super.done();
if (this.isSuccess()) {
// Settle
settle();
}
}
private void settle() {
if(!stage){
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
new ScriptArgs(this.isSuccess() ? 1 : 0));
// Battle pass trigger
this.getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON));
}
}
private void settle() {
if (!stage) {
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
new ScriptArgs(this.isSuccess() ? 1 : 0));
// Battle pass trigger
this.getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON));
}
}
private List<GameItem> rollRewards(boolean useCondensed) {
List<GameItem> rewards = new ArrayList<>();
int dungeonId = this.getScene().getDungeonData().getId();
// If we have specific drop data for this dungeon, we use it.
if (dungeonDropData.containsKey(dungeonId)) {
List<DungeonDropEntry> dropEntries = dungeonDropData.get(dungeonId);
private List<GameItem> rollRewards(boolean useCondensed) {
List<GameItem> rewards = new ArrayList<>();
int dungeonId = this.getScene().getDungeonData().getId();
// If we have specific drop data for this dungeon, we use it.
if (dungeonDropData.containsKey(dungeonId)) {
List<DungeonDropEntry> dropEntries = dungeonDropData.get(dungeonId);
// Roll for each drop group.
for (var entry : dropEntries) {
// Determine the number of drops we get for this entry.
int start = entry.getCounts().get(0);
int end = entry.getCounts().get(entry.getCounts().size() - 1);
var candidateAmounts = IntStream.range(start, end + 1).boxed().collect(Collectors.toList());
// Roll for each drop group.
for (var entry : dropEntries) {
// Determine the number of drops we get for this entry.
int start = entry.getCounts().get(0);
int end = entry.getCounts().get(entry.getCounts().size() - 1);
var candidateAmounts = IntStream.range(start, end + 1).boxed().collect(Collectors.toList());
int amount = Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
int amount = Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
if (useCondensed) {
amount += Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
}
if (useCondensed) {
amount += Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
}
// Double rewards in multiplay mode, if specified.
if (entry.isMpDouble() && this.getScene().getPlayerCount() > 1) {
amount *= 2;
}
// Double rewards in multiplay mode, if specified.
if (entry.isMpDouble() && this.getScene().getPlayerCount() > 1) {
amount *= 2;
}
// Roll items for this group.
// Here, we have to handle stacking, or the client will not display results correctly.
// For now, we use the following logic: If the possible drop item are a list of multiple items,
// we roll them separately. If not, we stack them. This should work out in practice, at least
// for the currently existing set of dungeons.
if (entry.getItems().size() == 1) {
rewards.add(new GameItem(entry.getItems().get(0), amount));
}
else {
for (int i = 0; i < amount; i++) {
// int itemIndex = ThreadLocalRandom.current().nextInt(0, entry.getItems().size());
// int itemId = entry.getItems().get(itemIndex);
int itemId = Utils.drawRandomListElement(entry.getItems(), entry.getItemProbabilities());
rewards.add(new GameItem(itemId, 1));
}
}
}
}
// Otherwise, we fall back to the preview data.
else {
Grasscutter.getLogger().info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId);
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
}
}
// Roll items for this group.
// Here, we have to handle stacking, or the client will not display results correctly.
// For now, we use the following logic: If the possible drop item are a list of multiple items,
// we roll them separately. If not, we stack them. This should work out in practice, at least
// for the currently existing set of dungeons.
if (entry.getItems().size() == 1) {
rewards.add(new GameItem(entry.getItems().get(0), amount));
}
else {
for (int i = 0; i < amount; i++) {
// int itemIndex = ThreadLocalRandom.current().nextInt(0, entry.getItems().size());
// int itemId = entry.getItems().get(itemIndex);
int itemId = Utils.drawRandomListElement(entry.getItems(), entry.getItemProbabilities());
rewards.add(new GameItem(itemId, 1));
}
}
}
}
// Otherwise, we fall back to the preview data.
else {
Grasscutter.getLogger().info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId);
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
}
}
return rewards;
}
return rewards;
}
public void getStatueDrops(Player player, GadgetInteractReq request) {
DungeonData dungeonData = getScene().getDungeonData();
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
public void getStatueDrops(Player player, GadgetInteractReq request) {
DungeonData dungeonData = getScene().getDungeonData();
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
return;
}
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
return;
}
// Already rewarded
if (getRewardedPlayers().contains(player.getUid())) {
return;
}
// Already rewarded
if (getRewardedPlayers().contains(player.getUid())) {
return;
}
// Get rewards.
List<GameItem> rewards = new ArrayList<>();
// Get rewards.
List<GameItem> rewards = new ArrayList<>();
if (request.getIsUseCondenseResin()) {
// Check if condensed resin is usable here.
// For this, we use the following logic for now:
// The normal resin cost of the dungeon has to be 20.
if (resinCost != 20) {
return;
}
if (request.getIsUseCondenseResin()) {
// Check if condensed resin is usable here.
// For this, we use the following logic for now:
// The normal resin cost of the dungeon has to be 20.
if (resinCost != 20) {
return;
}
// Make sure the player has condensed resin.
GameItem condensedResin = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(220007);
if (condensedResin == null || condensedResin.getCount() <= 0) {
return;
}
// Make sure the player has condensed resin.
GameItem condensedResin = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(220007);
if (condensedResin == null || condensedResin.getCount() <= 0) {
return;
}
// Deduct.
player.getInventory().removeItem(condensedResin, 1);
// Deduct.
player.getInventory().removeItem(condensedResin, 1);
// Roll rewards.
rewards.addAll(this.rollRewards(true));
}
else {
// If the player used regular resin, try to deduct.
// Stop if insufficient resin.
boolean success = player.getResinManager().useResin(resinCost);
if (!success) {
return;
}
// Roll rewards.
rewards.addAll(this.rollRewards(true));
}
else {
// If the player used regular resin, try to deduct.
// Stop if insufficient resin.
boolean success = player.getResinManager().useResin(resinCost);
if (!success) {
return;
}
// Roll rewards.
rewards.addAll(this.rollRewards(false));
}
// Roll rewards.
rewards.addAll(this.rollRewards(false));
}
// Add rewards to player and send notification.
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
// Add rewards to player and send notification.
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
getRewardedPlayers().add(player.getUid());
}
getRewardedPlayers().add(player.getUid());
}
}

View File

@ -40,278 +40,278 @@ import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
public class EntityAvatar extends GameEntity {
private final Avatar avatar;
private PlayerDieType killedType;
private int killedBy;
public EntityAvatar(Scene scene, Avatar avatar) {
super(scene);
this.avatar = avatar;
this.avatar.setCurrentEnergy();
this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
GameItem weapon = this.getAvatar().getWeapon();
if (weapon != null) {
weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON));
}
}
public EntityAvatar(Avatar avatar) {
super(null);
this.avatar = avatar;
this.avatar.setCurrentEnergy();
}
private final Avatar avatar;
public Player getPlayer() {
return avatar.getPlayer();
}
private PlayerDieType killedType;
private int killedBy;
@Override
public Position getPosition() {
return getPlayer().getPosition();
}
@Override
public Position getRotation() {
return getPlayer().getRotation();
}
public EntityAvatar(Scene scene, Avatar avatar) {
super(scene);
this.avatar = avatar;
this.avatar.setCurrentEnergy();
this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
public Avatar getAvatar() {
return avatar;
}
public int getKilledBy() {
return killedBy;
}
public PlayerDieType getKilledType() {
return killedType;
}
GameItem weapon = this.getAvatar().getWeapon();
if (weapon != null) {
weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON));
}
}
@Override
public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return getAvatar().getFightProperties();
}
public int getWeaponEntityId() {
if (getAvatar().getWeapon() != null) {
return getAvatar().getWeapon().getWeaponEntityId();
}
return 0;
}
public EntityAvatar(Avatar avatar) {
super(null);
this.avatar = avatar;
this.avatar.setCurrentEnergy();
}
@Override
public void onDeath(int killerId) {
this.killedType = PlayerDieType.PLAYER_DIE_TYPE_KILL_BY_MONSTER;
this.killedBy = killerId;
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
}
public Player getPlayer() {
return avatar.getPlayer();
}
public void onDeath(PlayerDieType dieType, int killerId) {
this.killedType = dieType;
this.killedBy = killerId;
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
}
@Override
public float heal(float amount) {
float healed = super.heal(amount);
if (healed > 0f) {
getScene().broadcastPacket(
new PacketEntityFightPropChangeReasonNotify(this, FightProperty.FIGHT_PROP_CUR_HP, healed, PropChangeReason.PROP_CHANGE_REASON_ABILITY, ChangeHpReason.CHANGE_HP_REASON_ADD_ABILITY)
);
}
return healed;
}
public void clearEnergy(ChangeEnergyReason reason) {
// Fight props.
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
@Override
public Position getPosition() {
return getPlayer().getPosition();
}
// Get max energy.
float maxEnergy = this.avatar.getFightProperty(maxEnergyProp);
@Override
public Position getRotation() {
return getPlayer().getRotation();
}
// Set energy to zero.
this.avatar.setCurrentEnergy(curEnergyProp, 0);
public Avatar getAvatar() {
return avatar;
}
// Send packets.
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
public int getKilledBy() {
return killedBy;
}
if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) {
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -maxEnergy, reason));
}
}
public void addEnergy(float amount, PropChangeReason reason) {
this.addEnergy(amount, reason, false);
}
public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) {
// Get current and maximum energy for this avatar.
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
public PlayerDieType getKilledType() {
return killedType;
}
float curEnergy = this.getFightProperty(curEnergyProp);
float maxEnergy = this.getFightProperty(maxEnergyProp);
// Get energy recharge.
float energyRecharge = this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
@Override
public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
}
// Scale amount by energy recharge, if the amount is not flat.
if (!isFlat) {
amount *= energyRecharge;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return getAvatar().getFightProperties();
}
// Determine the new energy value.
float newEnergy = Math.min(curEnergy + amount, maxEnergy);
// Set energy and notify.
if (newEnergy != curEnergy) {
this.avatar.setCurrentEnergy(curEnergyProp, newEnergy);
this.getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp));
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason));
}
}
public SceneAvatarInfo getSceneAvatarInfo() {
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()
.setUid(this.getPlayer().getUid())
.setAvatarId(this.getAvatar().getAvatarId())
.setGuid(this.getAvatar().getGuid())
.setPeerId(this.getPlayer().getPeerId())
.addAllTalentIdList(this.getAvatar().getTalentIdList())
.setCoreProudSkillLevel(this.getAvatar().getCoreProudSkillLevel())
.putAllSkillLevelMap(this.getAvatar().getSkillLevelMap())
.setSkillDepotId(this.getAvatar().getSkillDepotId())
.addAllInherentProudSkillList(this.getAvatar().getProudSkillList())
.putAllProudSkillExtraLevelMap(this.getAvatar().getProudSkillBonusMap())
.addAllTeamResonanceList(this.getAvatar().getPlayer().getTeamManager().getTeamResonances())
.setWearingFlycloakId(this.getAvatar().getFlyCloak())
.setCostumeId(this.getAvatar().getCostume())
.setBornTime(this.getAvatar().getBornTime());
for (GameItem item : avatar.getEquips().values()) {
if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) {
avatarInfo.setWeapon(item.createSceneWeaponInfo());
} else {
avatarInfo.addReliquaryList(item.createSceneReliquaryInfo());
}
avatarInfo.addEquipIdList(item.getItemId());
}
return avatarInfo.build();
}
public int getWeaponEntityId() {
if (getAvatar().getWeapon() != null) {
return getAvatar().getWeapon().getWeaponEntityId();
}
return 0;
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR)
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLastMoveSceneTimeMs(this.getLastMoveSceneTimeMs())
.setLastMoveReliableSeq(this.getLastMoveReliableSeq())
.setLifeState(this.getLifeState().getValue());
if (this.getScene() != null) {
entityInfo.setMotionInfo(this.getMotionInfo());
}
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
if (entry.getIntKey() == 0) {
continue;
}
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
entityInfo.addFightPropList(fightProp);
}
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getAvatar().getLevel()))
.build();
entityInfo.addPropList(pair);
entityInfo.setAvatar(this.getSceneAvatarInfo());
return entityInfo.build();
}
public AbilityControlBlock getAbilityControlBlock() {
AvatarData data = this.getAvatar().getAvatarData();
AbilityControlBlock.Builder abilityControlBlock = AbilityControlBlock.newBuilder();
int embryoId = 0;
// Add avatar abilities
if (data.getAbilities() != null) {
for (int id : data.getAbilities()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
}
// Add default abilities
for (int id : GameConstants.DEFAULT_ABILITY_HASHES) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
// Add team resonances
for (int id : this.getPlayer().getTeamManager().getTeamResonancesConfig()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
// Add skill depot abilities
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.getAvatar().getSkillDepotId());
if (skillDepot != null && skillDepot.getAbilities() != null) {
for (int id : skillDepot.getAbilities()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
}
// Add equip abilities
if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) {
for (String skill : this.getAvatar().getExtraAbilityEmbryos()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(Utils.abilityHash(skill))
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
}
//
return abilityControlBlock.build();
}
@Override
public void onDeath(int killerId) {
this.killedType = PlayerDieType.PLAYER_DIE_TYPE_KILL_BY_MONSTER;
this.killedBy = killerId;
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
}
public void onDeath(PlayerDieType dieType, int killerId) {
this.killedType = dieType;
this.killedBy = killerId;
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
}
@Override
public float heal(float amount) {
float healed = super.heal(amount);
if (healed > 0f) {
getScene().broadcastPacket(
new PacketEntityFightPropChangeReasonNotify(this, FightProperty.FIGHT_PROP_CUR_HP, healed, PropChangeReason.PROP_CHANGE_REASON_ABILITY, ChangeHpReason.CHANGE_HP_REASON_ADD_ABILITY)
);
}
return healed;
}
public void clearEnergy(ChangeEnergyReason reason) {
// Fight props.
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
// Get max energy.
float maxEnergy = this.avatar.getFightProperty(maxEnergyProp);
// Set energy to zero.
this.avatar.setCurrentEnergy(curEnergyProp, 0);
// Send packets.
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) {
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -maxEnergy, reason));
}
}
public void addEnergy(float amount, PropChangeReason reason) {
this.addEnergy(amount, reason, false);
}
public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) {
// Get current and maximum energy for this avatar.
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
float curEnergy = this.getFightProperty(curEnergyProp);
float maxEnergy = this.getFightProperty(maxEnergyProp);
// Get energy recharge.
float energyRecharge = this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
// Scale amount by energy recharge, if the amount is not flat.
if (!isFlat) {
amount *= energyRecharge;
}
// Determine the new energy value.
float newEnergy = Math.min(curEnergy + amount, maxEnergy);
// Set energy and notify.
if (newEnergy != curEnergy) {
this.avatar.setCurrentEnergy(curEnergyProp, newEnergy);
this.getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp));
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason));
}
}
public SceneAvatarInfo getSceneAvatarInfo() {
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()
.setUid(this.getPlayer().getUid())
.setAvatarId(this.getAvatar().getAvatarId())
.setGuid(this.getAvatar().getGuid())
.setPeerId(this.getPlayer().getPeerId())
.addAllTalentIdList(this.getAvatar().getTalentIdList())
.setCoreProudSkillLevel(this.getAvatar().getCoreProudSkillLevel())
.putAllSkillLevelMap(this.getAvatar().getSkillLevelMap())
.setSkillDepotId(this.getAvatar().getSkillDepotId())
.addAllInherentProudSkillList(this.getAvatar().getProudSkillList())
.putAllProudSkillExtraLevelMap(this.getAvatar().getProudSkillBonusMap())
.addAllTeamResonanceList(this.getAvatar().getPlayer().getTeamManager().getTeamResonances())
.setWearingFlycloakId(this.getAvatar().getFlyCloak())
.setCostumeId(this.getAvatar().getCostume())
.setBornTime(this.getAvatar().getBornTime());
for (GameItem item : avatar.getEquips().values()) {
if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) {
avatarInfo.setWeapon(item.createSceneWeaponInfo());
} else {
avatarInfo.addReliquaryList(item.createSceneReliquaryInfo());
}
avatarInfo.addEquipIdList(item.getItemId());
}
return avatarInfo.build();
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR)
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLastMoveSceneTimeMs(this.getLastMoveSceneTimeMs())
.setLastMoveReliableSeq(this.getLastMoveReliableSeq())
.setLifeState(this.getLifeState().getValue());
if (this.getScene() != null) {
entityInfo.setMotionInfo(this.getMotionInfo());
}
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
if (entry.getIntKey() == 0) {
continue;
}
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
entityInfo.addFightPropList(fightProp);
}
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getAvatar().getLevel()))
.build();
entityInfo.addPropList(pair);
entityInfo.setAvatar(this.getSceneAvatarInfo());
return entityInfo.build();
}
public AbilityControlBlock getAbilityControlBlock() {
AvatarData data = this.getAvatar().getAvatarData();
AbilityControlBlock.Builder abilityControlBlock = AbilityControlBlock.newBuilder();
int embryoId = 0;
// Add avatar abilities
if (data.getAbilities() != null) {
for (int id : data.getAbilities()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
}
// Add default abilities
for (int id : GameConstants.DEFAULT_ABILITY_HASHES) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
// Add team resonances
for (int id : this.getPlayer().getTeamManager().getTeamResonancesConfig()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
// Add skill depot abilities
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.getAvatar().getSkillDepotId());
if (skillDepot != null && skillDepot.getAbilities() != null) {
for (int id : skillDepot.getAbilities()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(id)
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
}
// Add equip abilities
if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) {
for (String skill : this.getAvatar().getExtraAbilityEmbryos()) {
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
.setAbilityId(++embryoId)
.setAbilityNameHash(Utils.abilityHash(skill))
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
.build();
abilityControlBlock.addAbilityEmbryoList(emb);
}
}
//
return abilityControlBlock.build();
}
}

View File

@ -38,125 +38,125 @@ import lombok.ToString;
@ToString(callSuper = true)
public class EntityGadget extends EntityBaseGadget {
private final GadgetData data;
private final Position pos;
private final Position rot;
private int gadgetId;
private final GadgetData data;
private final Position pos;
private final Position rot;
private int gadgetId;
private int state;
private int pointType;
private GadgetContent content;
private Int2FloatOpenHashMap fightProp;
private SceneGadget metaGadget;
private int state;
private int pointType;
private GadgetContent content;
private Int2FloatOpenHashMap fightProp;
private SceneGadget metaGadget;
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) {
super(scene);
this.data = GameData.getGadgetDataMap().get(gadgetId);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.gadgetId = gadgetId;
this.pos = pos.clone();
this.rot = rot != null ? rot.clone() : new Position();
}
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) {
super(scene);
this.data = GameData.getGadgetDataMap().get(gadgetId);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.gadgetId = gadgetId;
this.pos = pos.clone();
this.rot = rot != null ? rot.clone() : new Position();
}
public EntityGadget(Scene scene, int gadgetId, Position pos) {
this(scene, gadgetId, pos, new Position());
}
public EntityGadget(Scene scene, int gadgetId, Position pos) {
this(scene, gadgetId, pos, new Position());
}
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
this(scene, gadgetId, pos, rot);
this.content = content;
}
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
this(scene, gadgetId, pos, rot);
this.content = content;
}
public GadgetData getGadgetData() {
return data;
}
public GadgetData getGadgetData() {
return data;
}
@Override
public Position getPosition() {
return this.pos;
}
@Override
public Position getPosition() {
return this.pos;
}
@Override
public Position getRotation() {
return this.rot;
}
@Override
public Position getRotation() {
return this.rot;
}
public int getGadgetId() {
return gadgetId;
}
public int getGadgetId() {
return gadgetId;
}
public void setGadgetId(int gadgetId) {
this.gadgetId = gadgetId;
}
public void setGadgetId(int gadgetId) {
this.gadgetId = gadgetId;
}
public int getState() {
return state;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public void setState(int state) {
this.state = state;
}
public void updateState(int state){
this.setState(state);
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
}
public void updateState(int state) {
this.setState(state);
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
}
public int getPointType() {
return pointType;
}
public int getPointType() {
return pointType;
}
public void setPointType(int pointType) {
this.pointType = pointType;
}
public void setPointType(int pointType) {
this.pointType = pointType;
}
public GadgetContent getContent() {
return content;
}
public GadgetContent getContent() {
return content;
}
@Deprecated // Dont use!
public void setContent(GadgetContent content) {
this.content = this.content == null ? content : this.content;
}
@Deprecated // Dont use!
public void setContent(GadgetContent content) {
this.content = this.content == null ? content : this.content;
}
public SceneGadget getMetaGadget() {
return metaGadget;
}
public SceneGadget getMetaGadget() {
return metaGadget;
}
public void setMetaGadget(SceneGadget metaGadget) {
this.metaGadget = metaGadget;
}
public void setMetaGadget(SceneGadget metaGadget) {
this.metaGadget = metaGadget;
}
// TODO refactor
public void buildContent() {
if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) {
return;
}
// TODO refactor
public void buildContent() {
if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) {
return;
}
EntityType type = getGadgetData().getType();
GadgetContent content = switch (type) {
case GatherPoint -> new GadgetGatherPoint(this);
case GatherObject -> new GadgetGatherObject(this);
case Worktop -> new GadgetWorktop(this);
case RewardStatue -> new GadgetRewardStatue(this);
case Chest -> new GadgetChest(this);
EntityType type = getGadgetData().getType();
GadgetContent content = switch (type) {
case GatherPoint -> new GadgetGatherPoint(this);
case GatherObject -> new GadgetGatherObject(this);
case Worktop -> new GadgetWorktop(this);
case RewardStatue -> new GadgetRewardStatue(this);
case Chest -> new GadgetChest(this);
case Gadget -> new GadgetObject(this);
default -> null;
};
default -> null;
};
this.content = content;
}
this.content = content;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
if (this.fightProp == null) this.fightProp = new Int2FloatOpenHashMap();
return this.fightProp;
}
@Override
@Override
public Int2FloatOpenHashMap getFightProperties() {
if (this.fightProp == null) this.fightProp = new Int2FloatOpenHashMap();
return this.fightProp;
}
@Override
public void onInteract(Player player, GadgetInteractReq interactReq) {
if (this.getContent() == null) {
if (this.getContent() == null) {
return;
}
@ -165,68 +165,68 @@ public class EntityGadget extends EntityBaseGadget {
if (shouldDelete) {
this.getScene().killEntity(this);
}
}
}
@Override
public void onCreate() {
// Lua event
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(this.getConfigId()));
}
@Override
public void onCreate() {
// Lua event
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(this.getConfigId()));
}
@Override
public void onDeath(int killerId) {
if (this.getSpawnEntry() != null) {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
}
if (getScene().getChallenge() != null) {
getScene().getChallenge().onGadgetDeath(this);
}
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_GADGET_DIE, new ScriptArgs(this.getConfigId()));
}
@Override
public void onDeath(int killerId) {
if (this.getSpawnEntry() != null) {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
}
if (getScene().getChallenge() != null) {
getScene().getChallenge().onGadgetDeath(this);
}
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_GADGET_DIE, new ScriptArgs(this.getConfigId()));
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build();
entityInfo.addPropList(pair);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build();
entityInfo.addPropList(pair);
// We do not use the getter to null check because the getter will create a fight prop map if it is null
if (this.fightProp != null) {
this.addAllFightPropsToEntityInfo(entityInfo);
}
// We do not use the getter to null check because the getter will create a fight prop map if it is null
if (this.fightProp != null) {
this.addAllFightPropsToEntityInfo(entityInfo);
}
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setGroupId(this.getGroupId())
.setConfigId(this.getConfigId())
.setGadgetState(this.getState())
.setIsEnableInteract(true)
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setGroupId(this.getGroupId())
.setConfigId(this.getConfigId())
.setGadgetState(this.getState())
.setIsEnableInteract(true)
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
if (this.getContent() != null) {
this.getContent().onBuildProto(gadgetInfo);
}
if (this.getContent() != null) {
this.getContent().onBuildProto(gadgetInfo);
}
entityInfo.setGadget(gadgetInfo);
entityInfo.setGadget(gadgetInfo);
return entityInfo.build();
}
return entityInfo.build();
}
}

View File

@ -28,95 +28,95 @@ import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
public class EntityItem extends EntityBaseGadget {
private final Position pos;
private final Position rot;
private final GameItem item;
private final long guid;
private final Position pos;
private final Position rot;
private final boolean share;
private final GameItem item;
private final long guid;
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) {
super(scene);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.pos = new Position(pos);
this.rot = new Position();
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
this.item = new GameItem(itemData, count);
this.share = true;
}
private final boolean share;
// In official game, some drop items are shared to all players, and some other items are independent to all players
// For example, if you killed a monster in MP mode, all players could get drops but rarity and number of them are different
// but if you broke regional mine, when someone picked up the drop then it disappeared
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count, boolean share) {
super(scene);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.pos = new Position(pos);
this.rot = new Position();
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
this.item = new GameItem(itemData, count);
this.share = share;
}
@Override
public int getId() {
return this.id;
}
private GameItem getItem() {
return this.item;
}
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) {
super(scene);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.pos = new Position(pos);
this.rot = new Position();
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
this.item = new GameItem(itemData, count);
this.share = true;
}
public ItemData getItemData() {
return this.getItem().getItemData();
}
// In official game, some drop items are shared to all players, and some other items are independent to all players
// For example, if you killed a monster in MP mode, all players could get drops but rarity and number of them are different
// but if you broke regional mine, when someone picked up the drop then it disappeared
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count, boolean share) {
super(scene);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.pos = new Position(pos);
this.rot = new Position();
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
this.item = new GameItem(itemData, count);
this.share = share;
}
public long getGuid() {
return guid;
}
@Override
public int getId() {
return this.id;
}
public int getCount() {
return this.getItem().getCount();
}
@Override
public int getGadgetId() {
return this.getItemData().getGadgetId();
}
private GameItem getItem() {
return this.item;
}
@Override
public Position getPosition() {
return this.pos;
}
public ItemData getItemData() {
return this.getItem().getItemData();
}
@Override
public Position getRotation() {
return this.rot;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return null;
}
public long getGuid() {
return guid;
}
public boolean isShare() {
return share;
}
@Override
public void onInteract(Player player, GadgetInteractReq interactReq) {
// check drop owner to avoid someone picked up item in others' world
public int getCount() {
return this.getItem().getCount();
}
@Override
public int getGadgetId() {
return this.getItemData().getGadgetId();
}
@Override
public Position getPosition() {
return this.pos;
}
@Override
public Position getRotation() {
return this.rot;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return null;
}
public boolean isShare() {
return share;
}
@Override
public void onInteract(Player player, GadgetInteractReq interactReq) {
// check drop owner to avoid someone picked up item in others' world
if (!this.isShare()) {
int dropOwner = (int) (this.getGuid() >> 32);
if (dropOwner != player.getUid()) {
return;
}
}
this.getScene().removeEntity(this);
GameItem item = new GameItem(this.getItemData(), this.getCount());
// Add to inventory
boolean success = player.getInventory().addItem(item, ActionReason.SubfieldDrop);
if (success) {
@ -128,39 +128,39 @@ public class EntityItem extends EntityBaseGadget {
}
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build();
entityInfo.addPropList(pair);
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getItemData().getGadgetId())
.setTrifleItem(this.getItem().toProto())
.setBornType(GadgetBornType.GADGET_BORN_TYPE_IN_AIR)
.setAuthorityPeerId(this.getWorld().getHostPeerId())
.setIsEnableInteract(true);
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
.setBornPos(Vector.newBuilder())
.build();
entityInfo.setGadget(gadgetInfo);
return entityInfo.build();
}
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(1);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
.build();
entityInfo.addPropList(pair);
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getItemData().getGadgetId())
.setTrifleItem(this.getItem().toProto())
.setBornType(GadgetBornType.GADGET_BORN_TYPE_IN_AIR)
.setAuthorityPeerId(this.getWorld().getHostPeerId())
.setIsEnableInteract(true);
entityInfo.setGadget(gadgetInfo);
return entityInfo.build();
}
}

View File

@ -39,257 +39,257 @@ import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
public class EntityMonster extends GameEntity {
private final MonsterData monsterData;
private final Int2FloatOpenHashMap fightProp;
private final Position pos;
private final Position rot;
private final Position bornPos;
private final int level;
private int weaponEntityId;
private int poseId;
public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) {
super(scene);
this.id = getWorld().getNextEntityId(EntityIdType.MONSTER);
this.monsterData = monsterData;
this.fightProp = new Int2FloatOpenHashMap();
this.pos = new Position(pos);
this.rot = new Position();
this.bornPos = getPosition().clone();
this.level = level;
// Monster weapon
if (getMonsterWeaponId() > 0) {
this.weaponEntityId = getWorld().getNextEntityId(EntityIdType.WEAPON);
}
this.recalcStats();
}
@Override
public int getId() {
return this.id;
}
private final MonsterData monsterData;
private final Int2FloatOpenHashMap fightProp;
public MonsterData getMonsterData() {
return monsterData;
}
public int getMonsterWeaponId() {
return getMonsterData().getWeaponId();
}
private int getMonsterId() {
return this.getMonsterData().getId();
}
private final Position pos;
private final Position rot;
private final Position bornPos;
private final int level;
private int weaponEntityId;
private int poseId;
public int getLevel() {
return level;
}
public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) {
super(scene);
this.id = getWorld().getNextEntityId(EntityIdType.MONSTER);
this.monsterData = monsterData;
this.fightProp = new Int2FloatOpenHashMap();
this.pos = new Position(pos);
this.rot = new Position();
this.bornPos = getPosition().clone();
this.level = level;
@Override
public Position getPosition() {
return this.pos;
}
// Monster weapon
if (getMonsterWeaponId() > 0) {
this.weaponEntityId = getWorld().getNextEntityId(EntityIdType.WEAPON);
}
@Override
public Position getRotation() {
return this.rot;
}
public Position getBornPos() {
return bornPos;
}
this.recalcStats();
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return fightProp;
}
@Override
public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
}
@Override
public int getId() {
return this.id;
}
public int getPoseId() {
return poseId;
}
public MonsterData getMonsterData() {
return monsterData;
}
public void setPoseId(int poseId) {
this.poseId = poseId;
}
@Override
public int getMonsterWeaponId() {
return getMonsterData().getWeaponId();
}
private int getMonsterId() {
return this.getMonsterData().getId();
}
public int getLevel() {
return level;
}
@Override
public Position getPosition() {
return this.pos;
}
@Override
public Position getRotation() {
return this.rot;
}
public Position getBornPos() {
return bornPos;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return fightProp;
}
@Override
public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
}
public int getPoseId() {
return poseId;
}
public void setPoseId(int poseId) {
this.poseId = poseId;
}
@Override
public void onInteract(Player player, GadgetInteractReq interactReq) {
EnvAnimalGatherConfigData gatherData = GameData.getEnvAnimalGatherConfigDataMap().get(this.getMonsterData().getId());
EnvAnimalGatherConfigData gatherData = GameData.getEnvAnimalGatherConfigDataMap().get(this.getMonsterData().getId());
if (gatherData == null) {
return;
}
player.getInventory().addItem(gatherData.getGatherItem(), ActionReason.SubfieldDrop);
this.getScene().killEntity(this);
}
@Override
public void onCreate() {
// Lua event
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(this.getConfigId()));
}
@Override
public void damage(float amount, int killerId) {
// Get HP before damage.
float hpBeforeDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
@Override
public void onCreate() {
// Lua event
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(this.getConfigId()));
}
// Apply damage.
super.damage(amount, killerId);
@Override
public void damage(float amount, int killerId) {
// Get HP before damage.
float hpBeforeDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
// Get HP after damage.
float hpAfterDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
// Apply damage.
super.damage(amount, killerId);
// Invoke energy drop logic.
for (Player player : this.getScene().getPlayers()) {
player.getEnergyManager().handleMonsterEnergyDrop(this, hpBeforeDamage, hpAfterDamage);
}
}
// Get HP after damage.
float hpAfterDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
@Override
public void onDeath(int killerId) {
if (this.getSpawnEntry() != null) {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
}
// first set the challenge data
if (getScene().getChallenge() != null) {
getScene().getChallenge().onMonsterDeath(this);
}
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
if(getScene().getScriptManager().getScriptMonsterSpawnService() != null){
getScene().getScriptManager().getScriptMonsterSpawnService().onMonsterDead(this);
}
// prevent spawn monster after success
if(getScene().getChallenge() != null && getScene().getChallenge().inProgress()){
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
}else if(getScene().getChallenge() == null){
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
}
}
// Battle Pass trigger
getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
}
public void recalcStats() {
// Monster data
MonsterData data = this.getMonsterData();
// Invoke energy drop logic.
for (Player player : this.getScene().getPlayers()) {
player.getEnergyManager().handleMonsterEnergyDrop(this, hpBeforeDamage, hpAfterDamage);
}
}
// Get hp percent, set to 100% if none
float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
// Clear properties
this.getFightProperties().clear();
// Base stats
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, data.getBaseHp());
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, data.getBaseAttack());
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, data.getBaseDefense());
this.setFightProperty(FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, data.getPhysicalSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_FIRE_SUB_HURT, .1f);
this.setFightProperty(FightProperty.FIGHT_PROP_ELEC_SUB_HURT, data.getElecSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_WATER_SUB_HURT, data.getWaterSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_GRASS_SUB_HURT, data.getGrassSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_WIND_SUB_HURT, data.getWindSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_ROCK_SUB_HURT, .1f);
this.setFightProperty(FightProperty.FIGHT_PROP_ICE_SUB_HURT, data.getIceSubHurt());
// Level curve
MonsterCurveData curve = GameData.getMonsterCurveDataMap().get(this.getLevel());
if (curve != null) {
for (PropGrowCurve growCurve : data.getPropGrowCurves()) {
FightProperty prop = FightProperty.getPropByName(growCurve.getType());
this.setFightProperty(prop, this.getFightProperty(prop) * curve.getMultByProp(growCurve.getGrowCurve()));
}
}
// Set % stats
this.setFightProperty(
FightProperty.FIGHT_PROP_MAX_HP,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_HP) * (1f + getFightProperty(FightProperty.FIGHT_PROP_HP_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_HP)
);
this.setFightProperty(
FightProperty.FIGHT_PROP_CUR_ATTACK,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK) * (1f + getFightProperty(FightProperty.FIGHT_PROP_ATTACK_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_ATTACK)
);
this.setFightProperty(
FightProperty.FIGHT_PROP_CUR_DEFENSE,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE) * (1f + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE)
);
// Set current hp
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
}
@Override
public void onDeath(int killerId) {
if (this.getSpawnEntry() != null) {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
}
// first set the challenge data
if (getScene().getChallenge() != null) {
getScene().getChallenge().onMonsterDeath(this);
}
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
if (getScene().getScriptManager().getScriptMonsterSpawnService() != null) {
getScene().getScriptManager().getScriptMonsterSpawnService().onMonsterDead(this);
}
// prevent spawn monster after success
if (getScene().getChallenge() != null && getScene().getChallenge().inProgress()) {
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
}else if (getScene().getChallenge() == null) {
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
}
}
// Battle Pass trigger
getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(this.getBornPos().toProto()))
.setBornPos(this.getBornPos().toProto())
.build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER)
.setMotionInfo(this.getMotionInfo())
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(this.getLifeState().getValue());
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
if (entry.getIntKey() == 0) {
continue;
}
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
entityInfo.addFightPropList(fightProp);
}
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel()))
.build();
entityInfo.addPropList(pair);
SceneMonsterInfo.Builder monsterInfo = SceneMonsterInfo.newBuilder()
.setMonsterId(getMonsterId())
.setGroupId(this.getGroupId())
.setConfigId(this.getConfigId())
.addAllAffixList(getMonsterData().getAffix())
.setAuthorityPeerId(getWorld().getHostPeerId())
.setPoseId(this.getPoseId())
.setBlockId(3001)
.setBornType(MonsterBornType.MONSTER_BORN_TYPE_DEFAULT)
.setSpecialNameId(40);
if (getMonsterData().getDescribeData() != null) {
monsterInfo.setTitleId(getMonsterData().getDescribeData().getTitleID());
}
if (this.getMonsterWeaponId() > 0) {
SceneWeaponInfo weaponInfo = SceneWeaponInfo.newBuilder()
.setEntityId(this.weaponEntityId)
.setGadgetId(this.getMonsterWeaponId())
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.build();
monsterInfo.addWeaponList(weaponInfo);
}
entityInfo.setMonster(monsterInfo);
public void recalcStats() {
// Monster data
MonsterData data = this.getMonsterData();
return entityInfo.build();
}
// Get hp percent, set to 100% if none
float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
// Clear properties
this.getFightProperties().clear();
// Base stats
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, data.getBaseHp());
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, data.getBaseAttack());
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, data.getBaseDefense());
this.setFightProperty(FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, data.getPhysicalSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_FIRE_SUB_HURT, .1f);
this.setFightProperty(FightProperty.FIGHT_PROP_ELEC_SUB_HURT, data.getElecSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_WATER_SUB_HURT, data.getWaterSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_GRASS_SUB_HURT, data.getGrassSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_WIND_SUB_HURT, data.getWindSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_ROCK_SUB_HURT, .1f);
this.setFightProperty(FightProperty.FIGHT_PROP_ICE_SUB_HURT, data.getIceSubHurt());
// Level curve
MonsterCurveData curve = GameData.getMonsterCurveDataMap().get(this.getLevel());
if (curve != null) {
for (PropGrowCurve growCurve : data.getPropGrowCurves()) {
FightProperty prop = FightProperty.getPropByName(growCurve.getType());
this.setFightProperty(prop, this.getFightProperty(prop) * curve.getMultByProp(growCurve.getGrowCurve()));
}
}
// Set % stats
this.setFightProperty(
FightProperty.FIGHT_PROP_MAX_HP,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_HP) * (1f + getFightProperty(FightProperty.FIGHT_PROP_HP_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_HP)
);
this.setFightProperty(
FightProperty.FIGHT_PROP_CUR_ATTACK,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK) * (1f + getFightProperty(FightProperty.FIGHT_PROP_ATTACK_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_ATTACK)
);
this.setFightProperty(
FightProperty.FIGHT_PROP_CUR_DEFENSE,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE) * (1f + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE)
);
// Set current hp
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
}
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(this.getBornPos().toProto()))
.setBornPos(this.getBornPos().toProto())
.build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER)
.setMotionInfo(this.getMotionInfo())
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setEntityClientData(EntityClientData.newBuilder())
.setEntityAuthorityInfo(authority)
.setLifeState(this.getLifeState().getValue());
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
if (entry.getIntKey() == 0) {
continue;
}
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
entityInfo.addFightPropList(fightProp);
}
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel()))
.build();
entityInfo.addPropList(pair);
SceneMonsterInfo.Builder monsterInfo = SceneMonsterInfo.newBuilder()
.setMonsterId(getMonsterId())
.setGroupId(this.getGroupId())
.setConfigId(this.getConfigId())
.addAllAffixList(getMonsterData().getAffix())
.setAuthorityPeerId(getWorld().getHostPeerId())
.setPoseId(this.getPoseId())
.setBlockId(3001)
.setBornType(MonsterBornType.MONSTER_BORN_TYPE_DEFAULT)
.setSpecialNameId(40);
if (getMonsterData().getDescribeData() != null) {
monsterInfo.setTitleId(getMonsterData().getDescribeData().getTitleID());
}
if (this.getMonsterWeaponId() > 0) {
SceneWeaponInfo weaponInfo = SceneWeaponInfo.newBuilder()
.setEntityId(this.weaponEntityId)
.setGadgetId(this.getMonsterWeaponId())
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.build();
monsterInfo.addWeaponList(weaponInfo);
}
entityInfo.setMonster(monsterInfo);
return entityInfo.build();
}
}

View File

@ -31,104 +31,104 @@ import java.util.ArrayList;
public class EntityVehicle extends EntityBaseGadget {
private final Player owner;
private final Int2FloatOpenHashMap fightProp;
private final Player owner;
private final Int2FloatOpenHashMap fightProp;
private final Position pos;
private final Position rot;
private final Position pos;
private final Position rot;
private final int pointId;
private final int gadgetId;
private final int pointId;
private final int gadgetId;
private float curStamina;
private List<VehicleMember> vehicleMembers;
private float curStamina;
private List<VehicleMember> vehicleMembers;
public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
super(scene);
this.owner = player;
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.fightProp = new Int2FloatOpenHashMap();
this.pos = new Position(pos);
this.rot = new Position(rot);
this.gadgetId = gadgetId;
this.pointId = pointId;
this.curStamina = 240;
this.vehicleMembers = new ArrayList<VehicleMember>();
}
public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
super(scene);
this.owner = player;
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.fightProp = new Int2FloatOpenHashMap();
this.pos = new Position(pos);
this.rot = new Position(rot);
this.gadgetId = gadgetId;
this.pointId = pointId;
this.curStamina = 240;
this.vehicleMembers = new ArrayList<VehicleMember>();
}
@Override
public int getGadgetId() { return gadgetId; }
@Override
public int getGadgetId() { return gadgetId; }
public Player getOwner() {
return owner;
}
public Player getOwner() {
return owner;
}
public float getCurStamina() { return curStamina; }
public float getCurStamina() { return curStamina; }
public void setCurStamina(float stamina) { this.curStamina = stamina; }
public void setCurStamina(float stamina) { this.curStamina = stamina; }
public int getPointId() { return pointId; }
public int getPointId() { return pointId; }
public List<VehicleMember> getVehicleMembers() { return vehicleMembers; }
public List<VehicleMember> getVehicleMembers() { return vehicleMembers; }
@Override
public Int2FloatOpenHashMap getFightProperties() {
return fightProp;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return fightProp;
}
@Override
public Position getPosition() { return this.pos; }
@Override
public Position getPosition() { return this.pos; }
@Override
public Position getRotation() {
return this.rot;
}
@Override
public SceneEntityInfo toProto() {
@Override
public Position getRotation() {
return this.rot;
}
VehicleInfo vehicle = VehicleInfo.newBuilder()
.setOwnerUid(this.owner.getUid())
.setCurStamina(getCurStamina())
.build();
@Override
public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(getPosition().toProto()))
.setBornPos(getPosition().toProto())
.build();
VehicleInfo vehicle = VehicleInfo.newBuilder()
.setOwnerUid(this.owner.getUid())
.setCurStamina(getCurStamina())
.build();
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setAuthorityPeerId(this.getOwner().getPeerId())
.setIsEnableInteract(true)
.setVehicleInfo(vehicle);
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(getPosition().toProto()))
.setBornPos(getPosition().toProto())
.build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setGadget(gadgetInfo)
.setEntityAuthorityInfo(authority)
.setLifeState(1);
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
.setGadgetId(this.getGadgetId())
.setAuthorityPeerId(this.getOwner().getPeerId())
.setIsEnableInteract(true)
.setVehicleInfo(vehicle);
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 47))
.build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
.setGadget(gadgetInfo)
.setEntityAuthorityInfo(authority)
.setLifeState(1);
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
if (entry.getIntKey() == 0) {
continue;
}
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
entityInfo.addFightPropList(fightProp);
}
PropPair pair = PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 47))
.build();
entityInfo.addPropList(pair);
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
if (entry.getIntKey() == 0) {
continue;
}
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
entityInfo.addFightPropList(fightProp);
}
return entityInfo.build();
}
entityInfo.addPropList(pair);
return entityInfo.build();
}
}

View File

@ -23,236 +23,236 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public abstract class GameEntity {
protected int id;
private final Scene scene;
private SpawnDataEntry spawnEntry;
private int blockId;
private int configId;
private int groupId;
private MotionState moveState;
private int lastMoveSceneTimeMs;
private int lastMoveReliableSeq;
// Abilities
private Map<String, Float> metaOverrideMap;
private Int2ObjectMap<String> metaModifiers;
public GameEntity(Scene scene) {
this.scene = scene;
this.moveState = MotionState.MOTION_STATE_NONE;
}
public int getId() {
return this.id;
}
public int getEntityType() {
return getId() >> 24;
}
public World getWorld() {
return this.getScene().getWorld();
}
protected int id;
private final Scene scene;
private SpawnDataEntry spawnEntry;
public Scene getScene() {
return this.scene;
}
public boolean isAlive() {
return true;
}
private int blockId;
private int configId;
private int groupId;
public LifeState getLifeState() {
return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
}
public Map<String, Float> getMetaOverrideMap() {
if (this.metaOverrideMap == null) {
this.metaOverrideMap = new HashMap<>();
}
return this.metaOverrideMap;
}
public Int2ObjectMap<String> getMetaModifiers() {
if (this.metaModifiers == null) {
this.metaModifiers = new Int2ObjectOpenHashMap<>();
}
return this.metaModifiers;
}
private MotionState moveState;
private int lastMoveSceneTimeMs;
private int lastMoveReliableSeq;
public abstract Int2FloatOpenHashMap getFightProperties();
public abstract Position getPosition();
public abstract Position getRotation();
public MotionState getMotionState() {
return moveState;
}
// Abilities
private Map<String, Float> metaOverrideMap;
private Int2ObjectMap<String> metaModifiers;
public void setMotionState(MotionState moveState) {
this.moveState = moveState;
}
public GameEntity(Scene scene) {
this.scene = scene;
this.moveState = MotionState.MOTION_STATE_NONE;
}
public int getLastMoveSceneTimeMs() {
return lastMoveSceneTimeMs;
}
public int getId() {
return this.id;
}
public void setLastMoveSceneTimeMs(int lastMoveSceneTimeMs) {
this.lastMoveSceneTimeMs = lastMoveSceneTimeMs;
}
public int getEntityType() {
return getId() >> 24;
}
public int getLastMoveReliableSeq() {
return lastMoveReliableSeq;
}
public World getWorld() {
return this.getScene().getWorld();
}
public void setLastMoveReliableSeq(int lastMoveReliableSeq) {
this.lastMoveReliableSeq = lastMoveReliableSeq;
}
public void setFightProperty(FightProperty prop, float value) {
this.getFightProperties().put(prop.getId(), value);
}
private void setFightProperty(int id, float value) {
this.getFightProperties().put(id, value);
}
public void addFightProperty(FightProperty prop, float value) {
this.getFightProperties().put(prop.getId(), getFightProperty(prop) + value);
}
public float getFightProperty(FightProperty prop) {
return getFightProperties().getOrDefault(prop.getId(), 0f);
}
public void addAllFightPropsToEntityInfo(SceneEntityInfo.Builder entityInfo) {
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
if (entry.getIntKey() == 0) {
continue;
}
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
entityInfo.addFightPropList(fightProp);
}
}
public int getBlockId() {
return blockId;
}
public Scene getScene() {
return this.scene;
}
public void setBlockId(int blockId) {
this.blockId = blockId;
}
public int getConfigId() {
return configId;
}
public boolean isAlive() {
return true;
}
public void setConfigId(int configId) {
this.configId = configId;
}
public int getGroupId() {
return groupId;
}
public LifeState getLifeState() {
return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
protected MotionInfo getMotionInfo() {
MotionInfo proto = MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(Vector.newBuilder())
.setState(this.getMotionState())
.build();
return proto;
}
public Map<String, Float> getMetaOverrideMap() {
if (this.metaOverrideMap == null) {
this.metaOverrideMap = new HashMap<>();
}
return this.metaOverrideMap;
}
public SpawnDataEntry getSpawnEntry() {
return spawnEntry;
}
public Int2ObjectMap<String> getMetaModifiers() {
if (this.metaModifiers == null) {
this.metaModifiers = new Int2ObjectOpenHashMap<>();
}
return this.metaModifiers;
}
public void setSpawnEntry(SpawnDataEntry spawnEntry) {
this.spawnEntry = spawnEntry;
}
public float heal(float amount) {
if (this.getFightProperties() == null) {
return 0f;
}
public abstract Int2FloatOpenHashMap getFightProperties();
float curHp = getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
float maxHp = getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
if (curHp >= maxHp) {
return 0f;
}
float healed = Math.min(maxHp - curHp, amount);
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, healed);
getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
return healed;
}
public void damage(float amount) {
damage(amount, 0);
}
public void damage(float amount, int killerId) {
// Sanity check
if (getFightProperties() == null) {
return;
}
// Lose hp
addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -amount);
// Check if dead
boolean isDead = false;
if (getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
isDead = true;
}
// Packets
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
// Check if dead
if (isDead) {
getScene().killEntity(this, killerId);
}
}
/**
public abstract Position getPosition();
public abstract Position getRotation();
public MotionState getMotionState() {
return moveState;
}
public void setMotionState(MotionState moveState) {
this.moveState = moveState;
}
public int getLastMoveSceneTimeMs() {
return lastMoveSceneTimeMs;
}
public void setLastMoveSceneTimeMs(int lastMoveSceneTimeMs) {
this.lastMoveSceneTimeMs = lastMoveSceneTimeMs;
}
public int getLastMoveReliableSeq() {
return lastMoveReliableSeq;
}
public void setLastMoveReliableSeq(int lastMoveReliableSeq) {
this.lastMoveReliableSeq = lastMoveReliableSeq;
}
public void setFightProperty(FightProperty prop, float value) {
this.getFightProperties().put(prop.getId(), value);
}
private void setFightProperty(int id, float value) {
this.getFightProperties().put(id, value);
}
public void addFightProperty(FightProperty prop, float value) {
this.getFightProperties().put(prop.getId(), getFightProperty(prop) + value);
}
public float getFightProperty(FightProperty prop) {
return getFightProperties().getOrDefault(prop.getId(), 0f);
}
public void addAllFightPropsToEntityInfo(SceneEntityInfo.Builder entityInfo) {
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
if (entry.getIntKey() == 0) {
continue;
}
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
entityInfo.addFightPropList(fightProp);
}
}
public int getBlockId() {
return blockId;
}
public void setBlockId(int blockId) {
this.blockId = blockId;
}
public int getConfigId() {
return configId;
}
public void setConfigId(int configId) {
this.configId = configId;
}
public int getGroupId() {
return groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
protected MotionInfo getMotionInfo() {
MotionInfo proto = MotionInfo.newBuilder()
.setPos(getPosition().toProto())
.setRot(getRotation().toProto())
.setSpeed(Vector.newBuilder())
.setState(this.getMotionState())
.build();
return proto;
}
public SpawnDataEntry getSpawnEntry() {
return spawnEntry;
}
public void setSpawnEntry(SpawnDataEntry spawnEntry) {
this.spawnEntry = spawnEntry;
}
public float heal(float amount) {
if (this.getFightProperties() == null) {
return 0f;
}
float curHp = getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
float maxHp = getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
if (curHp >= maxHp) {
return 0f;
}
float healed = Math.min(maxHp - curHp, amount);
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, healed);
getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
return healed;
}
public void damage(float amount) {
damage(amount, 0);
}
public void damage(float amount, int killerId) {
// Sanity check
if (getFightProperties() == null) {
return;
}
// Lose hp
addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -amount);
// Check if dead
boolean isDead = false;
if (getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
isDead = true;
}
// Packets
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
// Check if dead
if (isDead) {
getScene().killEntity(this, killerId);
}
}
/**
* Called when a player interacts with this entity
* @param player Player that is interacting with this entity
* @param interactReq Interact request protobuf data
* @param interactReq Interact request protobuf data
*/
public void onInteract(Player player, GadgetInteractReq interactReq) {
}
/**
* Called when this entity is added to the world
*/
public void onCreate() {
}
/**
public void onCreate() {
}
/**
* Called when this entity dies
* @param killerId Entity id of the entity that killed this entity
*/
public void onDeath(int killerId) {
}
public abstract SceneEntityInfo toProto();
public void onDeath(int killerId) {
}
public abstract SceneEntityInfo toProto();
}

View File

@ -15,51 +15,51 @@ import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
public class GadgetChest extends GadgetContent {
public GadgetChest(EntityGadget gadget) {
super(gadget);
}
public boolean onInteract(Player player, GadgetInteractReq req) {
var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataSystem().getChestInteractHandlerMap();
var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName());
if(handler == null){
Grasscutter.getLogger().warn("Could not found the handler of this type of Chests {}", getGadget().getGadgetData().getJsonName());
return false;
}
public GadgetChest(EntityGadget gadget) {
super(gadget);
}
if(req.getOpType() == InterOpType.INTER_OP_TYPE_START && handler.isTwoStep()){
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START));
return false;
}else{
var success = handler.onInteract(this, player);
if (!success) {
return false;
}
public boolean onInteract(Player player, GadgetInteractReq req) {
var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataSystem().getChestInteractHandlerMap();
var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName());
if (handler == null) {
Grasscutter.getLogger().warn("Could not found the handler of this type of Chests {}", getGadget().getGadgetData().getJsonName());
return false;
}
getGadget().updateState(ScriptGadgetState.ChestOpened);
player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST));
return true;
}
}
if (req.getOpType() == InterOpType.INTER_OP_TYPE_START && handler.isTwoStep()) {
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START));
return false;
}else {
var success = handler.onInteract(this, player);
if (!success) {
return false;
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
if(getGadget().getMetaGadget() == null){
return;
}
getGadget().updateState(ScriptGadgetState.ChestOpened);
player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST));
var bossChest = getGadget().getMetaGadget().boss_chest;
if(bossChest != null){
var players = getGadget().getScene().getPlayers().stream().map(Player::getUid).toList();
return true;
}
}
gadgetInfo.setBossChest(BossChestInfo.newBuilder()
.setMonsterConfigId(bossChest.monster_config_id)
.setResin(bossChest.resin)
.addAllQualifyUidList(players)
.addAllRemainUidList(players)
.build());
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
if (getGadget().getMetaGadget() == null) {
return;
}
}
var bossChest = getGadget().getMetaGadget().boss_chest;
if (bossChest != null) {
var players = getGadget().getScene().getPlayers().stream().map(Player::getUid).toList();
gadgetInfo.setBossChest(BossChestInfo.newBuilder()
.setMonsterConfigId(bossChest.monster_config_id)
.setResin(bossChest.resin)
.addAllQualifyUidList(players)
.addAllRemainUidList(players)
.build());
}
}
}

View File

@ -23,7 +23,7 @@ public class BossChestInteractHandler implements ChestInteractHandler{
var monster = chest.getGadget().getMetaGadget().group.monsters.get(chest.getGadget().getMetaGadget().boss_chest.monster_config_id);
var reward = worldDataManager.getRewardByBossId(monster.monster_id);
if(reward == null){
if (reward == null) {
Grasscutter.getLogger().warn("Could not found the reward of boss monster {}", monster.monster_id);
return false;
}

View File

@ -24,16 +24,16 @@ public class ExpeditionSystem extends BaseGameSystem {
this.expeditionRewardData = new Int2ObjectOpenHashMap<>();
this.load();
}
public Int2ObjectMap<List<ExpeditionRewardDataList>> getExpeditionRewardDataList() {
return expeditionRewardData;
public Int2ObjectMap<List<ExpeditionRewardDataList>> getExpeditionRewardDataList() {
return expeditionRewardData;
}
public synchronized void load() {
try (Reader fileReader = DataLoader.loadReader("ExpeditionReward.json")) {
getExpeditionRewardDataList().clear();
List<ExpeditionRewardInfo> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ExpeditionRewardInfo.class).getType());
if(banners.size() > 0) {
if (banners.size() > 0) {
for (ExpeditionRewardInfo di : banners) {
getExpeditionRewardDataList().put(di.getExpId(), di.getExpeditionRewardDataList());
}

View File

@ -15,243 +15,243 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public class FriendsList extends BasePlayerManager {
private final Int2ObjectMap<Friendship> friends;
private final Int2ObjectMap<Friendship> pendingFriends;
private boolean loaded = false;
public FriendsList(Player player) {
super(player);
this.friends = new Int2ObjectOpenHashMap<Friendship>();
this.pendingFriends = new Int2ObjectOpenHashMap<Friendship>();
}
public boolean hasLoaded() {
return loaded;
}
public synchronized Int2ObjectMap<Friendship> getFriends() {
return friends;
}
public synchronized Int2ObjectMap<Friendship> getPendingFriends() {
return this.pendingFriends;
}
public synchronized boolean isFriendsWith(int uid) {
return this.getFriends().containsKey(uid);
}
private synchronized Friendship getFriendshipById(int id) {
Friendship friendship = this.getFriends().get(id);
if (friendship == null) {
friendship = this.getPendingFriendById(id);
}
return friendship;
}
private synchronized Friendship getFriendById(int id) {
return this.getFriends().get(id);
}
private synchronized Friendship getPendingFriendById(int id) {
return this.getPendingFriends().get(id);
}
public void addFriend(Friendship friendship) {
getFriends().put(friendship.getFriendId(), friendship);
}
public void addPendingFriend(Friendship friendship) {
getPendingFriends().put(friendship.getFriendId(), friendship);
}
public synchronized void handleFriendRequest(int targetUid, DealAddFriendResultType result) {
// Check if player has sent friend request
Friendship myFriendship = this.getPendingFriendById(targetUid);
if (myFriendship == null) {
return;
}
// Make sure asker cant do anything
if (myFriendship.getAskerId() == this.getPlayer().getUid()) {
return;
}
private final Int2ObjectMap<Friendship> friends;
private final Int2ObjectMap<Friendship> pendingFriends;
Player target = getPlayer().getSession().getServer().getPlayerByUid(targetUid, true);
if (target == null) {
return; // Should never happen
}
private boolean loaded = false;
// Get target's friendship
Friendship theirFriendship = null;
if (target.isOnline()) {
theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getUid());
} else {
theirFriendship = DatabaseHelper.getReverseFriendship(myFriendship);
}
public FriendsList(Player player) {
super(player);
this.friends = new Int2ObjectOpenHashMap<Friendship>();
this.pendingFriends = new Int2ObjectOpenHashMap<Friendship>();
}
if (theirFriendship == null) {
// They dont have us on their friends list anymore, rip
this.getPendingFriends().remove(myFriendship.getOwnerId());
myFriendship.delete();
return;
}
public boolean hasLoaded() {
return loaded;
}
// Handle
if (result == DealAddFriendResultType.DEAL_ADD_FRIEND_RESULT_TYPE_ACCEPT) { // Request accepted
myFriendship.setIsFriend(true);
theirFriendship.setIsFriend(true);
this.getPendingFriends().remove(myFriendship.getOwnerId());
this.addFriend(myFriendship);
if (target.isOnline()) {
target.getFriendsList().getPendingFriends().remove(this.getPlayer().getUid());
target.getFriendsList().addFriend(theirFriendship);
}
myFriendship.save();
theirFriendship.save();
} else { // Request declined
// Delete from my pending friends
this.getPendingFriends().remove(myFriendship.getOwnerId());
myFriendship.delete();
// Delete from target uid
if (target.isOnline()) {
theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getUid());
}
theirFriendship.delete();
}
// Packet
this.getPlayer().sendPacket(new PacketDealAddFriendRsp(targetUid, result));
}
public synchronized void deleteFriend(int targetUid) {
Friendship myFriendship = this.getFriendById(targetUid);
if (myFriendship == null) {
return;
}
this.getFriends().remove(targetUid);
myFriendship.delete();
Friendship theirFriendship = null;
Player friend = myFriendship.getFriendProfile().getPlayer();
if (friend != null) {
// Friend online
theirFriendship = friend.getFriendsList().getFriendById(this.getPlayer().getUid());
if (theirFriendship != null) {
friend.getFriendsList().getFriends().remove(theirFriendship.getFriendId());
theirFriendship.delete();
friend.sendPacket(new PacketDeleteFriendNotify(theirFriendship.getFriendId()));
}
} else {
// Friend offline
theirFriendship = DatabaseHelper.getReverseFriendship(myFriendship);
if (theirFriendship != null) {
theirFriendship.delete();
}
}
// Packet
this.getPlayer().sendPacket(new PacketDeleteFriendRsp(targetUid));
}
public synchronized void sendFriendRequest(int targetUid) {
Player target = getPlayer().getSession().getServer().getPlayerByUid(targetUid, true);
public synchronized Int2ObjectMap<Friendship> getFriends() {
return friends;
}
if (target == null || target == this.getPlayer()) {
return;
}
// Check if friend already exists
if (this.getPendingFriends().containsKey(targetUid) || this.getFriends().containsKey(targetUid)) {
return;
}
// Create friendships
Friendship myFriendship = new Friendship(getPlayer(), target, getPlayer());
Friendship theirFriendship = new Friendship(target, getPlayer(), getPlayer());
// Add pending lists
this.addPendingFriend(myFriendship);
public synchronized Int2ObjectMap<Friendship> getPendingFriends() {
return this.pendingFriends;
}
if (target.isOnline() && target.getFriendsList().hasLoaded()) {
target.getFriendsList().addPendingFriend(theirFriendship);
target.sendPacket(new PacketAskAddFriendNotify(theirFriendship));
}
// Save
myFriendship.save();
theirFriendship.save();
// Packets
this.getPlayer().sendPacket(new PacketAskAddFriendRsp(targetUid));
}
/** Gets total amount of potential friends
* */
public int getFullFriendCount() {
return this.getPendingFriends().size() + this.getFriends().size();
}
public synchronized boolean isFriendsWith(int uid) {
return this.getFriends().containsKey(uid);
}
public synchronized void loadFromDatabase() {
if (this.hasLoaded()) {
return;
}
// Get friendships from the db
List<Friendship> friendships = DatabaseHelper.getFriends(player);
friendships.forEach(this::loadFriendFromDatabase);
// Set loaded flag
this.loaded = true;
}
private void loadFriendFromDatabase(Friendship friendship) {
// Set friendship owner
friendship.setOwner(getPlayer());
private synchronized Friendship getFriendshipById(int id) {
Friendship friendship = this.getFriends().get(id);
if (friendship == null) {
friendship = this.getPendingFriendById(id);
}
return friendship;
}
// Check if friend is online
Player friend = getPlayer().getSession().getServer().getPlayerByUid(friendship.getFriendProfile().getUid());
if (friend != null) {
// Set friend to online mode
friendship.setFriendProfile(friend);
// Update our status on friend's client if theyre online
if (friend.getFriendsList().hasLoaded()) {
Friendship theirFriendship = friend.getFriendsList().getFriendshipById(getPlayer().getUid());
if (theirFriendship != null) {
// Update friend profile
theirFriendship.setFriendProfile(getPlayer());
} else {
// They dont have us on their friends list anymore, rip
friendship.delete();
return;
}
}
}
// Finally, load to our friends list
if (friendship.isFriend()) {
getFriends().put(friendship.getFriendId(), friendship);
} else {
getPendingFriends().put(friendship.getFriendId(), friendship);
// TODO - Hacky fix to force client to see a notification for a friendship
if (getPendingFriends().size() == 1) {
getPlayer().getSession().send(new PacketAskAddFriendNotify(friendship));
}
}
}
public void save() {
// Update all our friends
List<Friendship> friendships = DatabaseHelper.getReverseFriends(getPlayer());
for (Friendship friend : friendships) {
friend.setFriendProfile(this.getPlayer());
friend.save();
}
}
private synchronized Friendship getFriendById(int id) {
return this.getFriends().get(id);
}
private synchronized Friendship getPendingFriendById(int id) {
return this.getPendingFriends().get(id);
}
public void addFriend(Friendship friendship) {
getFriends().put(friendship.getFriendId(), friendship);
}
public void addPendingFriend(Friendship friendship) {
getPendingFriends().put(friendship.getFriendId(), friendship);
}
public synchronized void handleFriendRequest(int targetUid, DealAddFriendResultType result) {
// Check if player has sent friend request
Friendship myFriendship = this.getPendingFriendById(targetUid);
if (myFriendship == null) {
return;
}
// Make sure asker cant do anything
if (myFriendship.getAskerId() == this.getPlayer().getUid()) {
return;
}
Player target = getPlayer().getSession().getServer().getPlayerByUid(targetUid, true);
if (target == null) {
return; // Should never happen
}
// Get target's friendship
Friendship theirFriendship = null;
if (target.isOnline()) {
theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getUid());
} else {
theirFriendship = DatabaseHelper.getReverseFriendship(myFriendship);
}
if (theirFriendship == null) {
// They dont have us on their friends list anymore, rip
this.getPendingFriends().remove(myFriendship.getOwnerId());
myFriendship.delete();
return;
}
// Handle
if (result == DealAddFriendResultType.DEAL_ADD_FRIEND_RESULT_TYPE_ACCEPT) { // Request accepted
myFriendship.setIsFriend(true);
theirFriendship.setIsFriend(true);
this.getPendingFriends().remove(myFriendship.getOwnerId());
this.addFriend(myFriendship);
if (target.isOnline()) {
target.getFriendsList().getPendingFriends().remove(this.getPlayer().getUid());
target.getFriendsList().addFriend(theirFriendship);
}
myFriendship.save();
theirFriendship.save();
} else { // Request declined
// Delete from my pending friends
this.getPendingFriends().remove(myFriendship.getOwnerId());
myFriendship.delete();
// Delete from target uid
if (target.isOnline()) {
theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getUid());
}
theirFriendship.delete();
}
// Packet
this.getPlayer().sendPacket(new PacketDealAddFriendRsp(targetUid, result));
}
public synchronized void deleteFriend(int targetUid) {
Friendship myFriendship = this.getFriendById(targetUid);
if (myFriendship == null) {
return;
}
this.getFriends().remove(targetUid);
myFriendship.delete();
Friendship theirFriendship = null;
Player friend = myFriendship.getFriendProfile().getPlayer();
if (friend != null) {
// Friend online
theirFriendship = friend.getFriendsList().getFriendById(this.getPlayer().getUid());
if (theirFriendship != null) {
friend.getFriendsList().getFriends().remove(theirFriendship.getFriendId());
theirFriendship.delete();
friend.sendPacket(new PacketDeleteFriendNotify(theirFriendship.getFriendId()));
}
} else {
// Friend offline
theirFriendship = DatabaseHelper.getReverseFriendship(myFriendship);
if (theirFriendship != null) {
theirFriendship.delete();
}
}
// Packet
this.getPlayer().sendPacket(new PacketDeleteFriendRsp(targetUid));
}
public synchronized void sendFriendRequest(int targetUid) {
Player target = getPlayer().getSession().getServer().getPlayerByUid(targetUid, true);
if (target == null || target == this.getPlayer()) {
return;
}
// Check if friend already exists
if (this.getPendingFriends().containsKey(targetUid) || this.getFriends().containsKey(targetUid)) {
return;
}
// Create friendships
Friendship myFriendship = new Friendship(getPlayer(), target, getPlayer());
Friendship theirFriendship = new Friendship(target, getPlayer(), getPlayer());
// Add pending lists
this.addPendingFriend(myFriendship);
if (target.isOnline() && target.getFriendsList().hasLoaded()) {
target.getFriendsList().addPendingFriend(theirFriendship);
target.sendPacket(new PacketAskAddFriendNotify(theirFriendship));
}
// Save
myFriendship.save();
theirFriendship.save();
// Packets
this.getPlayer().sendPacket(new PacketAskAddFriendRsp(targetUid));
}
/** Gets total amount of potential friends
* */
public int getFullFriendCount() {
return this.getPendingFriends().size() + this.getFriends().size();
}
public synchronized void loadFromDatabase() {
if (this.hasLoaded()) {
return;
}
// Get friendships from the db
List<Friendship> friendships = DatabaseHelper.getFriends(player);
friendships.forEach(this::loadFriendFromDatabase);
// Set loaded flag
this.loaded = true;
}
private void loadFriendFromDatabase(Friendship friendship) {
// Set friendship owner
friendship.setOwner(getPlayer());
// Check if friend is online
Player friend = getPlayer().getSession().getServer().getPlayerByUid(friendship.getFriendProfile().getUid());
if (friend != null) {
// Set friend to online mode
friendship.setFriendProfile(friend);
// Update our status on friend's client if theyre online
if (friend.getFriendsList().hasLoaded()) {
Friendship theirFriendship = friend.getFriendsList().getFriendshipById(getPlayer().getUid());
if (theirFriendship != null) {
// Update friend profile
theirFriendship.setFriendProfile(getPlayer());
} else {
// They dont have us on their friends list anymore, rip
friendship.delete();
return;
}
}
}
// Finally, load to our friends list
if (friendship.isFriend()) {
getFriends().put(friendship.getFriendId(), friendship);
} else {
getPendingFriends().put(friendship.getFriendId(), friendship);
// TODO - Hacky fix to force client to see a notification for a friendship
if (getPendingFriends().size() == 1) {
getPlayer().getSession().send(new PacketAskAddFriendNotify(friendship));
}
}
}
public void save() {
// Update all our friends
List<Friendship> friendships = DatabaseHelper.getReverseFriends(getPlayer());
for (Friendship friend : friendships) {
friend.setFriendProfile(this.getPlayer());
friend.save();
}
}
}

View File

@ -10,164 +10,164 @@ import emu.grasscutter.utils.Utils;
import lombok.Getter;
public class GachaBanner {
@Getter private int gachaType;
@Getter private int scheduleId;
@Getter private String prefabPath;
@Getter private String previewPrefabPath;
@Getter private String titlePath;
private int costItemId = 0;
private int costItemAmount = 1;
private int costItemId10 = 0;
private int costItemAmount10 = 10;
@Getter private int beginTime;
@Getter private int endTime;
@Getter private int sortId;
@Getter private int gachaTimesLimit = Integer.MAX_VALUE;
private int[] rateUpItems4 = {};
private int[] rateUpItems5 = {};
@Getter private int[] fallbackItems3 = {11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304};
@Getter private int[] fallbackItems4Pool1 = {1014, 1020, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064};
@Getter private int[] fallbackItems4Pool2 = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405};
@Getter private int[] fallbackItems5Pool1 = {1003, 1016, 1042, 1035, 1041};
@Getter private int[] fallbackItems5Pool2 = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502};
@Getter private boolean removeC6FromPool = false;
@Getter private boolean autoStripRateUpFromFallback = true;
private int[][] weights4 = {{1,510}, {8,510}, {10,10000}};
private int[][] weights5 = {{1,75}, {73,150}, {90,10000}};
private int[][] poolBalanceWeights4 = {{1,255}, {17,255}, {21,10455}};
private int[][] poolBalanceWeights5 = {{1,30}, {147,150}, {181,10230}};
private int eventChance4 = 50; // Chance to win a featured event item
private int eventChance5 = 50; // Chance to win a featured event item
@Getter private BannerType bannerType = BannerType.STANDARD;
@Getter private int gachaType;
@Getter private int scheduleId;
@Getter private String prefabPath;
@Getter private String previewPrefabPath;
@Getter private String titlePath;
private int costItemId = 0;
private int costItemAmount = 1;
private int costItemId10 = 0;
private int costItemAmount10 = 10;
@Getter private int beginTime;
@Getter private int endTime;
@Getter private int sortId;
@Getter private int gachaTimesLimit = Integer.MAX_VALUE;
private int[] rateUpItems4 = {};
private int[] rateUpItems5 = {};
@Getter private int[] fallbackItems3 = {11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304};
@Getter private int[] fallbackItems4Pool1 = {1014, 1020, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064};
@Getter private int[] fallbackItems4Pool2 = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405};
@Getter private int[] fallbackItems5Pool1 = {1003, 1016, 1042, 1035, 1041};
@Getter private int[] fallbackItems5Pool2 = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502};
@Getter private boolean removeC6FromPool = false;
@Getter private boolean autoStripRateUpFromFallback = true;
private int[][] weights4 = {{1,510}, {8,510}, {10,10000}};
private int[][] weights5 = {{1,75}, {73,150}, {90,10000}};
private int[][] poolBalanceWeights4 = {{1,255}, {17,255}, {21,10455}};
private int[][] poolBalanceWeights5 = {{1,30}, {147,150}, {181,10230}};
private int eventChance4 = 50; // Chance to win a featured event item
private int eventChance5 = 50; // Chance to win a featured event item
@Getter private BannerType bannerType = BannerType.STANDARD;
// Kinda wanna deprecate these but they're in people's configs
private int[] rateUpItems1 = {};
private int[] rateUpItems2 = {};
private int eventChance = -1;
private int costItem = 0;
@Getter private int wishMaxProgress = 2;
// Kinda wanna deprecate these but they're in people's configs
private int[] rateUpItems1 = {};
private int[] rateUpItems2 = {};
private int eventChance = -1;
private int costItem = 0;
@Getter private int wishMaxProgress = 2;
public ItemParamData getCost(int numRolls) {
return switch (numRolls) {
case 10 -> new ItemParamData((costItemId10 > 0) ? costItemId10 : getCostItem(), costItemAmount10);
default -> new ItemParamData(getCostItem(), costItemAmount * numRolls);
};
}
public ItemParamData getCost(int numRolls) {
return switch (numRolls) {
case 10 -> new ItemParamData((costItemId10 > 0) ? costItemId10 : getCostItem(), costItemAmount10);
default -> new ItemParamData(getCostItem(), costItemAmount * numRolls);
};
}
public int getCostItem() {
return (costItem > 0) ? costItem : costItemId;
}
public int getCostItem() {
return (costItem > 0) ? costItem : costItemId;
}
public int[] getRateUpItems4() {
return (rateUpItems2.length > 0) ? rateUpItems2 : rateUpItems4;
}
public int[] getRateUpItems5() {
return (rateUpItems1.length > 0) ? rateUpItems1 : rateUpItems5;
}
public int[] getRateUpItems4() {
return (rateUpItems2.length > 0) ? rateUpItems2 : rateUpItems4;
}
public int[] getRateUpItems5() {
return (rateUpItems1.length > 0) ? rateUpItems1 : rateUpItems5;
}
public boolean hasEpitomized() {
return bannerType.equals(BannerType.WEAPON);
}
public boolean hasEpitomized() {
return bannerType.equals(BannerType.WEAPON);
}
public int getWeight(int rarity, int pity) {
return switch(rarity) {
case 4 -> Utils.lerp(pity, weights4);
default -> Utils.lerp(pity, weights5);
};
}
public int getWeight(int rarity, int pity) {
return switch (rarity) {
case 4 -> Utils.lerp(pity, weights4);
default -> Utils.lerp(pity, weights5);
};
}
public int getPoolBalanceWeight(int rarity, int pity) {
return switch(rarity) {
case 4 -> Utils.lerp(pity, poolBalanceWeights4);
default -> Utils.lerp(pity, poolBalanceWeights5);
};
}
public int getPoolBalanceWeight(int rarity, int pity) {
return switch (rarity) {
case 4 -> Utils.lerp(pity, poolBalanceWeights4);
default -> Utils.lerp(pity, poolBalanceWeights5);
};
}
public int getEventChance(int rarity) {
return switch(rarity) {
case 4 -> eventChance4;
default -> (eventChance > -1) ? eventChance : eventChance5;
};
}
public GachaInfo toProto(Player player) {
// TODO: use other Nonce/key insteadof session key to ensure the overall security for the player
String sessionKey = player.getAccount().getSessionKey();
String record = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort)
+ "/gacha?s=" + sessionKey + "&gachaType=" + gachaType;
String details = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort)
+ "/gacha/details?s=" + sessionKey + "&scheduleId=" + scheduleId;
public int getEventChance(int rarity) {
return switch (rarity) {
case 4 -> eventChance4;
default -> (eventChance > -1) ? eventChance : eventChance5;
};
}
// Grasscutter.getLogger().info("record = " + record);
ItemParamData costItem1 = this.getCost(1);
ItemParamData costItem10 = this.getCost(10);
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(this);
int leftGachaTimes = switch(gachaTimesLimit) {
case Integer.MAX_VALUE -> Integer.MAX_VALUE;
default -> Math.max(gachaTimesLimit - gachaInfo.getTotalPulls(), 0);
};
GachaInfo.Builder info = GachaInfo.newBuilder()
.setGachaType(this.getGachaType())
.setScheduleId(this.getScheduleId())
.setBeginTime(this.getBeginTime())
.setEndTime(this.getEndTime())
.setCostItemId(costItem1.getId())
.setCostItemNum(costItem1.getCount())
.setTenCostItemId(costItem10.getId())
.setTenCostItemNum(costItem10.getCount())
.setGachaPrefabPath(this.getPrefabPath())
.setGachaPreviewPrefabPath(this.getPreviewPrefabPath())
.setGachaProbUrl(details)
.setGachaProbUrlOversea(details)
.setGachaRecordUrl(record)
.setGachaRecordUrlOversea(record)
.setLeftGachaTimes(leftGachaTimes)
.setGachaTimesLimit(gachaTimesLimit)
.setGachaSortId(this.getSortId());
public GachaInfo toProto(Player player) {
// TODO: use other Nonce/key insteadof session key to ensure the overall security for the player
String sessionKey = player.getAccount().getSessionKey();
if(hasEpitomized()) {
info.setWishItemId(gachaInfo.getWishItemId())
.setWishProgress(gachaInfo.getFailedChosenItemPulls())
.setWishMaxProgress(this.getWishMaxProgress());
}
String record = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort)
+ "/gacha?s=" + sessionKey + "&gachaType=" + gachaType;
String details = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort)
+ "/gacha/details?s=" + sessionKey + "&scheduleId=" + scheduleId;
if (this.getTitlePath() != null) {
info.setTitleTextmap(this.getTitlePath());
}
if (this.getRateUpItems5().length > 0) {
GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(1);
for (int id : getRateUpItems5()) {
upInfo.addItemIdList(id);
info.addDisplayUp5ItemList(id);
}
info.addGachaUpInfoList(upInfo);
}
if (this.getRateUpItems4().length > 0) {
GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(2);
for (int id : getRateUpItems4()) {
upInfo.addItemIdList(id);
if (info.getDisplayUp4ItemListCount() == 0) {
info.addDisplayUp4ItemList(id);
}
}
info.addGachaUpInfoList(upInfo);
}
return info.build();
}
public enum BannerType {
STANDARD, EVENT, WEAPON;
}
// Grasscutter.getLogger().info("record = " + record);
ItemParamData costItem1 = this.getCost(1);
ItemParamData costItem10 = this.getCost(10);
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(this);
int leftGachaTimes = switch (gachaTimesLimit) {
case Integer.MAX_VALUE -> Integer.MAX_VALUE;
default -> Math.max(gachaTimesLimit - gachaInfo.getTotalPulls(), 0);
};
GachaInfo.Builder info = GachaInfo.newBuilder()
.setGachaType(this.getGachaType())
.setScheduleId(this.getScheduleId())
.setBeginTime(this.getBeginTime())
.setEndTime(this.getEndTime())
.setCostItemId(costItem1.getId())
.setCostItemNum(costItem1.getCount())
.setTenCostItemId(costItem10.getId())
.setTenCostItemNum(costItem10.getCount())
.setGachaPrefabPath(this.getPrefabPath())
.setGachaPreviewPrefabPath(this.getPreviewPrefabPath())
.setGachaProbUrl(details)
.setGachaProbUrlOversea(details)
.setGachaRecordUrl(record)
.setGachaRecordUrlOversea(record)
.setLeftGachaTimes(leftGachaTimes)
.setGachaTimesLimit(gachaTimesLimit)
.setGachaSortId(this.getSortId());
if (hasEpitomized()) {
info.setWishItemId(gachaInfo.getWishItemId())
.setWishProgress(gachaInfo.getFailedChosenItemPulls())
.setWishMaxProgress(this.getWishMaxProgress());
}
if (this.getTitlePath() != null) {
info.setTitleTextmap(this.getTitlePath());
}
if (this.getRateUpItems5().length > 0) {
GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(1);
for (int id : getRateUpItems5()) {
upInfo.addItemIdList(id);
info.addDisplayUp5ItemList(id);
}
info.addGachaUpInfoList(upInfo);
}
if (this.getRateUpItems4().length > 0) {
GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(2);
for (int id : getRateUpItems4()) {
upInfo.addItemIdList(id);
if (info.getDisplayUp4ItemListCount() == 0) {
info.addDisplayUp4ItemList(id);
}
}
info.addGachaUpInfoList(upInfo);
}
return info.build();
}
public enum BannerType {
STANDARD, EVENT, WEAPON;
}
}

View File

@ -48,400 +48,400 @@ import it.unimi.dsi.fastutil.ints.IntList;
import org.greenrobot.eventbus.Subscribe;
public class GachaSystem extends BaseGameSystem {
private final Int2ObjectMap<GachaBanner> gachaBanners;
private WatchService watchService;
private final Int2ObjectMap<GachaBanner> gachaBanners;
private WatchService watchService;
private static final int starglitterId = 221;
private static final int stardustId = 222;
private int[] fallbackItems4Pool2Default = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405};
private int[] fallbackItems5Pool2Default = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502};
private static final int starglitterId = 221;
private static final int stardustId = 222;
private int[] fallbackItems4Pool2Default = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405};
private int[] fallbackItems5Pool2Default = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502};
public GachaSystem(GameServer server) {
super(server);
this.gachaBanners = new Int2ObjectOpenHashMap<>();
this.load();
this.startWatcher(server);
}
public GachaSystem(GameServer server) {
super(server);
this.gachaBanners = new Int2ObjectOpenHashMap<>();
this.load();
this.startWatcher(server);
}
public Int2ObjectMap<GachaBanner> getGachaBanners() {
return gachaBanners;
}
public Int2ObjectMap<GachaBanner> getGachaBanners() {
return gachaBanners;
}
public int randomRange(int min, int max) { // Both are inclusive
return ThreadLocalRandom.current().nextInt(max - min + 1) + min;
}
public int randomRange(int min, int max) { // Both are inclusive
return ThreadLocalRandom.current().nextInt(max - min + 1) + min;
}
public int getRandom(int[] array) {
return array[randomRange(0, array.length - 1)];
}
public int getRandom(int[] array) {
return array[randomRange(0, array.length - 1)];
}
public synchronized void load() {
try (Reader fileReader = DataLoader.loadReader("Banners.json")) {
getGachaBanners().clear();
List<GachaBanner> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, GachaBanner.class).getType());
if(banners.size() > 0) {
for (GachaBanner banner : banners) {
getGachaBanners().put(banner.getScheduleId(), banner);
}
Grasscutter.getLogger().debug("Banners successfully loaded.");
} else {
Grasscutter.getLogger().error("Unable to load banners. Banners size is 0.");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public synchronized void load() {
try (Reader fileReader = DataLoader.loadReader("Banners.json")) {
getGachaBanners().clear();
List<GachaBanner> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, GachaBanner.class).getType());
if (banners.size() > 0) {
for (GachaBanner banner : banners) {
getGachaBanners().put(banner.getScheduleId(), banner);
}
Grasscutter.getLogger().debug("Banners successfully loaded.");
} else {
Grasscutter.getLogger().error("Unable to load banners. Banners size is 0.");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private class BannerPools {
public int[] rateUpItems4;
public int[] rateUpItems5;
public int[] fallbackItems4Pool1;
public int[] fallbackItems4Pool2;
public int[] fallbackItems5Pool1;
public int[] fallbackItems5Pool2;
private class BannerPools {
public int[] rateUpItems4;
public int[] rateUpItems5;
public int[] fallbackItems4Pool1;
public int[] fallbackItems4Pool2;
public int[] fallbackItems5Pool1;
public int[] fallbackItems5Pool2;
public BannerPools(GachaBanner banner) {
rateUpItems4 = banner.getRateUpItems4();
rateUpItems5 = banner.getRateUpItems5();
fallbackItems4Pool1 = banner.getFallbackItems4Pool1();
fallbackItems4Pool2 = banner.getFallbackItems4Pool2();
fallbackItems5Pool1 = banner.getFallbackItems5Pool1();
fallbackItems5Pool2 = banner.getFallbackItems5Pool2();
public BannerPools(GachaBanner banner) {
rateUpItems4 = banner.getRateUpItems4();
rateUpItems5 = banner.getRateUpItems5();
fallbackItems4Pool1 = banner.getFallbackItems4Pool1();
fallbackItems4Pool2 = banner.getFallbackItems4Pool2();
fallbackItems5Pool1 = banner.getFallbackItems5Pool1();
fallbackItems5Pool2 = banner.getFallbackItems5Pool2();
if (banner.isAutoStripRateUpFromFallback()) {
fallbackItems4Pool1 = Utils.setSubtract(fallbackItems4Pool1, rateUpItems4);
fallbackItems4Pool2 = Utils.setSubtract(fallbackItems4Pool2, rateUpItems4);
fallbackItems5Pool1 = Utils.setSubtract(fallbackItems5Pool1, rateUpItems5);
fallbackItems5Pool2 = Utils.setSubtract(fallbackItems5Pool2, rateUpItems5);
}
}
if (banner.isAutoStripRateUpFromFallback()) {
fallbackItems4Pool1 = Utils.setSubtract(fallbackItems4Pool1, rateUpItems4);
fallbackItems4Pool2 = Utils.setSubtract(fallbackItems4Pool2, rateUpItems4);
fallbackItems5Pool1 = Utils.setSubtract(fallbackItems5Pool1, rateUpItems5);
fallbackItems5Pool2 = Utils.setSubtract(fallbackItems5Pool2, rateUpItems5);
}
}
public void removeFromAllPools(int[] itemIds) {
rateUpItems4 = Utils.setSubtract(rateUpItems4, itemIds);
rateUpItems5 = Utils.setSubtract(rateUpItems5, itemIds);
fallbackItems4Pool1 = Utils.setSubtract(fallbackItems4Pool1, itemIds);
fallbackItems4Pool2 = Utils.setSubtract(fallbackItems4Pool2, itemIds);
fallbackItems5Pool1 = Utils.setSubtract(fallbackItems5Pool1, itemIds);
fallbackItems5Pool2 = Utils.setSubtract(fallbackItems5Pool2, itemIds);
}
}
public void removeFromAllPools(int[] itemIds) {
rateUpItems4 = Utils.setSubtract(rateUpItems4, itemIds);
rateUpItems5 = Utils.setSubtract(rateUpItems5, itemIds);
fallbackItems4Pool1 = Utils.setSubtract(fallbackItems4Pool1, itemIds);
fallbackItems4Pool2 = Utils.setSubtract(fallbackItems4Pool2, itemIds);
fallbackItems5Pool1 = Utils.setSubtract(fallbackItems5Pool1, itemIds);
fallbackItems5Pool2 = Utils.setSubtract(fallbackItems5Pool2, itemIds);
}
}
private synchronized int checkPlayerAvatarConstellationLevel(Player player, int itemId) { // Maybe this would be useful in the Player class?
ItemData itemData = GameData.getItemDataMap().get(itemId);
if ((itemData == null) || (itemData.getMaterialType() != MaterialType.MATERIAL_AVATAR)){
return -2; // Not an Avatar
}
Avatar avatar = player.getAvatars().getAvatarById((itemId % 1000) + 10000000);
if (avatar == null) {
return -1; // Doesn't have
}
// Constellation
int constLevel = avatar.getCoreProudSkillLevel();
GameItem constItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId + 100);
constLevel += (constItem == null)? 0 : constItem.getCount();
return constLevel;
}
private synchronized int checkPlayerAvatarConstellationLevel(Player player, int itemId) { // Maybe this would be useful in the Player class?
ItemData itemData = GameData.getItemDataMap().get(itemId);
if ((itemData == null) || (itemData.getMaterialType() != MaterialType.MATERIAL_AVATAR)) {
return -2; // Not an Avatar
}
Avatar avatar = player.getAvatars().getAvatarById((itemId % 1000) + 10000000);
if (avatar == null) {
return -1; // Doesn't have
}
// Constellation
int constLevel = avatar.getCoreProudSkillLevel();
GameItem constItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId + 100);
constLevel += (constItem == null)? 0 : constItem.getCount();
return constLevel;
}
private synchronized int[] removeC6FromPool(int[] itemPool, Player player) {
IntList temp = new IntArrayList();
for (int itemId : itemPool) {
if (checkPlayerAvatarConstellationLevel(player, itemId) < 6) {
temp.add(itemId);
}
}
return temp.toIntArray();
}
private synchronized int[] removeC6FromPool(int[] itemPool, Player player) {
IntList temp = new IntArrayList();
for (int itemId : itemPool) {
if (checkPlayerAvatarConstellationLevel(player, itemId) < 6) {
temp.add(itemId);
}
}
return temp.toIntArray();
}
private synchronized int drawRoulette(int[] weights, int cutoff) {
// This follows the logic laid out in issue #183
// Simple weighted selection with an upper bound for the roll that cuts off trailing entries
// All weights must be >= 0
int total = 0;
for (int weight : weights) {
if (weight < 0) {
throw new IllegalArgumentException("Weights must be non-negative!");
}
total += weight;
}
int roll = ThreadLocalRandom.current().nextInt((total < cutoff)? total : cutoff);
int subTotal = 0;
for (int i=0; i<weights.length; i++) {
subTotal += weights[i];
if (roll < subTotal) {
return i;
}
}
// throw new IllegalStateException();
return 0; // This should only be reachable if total==0
}
private synchronized int drawRoulette(int[] weights, int cutoff) {
// This follows the logic laid out in issue #183
// Simple weighted selection with an upper bound for the roll that cuts off trailing entries
// All weights must be >= 0
int total = 0;
for (int weight : weights) {
if (weight < 0) {
throw new IllegalArgumentException("Weights must be non-negative!");
}
total += weight;
}
int roll = ThreadLocalRandom.current().nextInt((total < cutoff)? total : cutoff);
int subTotal = 0;
for (int i=0; i<weights.length; i++) {
subTotal += weights[i];
if (roll < subTotal) {
return i;
}
}
// throw new IllegalStateException();
return 0; // This should only be reachable if total==0
}
private synchronized int doFallbackRarePull(int[] fallback1, int[] fallback2, int rarity, GachaBanner banner, PlayerGachaBannerInfo gachaInfo) {
if (fallback1.length < 1) {
if (fallback2.length < 1) {
return getRandom((rarity==5)? fallbackItems5Pool2Default : fallbackItems4Pool2Default);
} else {
return getRandom(fallback2);
}
} else if (fallback2.length < 1) {
return getRandom(fallback1);
} else { // Both pools are possible, use the pool balancer
int pityPool1 = banner.getPoolBalanceWeight(rarity, gachaInfo.getPityPool(rarity, 1));
int pityPool2 = banner.getPoolBalanceWeight(rarity, gachaInfo.getPityPool(rarity, 2));
int chosenPool = switch ((pityPool1 >= pityPool2)? 1 : 0) { // Larger weight must come first for the hard cutoff to function correctly
case 1 -> 1 + drawRoulette(new int[] {pityPool1, pityPool2}, 10000);
default -> 2 - drawRoulette(new int[] {pityPool2, pityPool1}, 10000);
};
return switch (chosenPool) {
case 1:
gachaInfo.setPityPool(rarity, 1, 0);
yield getRandom(fallback1);
default:
gachaInfo.setPityPool(rarity, 2, 0);
yield getRandom(fallback2);
};
}
}
private synchronized int doFallbackRarePull(int[] fallback1, int[] fallback2, int rarity, GachaBanner banner, PlayerGachaBannerInfo gachaInfo) {
if (fallback1.length < 1) {
if (fallback2.length < 1) {
return getRandom((rarity==5)? fallbackItems5Pool2Default : fallbackItems4Pool2Default);
} else {
return getRandom(fallback2);
}
} else if (fallback2.length < 1) {
return getRandom(fallback1);
} else { // Both pools are possible, use the pool balancer
int pityPool1 = banner.getPoolBalanceWeight(rarity, gachaInfo.getPityPool(rarity, 1));
int pityPool2 = banner.getPoolBalanceWeight(rarity, gachaInfo.getPityPool(rarity, 2));
int chosenPool = switch ((pityPool1 >= pityPool2)? 1 : 0) { // Larger weight must come first for the hard cutoff to function correctly
case 1 -> 1 + drawRoulette(new int[] {pityPool1, pityPool2}, 10000);
default -> 2 - drawRoulette(new int[] {pityPool2, pityPool1}, 10000);
};
return switch (chosenPool) {
case 1:
gachaInfo.setPityPool(rarity, 1, 0);
yield getRandom(fallback1);
default:
gachaInfo.setPityPool(rarity, 2, 0);
yield getRandom(fallback2);
};
}
}
private synchronized int doRarePull(int[] featured, int[] fallback1, int[] fallback2, int rarity, GachaBanner banner, PlayerGachaBannerInfo gachaInfo) {
int itemId = 0;
boolean epitomized = (banner.hasEpitomized()) && (rarity == 5) && (gachaInfo.getWishItemId() != 0);
boolean pityEpitomized = (gachaInfo.getFailedChosenItemPulls() >= banner.getWishMaxProgress()); // Maximum fate points reached
boolean pityFeatured = (gachaInfo.getFailedFeaturedItemPulls(rarity) >= 1); // Lost previous coinflip
boolean rollFeatured = (this.randomRange(1, 100) <= banner.getEventChance(rarity)); // Won this coinflip
boolean pullFeatured = pityFeatured || rollFeatured;
private synchronized int doRarePull(int[] featured, int[] fallback1, int[] fallback2, int rarity, GachaBanner banner, PlayerGachaBannerInfo gachaInfo) {
int itemId = 0;
boolean epitomized = (banner.hasEpitomized()) && (rarity == 5) && (gachaInfo.getWishItemId() != 0);
boolean pityEpitomized = (gachaInfo.getFailedChosenItemPulls() >= banner.getWishMaxProgress()); // Maximum fate points reached
boolean pityFeatured = (gachaInfo.getFailedFeaturedItemPulls(rarity) >= 1); // Lost previous coinflip
boolean rollFeatured = (this.randomRange(1, 100) <= banner.getEventChance(rarity)); // Won this coinflip
boolean pullFeatured = pityFeatured || rollFeatured;
if (epitomized && pityEpitomized) { // Auto pick item when epitomized points reached
gachaInfo.setFailedFeaturedItemPulls(rarity, 0); // Epitomized item will always be a featured one
itemId = gachaInfo.getWishItemId();
} else {
if (pullFeatured && (featured.length > 0)) {
gachaInfo.setFailedFeaturedItemPulls(rarity, 0);
itemId = getRandom(featured);
} else {
gachaInfo.addFailedFeaturedItemPulls(rarity, 1); // This could be moved into doFallbackRarePull but having it here makes it clearer
itemId = doFallbackRarePull(fallback1, fallback2, rarity, banner, gachaInfo);
}
}
if (epitomized && pityEpitomized) { // Auto pick item when epitomized points reached
gachaInfo.setFailedFeaturedItemPulls(rarity, 0); // Epitomized item will always be a featured one
itemId = gachaInfo.getWishItemId();
} else {
if (pullFeatured && (featured.length > 0)) {
gachaInfo.setFailedFeaturedItemPulls(rarity, 0);
itemId = getRandom(featured);
} else {
gachaInfo.addFailedFeaturedItemPulls(rarity, 1); // This could be moved into doFallbackRarePull but having it here makes it clearer
itemId = doFallbackRarePull(fallback1, fallback2, rarity, banner, gachaInfo);
}
}
if (epitomized) {
if(itemId == gachaInfo.getWishItemId()) { // Reset epitomized points when got wished item
gachaInfo.setFailedChosenItemPulls(0);
} else { // Add epitomized points if not get wished item
gachaInfo.addFailedChosenItemPulls(1);
}
}
return itemId;
}
if (epitomized) {
if (itemId == gachaInfo.getWishItemId()) { // Reset epitomized points when got wished item
gachaInfo.setFailedChosenItemPulls(0);
} else { // Add epitomized points if not get wished item
gachaInfo.addFailedChosenItemPulls(1);
}
}
return itemId;
}
private synchronized int doPull(GachaBanner banner, PlayerGachaBannerInfo gachaInfo, BannerPools pools) {
// Pre-increment all pity pools (yes this makes all calculations assume 1-indexed pity)
gachaInfo.incPityAll();
private synchronized int doPull(GachaBanner banner, PlayerGachaBannerInfo gachaInfo, BannerPools pools) {
// Pre-increment all pity pools (yes this makes all calculations assume 1-indexed pity)
gachaInfo.incPityAll();
int[] weights = {banner.getWeight(5, gachaInfo.getPity5()), banner.getWeight(4, gachaInfo.getPity4()), 10000};
int levelWon = 5 - drawRoulette(weights, 10000);
int[] weights = {banner.getWeight(5, gachaInfo.getPity5()), banner.getWeight(4, gachaInfo.getPity4()), 10000};
int levelWon = 5 - drawRoulette(weights, 10000);
return switch (levelWon) {
case 5:
gachaInfo.setPity5(0);
yield doRarePull(pools.rateUpItems5, pools.fallbackItems5Pool1, pools.fallbackItems5Pool2, 5, banner, gachaInfo);
case 4:
gachaInfo.setPity4(0);
yield doRarePull(pools.rateUpItems4, pools.fallbackItems4Pool1, pools.fallbackItems4Pool2, 4, banner, gachaInfo);
default:
yield getRandom(banner.getFallbackItems3());
};
}
return switch (levelWon) {
case 5:
gachaInfo.setPity5(0);
yield doRarePull(pools.rateUpItems5, pools.fallbackItems5Pool1, pools.fallbackItems5Pool2, 5, banner, gachaInfo);
case 4:
gachaInfo.setPity4(0);
yield doRarePull(pools.rateUpItems4, pools.fallbackItems4Pool1, pools.fallbackItems4Pool2, 4, banner, gachaInfo);
default:
yield getRandom(banner.getFallbackItems3());
};
}
public synchronized void doPulls(Player player, int scheduleId, int times) {
// Sanity check
if (times != 10 && times != 1) {
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_INVALID_TIMES));
return;
}
Inventory inventory = player.getInventory();
if (inventory.getInventoryTab(ItemType.ITEM_WEAPON).getSize() + times > inventory.getInventoryTab(ItemType.ITEM_WEAPON).getMaxCapacity()) {
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_ITEM_EXCEED_LIMIT));
return;
}
public synchronized void doPulls(Player player, int scheduleId, int times) {
// Sanity check
if (times != 10 && times != 1) {
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_INVALID_TIMES));
return;
}
Inventory inventory = player.getInventory();
if (inventory.getInventoryTab(ItemType.ITEM_WEAPON).getSize() + times > inventory.getInventoryTab(ItemType.ITEM_WEAPON).getMaxCapacity()) {
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_ITEM_EXCEED_LIMIT));
return;
}
// Get banner
GachaBanner banner = this.getGachaBanners().get(scheduleId);
if (banner == null) {
player.sendPacket(new PacketDoGachaRsp());
return;
}
// Get banner
GachaBanner banner = this.getGachaBanners().get(scheduleId);
if (banner == null) {
player.sendPacket(new PacketDoGachaRsp());
return;
}
// Check against total limit
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(banner);
int gachaTimesLimit = banner.getGachaTimesLimit();
if (gachaTimesLimit != Integer.MAX_VALUE && (gachaInfo.getTotalPulls() + times) > gachaTimesLimit) {
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_TIMES_LIMIT));
return;
}
// Check against total limit
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(banner);
int gachaTimesLimit = banner.getGachaTimesLimit();
if (gachaTimesLimit != Integer.MAX_VALUE && (gachaInfo.getTotalPulls() + times) > gachaTimesLimit) {
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_TIMES_LIMIT));
return;
}
// Spend currency
ItemParamData cost = banner.getCost(times);
if (cost.getCount() > 0 && !inventory.payItem(cost)) {
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_COST_ITEM_NOT_ENOUGH));
return;
}
// Spend currency
ItemParamData cost = banner.getCost(times);
if (cost.getCount() > 0 && !inventory.payItem(cost)) {
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_COST_ITEM_NOT_ENOUGH));
return;
}
// Add to character
gachaInfo.addTotalPulls(times);
BannerPools pools = new BannerPools(banner);
List<GachaItem> list = new ArrayList<>();
int stardust = 0, starglitter = 0;
// Add to character
gachaInfo.addTotalPulls(times);
BannerPools pools = new BannerPools(banner);
List<GachaItem> list = new ArrayList<>();
int stardust = 0, starglitter = 0;
if (banner.isRemoveC6FromPool()) { // The ultimate form of pity (non-vanilla)
pools.rateUpItems4 = removeC6FromPool(pools.rateUpItems4, player);
pools.rateUpItems5 = removeC6FromPool(pools.rateUpItems5, player);
pools.fallbackItems4Pool1 = removeC6FromPool(pools.fallbackItems4Pool1, player);
pools.fallbackItems4Pool2 = removeC6FromPool(pools.fallbackItems4Pool2, player);
pools.fallbackItems5Pool1 = removeC6FromPool(pools.fallbackItems5Pool1, player);
pools.fallbackItems5Pool2 = removeC6FromPool(pools.fallbackItems5Pool2, player);
}
if (banner.isRemoveC6FromPool()) { // The ultimate form of pity (non-vanilla)
pools.rateUpItems4 = removeC6FromPool(pools.rateUpItems4, player);
pools.rateUpItems5 = removeC6FromPool(pools.rateUpItems5, player);
pools.fallbackItems4Pool1 = removeC6FromPool(pools.fallbackItems4Pool1, player);
pools.fallbackItems4Pool2 = removeC6FromPool(pools.fallbackItems4Pool2, player);
pools.fallbackItems5Pool1 = removeC6FromPool(pools.fallbackItems5Pool1, player);
pools.fallbackItems5Pool2 = removeC6FromPool(pools.fallbackItems5Pool2, player);
}
for (int i = 0; i < times; i++) {
// Roll
int itemId = doPull(banner, gachaInfo, pools);
ItemData itemData = GameData.getItemDataMap().get(itemId);
if (itemData == null) {
continue; // Maybe we should bail out if an item fails instead of rolling the rest?
}
for (int i = 0; i < times; i++) {
// Roll
int itemId = doPull(banner, gachaInfo, pools);
ItemData itemData = GameData.getItemDataMap().get(itemId);
if (itemData == null) {
continue; // Maybe we should bail out if an item fails instead of rolling the rest?
}
// Write gacha record
GachaRecord gachaRecord = new GachaRecord(itemId, player.getUid(), banner.getGachaType());
DatabaseHelper.saveGachaRecord(gachaRecord);
// Write gacha record
GachaRecord gachaRecord = new GachaRecord(itemId, player.getUid(), banner.getGachaType());
DatabaseHelper.saveGachaRecord(gachaRecord);
// Create gacha item
GachaItem.Builder gachaItem = GachaItem.newBuilder();
int addStardust = 0, addStarglitter = 0;
boolean isTransferItem = false;
// Create gacha item
GachaItem.Builder gachaItem = GachaItem.newBuilder();
int addStardust = 0, addStarglitter = 0;
boolean isTransferItem = false;
// Const check
int constellation = checkPlayerAvatarConstellationLevel(player, itemId);
switch (constellation) {
case -2: // Is weapon
switch (itemData.getRankLevel()) {
case 5 -> addStarglitter = 10;
case 4 -> addStarglitter = 2;
default -> addStardust = 15;
}
break;
case -1: // New character
gachaItem.setIsGachaItemNew(true);
break;
default:
if (constellation >= 6) { // C6, give consolation starglitter
addStarglitter = (itemData.getRankLevel()==5)? 25 : 5;
} else { // C0-C5, give constellation item
if (banner.isRemoveC6FromPool() && constellation == 5) { // New C6, remove it from the pools so we don't get C7 in a 10pull
pools.removeFromAllPools(new int[] {itemId});
}
addStarglitter = (itemData.getRankLevel()==5)? 10 : 2;
int constItemId = itemId + 100;
GameItem constItem = inventory.getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(constItemId);
gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(ItemParam.newBuilder().setItemId(constItemId).setCount(1)).setIsTransferItemNew(constItem == null));
inventory.addItem(constItemId, 1);
}
isTransferItem = true;
break;
}
// Const check
int constellation = checkPlayerAvatarConstellationLevel(player, itemId);
switch (constellation) {
case -2: // Is weapon
switch (itemData.getRankLevel()) {
case 5 -> addStarglitter = 10;
case 4 -> addStarglitter = 2;
default -> addStardust = 15;
}
break;
case -1: // New character
gachaItem.setIsGachaItemNew(true);
break;
default:
if (constellation >= 6) { // C6, give consolation starglitter
addStarglitter = (itemData.getRankLevel()==5)? 25 : 5;
} else { // C0-C5, give constellation item
if (banner.isRemoveC6FromPool() && constellation == 5) { // New C6, remove it from the pools so we don't get C7 in a 10pull
pools.removeFromAllPools(new int[] {itemId});
}
addStarglitter = (itemData.getRankLevel()==5)? 10 : 2;
int constItemId = itemId + 100;
GameItem constItem = inventory.getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(constItemId);
gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(ItemParam.newBuilder().setItemId(constItemId).setCount(1)).setIsTransferItemNew(constItem == null));
inventory.addItem(constItemId, 1);
}
isTransferItem = true;
break;
}
// Create item
GameItem item = new GameItem(itemData);
gachaItem.setGachaItem(item.toItemParam());
inventory.addItem(item);
// Create item
GameItem item = new GameItem(itemData);
gachaItem.setGachaItem(item.toItemParam());
inventory.addItem(item);
stardust += addStardust;
starglitter += addStarglitter;
stardust += addStardust;
starglitter += addStarglitter;
if (addStardust > 0) {
gachaItem.addTokenItemList(ItemParam.newBuilder().setItemId(stardustId).setCount(addStardust));
}
if (addStarglitter > 0) {
ItemParam starglitterParam = ItemParam.newBuilder().setItemId(starglitterId).setCount(addStarglitter).build();
if (isTransferItem) {
gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(starglitterParam));
}
gachaItem.addTokenItemList(starglitterParam);
}
if (addStardust > 0) {
gachaItem.addTokenItemList(ItemParam.newBuilder().setItemId(stardustId).setCount(addStardust));
}
if (addStarglitter > 0) {
ItemParam starglitterParam = ItemParam.newBuilder().setItemId(starglitterId).setCount(addStarglitter).build();
if (isTransferItem) {
gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(starglitterParam));
}
gachaItem.addTokenItemList(starglitterParam);
}
list.add(gachaItem.build());
}
list.add(gachaItem.build());
}
// Add stardust/starglitter
if (stardust > 0) {
inventory.addItem(stardustId, stardust);
}
if (starglitter > 0) {
inventory.addItem(starglitterId, starglitter);
}
// Add stardust/starglitter
if (stardust > 0) {
inventory.addItem(stardustId, stardust);
}
if (starglitter > 0) {
inventory.addItem(starglitterId, starglitter);
}
// Packets
player.sendPacket(new PacketDoGachaRsp(banner, list, gachaInfo));
// Packets
player.sendPacket(new PacketDoGachaRsp(banner, list, gachaInfo));
// Battle Pass trigger
player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_GACHA_NUM, 0, times);
}
// Battle Pass trigger
player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_GACHA_NUM, 0, times);
}
private synchronized void startWatcher(GameServer server) {
if(this.watchService == null) {
try {
this.watchService = FileSystems.getDefault().newWatchService();
Path path = new File(DATA()).toPath();
path.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}, SensitivityWatchEventModifier.HIGH);
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to load the Gacha Manager Watch Service. If ServerOptions.watchGacha is true it will not auto-reload");
e.printStackTrace();
}
} else {
Grasscutter.getLogger().error("Cannot reinitialise watcher ");
}
}
private synchronized void startWatcher(GameServer server) {
if (this.watchService == null) {
try {
this.watchService = FileSystems.getDefault().newWatchService();
Path path = new File(DATA()).toPath();
path.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}, SensitivityWatchEventModifier.HIGH);
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to load the Gacha Manager Watch Service. If ServerOptions.watchGacha is true it will not auto-reload");
e.printStackTrace();
}
} else {
Grasscutter.getLogger().error("Cannot reinitialise watcher ");
}
}
@Subscribe
public synchronized void watchBannerJson(GameServerTickEvent tickEvent) {
if(GAME_OPTIONS.watchGachaConfig) {
try {
WatchKey watchKey = watchService.take();
@Subscribe
public synchronized void watchBannerJson(GameServerTickEvent tickEvent) {
if (GAME_OPTIONS.watchGachaConfig) {
try {
WatchKey watchKey = watchService.take();
for (WatchEvent<?> event : watchKey.pollEvents()) {
final Path changed = (Path) event.context();
if (changed.endsWith("Banners.json")) {
Grasscutter.getLogger().info("Change detected with banners.json. Reloading gacha config");
this.load();
}
}
for (WatchEvent<?> event : watchKey.pollEvents()) {
final Path changed = (Path) event.context();
if (changed.endsWith("Banners.json")) {
Grasscutter.getLogger().info("Change detected with banners.json. Reloading gacha config");
this.load();
}
}
boolean valid = watchKey.reset();
if (!valid) {
Grasscutter.getLogger().error("Unable to reset Gacha Manager Watch Key. Auto-reload of banners.json will no longer work.");
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
boolean valid = watchKey.reset();
if (!valid) {
Grasscutter.getLogger().error("Unable to reset Gacha Manager Watch Key. Auto-reload of banners.json will no longer work.");
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private synchronized GetGachaInfoRsp createProto(Player player) {
GetGachaInfoRsp.Builder proto = GetGachaInfoRsp.newBuilder().setGachaRandom(12345);
private synchronized GetGachaInfoRsp createProto(Player player) {
GetGachaInfoRsp.Builder proto = GetGachaInfoRsp.newBuilder().setGachaRandom(12345);
long currentTime = System.currentTimeMillis() / 1000L;
long currentTime = System.currentTimeMillis() / 1000L;
for (GachaBanner banner : getGachaBanners().values()) {
if ((banner.getEndTime() >= currentTime && banner.getBeginTime() <= currentTime) || (banner.getBannerType() == BannerType.STANDARD))
{
proto.addGachaInfoList(banner.toProto(player));
}
}
for (GachaBanner banner : getGachaBanners().values()) {
if ((banner.getEndTime() >= currentTime && banner.getBeginTime() <= currentTime) || (banner.getBannerType() == BannerType.STANDARD))
{
proto.addGachaInfoList(banner.toProto(player));
}
}
return proto.build();
}
return proto.build();
}
public GetGachaInfoRsp toProto(Player player) {
return createProto(player);
}
public GetGachaInfoRsp toProto(Player player) {
return createProto(player);
}
}

View File

@ -33,199 +33,199 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
private final Long2ObjectMap<GameItem> store;
private final Int2ObjectMap<InventoryTab> inventoryTypes;
public Inventory(Player player) {
super(player);
this.store = new Long2ObjectOpenHashMap<>();
this.inventoryTypes = new Int2ObjectOpenHashMap<>();
this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(INVENTORY_LIMITS.weapons));
this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(INVENTORY_LIMITS.relics));
this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(INVENTORY_LIMITS.materials));
this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(INVENTORY_LIMITS.furniture));
}
private final Long2ObjectMap<GameItem> store;
private final Int2ObjectMap<InventoryTab> inventoryTypes;
public AvatarStorage getAvatarStorage() {
return this.getPlayer().getAvatars();
}
public Inventory(Player player) {
super(player);
public Long2ObjectMap<GameItem> getItems() {
return store;
}
public Int2ObjectMap<InventoryTab> getInventoryTypes() {
return inventoryTypes;
}
public InventoryTab getInventoryTab(ItemType type) {
return getInventoryTypes().get(type.getValue());
}
public void createInventoryTab(ItemType type, InventoryTab tab) {
this.getInventoryTypes().put(type.getValue(), tab);
}
public GameItem getItemByGuid(long id) {
return this.getItems().get(id);
}
public boolean addItem(int itemId) {
return addItem(itemId, 1);
}
public boolean addItem(int itemId, int count) {
this.store = new Long2ObjectOpenHashMap<>();
this.inventoryTypes = new Int2ObjectOpenHashMap<>();
this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(INVENTORY_LIMITS.weapons));
this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(INVENTORY_LIMITS.relics));
this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(INVENTORY_LIMITS.materials));
this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(INVENTORY_LIMITS.furniture));
}
public AvatarStorage getAvatarStorage() {
return this.getPlayer().getAvatars();
}
public Long2ObjectMap<GameItem> getItems() {
return store;
}
public Int2ObjectMap<InventoryTab> getInventoryTypes() {
return inventoryTypes;
}
public InventoryTab getInventoryTab(ItemType type) {
return getInventoryTypes().get(type.getValue());
}
public void createInventoryTab(ItemType type, InventoryTab tab) {
this.getInventoryTypes().put(type.getValue(), tab);
}
public GameItem getItemByGuid(long id) {
return this.getItems().get(id);
}
public boolean addItem(int itemId) {
return addItem(itemId, 1);
}
public boolean addItem(int itemId, int count) {
return addItem(itemId, count, null);
}
public boolean addItem(int itemId, int count, ActionReason reason) {
ItemData itemData = GameData.getItemDataMap().get(itemId);
if (itemData == null) {
return false;
}
GameItem item = new GameItem(itemData, count);
return addItem(item, reason);
}
public boolean addItem(GameItem item) {
GameItem result = putItem(item);
if (result != null) {
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
getPlayer().sendPacket(new PacketStoreItemChangeNotify(result));
return true;
}
return false;
}
public boolean addItem(GameItem item, ActionReason reason) {
boolean result = addItem(item);
if (result && reason != null) {
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
}
return result;
}
public boolean addItem(GameItem item, ActionReason reason, boolean forceNotify) {
boolean result = addItem(item);
public boolean addItem(int itemId, int count, ActionReason reason) {
ItemData itemData = GameData.getItemDataMap().get(itemId);
if (reason != null && (forceNotify || result)) {
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
}
if (itemData == null) {
return false;
}
return result;
}
public boolean addItem(ItemParamData itemParam) {
GameItem item = new GameItem(itemData, count);
return addItem(item, reason);
}
public boolean addItem(GameItem item) {
GameItem result = putItem(item);
if (result != null) {
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
getPlayer().sendPacket(new PacketStoreItemChangeNotify(result));
return true;
}
return false;
}
public boolean addItem(GameItem item, ActionReason reason) {
boolean result = addItem(item);
if (result && reason != null) {
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
}
return result;
}
public boolean addItem(GameItem item, ActionReason reason, boolean forceNotify) {
boolean result = addItem(item);
if (reason != null && (forceNotify || result)) {
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
}
return result;
}
public boolean addItem(ItemParamData itemParam) {
return addItem(itemParam, null);
}
public boolean addItem(ItemParamData itemParam, ActionReason reason) {
if (itemParam == null) return false;
public boolean addItem(ItemParamData itemParam, ActionReason reason) {
if (itemParam == null) return false;
return addItem(itemParam.getId(), itemParam.getCount(), reason);
}
public void addItems(Collection<GameItem> items) {
this.addItems(items, null);
}
public void addItems(Collection<GameItem> items, ActionReason reason) {
List<GameItem> changedItems = new LinkedList<>();
for (GameItem item : items) {
GameItem result = putItem(item);
if (result != null) {
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
changedItems.add(result);
}
}
if (changedItems.size() == 0) {
return;
}
if (reason != null) {
getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason));
}
getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
}
public void addItemParams(Collection<ItemParam> items) {
addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), null);
}
public void addItemParamDatas(Collection<ItemParamData> items) {
addItemParamDatas(items, null);
}
public void addItemParamDatas(Collection<ItemParamData> items, ActionReason reason) {
addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), reason);
}
private synchronized GameItem putItem(GameItem item) {
// Dont add items that dont have a valid item definition.
if (item.getItemData() == null) {
return null;
}
// Add item to inventory store
ItemType type = item.getItemData().getItemType();
InventoryTab tab = getInventoryTab(type);
// Add
switch (type) {
case ITEM_WEAPON:
case ITEM_RELIQUARY:
if (tab.getSize() >= tab.getMaxCapacity()) {
return null;
}
// Duplicates cause problems
item.setCount(Math.max(item.getCount(), 1));
// Adds to inventory
public void addItems(Collection<GameItem> items) {
this.addItems(items, null);
}
public void addItems(Collection<GameItem> items, ActionReason reason) {
List<GameItem> changedItems = new LinkedList<>();
for (GameItem item : items) {
GameItem result = putItem(item);
if (result != null) {
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
changedItems.add(result);
}
}
if (changedItems.size() == 0) {
return;
}
if (reason != null) {
getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason));
}
getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
}
public void addItemParams(Collection<ItemParam> items) {
addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), null);
}
public void addItemParamDatas(Collection<ItemParamData> items) {
addItemParamDatas(items, null);
}
public void addItemParamDatas(Collection<ItemParamData> items, ActionReason reason) {
addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), reason);
}
private synchronized GameItem putItem(GameItem item) {
// Dont add items that dont have a valid item definition.
if (item.getItemData() == null) {
return null;
}
// Add item to inventory store
ItemType type = item.getItemData().getItemType();
InventoryTab tab = getInventoryTab(type);
// Add
switch (type) {
case ITEM_WEAPON:
case ITEM_RELIQUARY:
if (tab.getSize() >= tab.getMaxCapacity()) {
return null;
}
// Duplicates cause problems
item.setCount(Math.max(item.getCount(), 1));
// Adds to inventory
this.putItem(item, tab);
// Set ownership and save to db
item.save();
return item;
case ITEM_VIRTUAL:
// Handle
this.addVirtualItem(item.getItemId(), item.getCount());
return item;
// Set ownership and save to db
item.save();
return item;
case ITEM_VIRTUAL:
// Handle
this.addVirtualItem(item.getItemId(), item.getCount());
return item;
default:
switch (item.getItemData().getMaterialType()) {
case MATERIAL_ADSORBATE:
this.player.getEnergyManager().handlePickupElemBall(item);
return null;
case MATERIAL_AVATAR:
// Get avatar id
int avatarId = (item.getItemId() % 1000) + 10000000;
// Dont let people give themselves extra main characters
if (avatarId == GameConstants.MAIN_CHARACTER_MALE || avatarId == GameConstants.MAIN_CHARACTER_FEMALE) {
return null;
}
// Add avatar
AvatarData avatarData = GameData.getAvatarDataMap().get(avatarId);
if (avatarData != null && !this.player.getAvatars().hasAvatar(avatarId)) {
this.player.addAvatar(new Avatar(avatarData));
}
return null;
case MATERIAL_FLYCLOAK:
AvatarFlycloakData flycloakData = GameData.getAvatarFlycloakDataMap().get(item.getItemId());
if (flycloakData != null && !this.player.getFlyCloakList().contains(item.getItemId())) {
this.player.addFlycloak(item.getItemId());
}
return null;
case MATERIAL_COSTUME:
AvatarCostumeData costumeData = GameData.getAvatarCostumeDataItemIdMap().get(item.getItemId());
this.player.getEnergyManager().handlePickupElemBall(item);
return null;
case MATERIAL_AVATAR:
// Get avatar id
int avatarId = (item.getItemId() % 1000) + 10000000;
// Dont let people give themselves extra main characters
if (avatarId == GameConstants.MAIN_CHARACTER_MALE || avatarId == GameConstants.MAIN_CHARACTER_FEMALE) {
return null;
}
// Add avatar
AvatarData avatarData = GameData.getAvatarDataMap().get(avatarId);
if (avatarData != null && !this.player.getAvatars().hasAvatar(avatarId)) {
this.player.addAvatar(new Avatar(avatarData));
}
return null;
case MATERIAL_FLYCLOAK:
AvatarFlycloakData flycloakData = GameData.getAvatarFlycloakDataMap().get(item.getItemId());
if (flycloakData != null && !this.player.getFlyCloakList().contains(item.getItemId())) {
this.player.addFlycloak(item.getItemId());
}
return null;
case MATERIAL_COSTUME:
AvatarCostumeData costumeData = GameData.getAvatarCostumeDataItemIdMap().get(item.getItemId());
if (costumeData != null && !this.player.getCostumeList().contains(costumeData.getId())) {
this.player.addCostume(costumeData.getId());
}
@ -233,46 +233,46 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
case MATERIAL_NAMECARD:
if (!this.player.getNameCardList().contains(item.getItemId())) {
this.player.addNameCard(item.getItemId());
}
return null;
}
return null;
default:
if (tab == null) {
return null;
}
GameItem existingItem = tab.getItemById(item.getItemId());
if (existingItem == null) {
// Item type didnt exist before, we will add it to main inventory map if there is enough space
if (tab.getSize() >= tab.getMaxCapacity()) {
return null;
}
this.putItem(item, tab);
// Set ownership and save to db
item.save();
return item;
} else {
// Add count
existingItem.setCount(Math.min(existingItem.getCount() + item.getCount(), item.getItemData().getStackLimit()));
existingItem.save();
return existingItem;
}
}
GameItem existingItem = tab.getItemById(item.getItemId());
if (existingItem == null) {
// Item type didnt exist before, we will add it to main inventory map if there is enough space
if (tab.getSize() >= tab.getMaxCapacity()) {
return null;
}
this.putItem(item, tab);
// Set ownership and save to db
item.save();
return item;
} else {
// Add count
existingItem.setCount(Math.min(existingItem.getCount() + item.getCount(), item.getItemData().getStackLimit()));
existingItem.save();
return existingItem;
}
}
}
}
private synchronized void putItem(GameItem item, InventoryTab tab) {
}
private synchronized void putItem(GameItem item, InventoryTab tab) {
this.player.getCodex().checkAddedItem(item);
// Set owner and guid FIRST!
item.setOwner(this.player);
// Put in item store
getItems().put(item.getGuid(), item);
if (tab != null) {
tab.onAddItem(item);
}
}
private void addVirtualItem(int itemId, int count) {
switch (itemId) {
case 101 -> // Character exp
// Put in item store
getItems().put(item.getGuid(), item);
if (tab != null) {
tab.onAddItem(item);
}
}
private void addVirtualItem(int itemId, int count) {
switch (itemId) {
case 101 -> // Character exp
this.player.getServer().getInventorySystem().upgradeAvatar(this.player, this.player.getTeamManager().getCurrentAvatarEntity().getAvatar(), count);
case 102 -> // Adventure exp
this.player.addExpDirectly(count);
@ -290,216 +290,216 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
this.player.setCrystals(this.player.getCrystals() + count);
case 204 -> // Home Coin
this.player.setHomeCoin(this.player.getHomeCoin() + count);
}
}
}
}
private int getVirtualItemCount(int itemId) {
switch (itemId) {
case 201: // Primogem
return this.player.getPrimogems();
case 202: // Mora
return this.player.getMora();
case 203: // Genesis Crystals
return this.player.getCrystals();
case 106: // Resin
return this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
private int getVirtualItemCount(int itemId) {
switch (itemId) {
case 201: // Primogem
return this.player.getPrimogems();
case 202: // Mora
return this.player.getMora();
case 203: // Genesis Crystals
return this.player.getCrystals();
case 106: // Resin
return this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
case 107: // Legendary Key
return this.player.getProperty(PlayerProperty.PROP_PLAYER_LEGENDARY_KEY);
case 204: // Home Coin
return this.player.getHomeCoin();
default:
GameItem item = getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId); // What if we ever want to operate on weapons/relics/furniture? :S
return (item == null) ? 0 : item.getCount();
}
}
case 204: // Home Coin
return this.player.getHomeCoin();
default:
GameItem item = getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId); // What if we ever want to operate on weapons/relics/furniture? :S
return (item == null) ? 0 : item.getCount();
}
}
public boolean payItem(int id, int count) {
return payItem(new ItemParamData(id, count));
}
public boolean payItem(int id, int count) {
return payItem(new ItemParamData(id, count));
}
public boolean payItem(ItemParamData costItem) {
return payItems(new ItemParamData[] {costItem}, 1, null);
}
public boolean payItem(ItemParamData costItem) {
return payItems(new ItemParamData[] {costItem}, 1, null);
}
public boolean payItems(ItemParamData[] costItems) {
return payItems(costItems, 1, null);
}
public boolean payItems(ItemParamData[] costItems) {
return payItems(costItems, 1, null);
}
public boolean payItems(ItemParamData[] costItems, int quantity) {
return payItems(costItems, quantity, null);
}
public synchronized boolean payItems(ItemParamData[] costItems, int quantity, ActionReason reason) {
// Make sure player has requisite items
for (ItemParamData cost : costItems) {
if (getVirtualItemCount(cost.getId()) < (cost.getCount() * quantity)) {
return false;
}
}
// All costs are satisfied, now remove them all
for (ItemParamData cost : costItems) {
switch (cost.getId()) {
case 201 -> // Primogem
player.setPrimogems(player.getPrimogems() - (cost.getCount() * quantity));
case 202 -> // Mora
player.setMora(player.getMora() - (cost.getCount() * quantity));
case 203 -> // Genesis Crystals
player.setCrystals(player.getCrystals() - (cost.getCount() * quantity));
case 106 -> // Resin
player.getResinManager().useResin(cost.getCount() * quantity);
public boolean payItems(ItemParamData[] costItems, int quantity) {
return payItems(costItems, quantity, null);
}
public synchronized boolean payItems(ItemParamData[] costItems, int quantity, ActionReason reason) {
// Make sure player has requisite items
for (ItemParamData cost : costItems) {
if (getVirtualItemCount(cost.getId()) < (cost.getCount() * quantity)) {
return false;
}
}
// All costs are satisfied, now remove them all
for (ItemParamData cost : costItems) {
switch (cost.getId()) {
case 201 -> // Primogem
player.setPrimogems(player.getPrimogems() - (cost.getCount() * quantity));
case 202 -> // Mora
player.setMora(player.getMora() - (cost.getCount() * quantity));
case 203 -> // Genesis Crystals
player.setCrystals(player.getCrystals() - (cost.getCount() * quantity));
case 106 -> // Resin
player.getResinManager().useResin(cost.getCount() * quantity);
case 107 -> // LegendaryKey
player.useLegendaryKey(cost.getCount() * quantity);
case 204 -> // Home Coin
player.setHomeCoin(player.getHomeCoin() - (cost.getCount() * quantity));
default ->
removeItem(getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()), cost.getCount() * quantity);
}
}
case 204 -> // Home Coin
player.setHomeCoin(player.getHomeCoin() - (cost.getCount() * quantity));
default ->
removeItem(getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()), cost.getCount() * quantity);
}
}
if (reason != null) { // Do we need these?
// getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason));
}
// getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
return true;
}
public void removeItems(List<GameItem> items) {
// TODO Bulk delete
for (GameItem item : items) {
this.removeItem(item, item.getCount());
}
}
public boolean removeItem(long guid) {
return removeItem(guid, 1);
}
public synchronized boolean removeItem(long guid, int count) {
GameItem item = this.getItemByGuid(guid);
if (item == null) {
return false;
}
return removeItem(item, count);
}
public synchronized boolean removeItem(GameItem item) {
return removeItem(item, item.getCount());
}
public synchronized boolean removeItem(GameItem item, int count) {
// Sanity check
if (count <= 0 || item == null) {
return false;
}
if (reason != null) { // Do we need these?
// getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason));
}
// getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
return true;
}
if (item.getItemData().isEquip()) {
item.setCount(0);
} else {
item.setCount(item.getCount() - count);
}
if (item.getCount() <= 0) {
// Remove from inventory tab too
InventoryTab tab = null;
if (item.getItemData() != null) {
tab = getInventoryTab(item.getItemData().getItemType());
}
// Remove if less than 0
deleteItem(item, tab);
//
getPlayer().sendPacket(new PacketStoreItemDelNotify(item));
} else {
getPlayer().sendPacket(new PacketStoreItemChangeNotify(item));
}
// Battle pass trigger
int removeCount = Math.min(count, item.getCount());
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, item.getItemId(), removeCount);
// Update in db
item.save();
// Returns true on success
return true;
}
private void deleteItem(GameItem item, InventoryTab tab) {
getItems().remove(item.getGuid());
if (tab != null) {
tab.onRemoveItem(item);
}
}
public boolean equipItem(long avatarGuid, long equipGuid) {
Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(avatarGuid);
GameItem item = this.getItemByGuid(equipGuid);
if (avatar != null && item != null) {
return avatar.equipItem(item, true);
}
return false;
}
public boolean unequipItem(long avatarGuid, int slot) {
Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(avatarGuid);
EquipType equipType = EquipType.getTypeByValue(slot);
if (avatar != null && equipType != EquipType.EQUIP_WEAPON) {
if (avatar.unequipItem(equipType)) {
getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(avatar, equipType));
avatar.recalcStats();
return true;
}
}
return false;
}
public void loadFromDatabase() {
List<GameItem> items = DatabaseHelper.getInventoryItems(getPlayer());
for (GameItem item : items) {
// Should never happen
if (item.getObjectId() == null) {
continue;
}
ItemData itemData = GameData.getItemDataMap().get(item.getItemId());
if (itemData == null) {
continue;
}
item.setItemData(itemData);
InventoryTab tab = null;
if (item.getItemData() != null) {
tab = getInventoryTab(item.getItemData().getItemType());
}
putItem(item, tab);
// Equip to a character if possible
if (item.isEquipped()) {
Avatar avatar = getPlayer().getAvatars().getAvatarById(item.getEquipCharacter());
boolean hasEquipped = false;
if (avatar != null) {
hasEquipped = avatar.equipItem(item, false);
}
if (!hasEquipped) {
item.setEquipCharacter(0);
item.save();
}
}
}
}
public void removeItems(List<GameItem> items) {
// TODO Bulk delete
for (GameItem item : items) {
this.removeItem(item, item.getCount());
}
}
@Override
public Iterator<GameItem> iterator() {
return this.getItems().values().iterator();
}
public boolean removeItem(long guid) {
return removeItem(guid, 1);
}
public synchronized boolean removeItem(long guid, int count) {
GameItem item = this.getItemByGuid(guid);
if (item == null) {
return false;
}
return removeItem(item, count);
}
public synchronized boolean removeItem(GameItem item) {
return removeItem(item, item.getCount());
}
public synchronized boolean removeItem(GameItem item, int count) {
// Sanity check
if (count <= 0 || item == null) {
return false;
}
if (item.getItemData().isEquip()) {
item.setCount(0);
} else {
item.setCount(item.getCount() - count);
}
if (item.getCount() <= 0) {
// Remove from inventory tab too
InventoryTab tab = null;
if (item.getItemData() != null) {
tab = getInventoryTab(item.getItemData().getItemType());
}
// Remove if less than 0
deleteItem(item, tab);
//
getPlayer().sendPacket(new PacketStoreItemDelNotify(item));
} else {
getPlayer().sendPacket(new PacketStoreItemChangeNotify(item));
}
// Battle pass trigger
int removeCount = Math.min(count, item.getCount());
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, item.getItemId(), removeCount);
// Update in db
item.save();
// Returns true on success
return true;
}
private void deleteItem(GameItem item, InventoryTab tab) {
getItems().remove(item.getGuid());
if (tab != null) {
tab.onRemoveItem(item);
}
}
public boolean equipItem(long avatarGuid, long equipGuid) {
Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(avatarGuid);
GameItem item = this.getItemByGuid(equipGuid);
if (avatar != null && item != null) {
return avatar.equipItem(item, true);
}
return false;
}
public boolean unequipItem(long avatarGuid, int slot) {
Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(avatarGuid);
EquipType equipType = EquipType.getTypeByValue(slot);
if (avatar != null && equipType != EquipType.EQUIP_WEAPON) {
if (avatar.unequipItem(equipType)) {
getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(avatar, equipType));
avatar.recalcStats();
return true;
}
}
return false;
}
public void loadFromDatabase() {
List<GameItem> items = DatabaseHelper.getInventoryItems(getPlayer());
for (GameItem item : items) {
// Should never happen
if (item.getObjectId() == null) {
continue;
}
ItemData itemData = GameData.getItemDataMap().get(item.getItemId());
if (itemData == null) {
continue;
}
item.setItemData(itemData);
InventoryTab tab = null;
if (item.getItemData() != null) {
tab = getInventoryTab(item.getItemData().getItemType());
}
putItem(item, tab);
// Equip to a character if possible
if (item.isEquipped()) {
Avatar avatar = getPlayer().getAvatars().getAvatarById(item.getEquipCharacter());
boolean hasEquipped = false;
if (avatar != null) {
hasEquipped = avatar.equipItem(item, false);
}
if (!hasEquipped) {
item.setEquipCharacter(0);
item.save();
}
}
}
}
@Override
public Iterator<GameItem> iterator() {
return this.getItems().values().iterator();
}
}

View File

@ -13,89 +13,89 @@ import emu.grasscutter.server.packet.send.PacketDelMailRsp;
import emu.grasscutter.server.packet.send.PacketMailChangeNotify;
public class MailHandler extends BasePlayerManager {
private final List<Mail> mail;
public MailHandler(Player player) {
super(player);
this.mail = new ArrayList<>();
}
private final List<Mail> mail;
public List<Mail> getMail() {
return mail;
}
// ---------------------MAIL------------------------
public MailHandler(Player player) {
super(player);
public void sendMail(Mail message) {
// Call mail receive event.
PlayerReceiveMailEvent event = new PlayerReceiveMailEvent(this.getPlayer(), message); event.call();
if(event.isCanceled()) return; message = event.getMessage();
message.setOwnerUid(this.getPlayer().getUid());
message.save();
this.mail.add(message);
Grasscutter.getLogger().debug("Mail sent to user [" + this.getPlayer().getUid() + ":" + this.getPlayer().getNickname() + "]!");
if (this.getPlayer().isOnline()) {
this.getPlayer().sendPacket(new PacketMailChangeNotify(this.getPlayer(), message));
} // TODO: setup a way for the mail notification to show up when someone receives mail when they were offline
}
this.mail = new ArrayList<>();
}
public boolean deleteMail(int mailId) {
Mail message = getMailById(mailId);
public List<Mail> getMail() {
return mail;
}
if (message != null) {
this.getMail().remove(mailId);
message.expireTime = 0;
message.save();
return true;
}
// ---------------------MAIL------------------------
return false;
}
public void deleteMail(List<Integer> mailList) {
List<Integer> sortedMailList = new ArrayList<>();
sortedMailList.addAll(mailList);
Collections.sort(sortedMailList, Collections.reverseOrder());
List<Integer> deleted = new ArrayList<>();
for (int id : sortedMailList) {
if (this.deleteMail(id)) {
deleted.add(id);
}
}
player.getSession().send(new PacketDelMailRsp(player, deleted));
player.getSession().send(new PacketMailChangeNotify(player, null, deleted));
}
public void sendMail(Mail message) {
// Call mail receive event.
PlayerReceiveMailEvent event = new PlayerReceiveMailEvent(this.getPlayer(), message); event.call();
if (event.isCanceled()) return; message = event.getMessage();
public Mail getMailById(int index) { return this.mail.get(index); }
public int getMailIndex(Mail message) {
return this.mail.indexOf(message);
}
message.setOwnerUid(this.getPlayer().getUid());
message.save();
public boolean replaceMailByIndex(int index, Mail message) {
if(getMailById(index) != null) {
this.mail.set(index, message);
message.save();
return true;
} else {
return false;
}
}
this.mail.add(message);
public void loadFromDatabase() {
List<Mail> mailList = DatabaseHelper.getAllMail(this.getPlayer());
for (Mail mail : mailList) {
this.getMail().add(mail);
}
}
Grasscutter.getLogger().debug("Mail sent to user [" + this.getPlayer().getUid() + ":" + this.getPlayer().getNickname() + "]!");
if (this.getPlayer().isOnline()) {
this.getPlayer().sendPacket(new PacketMailChangeNotify(this.getPlayer(), message));
} // TODO: setup a way for the mail notification to show up when someone receives mail when they were offline
}
public boolean deleteMail(int mailId) {
Mail message = getMailById(mailId);
if (message != null) {
this.getMail().remove(mailId);
message.expireTime = 0;
message.save();
return true;
}
return false;
}
public void deleteMail(List<Integer> mailList) {
List<Integer> sortedMailList = new ArrayList<>();
sortedMailList.addAll(mailList);
Collections.sort(sortedMailList, Collections.reverseOrder());
List<Integer> deleted = new ArrayList<>();
for (int id : sortedMailList) {
if (this.deleteMail(id)) {
deleted.add(id);
}
}
player.getSession().send(new PacketDelMailRsp(player, deleted));
player.getSession().send(new PacketMailChangeNotify(player, null, deleted));
}
public Mail getMailById(int index) { return this.mail.get(index); }
public int getMailIndex(Mail message) {
return this.mail.indexOf(message);
}
public boolean replaceMailByIndex(int index, Mail message) {
if (getMailById(index) != null) {
this.mail.set(index, message);
message.save();
return true;
} else {
return false;
}
}
public void loadFromDatabase() {
List<Mail> mailList = DatabaseHelper.getAllMail(this.getPlayer());
for (Mail mail : mailList) {
this.getMail().add(mail);
}
}
}

View File

@ -102,9 +102,9 @@ public class CookingManager extends BasePlayerManager {
}
// Get result item information.
int qualityIndex =
quality == 0
? 2
int qualityIndex =
quality == 0
? 2
: quality - 1;
ItemParamData resultParam = recipeData.getQualityOutputVec().get(qualityIndex);

View File

@ -15,25 +15,25 @@ import java.util.ArrayList;
import java.util.List;
public class FurnitureManager extends BasePlayerManager {
public FurnitureManager(Player player) {
super(player);
}
public void onLogin(){
public void onLogin() {
notifyUnlockFurniture();
notifyUnlockFurnitureSuite();
}
public void notifyUnlockFurniture(){
public void notifyUnlockFurniture() {
player.getSession().send(new PacketUnlockedFurnitureFormulaDataNotify(player.getUnlockedFurniture()));
}
public void notifyUnlockFurnitureSuite(){
public void notifyUnlockFurnitureSuite() {
player.getSession().send(new PacketUnlockedFurnitureSuiteDataNotify(player.getUnlockedFurnitureSuite()));
}
public synchronized boolean unlockFurnitureOrSuite(GameItem useItem){
public synchronized boolean unlockFurnitureOrSuite(GameItem useItem) {
// Check
if (!List.of("ITEM_USE_UNLOCK_FURNITURE_FORMULA", "ITEM_USE_UNLOCK_FURNITURE_SUITE")
.contains(useItem.getItemData().getItemUse().get(0).getUseOp())) {
@ -45,10 +45,10 @@ public class FurnitureManager extends BasePlayerManager {
// Remove first
player.getInventory().removeItem(useItem, 1);
if("ITEM_USE_UNLOCK_FURNITURE_FORMULA".equals(useItem.getItemData().getItemUse().get(0).getUseOp())){
if ("ITEM_USE_UNLOCK_FURNITURE_FORMULA".equals(useItem.getItemData().getItemUse().get(0).getUseOp())) {
player.getUnlockedFurniture().add(furnitureIdOrSuiteId);
notifyUnlockFurniture();
}else{
}else {
player.getUnlockedFurnitureSuite().add(furnitureIdOrSuiteId);
notifyUnlockFurnitureSuite();
}
@ -57,19 +57,19 @@ public class FurnitureManager extends BasePlayerManager {
public void startMake(int makeId, int avatarId) {
var makeData = GameData.getFurnitureMakeConfigDataMap().get(makeId);
if(makeData == null){
if (makeData == null) {
player.getSession().send(new PacketFurnitureMakeStartRsp(Retcode.RET_FURNITURE_MAKE_CONFIG_ERROR_VALUE, null));
return;
}
// check slot count
if (player.getHome().getLevelData().getFurnitureMakeSlotCount() <= player.getHome().getFurnitureMakeSlotItemList().size()){
if (player.getHome().getLevelData().getFurnitureMakeSlotCount() <= player.getHome().getFurnitureMakeSlotItemList().size()) {
player.getSession().send(new PacketFurnitureMakeStartRsp(Retcode.RET_FURNITURE_MAKE_SLOT_FULL_VALUE, null));
return;
}
// pay items first
if(!player.getInventory().payItems(makeData.getMaterialItems().toArray(new ItemParamData[0]))){
if (!player.getInventory().payItems(makeData.getMaterialItems().toArray(new ItemParamData[0]))) {
player.getSession().send(new PacketFurnitureMakeStartRsp(Retcode.RET_HOME_FURNITURE_COUNT_NOT_ENOUGH_VALUE, null));
return;
}
@ -93,7 +93,7 @@ public class FurnitureManager extends BasePlayerManager {
}
public void queryStatus() {
if (player.getHome().getFurnitureMakeSlotItemList() == null){
if (player.getHome().getFurnitureMakeSlotItemList() == null) {
player.getHome().setFurnitureMakeSlotItemList(new ArrayList<>());
}
@ -103,7 +103,7 @@ public class FurnitureManager extends BasePlayerManager {
public void take(int index, int makeId, boolean isFastFinish) {
var makeData = GameData.getFurnitureMakeConfigDataMap().get(makeId);
if(makeData == null){
if (makeData == null) {
player.getSession().send(new PacketTakeFurnitureMakeRsp(Retcode.RET_FURNITURE_MAKE_CONFIG_ERROR_VALUE, makeId, null, null));
return;
}
@ -112,19 +112,19 @@ public class FurnitureManager extends BasePlayerManager {
.filter(x -> x.getIndex() == index && x.getMakeId() == makeId)
.findFirst();
if(slotItem.isEmpty()){
if (slotItem.isEmpty()) {
player.getSession().send(new PacketTakeFurnitureMakeRsp(Retcode.RET_FURNITURE_MAKE_NO_MAKE_DATA_VALUE, makeId, null, null));
return;
}
// pay the speedup item
if(isFastFinish && !player.getInventory().payItem(107013,1)){
if (isFastFinish && !player.getInventory().payItem(107013,1)) {
player.getSession().send(new PacketTakeFurnitureMakeRsp(Retcode.RET_FURNITURE_MAKE_UNFINISH_VALUE, makeId, null, null));
return;
}
// check if player can take
// if(slotItem.get().getBeginTime() + slotItem.get().getDurTime() >= Utils.getCurrentSeconds() && !isFastFinish){
// if (slotItem.get().getBeginTime() + slotItem.get().getDurTime() >= Utils.getCurrentSeconds() && !isFastFinish) {
// player.getSession().send(new PacketTakeFurnitureMakeRsp(Retcode.RET_FURNITURE_MAKE_UNFINISH_VALUE, makeId, null, null));
// return;
// }

View File

@ -26,7 +26,7 @@ public class ResinManager extends BasePlayerManager {
}
int currentResin = this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
// Check if the player has sufficient resin.
if (currentResin < amount) {
return false;
@ -39,16 +39,16 @@ public class ResinManager extends BasePlayerManager {
// Check if this has taken the player under the recharge cap,
// starting the recharging process.
if (this.player.getNextResinRefresh() == 0 && newResin < GAME_OPTIONS.resinOptions.cap) {
int currentTime = Utils.getCurrentSeconds();
int currentTime = Utils.getCurrentSeconds();
this.player.setNextResinRefresh(currentTime + GAME_OPTIONS.resinOptions.rechargeTime);
}
// Send packets.
this.player.sendPacket(new PacketResinChangeNotify(this.player));
// Battle Pass trigger
this.player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, 106, amount); // Resin item id = 106
return true;
}
@ -122,7 +122,7 @@ public class ResinManager extends BasePlayerManager {
* Player login.
********************/
public synchronized void onPlayerLogin() {
// If resin usage is disabled, set resin to cap.
// If resin usage is disabled, set resin to cap.
if (!GAME_OPTIONS.resinOptions.resinUsage) {
this.player.setProperty(PlayerProperty.PROP_PLAYER_RESIN, GAME_OPTIONS.resinOptions.cap);
this.player.setNextResinRefresh(0);
@ -132,7 +132,7 @@ public class ResinManager extends BasePlayerManager {
// we need to restart recharging here.
int currentResin = this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
int currentTime = Utils.getCurrentSeconds();
if (currentResin < GAME_OPTIONS.resinOptions.cap && this.player.getNextResinRefresh() == 0) {
this.player.setNextResinRefresh(currentTime + GAME_OPTIONS.resinOptions.rechargeTime);
}

View File

@ -1,191 +1,191 @@
package emu.grasscutter.game.managers;
import ch.qos.logback.classic.Logger;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
// Statue of the Seven Manager
public class SotSManager extends BasePlayerManager {
// NOTE: Spring volume balance *1 = fight prop HP *100
private final Logger logger = Grasscutter.getLogger();
private Timer autoRecoverTimer;
private final boolean enablePriorityHealing = false;
public final static int GlobalMaximumSpringVolume = PlayerProperty.PROP_MAX_SPRING_VOLUME.getMax();
public SotSManager(Player player) {
super(player);
}
public boolean getIsAutoRecoveryEnabled() {
return player.getProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE) == 1;
}
public void setIsAutoRecoveryEnabled(boolean enabled) {
player.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, enabled ? 1 : 0);
player.save();
}
public int getAutoRecoveryPercentage() {
return player.getProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT);
}
public void setAutoRecoveryPercentage(int percentage) {
player.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, percentage);
player.save();
}
public long getLastUsed() {
return player.getSpringLastUsed();
}
public void setLastUsed() {
player.setSpringLastUsed(System.currentTimeMillis() / 1000);
player.save();
}
public int getMaxVolume() {
return player.getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
}
public void setMaxVolume(int volume) {
player.setProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME, volume);
player.save();
}
public int getCurrentVolume() {
return player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME);
}
public void setCurrentVolume(int volume) {
player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, volume);
setLastUsed();
player.save();
}
public void handleEnterTransPointRegionNotify() {
logger.trace("Player entered statue region");
autoRevive();
if (autoRecoverTimer == null) {
autoRecoverTimer = new Timer();
autoRecoverTimer.schedule(new AutoRecoverTimerTick(), 2500, 15000);
}
}
public void handleExitTransPointRegionNotify() {
logger.trace("Player left statue region");
if (autoRecoverTimer != null) {
autoRecoverTimer.cancel();
autoRecoverTimer = null;
}
}
// autoRevive automatically revives all team members.
public void autoRevive() {
player.getTeamManager().getActiveTeam().forEach(entity -> {
boolean isAlive = entity.isAlive();
if (isAlive) {
return;
}
logger.trace("Reviving avatar " + entity.getAvatar().getAvatarData().getName());
player.getTeamManager().reviveAvatar(entity.getAvatar());
player.getTeamManager().healAvatar(entity.getAvatar(), 30, 0);
});
}
private class AutoRecoverTimerTick extends TimerTask {
// autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level.
public void run() {
refillSpringVolume();
logger.trace("isAutoRecoveryEnabled: " + getIsAutoRecoveryEnabled() + "\tautoRecoverPercentage: " + getAutoRecoveryPercentage());
if (getIsAutoRecoveryEnabled()) {
List<EntityAvatar> activeTeam = player.getTeamManager().getActiveTeam();
// When the statue does not have enough remaining volume:
// Enhanced experience: Enable priority healing
// The current active character will get healed first, then sequential.
// Vanilla experience: Disable priority healing
// Sequential healing based on character index.
int priorityIndex = enablePriorityHealing ? player.getTeamManager().getCurrentCharacterIndex() : -1;
if (priorityIndex >= 0) {
checkAndHealAvatar(activeTeam.get(priorityIndex));
}
for (int i = 0; i < activeTeam.size(); i++) {
if (i != priorityIndex) {
checkAndHealAvatar(activeTeam.get(i));
}
}
}
}
}
public void checkAndHealAvatar(EntityAvatar entity) {
int maxHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * 100);
int currentHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) * 100);
if (currentHP == maxHP) {
return;
}
int targetHP = maxHP * getAutoRecoveryPercentage() / 100;
if (targetHP > currentHP) {
int needHP = targetHP - currentHP;
int currentVolume = getCurrentVolume();
if (currentVolume >= needHP) {
// sufficient
setCurrentVolume(currentVolume - needHP);
} else {
// insufficient balance
needHP = currentVolume;
setCurrentVolume(0);
}
if (needHP > 0) {
logger.trace("Healing avatar " + entity.getAvatar().getAvatarData().getName() + " +" + needHP);
player.getTeamManager().healAvatar(entity.getAvatar(), 0, needHP);
player.getSession().send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP,
((float) needHP / 100), List.of(3), PropChangeReason.PROP_CHANGE_REASON_STATUE_RECOVER,
ChangeHpReason.CHANGE_HP_REASON_ADD_STATUE));
player.getSession().send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
}
}
}
public void refillSpringVolume() {
// Temporary: Max spring volume depends on level of the statues in Mondstadt and Liyue. Override until we have statue level.
// TODO: remove
// https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking
setMaxVolume(8500000);
// Temporary: Auto enable 100% statue recovery until we can adjust statue settings in game
// TODO: remove
setAutoRecoveryPercentage(100);
setIsAutoRecoveryEnabled(true);
int maxVolume = getMaxVolume();
int currentVolume = getCurrentVolume();
if (currentVolume < maxVolume) {
long now = System.currentTimeMillis() / 1000;
int secondsSinceLastUsed = (int) (now - getLastUsed());
// 15s = 1% max volume
int volumeRefilled = secondsSinceLastUsed * maxVolume / 15 / 100;
logger.trace("Statue has refilled HP volume: " + volumeRefilled);
currentVolume = Math.min(currentVolume + volumeRefilled, maxVolume);
logger.trace("Statue remaining HP volume: " + currentVolume);
setCurrentVolume(currentVolume);
}
}
}
package emu.grasscutter.game.managers;
import ch.qos.logback.classic.Logger;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
// Statue of the Seven Manager
public class SotSManager extends BasePlayerManager {
// NOTE: Spring volume balance *1 = fight prop HP *100
private final Logger logger = Grasscutter.getLogger();
private Timer autoRecoverTimer;
private final boolean enablePriorityHealing = false;
public final static int GlobalMaximumSpringVolume = PlayerProperty.PROP_MAX_SPRING_VOLUME.getMax();
public SotSManager(Player player) {
super(player);
}
public boolean getIsAutoRecoveryEnabled() {
return player.getProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE) == 1;
}
public void setIsAutoRecoveryEnabled(boolean enabled) {
player.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, enabled ? 1 : 0);
player.save();
}
public int getAutoRecoveryPercentage() {
return player.getProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT);
}
public void setAutoRecoveryPercentage(int percentage) {
player.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, percentage);
player.save();
}
public long getLastUsed() {
return player.getSpringLastUsed();
}
public void setLastUsed() {
player.setSpringLastUsed(System.currentTimeMillis() / 1000);
player.save();
}
public int getMaxVolume() {
return player.getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
}
public void setMaxVolume(int volume) {
player.setProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME, volume);
player.save();
}
public int getCurrentVolume() {
return player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME);
}
public void setCurrentVolume(int volume) {
player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, volume);
setLastUsed();
player.save();
}
public void handleEnterTransPointRegionNotify() {
logger.trace("Player entered statue region");
autoRevive();
if (autoRecoverTimer == null) {
autoRecoverTimer = new Timer();
autoRecoverTimer.schedule(new AutoRecoverTimerTick(), 2500, 15000);
}
}
public void handleExitTransPointRegionNotify() {
logger.trace("Player left statue region");
if (autoRecoverTimer != null) {
autoRecoverTimer.cancel();
autoRecoverTimer = null;
}
}
// autoRevive automatically revives all team members.
public void autoRevive() {
player.getTeamManager().getActiveTeam().forEach(entity -> {
boolean isAlive = entity.isAlive();
if (isAlive) {
return;
}
logger.trace("Reviving avatar " + entity.getAvatar().getAvatarData().getName());
player.getTeamManager().reviveAvatar(entity.getAvatar());
player.getTeamManager().healAvatar(entity.getAvatar(), 30, 0);
});
}
private class AutoRecoverTimerTick extends TimerTask {
// autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level.
public void run() {
refillSpringVolume();
logger.trace("isAutoRecoveryEnabled: " + getIsAutoRecoveryEnabled() + "\tautoRecoverPercentage: " + getAutoRecoveryPercentage());
if (getIsAutoRecoveryEnabled()) {
List<EntityAvatar> activeTeam = player.getTeamManager().getActiveTeam();
// When the statue does not have enough remaining volume:
// Enhanced experience: Enable priority healing
// The current active character will get healed first, then sequential.
// Vanilla experience: Disable priority healing
// Sequential healing based on character index.
int priorityIndex = enablePriorityHealing ? player.getTeamManager().getCurrentCharacterIndex() : -1;
if (priorityIndex >= 0) {
checkAndHealAvatar(activeTeam.get(priorityIndex));
}
for (int i = 0; i < activeTeam.size(); i++) {
if (i != priorityIndex) {
checkAndHealAvatar(activeTeam.get(i));
}
}
}
}
}
public void checkAndHealAvatar(EntityAvatar entity) {
int maxHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * 100);
int currentHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) * 100);
if (currentHP == maxHP) {
return;
}
int targetHP = maxHP * getAutoRecoveryPercentage() / 100;
if (targetHP > currentHP) {
int needHP = targetHP - currentHP;
int currentVolume = getCurrentVolume();
if (currentVolume >= needHP) {
// sufficient
setCurrentVolume(currentVolume - needHP);
} else {
// insufficient balance
needHP = currentVolume;
setCurrentVolume(0);
}
if (needHP > 0) {
logger.trace("Healing avatar " + entity.getAvatar().getAvatarData().getName() + " +" + needHP);
player.getTeamManager().healAvatar(entity.getAvatar(), 0, needHP);
player.getSession().send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP,
((float) needHP / 100), List.of(3), PropChangeReason.PROP_CHANGE_REASON_STATUE_RECOVER,
ChangeHpReason.CHANGE_HP_REASON_ADD_STATUE));
player.getSession().send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
}
}
}
public void refillSpringVolume() {
// Temporary: Max spring volume depends on level of the statues in Mondstadt and Liyue. Override until we have statue level.
// TODO: remove
// https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking
setMaxVolume(8500000);
// Temporary: Auto enable 100% statue recovery until we can adjust statue settings in game
// TODO: remove
setAutoRecoveryPercentage(100);
setIsAutoRecoveryEnabled(true);
int maxVolume = getMaxVolume();
int currentVolume = getCurrentVolume();
if (currentVolume < maxVolume) {
long now = System.currentTimeMillis() / 1000;
int secondsSinceLastUsed = (int) (now - getLastUsed());
// 15s = 1% max volume
int volumeRefilled = secondsSinceLastUsed * maxVolume / 15 / 100;
logger.trace("Statue has refilled HP volume: " + volumeRefilled);
currentVolume = Math.min(currentVolume + volumeRefilled, maxVolume);
logger.trace("Statue remaining HP volume: " + currentVolume);
setCurrentVolume(currentVolume);
}
}
}

View File

@ -21,197 +21,197 @@ import java.util.List;
import java.util.Map;
public class ChatManager implements ChatManagerHandler {
static final String PREFIXES = "[/!]";
static final Pattern RE_PREFIXES = Pattern.compile(PREFIXES);
static final Pattern RE_COMMANDS = Pattern.compile("\n" + PREFIXES);
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<>();
// 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;
private final GameServer server;
public ChatManager(GameServer server) {
this.server = server;
}
public ChatManager(GameServer server) {
this.server = server;
}
public GameServer getServer() {
return 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;
}
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 targetId, ChatInfo info) {
if (!this.history.containsKey(uid)) {
this.history.put(uid, new HashMap<>());
}
if (!this.history.get(uid).containsKey(targetId)) {
this.history.get(uid).put(targetId, new ArrayList<>());
}
/********************
* Chat history handling
********************/
private void putInHistory(int uid, int targetId, ChatInfo info) {
if (!this.history.containsKey(uid)) {
this.history.put(uid, new HashMap<>());
}
if (!this.history.get(uid).containsKey(targetId)) {
this.history.get(uid).put(targetId, new ArrayList<>());
}
this.history.get(uid).get(targetId).add(info);
}
this.history.get(uid).get(targetId).add(info);
}
public void clearHistoryOnLogout(Player player) {
if (this.history.containsKey(player.getUid())) {
this.history.remove(player.getUid());
}
}
public void clearHistoryOnLogout(Player player) {
if (this.history.containsKey(player.getUid())) {
this.history.remove(player.getUid());
}
}
public void handlePullPrivateChatReq(Player player, int targetUid) {
if (this.history.containsKey(player.getUid()) && this.history.get(player.getUid()).containsKey(targetUid)) {
player.sendPacket(new PacketPullPrivateChatRsp(this.history.get(player.getUid()).get(targetUid)));
}
else {
player.sendPacket(new PacketPullPrivateChatRsp(List.of()));
}
}
public void handlePullPrivateChatReq(Player player, int targetUid) {
if (this.history.containsKey(player.getUid()) && this.history.get(player.getUid()).containsKey(targetUid)) {
player.sendPacket(new PacketPullPrivateChatRsp(this.history.get(player.getUid()).get(targetUid)));
}
else {
player.sendPacket(new PacketPullPrivateChatRsp(List.of()));
}
}
public void handlePullRecentChatReq(Player 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.
if (this.history.containsKey(player.getUid()) && this.history.get(player.getUid()).containsKey(GameConstants.SERVER_CONSOLE_UID)) {
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));
}
else {
player.sendPacket(new PacketPullRecentChatRsp(List.of()));
}
}
public void handlePullRecentChatReq(Player 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.
if (this.history.containsKey(player.getUid()) && this.history.get(player.getUid()).containsKey(GameConstants.SERVER_CONSOLE_UID)) {
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));
}
else {
player.sendPacket(new PacketPullRecentChatRsp(List.of()));
}
}
/********************
* 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;
}
/********************
* Sending messages
********************/
public void sendPrivateMessageFromServer(int targetUid, String message) {
// Sanity checks.
if (message == null || message.length() == 0) {
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());
// Get target.
Player target = getServer().getPlayerByUid(targetUid);
if (target == null) {
return;
}
// 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, message);
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
// 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 sendPrivateMessageFromServer(int targetUid, int emote) {
// Get target.
Player target = getServer().getPlayerByUid(targetUid);
if (target == null) {
return;
}
// Send.
target.sendPacket(packet);
}
// 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());
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);
// Send.
target.sendPacket(packet);
}
// Check if command
if (tryInvokeCommand(player, target, message)) {
return;
}
public void sendPrivateMessage(Player player, int targetUid, String message) {
// Sanity checks.
if (message == null || message.length() == 0) {
return;
}
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
return;
}
// Get target.
Player target = getServer().getPlayerByUid(targetUid);
// Create chat packet.
var packet = new PacketPrivateChatNotify(player.getUid(), targetUid, message);
// Check if command
if (tryInvokeCommand(player, target, message)) {
return;
}
// Send and put in history.
player.sendPacket(packet);
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
return;
}
if (target != null) {
target.sendPacket(packet);
putInHistory(targetUid, player.getUid(), packet.getChatInfo());
}
}
// Create chat packet.
var packet = new PacketPrivateChatNotify(player.getUid(), targetUid, message);
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 in history.
player.sendPacket(packet);
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
// 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());
}
}
if (target != null) {
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);
public void sendTeamMessage(Player player, int channel, String message) {
// Sanity checks
if (message == null || message.length() == 0) {
return;
}
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
return;
}
// Check if command
if (tryInvokeCommand(player, null, message)) {
return;
}
// Create chat packet.
var packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), emote);
// 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));
}
// Send and put is history.
player.sendPacket(packet);
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
/********************
* Welcome messages
********************/
public 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);
}
if (target != null) {
target.sendPacket(packet);
putInHistory(targetUid, player.getUid(), packet.getChatInfo());
}
}
this.sendPrivateMessageFromServer(player.getUid(), "THIS IS AN EXPERIMENTAL BUILD OF GRASSCUTTER FOR 2.7.50/2.8\nDON'T LEAK <3");
}
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
********************/
public 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);
}
this.sendPrivateMessageFromServer(player.getUid(), "THIS IS AN EXPERIMENTAL BUILD OF GRASSCUTTER FOR 2.7.50/2.8\nDON'T LEAK <3");
}
}

View File

@ -21,7 +21,7 @@ public class DeforestationManager extends BasePlayerManager {
private final ArrayList<HitTreeRecord> currentRecord;
private final static HashMap<Integer, Integer> ColliderTypeToWoodItemID = new HashMap<>();
static {
/* define wood types which reflected to item id*/
ColliderTypeToWoodItemID.put(1,101301);
@ -37,17 +37,17 @@ public class DeforestationManager extends BasePlayerManager {
ColliderTypeToWoodItemID.put(11,101311);
ColliderTypeToWoodItemID.put(12,101312);
}
public DeforestationManager(Player player){
public DeforestationManager(Player player) {
super(player);
this.currentRecord = new ArrayList<>();
}
public void resetWood(){
public void resetWood() {
synchronized (currentRecord) {
currentRecord.clear();
}
}
public void onDeforestationInvoke(HitTreeNotifyOuterClass.HitTreeNotify hit){
public void onDeforestationInvoke(HitTreeNotifyOuterClass.HitTreeNotify hit) {
synchronized (currentRecord) {
//Grasscutter.getLogger().info("onDeforestationInvoke! Wood records {}", currentRecord);
VectorOuterClass.Vector hitPosition = hit.getTreePos();
@ -59,14 +59,14 @@ public class DeforestationManager extends BasePlayerManager {
HitTreeRecord record = searchRecord(positionHash);
if (record == null) {
record = new HitTreeRecord(positionHash);
}else{
}else {
currentRecord.remove(record);// move it to last position
}
currentRecord.add(record);
if(currentRecord.size()>RECORD_MAX_TIMES_OTHER_HIT_TREE){
if (currentRecord.size()>RECORD_MAX_TIMES_OTHER_HIT_TREE) {
currentRecord.remove(0);
}
if(record.record()) {
if (record.record()) {
EntityItem entity = new EntityItem(scene,
null,
GameData.getItemDataMap().get(itemId),
@ -82,7 +82,7 @@ public class DeforestationManager extends BasePlayerManager {
}
// unknown wood type
}
private HitTreeRecord searchRecord(int id){
private HitTreeRecord searchRecord(int id) {
for (HitTreeRecord record : currentRecord) {
if (record.getUnique() == id) {
return record;

View File

@ -448,4 +448,4 @@ public class EnergyManager extends BasePlayerManager {
}
}
}
}
}

View File

@ -30,298 +30,298 @@ import emu.grasscutter.utils.Utils;
public class ForgingManager extends BasePlayerManager {
public ForgingManager(Player player) {
super(player);
}
public ForgingManager(Player player) {
super(player);
}
/**********
Blueprint unlocking.
**********/
public synchronized boolean unlockForgingBlueprint(GameItem blueprintItem) {
// Make sure this is actually a forging blueprint.
if (!blueprintItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_FORGE")) {
return false;
}
/**********
Blueprint unlocking.
**********/
public synchronized boolean unlockForgingBlueprint(GameItem blueprintItem) {
// Make sure this is actually a forging blueprint.
if (!blueprintItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_FORGE")) {
return false;
}
// Determine the forging item we should unlock.
int forgeId = Integer.parseInt(blueprintItem.getItemData().getItemUse().get(0).getUseParam().get(0));
// Determine the forging item we should unlock.
int forgeId = Integer.parseInt(blueprintItem.getItemData().getItemUse().get(0).getUseParam().get(0));
// Remove the blueprint from the player's inventory.
// We need to do this here, before sending ForgeFormulaDataNotify, or the the forging UI won't correctly
// update when unlocking the blueprint.
player.getInventory().removeItem(blueprintItem, 1);
// Remove the blueprint from the player's inventory.
// We need to do this here, before sending ForgeFormulaDataNotify, or the the forging UI won't correctly
// update when unlocking the blueprint.
player.getInventory().removeItem(blueprintItem, 1);
// Tell the client that this blueprint is now unlocked and add the unlocked item to the player.
this.player.getUnlockedForgingBlueprints().add(forgeId);
this.player.sendPacket(new PacketForgeFormulaDataNotify(forgeId));
// Tell the client that this blueprint is now unlocked and add the unlocked item to the player.
this.player.getUnlockedForgingBlueprints().add(forgeId);
this.player.sendPacket(new PacketForgeFormulaDataNotify(forgeId));
return true;
}
return true;
}
/**********
Communicate forging information to the client.
**********/
private synchronized int determineNumberOfQueues() {
int adventureRank = player.getLevel();
return
(adventureRank >= 15) ? 4
: (adventureRank >= 10) ? 3
: (adventureRank >= 5) ? 2
: 1;
}
/**********
Communicate forging information to the client.
**********/
private synchronized int determineNumberOfQueues() {
int adventureRank = player.getLevel();
return
(adventureRank >= 15) ? 4
: (adventureRank >= 10) ? 3
: (adventureRank >= 5) ? 2
: 1;
}
private synchronized Map<Integer, ForgeQueueData> determineCurrentForgeQueueData() {
Map<Integer, ForgeQueueData> res = new HashMap<>();
int currentTime = Utils.getCurrentSeconds();
private synchronized Map<Integer, ForgeQueueData> determineCurrentForgeQueueData() {
Map<Integer, ForgeQueueData> res = new HashMap<>();
int currentTime = Utils.getCurrentSeconds();
// Create queue information for all active forges.
for (int i = 0; i < this.player.getActiveForges().size(); i++) {
ActiveForgeData activeForge = this.player.getActiveForges().get(i);
// Create queue information for all active forges.
for (int i = 0; i < this.player.getActiveForges().size(); i++) {
ActiveForgeData activeForge = this.player.getActiveForges().get(i);
ForgeQueueData data = ForgeQueueData.newBuilder()
.setQueueId(i + 1)
.setForgeId(activeForge.getForgeId())
.setFinishCount(activeForge.getFinishedCount(currentTime))
.setUnfinishCount(activeForge.getUnfinishedCount(currentTime))
.setTotalFinishTimestamp(activeForge.getTotalFinishTimestamp())
.setNextFinishTimestamp(activeForge.getNextFinishTimestamp(currentTime))
.setAvatarId(activeForge.getAvatarId())
.build();
ForgeQueueData data = ForgeQueueData.newBuilder()
.setQueueId(i + 1)
.setForgeId(activeForge.getForgeId())
.setFinishCount(activeForge.getFinishedCount(currentTime))
.setUnfinishCount(activeForge.getUnfinishedCount(currentTime))
.setTotalFinishTimestamp(activeForge.getTotalFinishTimestamp())
.setNextFinishTimestamp(activeForge.getNextFinishTimestamp(currentTime))
.setAvatarId(activeForge.getAvatarId())
.build();
res.put(i + 1, data);
}
res.put(i + 1, data);
}
return res;
}
return res;
}
public synchronized void sendForgeDataNotify() {
// Determine the number of queues and unlocked items.
int numQueues = this.determineNumberOfQueues();
var unlockedItems = this.player.getUnlockedForgingBlueprints();
var queueData = this.determineCurrentForgeQueueData();
public synchronized void sendForgeDataNotify() {
// Determine the number of queues and unlocked items.
int numQueues = this.determineNumberOfQueues();
var unlockedItems = this.player.getUnlockedForgingBlueprints();
var queueData = this.determineCurrentForgeQueueData();
// Send notification.
this.player.sendPacket(new PacketForgeDataNotify(unlockedItems, numQueues, queueData));
}
public synchronized void handleForgeGetQueueDataReq() {
// Determine the number of queues.
int numQueues = this.determineNumberOfQueues();
var queueData = this.determineCurrentForgeQueueData();
// Send notification.
this.player.sendPacket(new PacketForgeDataNotify(unlockedItems, numQueues, queueData));
}
// Reply.
this.player.sendPacket(new PacketForgeGetQueueDataRsp(Retcode.RET_SUCC, numQueues, queueData));
}
public synchronized void handleForgeGetQueueDataReq() {
// Determine the number of queues.
int numQueues = this.determineNumberOfQueues();
var queueData = this.determineCurrentForgeQueueData();
/**********
Initiate forging process.
**********/
private synchronized void sendForgeQueueDataNotify() {
var queueData = this.determineCurrentForgeQueueData();
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
}
private synchronized void sendForgeQueueDataNotify(boolean hasRemoved) {
var queueData = this.determineCurrentForgeQueueData();
// Reply.
this.player.sendPacket(new PacketForgeGetQueueDataRsp(Retcode.RET_SUCC, numQueues, queueData));
}
if (hasRemoved) {
this.player.sendPacket(new PacketForgeQueueDataNotify(Map.of(), List.of(1, 2, 3, 4)));
}
/**********
Initiate forging process.
**********/
private synchronized void sendForgeQueueDataNotify() {
var queueData = this.determineCurrentForgeQueueData();
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
}
private synchronized void sendForgeQueueDataNotify(boolean hasRemoved) {
var queueData = this.determineCurrentForgeQueueData();
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
}
if (hasRemoved) {
this.player.sendPacket(new PacketForgeQueueDataNotify(Map.of(), List.of(1, 2, 3, 4)));
}
public synchronized void handleForgeStartReq(ForgeStartReq req) {
// Refuse if all queues are already full.
if (this.player.getActiveForges().size() >= this.determineNumberOfQueues()) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_QUEUE_FULL));
return;
}
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
}
// Get the required forging information for the target item.
if (!GameData.getForgeDataMap().containsKey(req.getForgeId())) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FAIL)); //ToDo: Probably the wrong return code.
return;
}
public synchronized void handleForgeStartReq(ForgeStartReq req) {
// Refuse if all queues are already full.
if (this.player.getActiveForges().size() >= this.determineNumberOfQueues()) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_QUEUE_FULL));
return;
}
ForgeData forgeData = GameData.getForgeDataMap().get(req.getForgeId());
// Get the required forging information for the target item.
if (!GameData.getForgeDataMap().containsKey(req.getForgeId())) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FAIL)); //ToDo: Probably the wrong return code.
return;
}
//Check if the player has sufficient forge points.
int requiredPoints = forgeData.getForgePoint() * req.getForgeCount();
if (requiredPoints > this.player.getForgePoints()) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH));
return;
}
// Check if we have enough of each material and consume.
List<ItemParamData> material = new ArrayList<>(forgeData.getMaterialItems());
material.add(new ItemParamData(202, forgeData.getScoinCost()));
ForgeData forgeData = GameData.getForgeDataMap().get(req.getForgeId());
boolean success = player.getInventory().payItems(material.toArray(new ItemParamData[0]), req.getForgeCount(), ActionReason.ForgeCost);
//Check if the player has sufficient forge points.
int requiredPoints = forgeData.getForgePoint() * req.getForgeCount();
if (requiredPoints > this.player.getForgePoints()) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH));
return;
}
if (!success) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code.
}
// Check if we have enough of each material and consume.
List<ItemParamData> material = new ArrayList<>(forgeData.getMaterialItems());
material.add(new ItemParamData(202, forgeData.getScoinCost()));
// Consume forge points.
this.player.setForgePoints(this.player.getForgePoints() - requiredPoints);
boolean success = player.getInventory().payItems(material.toArray(new ItemParamData[0]), req.getForgeCount(), ActionReason.ForgeCost);
// Create and add active forge.
ActiveForgeData activeForge = new ActiveForgeData();
activeForge.setForgeId(req.getForgeId());
activeForge.setAvatarId(req.getAvatarId());
activeForge.setCount(req.getForgeCount());
activeForge.setStartTime(Utils.getCurrentSeconds());
activeForge.setForgeTime(forgeData.getForgeTime());
if (!success) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code.
}
this.player.getActiveForges().add(activeForge);
// Consume forge points.
this.player.setForgePoints(this.player.getForgePoints() - requiredPoints);
// Done.
this.sendForgeQueueDataNotify();
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_SUCC));
}
// Create and add active forge.
ActiveForgeData activeForge = new ActiveForgeData();
activeForge.setForgeId(req.getForgeId());
activeForge.setAvatarId(req.getAvatarId());
activeForge.setCount(req.getForgeCount());
activeForge.setStartTime(Utils.getCurrentSeconds());
activeForge.setForgeTime(forgeData.getForgeTime());
/**********
Forge queue manipulation (obtaining results and cancelling forges).
**********/
private synchronized void obtainItems(int queueId) {
// Determin how many items are finished.
int currentTime = Utils.getCurrentSeconds();
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
this.player.getActiveForges().add(activeForge);
int finished = forge.getFinishedCount(currentTime);
int unfinished = forge.getUnfinishedCount(currentTime);
// Done.
this.sendForgeQueueDataNotify();
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_SUCC));
}
// Sanity check: Are any items finished?
if (finished <= 0) {
return;
}
/**********
Forge queue manipulation (obtaining results and cancelling forges).
**********/
private synchronized void obtainItems(int queueId) {
// Determin how many items are finished.
int currentTime = Utils.getCurrentSeconds();
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
// Give finished items to the player.
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
int finished = forge.getFinishedCount(currentTime);
int unfinished = forge.getUnfinishedCount(currentTime);
int resultId = data.getResultItemId() > 0 ? data.getResultItemId() : data.getShowItemId();
ItemData resultItemData = GameData.getItemDataMap().get(resultId);
GameItem addItem = new GameItem(resultItemData, data.getResultItemCount() * finished);
this.player.getInventory().addItem(addItem);
// Battle pass trigger handler
this.player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_DO_FORGE, 0, finished);
// Sanity check: Are any items finished?
if (finished <= 0) {
return;
}
// Replace active forge with a new one for the unfinished items, if there are any.
if (unfinished > 0) {
ActiveForgeData remainingForge = new ActiveForgeData();
// Give finished items to the player.
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
remainingForge.setForgeId(forge.getForgeId());
remainingForge.setAvatarId(forge.getAvatarId());
remainingForge.setCount(unfinished);
remainingForge.setForgeTime(forge.getForgeTime());
remainingForge.setStartTime(forge.getStartTime() + finished * forge.getForgeTime());
int resultId = data.getResultItemId() > 0 ? data.getResultItemId() : data.getShowItemId();
ItemData resultItemData = GameData.getItemDataMap().get(resultId);
GameItem addItem = new GameItem(resultItemData, data.getResultItemCount() * finished);
this.player.getInventory().addItem(addItem);
this.player.getActiveForges().set(queueId - 1, remainingForge);
this.sendForgeQueueDataNotify();
}
// Otherwise, completely remove it.
else {
this.player.getActiveForges().remove(queueId - 1);
// this.sendForgeQueueDataNotify(queueId);
this.sendForgeQueueDataNotify(true);
}
// Battle pass trigger handler
this.player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_DO_FORGE, 0, finished);
// Send response.
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT, List.of(addItem), List.of(), List.of()));
}
// Replace active forge with a new one for the unfinished items, if there are any.
if (unfinished > 0) {
ActiveForgeData remainingForge = new ActiveForgeData();
private synchronized void cancelForge(int queueId) {
// Make sure there are no unfinished items.
int currentTime = Utils.getCurrentSeconds();
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
remainingForge.setForgeId(forge.getForgeId());
remainingForge.setAvatarId(forge.getAvatarId());
remainingForge.setCount(unfinished);
remainingForge.setForgeTime(forge.getForgeTime());
remainingForge.setStartTime(forge.getStartTime() + finished * forge.getForgeTime());
if (forge.getFinishedCount(currentTime) > 0) {
return;
}
// Return material items to the player.
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
this.player.getActiveForges().set(queueId - 1, remainingForge);
this.sendForgeQueueDataNotify();
}
// Otherwise, completely remove it.
else {
this.player.getActiveForges().remove(queueId - 1);
// this.sendForgeQueueDataNotify(queueId);
this.sendForgeQueueDataNotify(true);
}
var returnItems = new ArrayList<GameItem>();
for (var material : data.getMaterialItems()) {
if (material.getItemId() == 0) {
continue;
}
// Send response.
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT, List.of(addItem), List.of(), List.of()));
}
ItemData resultItemData = GameData.getItemDataMap().get(material.getItemId());
GameItem returnItem = new GameItem(resultItemData, material.getItemCount() * forge.getCount());
private synchronized void cancelForge(int queueId) {
// Make sure there are no unfinished items.
int currentTime = Utils.getCurrentSeconds();
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
this.player.getInventory().addItem(returnItem);
returnItems.add(returnItem);
}
if (forge.getFinishedCount(currentTime) > 0) {
return;
}
// Return Mora to the player.
this.player.setMora(this.player.getMora() + data.getScoinCost() * forge.getCount());
// Return material items to the player.
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
ItemData moraItem = GameData.getItemDataMap().get(202);
GameItem returnMora = new GameItem(moraItem, data.getScoinCost() * forge.getCount());
returnItems.add(returnMora);
var returnItems = new ArrayList<GameItem>();
for (var material : data.getMaterialItems()) {
if (material.getItemId() == 0) {
continue;
}
// Return forge points to the player.
int requiredPoints = data.getForgePoint() * forge.getCount();
int newPoints = Math.min(this.player.getForgePoints() + requiredPoints, 300_000);
ItemData resultItemData = GameData.getItemDataMap().get(material.getItemId());
GameItem returnItem = new GameItem(resultItemData, material.getItemCount() * forge.getCount());
this.player.setForgePoints(newPoints);
this.player.getInventory().addItem(returnItem);
returnItems.add(returnItem);
}
// Remove the forge queue.
this.player.getActiveForges().remove(queueId - 1);
this.sendForgeQueueDataNotify(true);
// Return Mora to the player.
this.player.setMora(this.player.getMora() + data.getScoinCost() * forge.getCount());
// Send response.
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE, List.of(), returnItems, List.of()));
}
ItemData moraItem = GameData.getItemDataMap().get(202);
GameItem returnMora = new GameItem(moraItem, data.getScoinCost() * forge.getCount());
returnItems.add(returnMora);
public synchronized void handleForgeQueueManipulateReq(ForgeQueueManipulateReq req) {
// Get info from the request.
int queueId = req.getForgeQueueId();
var manipulateType = req.getManipulateType();
// Handle according to the manipulation type.
switch (manipulateType) {
case FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT:
this.obtainItems(queueId);
break;
case FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE:
this.cancelForge(queueId);
break;
default:
break; //Should never happen.
}
}
// Return forge points to the player.
int requiredPoints = data.getForgePoint() * forge.getCount();
int newPoints = Math.min(this.player.getForgePoints() + requiredPoints, 300_000);
/**********
Periodic forging updates.
**********/
public synchronized void sendPlayerForgingUpdate() {
int currentTime = Utils.getCurrentSeconds();
this.player.setForgePoints(newPoints);
// Determine if sending an update is necessary.
// We only send an update if there are forges in the forge queue
// that have changed since the last notification.
if (this.player.getActiveForges().size() <= 0) {
return;
}
// Remove the forge queue.
this.player.getActiveForges().remove(queueId - 1);
this.sendForgeQueueDataNotify(true);
boolean hasChanges = this.player.getActiveForges().stream()
.filter(forge -> forge.updateChanged(currentTime))
.findAny()
.isPresent();
// Send response.
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE, List.of(), returnItems, List.of()));
}
if (!hasChanges) {
return;
}
public synchronized void handleForgeQueueManipulateReq(ForgeQueueManipulateReq req) {
// Get info from the request.
int queueId = req.getForgeQueueId();
var manipulateType = req.getManipulateType();
// Send notification.
this.sendForgeQueueDataNotify();
// Handle according to the manipulation type.
switch (manipulateType) {
case FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT:
this.obtainItems(queueId);
break;
case FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE:
this.cancelForge(queueId);
break;
default:
break; //Should never happen.
}
}
// Reset changed flags.
this.player.getActiveForges().stream()
.forEach(forge -> forge.setChanged(false));
}
/**********
Periodic forging updates.
**********/
public synchronized void sendPlayerForgingUpdate() {
int currentTime = Utils.getCurrentSeconds();
// Determine if sending an update is necessary.
// We only send an update if there are forges in the forge queue
// that have changed since the last notification.
if (this.player.getActiveForges().size() <= 0) {
return;
}
boolean hasChanges = this.player.getActiveForges().stream()
.filter(forge -> forge.updateChanged(currentTime))
.findAny()
.isPresent();
if (!hasChanges) {
return;
}
// Send notification.
this.sendForgeQueueDataNotify();
// Reset changed flags.
this.player.getActiveForges().stream()
.forEach(forge -> forge.setChanged(false));
}
}

View File

@ -18,7 +18,7 @@ public class MapMarksManager extends BasePlayerManager {
public MapMarksManager(Player player) {
super(player);
}
public Map<String, MapMark> getMapMarks() {
return getPlayer().getMapMarks();
}
@ -51,7 +51,7 @@ public class MapMarksManager extends BasePlayerManager {
}
player.getSession().send(new PacketMarkMapRsp(getMapMarks()));
}
public String getMapMarkKey(Position position) {
return "x" + (int)position.getX()+ "z" + (int)position.getZ();
}

View File

@ -159,7 +159,7 @@ public class StaminaManager extends BasePlayerManager {
}};
public static void initialize() {
// TODO: Initialize foods etc.
// TODO: Initialize foods etc.
}
public StaminaManager(Player player) {
@ -529,20 +529,20 @@ public class StaminaManager extends BasePlayerManager {
}
// Bow avatar charged attack
Avatar currentAvatar = player.getTeamManager().getCurrentAvatarEntity().getAvatar();
switch (currentAvatar.getAvatarData().getWeaponType()) {
case WEAPON_BOW:
return getBowSustainedCost(skillCasting);
case WEAPON_CLAYMORE:
return getClaymoreSustainedCost(skillCasting);
case WEAPON_CATALYST:
return getCatalystCost(skillCasting);
case WEAPON_POLE:
return getPolearmCost(skillCasting);
case WEAPON_SWORD_ONE_HAND:
return getSwordCost(skillCasting);
case WEAPON_BOW:
return getBowSustainedCost(skillCasting);
case WEAPON_CLAYMORE:
return getClaymoreSustainedCost(skillCasting);
case WEAPON_CATALYST:
return getCatalystCost(skillCasting);
case WEAPON_POLE:
return getPolearmCost(skillCasting);
case WEAPON_SWORD_ONE_HAND:
return getSwordCost(skillCasting);
}
return new Consumption();
}

View File

@ -4,17 +4,17 @@ import lombok.NonNull;
public abstract class BasePlayerDataManager {
protected transient Player player;
public BasePlayerDataManager() {}
public BasePlayerDataManager(@NonNull Player player) {
this.player = player;
}
public Player getPlayer() {
return this.player;
}
public void setPlayer(Player player) {
if (this.player == null) {
this.player = player;

View File

@ -4,15 +4,15 @@ import lombok.NonNull;
public abstract class BasePlayerManager {
protected transient final Player player;
public BasePlayerManager(@NonNull Player player) {
this.player = player;
}
public Player getPlayer() {
return this.player;
}
/**
* Saves the player to the database
*/

File diff suppressed because it is too large Load Diff

View File

@ -8,60 +8,60 @@ import dev.morphia.annotations.Entity;
@Entity(useDiscriminator = false)
public class PlayerCollectionRecords {
private Map<Integer, CollectionRecord> records;
private Map<Integer, CollectionRecord> getRecords() {
if (records == null) {
records = new HashMap<>();
records = new HashMap<>();
}
return records;
}
public void addRecord(int configId, long expiredMillisecond){
Map<Integer, CollectionRecord> records;
public void addRecord(int configId, long expiredMillisecond) {
Map<Integer, CollectionRecord> records;
synchronized (records = getRecords()) {
records.put(configId, new CollectionRecord(configId, expiredMillisecond + System.currentTimeMillis()));
}
}
public boolean findRecord(int configId) {
Map<Integer, CollectionRecord> records;
Map<Integer, CollectionRecord> records;
synchronized (records = getRecords()) {
CollectionRecord record = records.get(configId);
if (record == null) {
return false;
}
boolean expired = record.getExpiredTime() < System.currentTimeMillis();
if (expired) {
records.remove(configId);
return false;
CollectionRecord record = records.get(configId);
if (record == null) {
return false;
}
boolean expired = record.getExpiredTime() < System.currentTimeMillis();
if (expired) {
records.remove(configId);
return false;
}
return true;
}
}
@Entity
public static class CollectionRecord {
private int configId;
private long expiredTime;
@Deprecated // Morphia
public CollectionRecord() {}
private int configId;
private long expiredTime;
@Deprecated // Morphia
public CollectionRecord() {}
public CollectionRecord(int configId, long expiredTime) {
this.configId = configId;
this.expiredTime = expiredTime;
}
public int getConfigId() {
return configId;
}
public int getConfigId() {
return configId;
}
public long getExpiredTime() {
return expiredTime;
}
public long getExpiredTime() {
return expiredTime;
}
}
}

View File

@ -19,7 +19,7 @@ import static emu.grasscutter.game.props.OpenState.*;
public class PlayerOpenStateManager extends BasePlayerDataManager {
// Map of all open states that this player has. Do not put default values here.
private Map<Integer, Integer> map;
/*
//DO NOT MODIFY. Based on conversation of official server and client, game version 2.7
private static Set<OpenState> newPlayerOpenStates = Set.of(OPEN_STATE_DERIVATIVE_MALL,OPEN_STATE_PHOTOGRAPH,OPEN_STATE_BATTLE_PASS,OPEN_STATE_SHOP_TYPE_GENESISCRYSTAL,OPEN_STATE_SHOP_TYPE_RECOMMANDED,
@ -27,7 +27,7 @@ public class PlayerOpenStateManager extends BasePlayerDataManager {
OPEN_STATE_WEAPON_PROMOTE,OPEN_STATE_AVATAR_PROMOTE,OPEN_STATE_AVATAR_TALENT,OPEN_STATE_WEAPON_UPGRADE,OPEN_STATE_RESIN,OPEN_STATE_RELIQUARY_UPGRADE,
OPEN_STATE_SHOP_TYPE_VIRTUAL_SHOP,OPEN_STATE_RELIQUARY_PROMOTE);
*/
// For development. Remove entry when properly implemented
// TODO - Set as boolean in OpenState
public static final Set<OpenState> DEV_OPEN_STATES = Stream.of(OpenState.values())
@ -38,25 +38,25 @@ public class PlayerOpenStateManager extends BasePlayerDataManager {
super(player);
}
public Map<Integer, Integer> getOpenStateMap() {
if (this.map == null) this.map = new HashMap<>();
return this.map;
}
public Map<Integer, Integer> getOpenStateMap() {
if (this.map == null) this.map = new HashMap<>();
return this.map;
}
public int getOpenState(OpenState openState) {
return this.map.getOrDefault(openState.getValue(), 0);
}
public void setOpenState(OpenState openState, Integer value) {
Integer previousValue = this.map.getOrDefault(openState.getValue(),0);
if(value != previousValue) {
if (value != previousValue) {
this.map.put(openState.getValue(), value);
player.getSession().send(new PacketOpenStateChangeNotify(openState.getValue(),value));
}
}
public void setOpenStates(Map<OpenState,Integer> openStatesChanged) {
for(Map.Entry<OpenState, Integer> entry : openStatesChanged.entrySet()) {
for (Map.Entry<OpenState, Integer> entry : openStatesChanged.entrySet()) {
setOpenState(entry.getKey(), entry.getValue());
}
}
@ -70,4 +70,4 @@ public class PlayerOpenStateManager extends BasePlayerDataManager {
.filter(s -> s.getUnlockLevel() > 1 && s.getUnlockLevel() <= this.player.getLevel())
.forEach(s -> this.setOpenState(s, 1));
}
}
}

View File

@ -10,75 +10,75 @@ import emu.grasscutter.game.avatar.Avatar;
@Entity
public class TeamInfo {
private String name;
private List<Integer> avatars;
public TeamInfo() {
this.name = "";
this.avatars = new ArrayList<>(GAME_OPTIONS.avatarLimits.singlePlayerTeam);
}
private String name;
private List<Integer> avatars;
public TeamInfo(List<Integer> avatars) {
this.name = "";
this.avatars = avatars;
}
public TeamInfo() {
this.name = "";
this.avatars = new ArrayList<>(GAME_OPTIONS.avatarLimits.singlePlayerTeam);
}
public String getName() {
return name;
}
public TeamInfo(List<Integer> avatars) {
this.name = "";
this.avatars = avatars;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public List<Integer> getAvatars() {
return avatars;
}
public int size() {
return avatars.size();
}
public boolean contains(Avatar avatar) {
return getAvatars().contains(avatar.getAvatarId());
}
public void setName(String name) {
this.name = name;
}
public boolean addAvatar(Avatar avatar) {
if (contains(avatar)) {
return false;
}
getAvatars().add(avatar.getAvatarId());
return true;
}
public boolean removeAvatar(int slot) {
if (size() <= 1) {
return false;
}
getAvatars().remove(slot);
return true;
}
public void copyFrom(TeamInfo team) {
copyFrom(team, GAME_OPTIONS.avatarLimits.singlePlayerTeam);
}
public void copyFrom(TeamInfo team, int maxTeamSize) {
// Clone avatar ids from team to copy from
List<Integer> avatarIds = new ArrayList<>(team.getAvatars());
// Clear current avatar list first
this.getAvatars().clear();
// Copy from team
int len = Math.min(avatarIds.size(), maxTeamSize);
for (int i = 0; i < len; i++) {
int id = avatarIds.get(i);
this.getAvatars().add(id);
}
}
public List<Integer> getAvatars() {
return avatars;
}
public int size() {
return avatars.size();
}
public boolean contains(Avatar avatar) {
return getAvatars().contains(avatar.getAvatarId());
}
public boolean addAvatar(Avatar avatar) {
if (contains(avatar)) {
return false;
}
getAvatars().add(avatar.getAvatarId());
return true;
}
public boolean removeAvatar(int slot) {
if (size() <= 1) {
return false;
}
getAvatars().remove(slot);
return true;
}
public void copyFrom(TeamInfo team) {
copyFrom(team, GAME_OPTIONS.avatarLimits.singlePlayerTeam);
}
public void copyFrom(TeamInfo team, int maxTeamSize) {
// Clone avatar ids from team to copy from
List<Integer> avatarIds = new ArrayList<>(team.getAvatars());
// Clear current avatar list first
this.getAvatars().clear();
// Copy from team
int len = Math.min(avatarIds.size(), maxTeamSize);
for (int i = 0; i < len; i++) {
int id = avatarIds.get(i);
this.getAvatars().add(id);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -8,221 +8,221 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public enum OpenState {
OPEN_STATE_NONE (0),
OPEN_STATE_PAIMON (1),
OPEN_STATE_PAIMON_NAVIGATION (2),
OPEN_STATE_AVATAR_PROMOTE (3),
OPEN_STATE_AVATAR_TALENT (4),
OPEN_STATE_WEAPON_PROMOTE (5),
OPEN_STATE_WEAPON_AWAKEN (6),
OPEN_STATE_QUEST_REMIND (7),
OPEN_STATE_GAME_GUIDE (8),
OPEN_STATE_COOK (9),
OPEN_STATE_WEAPON_UPGRADE (10),
OPEN_STATE_RELIQUARY_UPGRADE (11),
OPEN_STATE_RELIQUARY_PROMOTE (12),
OPEN_STATE_WEAPON_PROMOTE_GUIDE (13),
OPEN_STATE_WEAPON_CHANGE_GUIDE (14),
OPEN_STATE_PLAYER_LVUP_GUIDE (15),
OPEN_STATE_FRESHMAN_GUIDE (16),
OPEN_STATE_SKIP_FRESHMAN_GUIDE (17),
OPEN_STATE_GUIDE_MOVE_CAMERA (18),
OPEN_STATE_GUIDE_SCALE_CAMERA (19),
OPEN_STATE_GUIDE_KEYBOARD (20),
OPEN_STATE_GUIDE_MOVE (21),
OPEN_STATE_GUIDE_JUMP (22),
OPEN_STATE_GUIDE_SPRINT (23),
OPEN_STATE_GUIDE_MAP (24),
OPEN_STATE_GUIDE_ATTACK (25),
OPEN_STATE_GUIDE_FLY (26),
OPEN_STATE_GUIDE_TALENT (27),
OPEN_STATE_GUIDE_RELIC (28),
OPEN_STATE_GUIDE_RELIC_PROM (29),
OPEN_STATE_COMBINE (30, 2),
OPEN_STATE_GACHA (31),
OPEN_STATE_GUIDE_GACHA (32),
OPEN_STATE_GUIDE_TEAM (33),
OPEN_STATE_GUIDE_PROUD (34),
OPEN_STATE_GUIDE_AVATAR_PROMOTE (35),
OPEN_STATE_GUIDE_ADVENTURE_CARD (36),
OPEN_STATE_FORGE (37, 2),
OPEN_STATE_GUIDE_BAG (38),
OPEN_STATE_EXPEDITION (39, 14),
OPEN_STATE_GUIDE_ADVENTURE_DAILYTASK (40),
OPEN_STATE_GUIDE_ADVENTURE_DUNGEON (41),
OPEN_STATE_TOWER (42),
OPEN_STATE_WORLD_STAMINA (43),
OPEN_STATE_TOWER_FIRST_ENTER (44),
OPEN_STATE_RESIN (45),
OPEN_STATE_LIMIT_REGION_FRESHMEAT (47),
OPEN_STATE_LIMIT_REGION_GLOBAL (48),
OPEN_STATE_MULTIPLAYER (49, 16),
OPEN_STATE_GUIDE_MOUSEPC (50),
OPEN_STATE_GUIDE_MULTIPLAYER (51),
OPEN_STATE_GUIDE_DUNGEONREWARD (52),
OPEN_STATE_GUIDE_BLOSSOM (53, 8),
OPEN_STATE_AVATAR_FASHION (54),
OPEN_STATE_PHOTOGRAPH (55),
OPEN_STATE_GUIDE_KSLQUEST (56),
OPEN_STATE_PERSONAL_LINE (57, 26),
OPEN_STATE_GUIDE_PERSONAL_LINE (58),
OPEN_STATE_GUIDE_APPEARANCE (59),
OPEN_STATE_GUIDE_PROCESS (60),
OPEN_STATE_GUIDE_PERSONAL_LINE_KEY (61),
OPEN_STATE_GUIDE_WIDGET (62),
OPEN_STATE_GUIDE_ACTIVITY_SKILL_ASTER (63),
OPEN_STATE_GUIDE_COLDCLIMATE (64),
OPEN_STATE_DERIVATIVE_MALL (65),
OPEN_STATE_GUIDE_EXITMULTIPLAYER (66),
OPEN_STATE_GUIDE_THEATREMACHANICUS_BUILD (67),
OPEN_STATE_GUIDE_THEATREMACHANICUS_REBUILD (68),
OPEN_STATE_GUIDE_THEATREMACHANICUS_CARD (69),
OPEN_STATE_GUIDE_THEATREMACHANICUS_MONSTER (70),
OPEN_STATE_GUIDE_THEATREMACHANICUS_MISSION_CHECK (71),
OPEN_STATE_GUIDE_THEATREMACHANICUS_BUILD_SELECT (72),
OPEN_STATE_GUIDE_THEATREMACHANICUS_CHALLENGE_START (73),
OPEN_STATE_GUIDE_CONVERT (74),
OPEN_STATE_GUIDE_THEATREMACHANICUS_MULTIPLAYER (75),
OPEN_STATE_GUIDE_COOP_TASK (76),
OPEN_STATE_GUIDE_HOMEWORLD_ADEPTIABODE (77),
OPEN_STATE_GUIDE_HOMEWORLD_DEPLOY (78),
OPEN_STATE_GUIDE_CHANNELLERSLAB_EQUIP (79),
OPEN_STATE_GUIDE_CHANNELLERSLAB_MP_SOLUTION (80),
OPEN_STATE_GUIDE_CHANNELLERSLAB_POWER (81),
OPEN_STATE_GUIDE_HIDEANDSEEK_SKILL (82),
OPEN_STATE_GUIDE_HOMEWORLD_MAPLIST (83),
OPEN_STATE_GUIDE_RELICRESOLVE (84),
OPEN_STATE_GUIDE_GGUIDE (85),
OPEN_STATE_GUIDE_GGUIDE_HINT (86),
OPEN_STATE_GUIDE_CHANNELLERSLAB_EQUIP_V2 (87),
OPEN_STATE_GUIDE_CHANNELLERSLAB_MP_SOLUTION_V2 (88),
OPEN_STATE_GUIDE_CHANNELLERSLAB_POWER_V2 (89),
OPEN_STATE_GUIDE_QUICK_TEAMMEMBERCHANGE (90), // Mobile only
OPEN_STATE_GGUIDE_FIRSTSHOW (91),
OPEN_STATE_GGUIDE_MAINPAGE_ENTRY_DISAPPEAR (92),
OPEN_STATE_CITY_REPUATION_MENGDE (800),
OPEN_STATE_CITY_REPUATION_LIYUE (801),
OPEN_STATE_CITY_REPUATION_UI_HINT (802, 25),
OPEN_STATE_CITY_REPUATION_INAZUMA (803),
OPEN_STATE_SHOP_TYPE_MALL (900),
OPEN_STATE_SHOP_TYPE_RECOMMANDED (901, 1),
OPEN_STATE_SHOP_TYPE_GENESISCRYSTAL (902, 1),
OPEN_STATE_SHOP_TYPE_GIFTPACKAGE (903, 1),
OPEN_STATE_SHOP_TYPE_PAIMON (1001),
OPEN_STATE_SHOP_TYPE_CITY (1002),
OPEN_STATE_SHOP_TYPE_BLACKSMITH (1003),
OPEN_STATE_SHOP_TYPE_GROCERY (1004, 5),
OPEN_STATE_SHOP_TYPE_FOOD (1005, 5),
OPEN_STATE_SHOP_TYPE_SEA_LAMP (1006, 13),
OPEN_STATE_SHOP_TYPE_VIRTUAL_SHOP (1007),
OPEN_STATE_SHOP_TYPE_LIYUE_GROCERY (1008, 5),
OPEN_STATE_SHOP_TYPE_LIYUE_SOUVENIR (1009),
OPEN_STATE_SHOP_TYPE_LIYUE_RESTAURANT (1010, 5),
OPEN_STATE_SHOP_TYPE_INAZUMA_SOUVENIR (1011),
OPEN_STATE_SHOP_TYPE_NPC_TOMOKI (1012),
OPEN_STATE_SHOP_TYPE_INAZUMA_SOUVENIR_BLACK_BAR (1013),
OPEN_ADVENTURE_MANUAL (1100),
OPEN_ADVENTURE_MANUAL_CITY_MENGDE (1101),
OPEN_ADVENTURE_MANUAL_CITY_LIYUE (1102),
OPEN_ADVENTURE_MANUAL_MONSTER (1103, 8),
OPEN_ADVENTURE_MANUAL_BOSS_DUNGEON (1104),
OPEN_STATE_ACTIVITY_SEALAMP (1200),
OPEN_STATE_ACTIVITY_SEALAMP_TAB2 (1201),
OPEN_STATE_ACTIVITY_SEALAMP_TAB3 (1202),
OPEN_STATE_BATTLE_PASS (1300, 1),
OPEN_STATE_BATTLE_PASS_ENTRY (1301, 20),
OPEN_STATE_ACTIVITY_CRUCIBLE (1400),
OPEN_STATE_ACTIVITY_NEWBEEBOUNS_OPEN (1401),
OPEN_STATE_ACTIVITY_NEWBEEBOUNS_CLOSE (1402),
OPEN_STATE_ACTIVITY_ENTRY_OPEN (1403),
OPEN_STATE_MENGDE_INFUSEDCRYSTAL (1404),
OPEN_STATE_LIYUE_INFUSEDCRYSTAL (1405),
OPEN_STATE_SNOW_MOUNTAIN_ELDER_TREE (1406),
OPEN_STATE_MIRACLE_RING (1407),
OPEN_STATE_COOP_LINE (1408, 26),
OPEN_STATE_INAZUMA_INFUSEDCRYSTAL (1409),
OPEN_STATE_FISH (1410),
OPEN_STATE_GUIDE_SUMO_TEAM_SKILL (1411),
OPEN_STATE_GUIDE_FISH_RECIPE (1412),
OPEN_STATE_HOME (1500),
OPEN_STATE_ACTIVITY_HOMEWORLD (1501, 28),
OPEN_STATE_ADEPTIABODE (1502),
OPEN_STATE_HOME_AVATAR (1503),
OPEN_STATE_HOME_EDIT (1504),
OPEN_STATE_HOME_EDIT_TIPS (1505),
OPEN_STATE_RELIQUARY_DECOMPOSE (1600, 45),
OPEN_STATE_ACTIVITY_H5 (1700, 10),
OPEN_STATE_ORAIONOKAMI (2000),
OPEN_STATE_GUIDE_CHESS_MISSION_CHECK (2001),
OPEN_STATE_GUIDE_CHESS_BUILD (2002),
OPEN_STATE_GUIDE_CHESS_WIND_TOWER_CIRCLE (2003),
OPEN_STATE_GUIDE_CHESS_CARD_SELECT (2004),
OPEN_STATE_INAZUMA_MAINQUEST_FINISHED (2005),
OPEN_STATE_PAIMON_LVINFO (2100, 7),
OPEN_STATE_TELEPORT_HUD (2101, 2),
OPEN_STATE_GUIDE_MAP_UNLOCK (2102),
OPEN_STATE_GUIDE_PAIMON_LVINFO (2103),
OPEN_STATE_GUIDE_AMBORTRANSPORT (2104),
OPEN_STATE_GUIDE_FLY_SECOND (2105),
OPEN_STATE_GUIDE_KAEYA_CLUE (2106),
OPEN_STATE_CAPTURE_CODEX (2107),
OPEN_STATE_ACTIVITY_FISH_OPEN (2200),
OPEN_STATE_ACTIVITY_FISH_CLOSE (2201),
OPEN_STATE_GUIDE_ROGUE_MAP (2205),
OPEN_STATE_GUIDE_ROGUE_RUNE (2206),
OPEN_STATE_GUIDE_BARTENDER_FORMULA (2210),
OPEN_STATE_GUIDE_BARTENDER_MIX (2211),
OPEN_STATE_GUIDE_BARTENDER_CUP (2212),
OPEN_STATE_GUIDE_MAIL_FAVORITES (2400),
OPEN_STATE_GUIDE_POTION_CONFIGURE (2401),
OPEN_STATE_GUIDE_LANV2_FIREWORK (2402),
OPEN_STATE_LOADINGTIPS_ENKANOMIYA (2403),
OPEN_STATE_MICHIAE_CASKET (2500, 30),
OPEN_STATE_MAIL_COLLECT_UNLOCK_RED_POINT (2501),
OPEN_STATE_LUMEN_STONE (2600),
OPEN_STATE_GUIDE_CRYSTALLINK_BUFF (2601),
OPEN_STATE_GUIDE_MUSIC_GAME_V3 (2700),
OPEN_STATE_GUIDE_MUSIC_GAME_V3_REAL_TIME_EDIT (2701),
OPEN_STATE_GUIDE_MUSIC_GAME_V3_TIMELINE_EDIT (2702),
OPEN_STATE_GUIDE_MUSIC_GAME_V3_SETTING (2703),
OPEN_STATE_GUIDE_ROBOTGACHA (2704),
OPEN_STATE_GUIDE_FRAGILE_RESIN (2800),
OPEN_ADVENTURE_MANUAL_EDUCATION (2801);
private final int value;
private final int unlockLevel;
private static final Int2ObjectMap<OpenState> map = new Int2ObjectOpenHashMap<>();
private static final Map<String, OpenState> stringMap = new HashMap<>();
static {
Stream.of(values()).forEach(e -> {
map.put(e.getValue(), e);
stringMap.put(e.name(), e);
});
}
private OpenState(int value) {
this.value = value;
this.unlockLevel = -1;
}
private OpenState(int value, int unlockLevel) {
this.value = value;
this.unlockLevel = unlockLevel;
}
OPEN_STATE_NONE (0),
OPEN_STATE_PAIMON (1),
OPEN_STATE_PAIMON_NAVIGATION (2),
OPEN_STATE_AVATAR_PROMOTE (3),
OPEN_STATE_AVATAR_TALENT (4),
OPEN_STATE_WEAPON_PROMOTE (5),
OPEN_STATE_WEAPON_AWAKEN (6),
OPEN_STATE_QUEST_REMIND (7),
OPEN_STATE_GAME_GUIDE (8),
OPEN_STATE_COOK (9),
OPEN_STATE_WEAPON_UPGRADE (10),
OPEN_STATE_RELIQUARY_UPGRADE (11),
OPEN_STATE_RELIQUARY_PROMOTE (12),
OPEN_STATE_WEAPON_PROMOTE_GUIDE (13),
OPEN_STATE_WEAPON_CHANGE_GUIDE (14),
OPEN_STATE_PLAYER_LVUP_GUIDE (15),
OPEN_STATE_FRESHMAN_GUIDE (16),
OPEN_STATE_SKIP_FRESHMAN_GUIDE (17),
OPEN_STATE_GUIDE_MOVE_CAMERA (18),
OPEN_STATE_GUIDE_SCALE_CAMERA (19),
OPEN_STATE_GUIDE_KEYBOARD (20),
OPEN_STATE_GUIDE_MOVE (21),
OPEN_STATE_GUIDE_JUMP (22),
OPEN_STATE_GUIDE_SPRINT (23),
OPEN_STATE_GUIDE_MAP (24),
OPEN_STATE_GUIDE_ATTACK (25),
OPEN_STATE_GUIDE_FLY (26),
OPEN_STATE_GUIDE_TALENT (27),
OPEN_STATE_GUIDE_RELIC (28),
OPEN_STATE_GUIDE_RELIC_PROM (29),
OPEN_STATE_COMBINE (30, 2),
OPEN_STATE_GACHA (31),
OPEN_STATE_GUIDE_GACHA (32),
OPEN_STATE_GUIDE_TEAM (33),
OPEN_STATE_GUIDE_PROUD (34),
OPEN_STATE_GUIDE_AVATAR_PROMOTE (35),
OPEN_STATE_GUIDE_ADVENTURE_CARD (36),
OPEN_STATE_FORGE (37, 2),
OPEN_STATE_GUIDE_BAG (38),
OPEN_STATE_EXPEDITION (39, 14),
OPEN_STATE_GUIDE_ADVENTURE_DAILYTASK (40),
OPEN_STATE_GUIDE_ADVENTURE_DUNGEON (41),
OPEN_STATE_TOWER (42),
OPEN_STATE_WORLD_STAMINA (43),
OPEN_STATE_TOWER_FIRST_ENTER (44),
OPEN_STATE_RESIN (45),
OPEN_STATE_LIMIT_REGION_FRESHMEAT (47),
OPEN_STATE_LIMIT_REGION_GLOBAL (48),
OPEN_STATE_MULTIPLAYER (49, 16),
OPEN_STATE_GUIDE_MOUSEPC (50),
OPEN_STATE_GUIDE_MULTIPLAYER (51),
OPEN_STATE_GUIDE_DUNGEONREWARD (52),
OPEN_STATE_GUIDE_BLOSSOM (53, 8),
OPEN_STATE_AVATAR_FASHION (54),
OPEN_STATE_PHOTOGRAPH (55),
OPEN_STATE_GUIDE_KSLQUEST (56),
OPEN_STATE_PERSONAL_LINE (57, 26),
OPEN_STATE_GUIDE_PERSONAL_LINE (58),
OPEN_STATE_GUIDE_APPEARANCE (59),
OPEN_STATE_GUIDE_PROCESS (60),
OPEN_STATE_GUIDE_PERSONAL_LINE_KEY (61),
OPEN_STATE_GUIDE_WIDGET (62),
OPEN_STATE_GUIDE_ACTIVITY_SKILL_ASTER (63),
OPEN_STATE_GUIDE_COLDCLIMATE (64),
OPEN_STATE_DERIVATIVE_MALL (65),
OPEN_STATE_GUIDE_EXITMULTIPLAYER (66),
OPEN_STATE_GUIDE_THEATREMACHANICUS_BUILD (67),
OPEN_STATE_GUIDE_THEATREMACHANICUS_REBUILD (68),
OPEN_STATE_GUIDE_THEATREMACHANICUS_CARD (69),
OPEN_STATE_GUIDE_THEATREMACHANICUS_MONSTER (70),
OPEN_STATE_GUIDE_THEATREMACHANICUS_MISSION_CHECK (71),
OPEN_STATE_GUIDE_THEATREMACHANICUS_BUILD_SELECT (72),
OPEN_STATE_GUIDE_THEATREMACHANICUS_CHALLENGE_START (73),
OPEN_STATE_GUIDE_CONVERT (74),
OPEN_STATE_GUIDE_THEATREMACHANICUS_MULTIPLAYER (75),
OPEN_STATE_GUIDE_COOP_TASK (76),
OPEN_STATE_GUIDE_HOMEWORLD_ADEPTIABODE (77),
OPEN_STATE_GUIDE_HOMEWORLD_DEPLOY (78),
OPEN_STATE_GUIDE_CHANNELLERSLAB_EQUIP (79),
OPEN_STATE_GUIDE_CHANNELLERSLAB_MP_SOLUTION (80),
OPEN_STATE_GUIDE_CHANNELLERSLAB_POWER (81),
OPEN_STATE_GUIDE_HIDEANDSEEK_SKILL (82),
OPEN_STATE_GUIDE_HOMEWORLD_MAPLIST (83),
OPEN_STATE_GUIDE_RELICRESOLVE (84),
OPEN_STATE_GUIDE_GGUIDE (85),
OPEN_STATE_GUIDE_GGUIDE_HINT (86),
OPEN_STATE_GUIDE_CHANNELLERSLAB_EQUIP_V2 (87),
OPEN_STATE_GUIDE_CHANNELLERSLAB_MP_SOLUTION_V2 (88),
OPEN_STATE_GUIDE_CHANNELLERSLAB_POWER_V2 (89),
OPEN_STATE_GUIDE_QUICK_TEAMMEMBERCHANGE (90), // Mobile only
OPEN_STATE_GGUIDE_FIRSTSHOW (91),
OPEN_STATE_GGUIDE_MAINPAGE_ENTRY_DISAPPEAR (92),
OPEN_STATE_CITY_REPUATION_MENGDE (800),
OPEN_STATE_CITY_REPUATION_LIYUE (801),
OPEN_STATE_CITY_REPUATION_UI_HINT (802, 25),
OPEN_STATE_CITY_REPUATION_INAZUMA (803),
OPEN_STATE_SHOP_TYPE_MALL (900),
OPEN_STATE_SHOP_TYPE_RECOMMANDED (901, 1),
OPEN_STATE_SHOP_TYPE_GENESISCRYSTAL (902, 1),
OPEN_STATE_SHOP_TYPE_GIFTPACKAGE (903, 1),
OPEN_STATE_SHOP_TYPE_PAIMON (1001),
OPEN_STATE_SHOP_TYPE_CITY (1002),
OPEN_STATE_SHOP_TYPE_BLACKSMITH (1003),
OPEN_STATE_SHOP_TYPE_GROCERY (1004, 5),
OPEN_STATE_SHOP_TYPE_FOOD (1005, 5),
OPEN_STATE_SHOP_TYPE_SEA_LAMP (1006, 13),
OPEN_STATE_SHOP_TYPE_VIRTUAL_SHOP (1007),
OPEN_STATE_SHOP_TYPE_LIYUE_GROCERY (1008, 5),
OPEN_STATE_SHOP_TYPE_LIYUE_SOUVENIR (1009),
OPEN_STATE_SHOP_TYPE_LIYUE_RESTAURANT (1010, 5),
OPEN_STATE_SHOP_TYPE_INAZUMA_SOUVENIR (1011),
OPEN_STATE_SHOP_TYPE_NPC_TOMOKI (1012),
OPEN_STATE_SHOP_TYPE_INAZUMA_SOUVENIR_BLACK_BAR (1013),
OPEN_ADVENTURE_MANUAL (1100),
OPEN_ADVENTURE_MANUAL_CITY_MENGDE (1101),
OPEN_ADVENTURE_MANUAL_CITY_LIYUE (1102),
OPEN_ADVENTURE_MANUAL_MONSTER (1103, 8),
OPEN_ADVENTURE_MANUAL_BOSS_DUNGEON (1104),
OPEN_STATE_ACTIVITY_SEALAMP (1200),
OPEN_STATE_ACTIVITY_SEALAMP_TAB2 (1201),
OPEN_STATE_ACTIVITY_SEALAMP_TAB3 (1202),
OPEN_STATE_BATTLE_PASS (1300, 1),
OPEN_STATE_BATTLE_PASS_ENTRY (1301, 20),
OPEN_STATE_ACTIVITY_CRUCIBLE (1400),
OPEN_STATE_ACTIVITY_NEWBEEBOUNS_OPEN (1401),
OPEN_STATE_ACTIVITY_NEWBEEBOUNS_CLOSE (1402),
OPEN_STATE_ACTIVITY_ENTRY_OPEN (1403),
OPEN_STATE_MENGDE_INFUSEDCRYSTAL (1404),
OPEN_STATE_LIYUE_INFUSEDCRYSTAL (1405),
OPEN_STATE_SNOW_MOUNTAIN_ELDER_TREE (1406),
OPEN_STATE_MIRACLE_RING (1407),
OPEN_STATE_COOP_LINE (1408, 26),
OPEN_STATE_INAZUMA_INFUSEDCRYSTAL (1409),
OPEN_STATE_FISH (1410),
OPEN_STATE_GUIDE_SUMO_TEAM_SKILL (1411),
OPEN_STATE_GUIDE_FISH_RECIPE (1412),
OPEN_STATE_HOME (1500),
OPEN_STATE_ACTIVITY_HOMEWORLD (1501, 28),
OPEN_STATE_ADEPTIABODE (1502),
OPEN_STATE_HOME_AVATAR (1503),
OPEN_STATE_HOME_EDIT (1504),
OPEN_STATE_HOME_EDIT_TIPS (1505),
OPEN_STATE_RELIQUARY_DECOMPOSE (1600, 45),
OPEN_STATE_ACTIVITY_H5 (1700, 10),
OPEN_STATE_ORAIONOKAMI (2000),
OPEN_STATE_GUIDE_CHESS_MISSION_CHECK (2001),
OPEN_STATE_GUIDE_CHESS_BUILD (2002),
OPEN_STATE_GUIDE_CHESS_WIND_TOWER_CIRCLE (2003),
OPEN_STATE_GUIDE_CHESS_CARD_SELECT (2004),
OPEN_STATE_INAZUMA_MAINQUEST_FINISHED (2005),
OPEN_STATE_PAIMON_LVINFO (2100, 7),
OPEN_STATE_TELEPORT_HUD (2101, 2),
OPEN_STATE_GUIDE_MAP_UNLOCK (2102),
OPEN_STATE_GUIDE_PAIMON_LVINFO (2103),
OPEN_STATE_GUIDE_AMBORTRANSPORT (2104),
OPEN_STATE_GUIDE_FLY_SECOND (2105),
OPEN_STATE_GUIDE_KAEYA_CLUE (2106),
OPEN_STATE_CAPTURE_CODEX (2107),
OPEN_STATE_ACTIVITY_FISH_OPEN (2200),
OPEN_STATE_ACTIVITY_FISH_CLOSE (2201),
OPEN_STATE_GUIDE_ROGUE_MAP (2205),
OPEN_STATE_GUIDE_ROGUE_RUNE (2206),
OPEN_STATE_GUIDE_BARTENDER_FORMULA (2210),
OPEN_STATE_GUIDE_BARTENDER_MIX (2211),
OPEN_STATE_GUIDE_BARTENDER_CUP (2212),
OPEN_STATE_GUIDE_MAIL_FAVORITES (2400),
OPEN_STATE_GUIDE_POTION_CONFIGURE (2401),
OPEN_STATE_GUIDE_LANV2_FIREWORK (2402),
OPEN_STATE_LOADINGTIPS_ENKANOMIYA (2403),
OPEN_STATE_MICHIAE_CASKET (2500, 30),
OPEN_STATE_MAIL_COLLECT_UNLOCK_RED_POINT (2501),
OPEN_STATE_LUMEN_STONE (2600),
OPEN_STATE_GUIDE_CRYSTALLINK_BUFF (2601),
OPEN_STATE_GUIDE_MUSIC_GAME_V3 (2700),
OPEN_STATE_GUIDE_MUSIC_GAME_V3_REAL_TIME_EDIT (2701),
OPEN_STATE_GUIDE_MUSIC_GAME_V3_TIMELINE_EDIT (2702),
OPEN_STATE_GUIDE_MUSIC_GAME_V3_SETTING (2703),
OPEN_STATE_GUIDE_ROBOTGACHA (2704),
OPEN_STATE_GUIDE_FRAGILE_RESIN (2800),
OPEN_ADVENTURE_MANUAL_EDUCATION (2801);
public int getValue() {
return value;
}
private final int value;
private final int unlockLevel;
private static final Int2ObjectMap<OpenState> map = new Int2ObjectOpenHashMap<>();
private static final Map<String, OpenState> stringMap = new HashMap<>();
public int getUnlockLevel() {
return this.unlockLevel;
}
public static OpenState getTypeByValue(int value) {
return map.getOrDefault(value, OPEN_STATE_NONE);
}
public static OpenState getTypeByName(String name) {
return stringMap.getOrDefault(name, OPEN_STATE_NONE);
}
static {
Stream.of(values()).forEach(e -> {
map.put(e.getValue(), e);
stringMap.put(e.name(), e);
});
}
private OpenState(int value) {
this.value = value;
this.unlockLevel = -1;
}
private OpenState(int value, int unlockLevel) {
this.value = value;
this.unlockLevel = unlockLevel;
}
public int getValue() {
return value;
}
public int getUnlockLevel() {
return this.unlockLevel;
}
public static OpenState getTypeByValue(int value) {
return map.getOrDefault(value, OPEN_STATE_NONE);
}
public static OpenState getTypeByName(String name) {
return stringMap.getOrDefault(name, OPEN_STATE_NONE);
}
}

View File

@ -22,47 +22,47 @@ import emu.grasscutter.utils.Utils;
@Entity
public class GameQuest {
@Transient private GameMainQuest mainQuest;
@Transient private QuestData questData;
@Transient private GameMainQuest mainQuest;
@Transient private QuestData questData;
private int questId;
private int mainQuestId;
private QuestState state;
private int questId;
private int mainQuestId;
private QuestState state;
private int startTime;
private int acceptTime;
private int finishTime;
private int startTime;
private int acceptTime;
private int finishTime;
private int[] finishProgressList;
private int[] failProgressList;
private int[] finishProgressList;
private int[] failProgressList;
@Deprecated // Morphia only. Do not use.
public GameQuest() {}
@Deprecated // Morphia only. Do not use.
public GameQuest() {}
public GameQuest(GameMainQuest mainQuest, QuestData questData) {
this.mainQuest = mainQuest;
this.questId = questData.getId();
this.mainQuestId = questData.getMainId();
this.questData = questData;
this.acceptTime = Utils.getCurrentSeconds();
this.startTime = this.acceptTime;
this.state = QuestState.QUEST_STATE_UNFINISHED;
public GameQuest(GameMainQuest mainQuest, QuestData questData) {
this.mainQuest = mainQuest;
this.questId = questData.getId();
this.mainQuestId = questData.getMainId();
this.questData = questData;
this.acceptTime = Utils.getCurrentSeconds();
this.startTime = this.acceptTime;
this.state = QuestState.QUEST_STATE_UNFINISHED;
if (questData.getFinishCond() != null && questData.getAcceptCond().size() != 0) {
this.finishProgressList = new int[questData.getFinishCond().size()];
}
if (questData.getFinishCond() != null && questData.getAcceptCond().size() != 0) {
this.finishProgressList = new int[questData.getFinishCond().size()];
}
if (questData.getFailCond() != null && questData.getFailCond().size() != 0) {
this.failProgressList = new int[questData.getFailCond().size()];
}
if (questData.getFailCond() != null && questData.getFailCond().size() != 0) {
this.failProgressList = new int[questData.getFailCond().size()];
}
this.mainQuest.getChildQuests().put(this.questId, this);
this.mainQuest.getChildQuests().put(this.questId, this);
this.getData().getBeginExec().forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
this.getOwner().getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_QUEST_STATE_EQUAL, this.questId, this.state.getValue());
if (ChapterData.beginQuestChapterMap.containsKey(questId)){
if (ChapterData.beginQuestChapterMap.containsKey(questId)) {
mainQuest.getOwner().sendPacket(new PacketChapterStateNotify(
ChapterData.beginQuestChapterMap.get(questId).getId(),
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_BEGIN
@ -70,112 +70,112 @@ public class GameQuest {
}
Grasscutter.getLogger().debug("Quest {} is started", questId);
}
}
public GameMainQuest getMainQuest() {
return mainQuest;
}
public GameMainQuest getMainQuest() {
return mainQuest;
}
public void setMainQuest(GameMainQuest mainQuest) {
this.mainQuest = mainQuest;
}
public void setMainQuest(GameMainQuest mainQuest) {
this.mainQuest = mainQuest;
}
public Player getOwner() {
return getMainQuest().getOwner();
}
public Player getOwner() {
return getMainQuest().getOwner();
}
public int getQuestId() {
return questId;
}
public int getQuestId() {
return questId;
}
public int getMainQuestId() {
return mainQuestId;
}
public int getMainQuestId() {
return mainQuestId;
}
public QuestData getData() {
return questData;
}
public QuestData getData() {
return questData;
}
public void setConfig(QuestData config) {
if (this.getQuestId() != config.getId()) return;
this.questData = config;
}
public void setConfig(QuestData config) {
if (this.getQuestId() != config.getId()) return;
this.questData = config;
}
public QuestState getState() {
return state;
}
public QuestState getState() {
return state;
}
public void setState(QuestState state) {
this.state = state;
}
public void setState(QuestState state) {
this.state = state;
}
public int getStartTime() {
return startTime;
}
public int getStartTime() {
return startTime;
}
public void setStartTime(int startTime) {
this.startTime = startTime;
}
public void setStartTime(int startTime) {
this.startTime = startTime;
}
public int getAcceptTime() {
return acceptTime;
}
public int getAcceptTime() {
return acceptTime;
}
public void setAcceptTime(int acceptTime) {
this.acceptTime = acceptTime;
}
public void setAcceptTime(int acceptTime) {
this.acceptTime = acceptTime;
}
public int getFinishTime() {
return finishTime;
}
public int getFinishTime() {
return finishTime;
}
public void setFinishTime(int finishTime) {
this.finishTime = finishTime;
}
public void setFinishTime(int finishTime) {
this.finishTime = finishTime;
}
public int[] getFinishProgressList() {
return finishProgressList;
}
public int[] getFinishProgressList() {
return finishProgressList;
}
public void setFinishProgress(int index, int value) {
finishProgressList[index] = value;
}
public void setFinishProgress(int index, int value) {
finishProgressList[index] = value;
}
public int[] getFailProgressList() {
return failProgressList;
}
public int[] getFailProgressList() {
return failProgressList;
}
public void setFailProgress(int index, int value) {
failProgressList[index] = value;
}
public void setFailProgress(int index, int value) {
failProgressList[index] = value;
}
public void finish() {
this.state = QuestState.QUEST_STATE_FINISHED;
this.finishTime = Utils.getCurrentSeconds();
public void finish() {
this.state = QuestState.QUEST_STATE_FINISHED;
this.finishTime = Utils.getCurrentSeconds();
if (this.getFinishProgressList() != null) {
for (int i = 0 ; i < getFinishProgressList().length; i++) {
getFinishProgressList()[i] = 1;
}
}
if (this.getFinishProgressList() != null) {
for (int i = 0 ; i < getFinishProgressList().length; i++) {
getFinishProgressList()[i] = 1;
}
}
this.getOwner().getSession().send(new PacketQuestProgressUpdateNotify(this));
this.getOwner().getSession().send(new PacketQuestListUpdateNotify(this));
this.getOwner().getSession().send(new PacketQuestProgressUpdateNotify(this));
this.getOwner().getSession().send(new PacketQuestListUpdateNotify(this));
if (this.getData().finishParent()) {
// This quest finishes the questline - the main quest will also save the quest to db so we dont have to call save() here
this.getMainQuest().finish();
} else {
// Try and accept other quests if possible
this.tryAcceptQuestLine();
this.save();
}
if (this.getData().finishParent()) {
// This quest finishes the questline - the main quest will also save the quest to db so we dont have to call save() here
this.getMainQuest().finish();
} else {
// Try and accept other quests if possible
this.tryAcceptQuestLine();
this.save();
}
this.getData().getFinishExec().forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
this.getOwner().getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_QUEST_STATE_EQUAL, this.questId, this.state.getValue());
if (ChapterData.endQuestChapterMap.containsKey(questId)){
if (ChapterData.endQuestChapterMap.containsKey(questId)) {
mainQuest.getOwner().sendPacket(new PacketChapterStateNotify(
ChapterData.endQuestChapterMap.get(questId).getId(),
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_END
@ -183,74 +183,74 @@ public class GameQuest {
}
Grasscutter.getLogger().debug("Quest {} is finished", questId);
}
}
public boolean tryAcceptQuestLine() {
try {
MainQuestData questConfig = GameData.getMainQuestDataMap().get(this.getMainQuestId());
public boolean tryAcceptQuestLine() {
try {
MainQuestData questConfig = GameData.getMainQuestDataMap().get(this.getMainQuestId());
for (SubQuestData subQuest : questConfig.getSubQuests()) {
GameQuest quest = getMainQuest().getChildQuestById(subQuest.getSubId());
for (SubQuestData subQuest : questConfig.getSubQuests()) {
GameQuest quest = getMainQuest().getChildQuestById(subQuest.getSubId());
if (quest == null) {
QuestData questData = GameData.getQuestDataMap().get(subQuest.getSubId());
if (quest == null) {
QuestData questData = GameData.getQuestDataMap().get(subQuest.getSubId());
if (questData == null || questData.getAcceptCond() == null
|| questData.getAcceptCond().size() == 0) {
continue;
}
if (questData == null || questData.getAcceptCond() == null
|| questData.getAcceptCond().size() == 0) {
continue;
}
int[] accept = new int[questData.getAcceptCond().size()];
int[] accept = new int[questData.getAcceptCond().size()];
// TODO
for (int i = 0; i < questData.getAcceptCond().size(); i++) {
QuestCondition condition = questData.getAcceptCond().get(i);
boolean result = getOwner().getServer().getQuestSystem().triggerCondition(this, condition,
// TODO
for (int i = 0; i < questData.getAcceptCond().size(); i++) {
QuestCondition condition = questData.getAcceptCond().get(i);
boolean result = getOwner().getServer().getQuestSystem().triggerCondition(this, condition,
condition.getParamStr(),
condition.getParam());
condition.getParam());
accept[i] = result ? 1 : 0;
}
accept[i] = result ? 1 : 0;
}
boolean shouldAccept = LogicType.calculate(questData.getAcceptCondComb(), accept);
boolean shouldAccept = LogicType.calculate(questData.getAcceptCondComb(), accept);
if (shouldAccept) {
this.getOwner().getQuestManager().addQuest(questData.getId());
}
}
}
} catch (Exception e) {
Grasscutter.getLogger().error("An error occurred while trying to accept quest.", e);
}
if (shouldAccept) {
this.getOwner().getQuestManager().addQuest(questData.getId());
}
}
}
} catch (Exception e) {
Grasscutter.getLogger().error("An error occurred while trying to accept quest.", e);
}
return false;
}
return false;
}
public void save() {
getMainQuest().save();
}
public void save() {
getMainQuest().save();
}
public Quest toProto() {
Quest.Builder proto = Quest.newBuilder()
.setQuestId(this.getQuestId())
.setState(this.getState().getValue())
.setParentQuestId(this.getMainQuestId())
.setStartTime(this.getStartTime())
.setStartGameTime(438)
.setAcceptTime(this.getAcceptTime());
public Quest toProto() {
Quest.Builder proto = Quest.newBuilder()
.setQuestId(this.getQuestId())
.setState(this.getState().getValue())
.setParentQuestId(this.getMainQuestId())
.setStartTime(this.getStartTime())
.setStartGameTime(438)
.setAcceptTime(this.getAcceptTime());
if (this.getFinishProgressList() != null) {
for (int i : this.getFinishProgressList()) {
proto.addFinishProgressList(i);
}
}
if (this.getFinishProgressList() != null) {
for (int i : this.getFinishProgressList()) {
proto.addFinishProgressList(i);
}
}
if (this.getFailProgressList() != null) {
for (int i : this.getFailProgressList()) {
proto.addFailProgressList(i);
}
}
if (this.getFailProgressList() != null) {
for (int i : this.getFailProgressList()) {
proto.addFailProgressList(i);
}
}
return proto.build();
}
return proto.build();
}
}

View File

@ -21,106 +21,106 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public class QuestManager extends BasePlayerManager {
private final Int2ObjectMap<GameMainQuest> quests;
private final Int2ObjectMap<GameMainQuest> quests;
public QuestManager(Player player) {
super(player);
this.quests = new Int2ObjectOpenHashMap<>();
}
public QuestManager(Player player) {
super(player);
this.quests = new Int2ObjectOpenHashMap<>();
}
public Int2ObjectMap<GameMainQuest> getQuests() {
return quests;
}
public GameMainQuest getMainQuestById(int mainQuestId) {
return getQuests().get(mainQuestId);
}
public GameQuest getQuestById(int questId) {
QuestData questConfig = GameData.getQuestDataMap().get(questId);
if (questConfig == null) {
return null;
}
GameMainQuest mainQuest = getQuests().get(questConfig.getMainId());
if (mainQuest == null) {
return null;
}
return mainQuest.getChildQuests().get(questId);
}
public void forEachQuest(Consumer<GameQuest> callback) {
for (GameMainQuest mainQuest : getQuests().values()) {
for (GameQuest quest : mainQuest.getChildQuests().values()) {
callback.accept(quest);
}
}
}
public Int2ObjectMap<GameMainQuest> getQuests() {
return quests;
}
public void forEachMainQuest(Consumer<GameMainQuest> callback) {
for (GameMainQuest mainQuest : getQuests().values()) {
callback.accept(mainQuest);
}
}
// TODO
public void forEachActiveQuest(Consumer<GameQuest> callback) {
for (GameMainQuest mainQuest : getQuests().values()) {
for (GameQuest quest : mainQuest.getChildQuests().values()) {
if (quest.getState() != QuestState.QUEST_STATE_FINISHED) {
callback.accept(quest);
}
}
}
}
public GameMainQuest addMainQuest(QuestData questConfig) {
GameMainQuest mainQuest = new GameMainQuest(getPlayer(), questConfig.getMainId());
getQuests().put(mainQuest.getParentQuestId(), mainQuest);
public GameMainQuest getMainQuestById(int mainQuestId) {
return getQuests().get(mainQuestId);
}
getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(mainQuest));
return mainQuest;
}
public GameQuest addQuest(int questId) {
QuestData questConfig = GameData.getQuestDataMap().get(questId);
if (questConfig == null) {
return null;
}
// Main quest
GameMainQuest mainQuest = this.getMainQuestById(questConfig.getMainId());
// Create main quest if it doesnt exist
if (mainQuest == null) {
mainQuest = addMainQuest(questConfig);
}
// Sub quest
GameQuest quest = mainQuest.getChildQuestById(questId);
if (quest != null) {
return null;
}
public GameQuest getQuestById(int questId) {
QuestData questConfig = GameData.getQuestDataMap().get(questId);
if (questConfig == null) {
return null;
}
// Create
quest = new GameQuest(mainQuest, questConfig);
GameMainQuest mainQuest = getQuests().get(questConfig.getMainId());
// Save main quest
mainQuest.save();
if (mainQuest == null) {
return null;
}
// Send packet
getPlayer().sendPacket(new PacketQuestListUpdateNotify(quest));
return mainQuest.getChildQuests().get(questId);
}
return quest;
}
public void startMainQuest(int mainQuestId){
public void forEachQuest(Consumer<GameQuest> callback) {
for (GameMainQuest mainQuest : getQuests().values()) {
for (GameQuest quest : mainQuest.getChildQuests().values()) {
callback.accept(quest);
}
}
}
public void forEachMainQuest(Consumer<GameMainQuest> callback) {
for (GameMainQuest mainQuest : getQuests().values()) {
callback.accept(mainQuest);
}
}
// TODO
public void forEachActiveQuest(Consumer<GameQuest> callback) {
for (GameMainQuest mainQuest : getQuests().values()) {
for (GameQuest quest : mainQuest.getChildQuests().values()) {
if (quest.getState() != QuestState.QUEST_STATE_FINISHED) {
callback.accept(quest);
}
}
}
}
public GameMainQuest addMainQuest(QuestData questConfig) {
GameMainQuest mainQuest = new GameMainQuest(getPlayer(), questConfig.getMainId());
getQuests().put(mainQuest.getParentQuestId(), mainQuest);
getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(mainQuest));
return mainQuest;
}
public GameQuest addQuest(int questId) {
QuestData questConfig = GameData.getQuestDataMap().get(questId);
if (questConfig == null) {
return null;
}
// Main quest
GameMainQuest mainQuest = this.getMainQuestById(questConfig.getMainId());
// Create main quest if it doesnt exist
if (mainQuest == null) {
mainQuest = addMainQuest(questConfig);
}
// Sub quest
GameQuest quest = mainQuest.getChildQuestById(questId);
if (quest != null) {
return null;
}
// Create
quest = new GameQuest(mainQuest, questConfig);
// Save main quest
mainQuest.save();
// Send packet
getPlayer().sendPacket(new PacketQuestListUpdateNotify(quest));
return quest;
}
public void startMainQuest(int mainQuestId) {
var mainQuestData = GameData.getMainQuestDataMap().get(mainQuestId);
if (mainQuestData == null){
if (mainQuestData == null) {
return;
}
@ -133,52 +133,52 @@ public class QuestManager extends BasePlayerManager {
triggerEvent(condType, "", params);
}
public void triggerEvent(QuestTrigger condType, String paramStr, int... params) {
public void triggerEvent(QuestTrigger condType, String paramStr, int... params) {
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
Set<GameQuest> changedQuests = new HashSet<>();
Set<GameQuest> changedQuests = new HashSet<>();
this.forEachActiveQuest(quest -> {
QuestData data = quest.getData();
this.forEachActiveQuest(quest -> {
QuestData data = quest.getData();
for (int i = 0; i < data.getFinishCond().size(); i++) {
if (quest.getFinishProgressList() == null
|| quest.getFinishProgressList().length == 0
|| quest.getFinishProgressList()[i] == 1) {
continue;
}
for (int i = 0; i < data.getFinishCond().size(); i++) {
if (quest.getFinishProgressList() == null
|| quest.getFinishProgressList().length == 0
|| quest.getFinishProgressList()[i] == 1) {
continue;
}
QuestCondition condition = data.getFinishCond().get(i);
QuestCondition condition = data.getFinishCond().get(i);
if (condition.getType() != condType) {
continue;
}
if (condition.getType() != condType) {
continue;
}
boolean result = getPlayer().getServer().getQuestSystem().triggerContent(quest, condition, paramStr, params);
boolean result = getPlayer().getServer().getQuestSystem().triggerContent(quest, condition, paramStr, params);
if (result) {
quest.getFinishProgressList()[i] = 1;
if (result) {
quest.getFinishProgressList()[i] = 1;
changedQuests.add(quest);
}
}
});
changedQuests.add(quest);
}
}
});
for (GameQuest quest : changedQuests) {
LogicType logicType = quest.getData().getFailCondComb();
int[] progress = quest.getFinishProgressList();
for (GameQuest quest : changedQuests) {
LogicType logicType = quest.getData().getFailCondComb();
int[] progress = quest.getFinishProgressList();
// Handle logical comb
boolean finish = LogicType.calculate(logicType, progress);
// Handle logical comb
boolean finish = LogicType.calculate(logicType, progress);
// Finish
if (finish) {
quest.finish();
} else {
getPlayer().sendPacket(new PacketQuestProgressUpdateNotify(quest));
quest.save();
}
}
}
// Finish
if (finish) {
quest.finish();
} else {
getPlayer().sendPacket(new PacketQuestProgressUpdateNotify(quest));
quest.save();
}
}
}
public List<QuestGroupSuite> getSceneGroupSuite(int sceneId) {
return getQuests().values().stream()
@ -189,18 +189,18 @@ public class QuestManager extends BasePlayerManager {
.filter(i -> i.getScene() == sceneId)
.toList();
}
public void loadFromDatabase() {
List<GameMainQuest> quests = DatabaseHelper.getAllQuests(getPlayer());
for (GameMainQuest mainQuest : quests) {
mainQuest.setOwner(this.getPlayer());
for (GameQuest quest : mainQuest.getChildQuests().values()) {
quest.setMainQuest(mainQuest);
quest.setConfig(GameData.getQuestDataMap().get(quest.getQuestId()));
}
this.getQuests().put(mainQuest.getParentQuestId(), mainQuest);
}
}
public void loadFromDatabase() {
List<GameMainQuest> quests = DatabaseHelper.getAllQuests(getPlayer());
for (GameMainQuest mainQuest : quests) {
mainQuest.setOwner(this.getPlayer());
for (GameQuest quest : mainQuest.getChildQuests().values()) {
quest.setMainQuest(mainQuest);
quest.setConfig(GameData.getQuestDataMap().get(quest.getQuestId()));
}
this.getQuests().put(mainQuest.getParentQuestId(), mainQuest);
}
}
}

View File

@ -17,81 +17,81 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@SuppressWarnings("unchecked")
public class QuestSystem extends BaseGameSystem {
private final Int2ObjectMap<QuestBaseHandler> condHandlers;
private final Int2ObjectMap<QuestBaseHandler> contHandlers;
private final Int2ObjectMap<QuestExecHandler> execHandlers;
private final Int2ObjectMap<QuestBaseHandler> condHandlers;
private final Int2ObjectMap<QuestBaseHandler> contHandlers;
private final Int2ObjectMap<QuestExecHandler> execHandlers;
public QuestSystem(GameServer server) {
super(server);
this.condHandlers = new Int2ObjectOpenHashMap<>();
this.contHandlers = new Int2ObjectOpenHashMap<>();
this.execHandlers = new Int2ObjectOpenHashMap<>();
public QuestSystem(GameServer server) {
super(server);
this.registerHandlers();
}
this.condHandlers = new Int2ObjectOpenHashMap<>();
this.contHandlers = new Int2ObjectOpenHashMap<>();
this.execHandlers = new Int2ObjectOpenHashMap<>();
public void registerHandlers() {
this.registerHandlers(this.condHandlers, "emu.grasscutter.game.quest.conditions", QuestBaseHandler.class);
this.registerHandlers(this.contHandlers, "emu.grasscutter.game.quest.content", QuestBaseHandler.class);
this.registerHandlers(this.execHandlers, "emu.grasscutter.game.quest.exec", QuestExecHandler.class);
}
this.registerHandlers();
}
public <T> void registerHandlers(Int2ObjectMap<T> map, String packageName, Class<T> clazz) {
Reflections reflections = new Reflections(packageName);
var handlerClasses = reflections.getSubTypesOf(clazz);
public void registerHandlers() {
this.registerHandlers(this.condHandlers, "emu.grasscutter.game.quest.conditions", QuestBaseHandler.class);
this.registerHandlers(this.contHandlers, "emu.grasscutter.game.quest.content", QuestBaseHandler.class);
this.registerHandlers(this.execHandlers, "emu.grasscutter.game.quest.exec", QuestExecHandler.class);
}
for (var obj : handlerClasses) {
this.registerPacketHandler(map, obj);
}
}
public <T> void registerHandlers(Int2ObjectMap<T> map, String packageName, Class<T> clazz) {
Reflections reflections = new Reflections(packageName);
var handlerClasses = reflections.getSubTypesOf(clazz);
public <T> void registerPacketHandler(Int2ObjectMap<T> map, Class<? extends T> handlerClass) {
try {
QuestValue opcode = handlerClass.getAnnotation(QuestValue.class);
for (var obj : handlerClasses) {
this.registerPacketHandler(map, obj);
}
}
if (opcode == null || opcode.value().getValue() <= 0) {
return;
}
public <T> void registerPacketHandler(Int2ObjectMap<T> map, Class<? extends T> handlerClass) {
try {
QuestValue opcode = handlerClass.getAnnotation(QuestValue.class);
map.put(opcode.value().getValue(), handlerClass.newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
if (opcode == null || opcode.value().getValue() <= 0) {
return;
}
// TODO make cleaner
map.put(opcode.value().getValue(), handlerClass.newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean triggerCondition(GameQuest quest, QuestCondition condition, String paramStr, int... params) {
QuestBaseHandler handler = condHandlers.get(condition.getType().getValue());
// TODO make cleaner
if (handler == null || quest.getData() == null) {
public boolean triggerCondition(GameQuest quest, QuestCondition condition, String paramStr, int... params) {
QuestBaseHandler handler = condHandlers.get(condition.getType().getValue());
if (handler == null || quest.getData() == null) {
Grasscutter.getLogger().debug("Could not trigger condition {} at {}", condition.getType().getValue(), quest.getData());
return false;
}
return false;
}
return handler.execute(quest, condition, paramStr, params);
}
return handler.execute(quest, condition, paramStr, params);
}
public boolean triggerContent(GameQuest quest, QuestCondition condition, String paramStr, int... params) {
QuestBaseHandler handler = contHandlers.get(condition.getType().getValue());
public boolean triggerContent(GameQuest quest, QuestCondition condition, String paramStr, int... params) {
QuestBaseHandler handler = contHandlers.get(condition.getType().getValue());
if (handler == null || quest.getData() == null) {
if (handler == null || quest.getData() == null) {
Grasscutter.getLogger().debug("Could not trigger content {} at {}", condition.getType().getValue(), quest.getData());
return false;
}
return false;
}
return handler.execute(quest, condition, paramStr, params);
}
return handler.execute(quest, condition, paramStr, params);
}
public boolean triggerExec(GameQuest quest, QuestExecParam execParam, String... params) {
QuestExecHandler handler = execHandlers.get(execParam.getType().getValue());
public boolean triggerExec(GameQuest quest, QuestExecParam execParam, String... params) {
QuestExecHandler handler = execHandlers.get(execParam.getType().getValue());
if (handler == null || quest.getData() == null) {
if (handler == null || quest.getData() == null) {
Grasscutter.getLogger().debug("Could not trigger exec {} at {}", execParam.getType().getValue(), quest.getData());
return false;
}
return false;
}
return handler.execute(quest, execParam, params);
}
return handler.execute(quest, execParam, params);
}
}

View File

@ -23,22 +23,22 @@ import java.util.Iterator;
import java.util.List;
public class ShopSystem extends BaseGameSystem {
private final Int2ObjectMap<List<ShopInfo>> shopData;
private final List<ShopChestTable> shopChestData;
private final List<ShopChestBatchUseTable> shopChestBatchUseData;
private final Int2ObjectMap<List<ShopInfo>> shopData;
private final List<ShopChestTable> shopChestData;
private final List<ShopChestBatchUseTable> shopChestBatchUseData;
private static final int REFRESH_HOUR = 4; // In GMT+8 server
private static final String TIME_ZONE = "Asia/Shanghai"; // GMT+8 Timezone
public ShopSystem(GameServer server) {
super(server);
this.shopData = new Int2ObjectOpenHashMap<>();
this.shopChestData = new ArrayList<>();
this.shopChestBatchUseData = new ArrayList<>();
this.load();
}
public Int2ObjectMap<List<ShopInfo>> getShopData() {
public ShopSystem(GameServer server) {
super(server);
this.shopData = new Int2ObjectOpenHashMap<>();
this.shopChestData = new ArrayList<>();
this.shopChestBatchUseData = new ArrayList<>();
this.load();
}
public Int2ObjectMap<List<ShopInfo>> getShopData() {
return shopData;
}
@ -50,96 +50,96 @@ public class ShopSystem extends BaseGameSystem {
return shopChestBatchUseData;
}
public static int getShopNextRefreshTime(ShopInfo shopInfo) {
return switch (shopInfo.getShopRefreshType()) {
case SHOP_REFRESH_DAILY -> Utils.getNextTimestampOfThisHour(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
case SHOP_REFRESH_WEEKLY -> Utils.getNextTimestampOfThisHourInNextWeek(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
case SHOP_REFRESH_MONTHLY -> Utils.getNextTimestampOfThisHourInNextMonth(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
default -> 0;
};
}
public static int getShopNextRefreshTime(ShopInfo shopInfo) {
return switch (shopInfo.getShopRefreshType()) {
case SHOP_REFRESH_DAILY -> Utils.getNextTimestampOfThisHour(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
case SHOP_REFRESH_WEEKLY -> Utils.getNextTimestampOfThisHourInNextWeek(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
case SHOP_REFRESH_MONTHLY -> Utils.getNextTimestampOfThisHourInNextMonth(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
default -> 0;
};
}
private void loadShop() {
try (Reader fileReader = DataLoader.loadReader("Shop.json")) {
getShopData().clear();
List<ShopTable> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopTable.class).getType());
if(banners.size() > 0) {
for (ShopTable shopTable : banners) {
for (ShopInfo cost : shopTable.getItems()) {
if (cost.getCostItemList() != null) {
Iterator<ItemParamData> iterator = cost.getCostItemList().iterator();
while (iterator.hasNext()) {
ItemParamData ipd = iterator.next();
if (ipd.getId() == 201) {
cost.setHcoin(cost.getHcoin() + ipd.getCount());
iterator.remove();
}
if (ipd.getId() == 203) {
cost.setMcoin(cost.getMcoin() + ipd.getCount());
iterator.remove();
}
}
}
}
getShopData().put(shopTable.getShopId(), shopTable.getItems());
}
Grasscutter.getLogger().debug("Shop data successfully loaded.");
} else {
Grasscutter.getLogger().error("Unable to load shop data. Shop data size is 0.");
}
private void loadShop() {
try (Reader fileReader = DataLoader.loadReader("Shop.json")) {
getShopData().clear();
List<ShopTable> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopTable.class).getType());
if (banners.size() > 0) {
for (ShopTable shopTable : banners) {
for (ShopInfo cost : shopTable.getItems()) {
if (cost.getCostItemList() != null) {
Iterator<ItemParamData> iterator = cost.getCostItemList().iterator();
while (iterator.hasNext()) {
ItemParamData ipd = iterator.next();
if (ipd.getId() == 201) {
cost.setHcoin(cost.getHcoin() + ipd.getCount());
iterator.remove();
}
if (ipd.getId() == 203) {
cost.setMcoin(cost.getMcoin() + ipd.getCount());
iterator.remove();
}
}
}
}
getShopData().put(shopTable.getShopId(), shopTable.getItems());
}
Grasscutter.getLogger().debug("Shop data successfully loaded.");
} else {
Grasscutter.getLogger().error("Unable to load shop data. Shop data size is 0.");
}
if (GAME_OPTIONS.enableShopItems) {
GameData.getShopGoodsDataEntries().forEach((k, v) -> {
if (!getShopData().containsKey(k.intValue()))
getShopData().put(k.intValue(), new ArrayList<>());
for (ShopGoodsData sgd : v) {
var shopInfo = new ShopInfo(sgd);
getShopData().get(k.intValue()).add(shopInfo);
}
});
}
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to load shop data.", e);
}
}
if (GAME_OPTIONS.enableShopItems) {
GameData.getShopGoodsDataEntries().forEach((k, v) -> {
if (!getShopData().containsKey(k.intValue()))
getShopData().put(k.intValue(), new ArrayList<>());
for (ShopGoodsData sgd : v) {
var shopInfo = new ShopInfo(sgd);
getShopData().get(k.intValue()).add(shopInfo);
}
});
}
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to load shop data.", e);
}
}
private void loadShopChest() {
try (Reader fileReader = DataLoader.loadReader("ShopChest.json")) {
getShopChestData().clear();
List<ShopChestTable> shopChestTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestTable.class).getType());
if (shopChestTableList.size() > 0) {
getShopChestData().addAll(shopChestTableList);
Grasscutter.getLogger().debug("ShopChest data successfully loaded.");
} else {
Grasscutter.getLogger().error("Unable to load ShopChest data. ShopChest data size is 0.");
}
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to load ShopChest data.", e);
}
}
private void loadShopChest() {
try (Reader fileReader = DataLoader.loadReader("ShopChest.json")) {
getShopChestData().clear();
List<ShopChestTable> shopChestTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestTable.class).getType());
if (shopChestTableList.size() > 0) {
getShopChestData().addAll(shopChestTableList);
Grasscutter.getLogger().debug("ShopChest data successfully loaded.");
} else {
Grasscutter.getLogger().error("Unable to load ShopChest data. ShopChest data size is 0.");
}
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to load ShopChest data.", e);
}
}
private void loadShopChestBatchUse() {
try (Reader fileReader = DataLoader.loadReader("ShopChestBatchUse.json")) {
getShopChestBatchUseData().clear();
List<ShopChestBatchUseTable> shopChestBatchUseTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestBatchUseTable.class).getType());
if (shopChestBatchUseTableList.size() > 0) {
getShopChestBatchUseData().addAll(shopChestBatchUseTableList);
Grasscutter.getLogger().debug("ShopChestBatchUse data successfully loaded.");
} else {
Grasscutter.getLogger().error("Unable to load ShopChestBatchUse data. ShopChestBatchUse data size is 0.");
}
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to load ShopChestBatchUse data.", e);
}
}
private void loadShopChestBatchUse() {
try (Reader fileReader = DataLoader.loadReader("ShopChestBatchUse.json")) {
getShopChestBatchUseData().clear();
List<ShopChestBatchUseTable> shopChestBatchUseTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestBatchUseTable.class).getType());
if (shopChestBatchUseTableList.size() > 0) {
getShopChestBatchUseData().addAll(shopChestBatchUseTableList);
Grasscutter.getLogger().debug("ShopChestBatchUse data successfully loaded.");
} else {
Grasscutter.getLogger().error("Unable to load ShopChestBatchUse data. ShopChestBatchUse data size is 0.");
}
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to load ShopChestBatchUse data.", e);
}
}
public synchronized void load() {
loadShop();
loadShopChest();
loadShopChestBatchUse();
}
public synchronized void load() {
loadShop();
loadShopChest();
loadShopChestBatchUse();
}
public GameServer getServer() {
return server;
}
public GameServer getServer() {
return server;
}
}

View File

@ -22,8 +22,8 @@ import java.util.*;
@Getter
public class AnnouncementSystem extends BaseGameSystem {
private final Map<Integer, AnnounceConfigItem> announceConfigItemMap;
public AnnouncementSystem(GameServer server){
public AnnouncementSystem(GameServer server) {
super(server);
this.announceConfigItemMap = new HashMap<>();
loadConfig();
@ -51,7 +51,7 @@ public class AnnouncementSystem extends BaseGameSystem {
}
public void broadcast(List<AnnounceConfigItem> tpl) {
if(tpl == null || tpl.size() == 0){
if (tpl == null || tpl.size() == 0) {
return;
}
@ -83,7 +83,7 @@ public class AnnouncementSystem extends BaseGameSystem {
boolean tick;
int interval;
public AnnounceDataOuterClass.AnnounceData.Builder toProto(){
public AnnounceDataOuterClass.AnnounceData.Builder toProto() {
var proto = AnnounceDataOuterClass.AnnounceData.newBuilder();
proto.setConfigId(templateId)
@ -91,11 +91,11 @@ public class AnnouncementSystem extends BaseGameSystem {
.setBeginTime(Utils.getCurrentSeconds() + 1)
.setEndTime(Utils.getCurrentSeconds() + 10);
if(type == AnnounceType.CENTER){
if (type == AnnounceType.CENTER) {
proto.setCenterSystemText(content)
.setCenterSystemFrequency(frequency)
;
}else{
}else {
proto.setCountDownText(content)
.setCountDownFrequency(frequency)
;

File diff suppressed because it is too large Load Diff

View File

@ -16,140 +16,140 @@ import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify;
public class MultiplayerSystem extends BaseGameSystem {
public MultiplayerSystem(GameServer server) {
super(server);
}
public MultiplayerSystem(GameServer server) {
super(server);
}
public void applyEnterMp(Player player, int targetUid) {
Player target = getServer().getPlayerByUid(targetUid);
if (target == null) {
player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_CANNOT_ENTER_MP));
return;
}
// Sanity checks - Dont let player join if already in multiplayer
if (player.getWorld().isMultiplayer()) {
return;
}
/*
if (target.getWorld().isDungeon()) {
player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpReason.SceneCannotEnter));
return;
}
*/
// Get request
CoopRequest request = target.getCoopRequests().get(player.getUid());
if (request != null && !request.isExpired()) {
// Join request already exists
return;
}
// Put request in
request = new CoopRequest(player);
target.getCoopRequests().put(player.getUid(), request);
// Packet
target.sendPacket(new PacketPlayerApplyEnterMpNotify(player));
}
public void applyEnterMp(Player player, int targetUid) {
Player target = getServer().getPlayerByUid(targetUid);
if (target == null) {
player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_CANNOT_ENTER_MP));
return;
}
public void applyEnterMpReply(Player hostPlayer, int applyUid, boolean isAgreed) {
// Checks
CoopRequest request = hostPlayer.getCoopRequests().get(applyUid);
if (request == null || request.isExpired()) {
return;
}
// Remove now that we are handling it
Player requester = request.getRequester();
hostPlayer.getCoopRequests().remove(applyUid);
// Sanity checks - Dont let the requesting player join if they are already in multiplayer
if (requester.getWorld().isMultiplayer()) {
request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_CANNOT_ENTER_MP));
return;
}
// Response packet
request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, isAgreed, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_JUDGE));
// Declined
if (!isAgreed) {
return;
}
// Sanity checks - Dont let player join if already in multiplayer
if (player.getWorld().isMultiplayer()) {
return;
}
// Success
if (!hostPlayer.getWorld().isMultiplayer()) {
// Player not in multiplayer, create multiplayer world
World world = new World(hostPlayer, true);
/*
if (target.getWorld().isDungeon()) {
player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpReason.SceneCannotEnter));
return;
}
*/
// Add
world.addPlayer(hostPlayer);
// Rejoin packet
hostPlayer.sendPacket(new PacketPlayerEnterSceneNotify(hostPlayer, hostPlayer, EnterType.ENTER_TYPE_SELF, EnterReason.HostFromSingleToMp, hostPlayer.getScene().getId(), hostPlayer.getPosition()));
}
// Set scene pos and id of requester to the host player's
requester.getPosition().set(hostPlayer.getPosition());
requester.getRotation().set(hostPlayer.getRotation());
requester.setSceneId(hostPlayer.getSceneId());
// Make requester join
hostPlayer.getWorld().addPlayer(requester);
// Get request
CoopRequest request = target.getCoopRequests().get(player.getUid());
// Packet
requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, hostPlayer, EnterType.ENTER_TYPE_OTHER, EnterReason.TeamJoin, hostPlayer.getScene().getId(), hostPlayer.getPosition()));
}
public boolean leaveCoop(Player player) {
// Make sure player's world is multiplayer
if (!player.getWorld().isMultiplayer()) {
return false;
}
// Make sure everyone's scene is loaded
for (Player p : player.getWorld().getPlayers()) {
if (p.getSceneLoadState() != SceneLoadState.LOADED) {
return false;
}
}
// Create new world for player
World world = new World(player);
world.addPlayer(player);
// Packet
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_TYPE_SELF, EnterReason.TeamBack, player.getScene().getId(), player.getPosition()));
return true;
}
if (request != null && !request.isExpired()) {
// Join request already exists
return;
}
public boolean kickPlayer(Player player, int targetUid) {
// Make sure player's world is multiplayer and that player is owner
if (!player.getWorld().isMultiplayer() || player.getWorld().getHost() != player) {
return false;
}
// Get victim and sanity checks
Player victim = player.getServer().getPlayerByUid(targetUid);
if (victim == null || victim == player) {
return false;
}
// Make sure victim's scene has loaded
if (victim.getSceneLoadState() != SceneLoadState.LOADED) {
return false;
}
// Kick
World world = new World(victim);
world.addPlayer(victim);
victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getScene().getId(), victim.getPosition()));
return true;
}
// Put request in
request = new CoopRequest(player);
target.getCoopRequests().put(player.getUid(), request);
// Packet
target.sendPacket(new PacketPlayerApplyEnterMpNotify(player));
}
public void applyEnterMpReply(Player hostPlayer, int applyUid, boolean isAgreed) {
// Checks
CoopRequest request = hostPlayer.getCoopRequests().get(applyUid);
if (request == null || request.isExpired()) {
return;
}
// Remove now that we are handling it
Player requester = request.getRequester();
hostPlayer.getCoopRequests().remove(applyUid);
// Sanity checks - Dont let the requesting player join if they are already in multiplayer
if (requester.getWorld().isMultiplayer()) {
request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_CANNOT_ENTER_MP));
return;
}
// Response packet
request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, isAgreed, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_JUDGE));
// Declined
if (!isAgreed) {
return;
}
// Success
if (!hostPlayer.getWorld().isMultiplayer()) {
// Player not in multiplayer, create multiplayer world
World world = new World(hostPlayer, true);
// Add
world.addPlayer(hostPlayer);
// Rejoin packet
hostPlayer.sendPacket(new PacketPlayerEnterSceneNotify(hostPlayer, hostPlayer, EnterType.ENTER_TYPE_SELF, EnterReason.HostFromSingleToMp, hostPlayer.getScene().getId(), hostPlayer.getPosition()));
}
// Set scene pos and id of requester to the host player's
requester.getPosition().set(hostPlayer.getPosition());
requester.getRotation().set(hostPlayer.getRotation());
requester.setSceneId(hostPlayer.getSceneId());
// Make requester join
hostPlayer.getWorld().addPlayer(requester);
// Packet
requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, hostPlayer, EnterType.ENTER_TYPE_OTHER, EnterReason.TeamJoin, hostPlayer.getScene().getId(), hostPlayer.getPosition()));
}
public boolean leaveCoop(Player player) {
// Make sure player's world is multiplayer
if (!player.getWorld().isMultiplayer()) {
return false;
}
// Make sure everyone's scene is loaded
for (Player p : player.getWorld().getPlayers()) {
if (p.getSceneLoadState() != SceneLoadState.LOADED) {
return false;
}
}
// Create new world for player
World world = new World(player);
world.addPlayer(player);
// Packet
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_TYPE_SELF, EnterReason.TeamBack, player.getScene().getId(), player.getPosition()));
return true;
}
public boolean kickPlayer(Player player, int targetUid) {
// Make sure player's world is multiplayer and that player is owner
if (!player.getWorld().isMultiplayer() || player.getWorld().getHost() != player) {
return false;
}
// Get victim and sanity checks
Player victim = player.getServer().getPlayerByUid(targetUid);
if (victim == null || victim == player) {
return false;
}
// Make sure victim's scene has loaded
if (victim.getSceneLoadState() != SceneLoadState.LOADED) {
return false;
}
// Kick
World world = new World(victim);
world.addPlayer(victim);
victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getScene().getId(), victim.getPosition()));
return true;
}
}

View File

@ -13,11 +13,11 @@ import java.util.List;
import java.util.Map;
public class TowerManager extends BasePlayerManager {
public TowerManager(Player player) {
super(player);
}
public TowerData getTowerData() {
return getPlayer().getTowerData();
}
@ -26,21 +26,21 @@ public class TowerManager extends BasePlayerManager {
return getTowerData().currentFloorId;
}
public int getCurrentLevelId(){
public int getCurrentLevelId() {
return getTowerData().currentLevelId + getTowerData().currentLevel;
}
/**
* form 1-3
*/
public int getCurrentLevel(){
public int getCurrentLevel() {
return getTowerData().currentLevel + 1;
}
private static final List<DungeonSettleListener> towerDungeonSettleListener = List.of(new TowerDungeonSettleListener());
public Map<Integer, TowerLevelRecord> getRecordMap() {
Map<Integer, TowerLevelRecord> recordMap = getTowerData().recordMap;
if(recordMap == null || recordMap.size()==0){
if (recordMap == null || recordMap.size()==0) {
recordMap = new HashMap<>();
recordMap.put(1001, new TowerLevelRecord(1001));
getTowerData().recordMap = recordMap;
@ -58,7 +58,7 @@ public class TowerManager extends BasePlayerManager {
.map(TowerLevelData::getId)
.orElse(0);
if (getTowerData().entryScene == 0){
if (getTowerData().entryScene == 0) {
getTowerData().entryScene = player.getSceneId();
}
@ -88,37 +88,37 @@ public class TowerManager extends BasePlayerManager {
player.getSession().send(new PacketTowerLevelStarCondNotify(getTowerData().currentFloorId, getCurrentLevel()));
}
public void notifyCurLevelRecordChange(){
public void notifyCurLevelRecordChange() {
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(getTowerData().currentFloorId, getCurrentLevel()));
}
public void notifyCurLevelRecordChangeWhenDone(int stars){
public void notifyCurLevelRecordChangeWhenDone(int stars) {
Map<Integer, TowerLevelRecord> recordMap = getRecordMap();
int currentFloorId = getTowerData().currentFloorId;
if(!recordMap.containsKey(currentFloorId)){
if (!recordMap.containsKey(currentFloorId)) {
recordMap.put(currentFloorId,
new TowerLevelRecord(currentFloorId).setLevelStars(getCurrentLevelId(),stars));
}else{
}else {
recordMap.put(currentFloorId,
recordMap.get(currentFloorId).setLevelStars(getCurrentLevelId(),stars));
}
getTowerData().currentLevel++;
if(!hasNextLevel()){
if (!hasNextLevel()) {
// set up the next floor
recordMap.putIfAbsent(getNextFloorId(), new TowerLevelRecord(getNextFloorId()));
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(getNextFloorId(), 1));
}else{
}else {
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, getCurrentLevel()));
}
}
public boolean hasNextLevel(){
public boolean hasNextLevel() {
return getTowerData().currentLevel < 3;
}
public int getNextFloorId() {
return player.getServer().getTowerSystem().getNextFloorId(getTowerData().currentFloorId);
}
public boolean hasNextFloor(){
public boolean hasNextFloor() {
return player.getServer().getTowerSystem().getNextFloorId(getTowerData().currentFloorId) > 0;
}
@ -126,9 +126,9 @@ public class TowerManager extends BasePlayerManager {
getTowerData().entryScene = 0;
}
public boolean canEnterScheduleFloor(){
public boolean canEnterScheduleFloor() {
Map<Integer, TowerLevelRecord> recordMap = getRecordMap();
if(!recordMap.containsKey(player.getServer().getTowerSystem().getLastEntranceFloor())){
if (!recordMap.containsKey(player.getServer().getTowerSystem().getLastEntranceFloor())) {
return false;
}
return recordMap.get(player.getServer().getTowerSystem().getLastEntranceFloor())

View File

@ -16,7 +16,7 @@ import java.util.ArrayList;
import java.util.List;
public class TowerSystem extends BaseGameSystem {
public TowerSystem(GameServer server) {
super(server);
this.load();
@ -24,7 +24,7 @@ public class TowerSystem extends BaseGameSystem {
private TowerScheduleConfig towerScheduleConfig;
public synchronized void load(){
public synchronized void load() {
try (Reader fileReader = DataLoader.loadReader("TowerSchedule.json")) {
towerScheduleConfig = Grasscutter.getGsonFactory().fromJson(fileReader, TowerScheduleConfig.class);
} catch (Exception e) {
@ -36,13 +36,13 @@ public class TowerSystem extends BaseGameSystem {
return towerScheduleConfig;
}
public TowerScheduleData getCurrentTowerScheduleData(){
public TowerScheduleData getCurrentTowerScheduleData() {
var data = GameData.getTowerScheduleDataMap().get(towerScheduleConfig.getScheduleId());
if(data == null){
if (data == null) {
Grasscutter.getLogger().error("Could not get current tower schedule data by schedule id {}, please check your resource files",
towerScheduleConfig.getScheduleId());
}
return data;
}
@ -56,29 +56,29 @@ public class TowerSystem extends BaseGameSystem {
return getCurrentTowerScheduleData().getSchedules().get(0).getFloorList();
}
public int getNextFloorId(int floorId){
public int getNextFloorId(int floorId) {
var entranceFloors = getCurrentTowerScheduleData().getEntranceFloorId();
var scheduleFloors = getScheduleFloors();
var nextId = 0;
// find in entrance floors first
for(int i=0;i<entranceFloors.size()-1;i++){
if(floorId == entranceFloors.get(i)){
for (int i=0;i<entranceFloors.size()-1;i++) {
if (floorId == entranceFloors.get(i)) {
nextId = entranceFloors.get(i+1);
}
}
if(floorId == entranceFloors.get(entranceFloors.size()-1)){
if (floorId == entranceFloors.get(entranceFloors.size()-1)) {
nextId = scheduleFloors.get(0);
}
if(nextId != 0){
if (nextId != 0) {
return nextId;
}
// find in schedule floors
for(int i=0; i < scheduleFloors.size() - 1; i++){
if(floorId == scheduleFloors.get(i)){
for (int i=0; i < scheduleFloors.size() - 1; i++) {
if (floorId == scheduleFloors.get(i)) {
nextId = scheduleFloors.get(i + 1);
}
}return nextId;

File diff suppressed because it is too large Load Diff

View File

@ -7,62 +7,62 @@ import emu.grasscutter.data.GameDepot;
import emu.grasscutter.utils.Position;
public class SpawnDataEntry {
private transient SpawnGroupEntry group;
private int monsterId;
private int gadgetId;
private int configId;
private int level;
private int poseId;
private int gatherItemId;
private transient SpawnGroupEntry group;
private int monsterId;
private int gadgetId;
private int configId;
private int level;
private int poseId;
private int gatherItemId;
private int gadgetState;
private Position pos;
private Position rot;
private Position pos;
private Position rot;
public SpawnGroupEntry getGroup() {
return group;
}
public SpawnGroupEntry getGroup() {
return group;
}
public void setGroup(SpawnGroupEntry group) {
this.group = group;
}
public void setGroup(SpawnGroupEntry group) {
this.group = group;
}
public int getMonsterId() {
return monsterId;
}
public int getMonsterId() {
return monsterId;
}
public int getGadgetId() {
return gadgetId;
}
public int getGadgetId() {
return gadgetId;
}
public int getGadgetState() {
return gadgetState;
}
public int getConfigId() {
return configId;
}
return configId;
}
public int getLevel() {
return level;
}
public int getLevel() {
return level;
}
public int getPoseId() {
return poseId;
}
public int getPoseId() {
return poseId;
}
public int getGatherItemId() {
return gatherItemId;
}
public int getGatherItemId() {
return gatherItemId;
}
public Position getPos() {
return pos;
}
public Position getPos() {
return pos;
}
public Position getRot() {
return rot;
}
public Position getRot() {
return rot;
}
public GridBlockId getBlockId(){
public GridBlockId getBlockId() {
int scale = GridBlockId.getScale(gadgetId);
return new GridBlockId(group.sceneId,scale,
(int)(pos.getX() / GameDepot.BLOCK_SIZE[scale]),
@ -70,32 +70,32 @@ public class SpawnDataEntry {
);
}
public static class SpawnGroupEntry {
private int sceneId;
private int groupId;
private int blockId;
private List<SpawnDataEntry> spawns;
public static class SpawnGroupEntry {
private int sceneId;
private int groupId;
private int blockId;
private List<SpawnDataEntry> spawns;
public int getSceneId() {
return sceneId;
}
public int getSceneId() {
return sceneId;
}
public int getGroupId() {
return groupId;
}
public int getGroupId() {
return groupId;
}
public int getBlockId() {
return blockId;
}
public int getBlockId() {
return blockId;
}
public void setBlockId(int blockId) {
this.blockId = blockId;
}
public void setBlockId(int blockId) {
this.blockId = blockId;
}
public List<SpawnDataEntry> getSpawns() {
return spawns;
}
}
public List<SpawnDataEntry> getSpawns() {
return spawns;
}
}
public static class GridBlockId {
int sceneId;
@ -133,7 +133,7 @@ public class SpawnDataEntry {
return Objects.hash(sceneId, scale, x, z);
}
public static GridBlockId[] getAdjacentGridBlockIds(int sceneId, Position pos){
public static GridBlockId[] getAdjacentGridBlockIds(int sceneId, Position pos) {
GridBlockId[] results = new GridBlockId[5*5*GameDepot.BLOCK_SIZE.length];
int t=0;
for (int scale = 0; scale < GameDepot.BLOCK_SIZE.length; scale++) {
@ -148,7 +148,7 @@ public class SpawnDataEntry {
return results;
}
public static int getScale(int gadgetId){
public static int getScale(int gadgetId) {
return 0;//you should implement here,this is index of GameDepot.BLOCK_SIZE
}
}

View File

@ -31,298 +31,298 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public class World implements Iterable<Player> {
private final GameServer server;
private final Player owner;
private final List<Player> players;
private final Int2ObjectMap<Scene> scenes;
private int levelEntityId;
private int nextEntityId = 0;
private int nextPeerId = 0;
private int worldLevel;
private final GameServer server;
private final Player owner;
private final List<Player> players;
private final Int2ObjectMap<Scene> scenes;
private boolean isMultiplayer;
public World(Player player) {
this(player, false);
}
public World(Player player, boolean isMultiplayer) {
this.owner = player;
this.server = player.getServer();
this.players = Collections.synchronizedList(new ArrayList<>());
this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
this.levelEntityId = getNextEntityId(EntityIdType.MPLEVEL);
this.worldLevel = player.getWorldLevel();
this.isMultiplayer = isMultiplayer;
private int levelEntityId;
private int nextEntityId = 0;
private int nextPeerId = 0;
private int worldLevel;
this.owner.getServer().registerWorld(this);
}
public Player getHost() {
return owner;
}
private boolean isMultiplayer;
public GameServer getServer() {
return server;
}
public World(Player player) {
this(player, false);
}
public int getLevelEntityId() {
return levelEntityId;
}
public World(Player player, boolean isMultiplayer) {
this.owner = player;
this.server = player.getServer();
this.players = Collections.synchronizedList(new ArrayList<>());
this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
public int getHostPeerId() {
if (this.getHost() == null) {
return 0;
}
return this.getHost().getPeerId();
}
public int getNextPeerId() {
return ++this.nextPeerId;
}
this.levelEntityId = getNextEntityId(EntityIdType.MPLEVEL);
this.worldLevel = player.getWorldLevel();
this.isMultiplayer = isMultiplayer;
public int getWorldLevel() {
return worldLevel;
}
this.owner.getServer().registerWorld(this);
}
public void setWorldLevel(int worldLevel) {
this.worldLevel = worldLevel;
}
public Player getHost() {
return owner;
}
public List<Player> getPlayers() {
return players;
}
public Int2ObjectMap<Scene> getScenes() {
return this.scenes;
}
public Scene getSceneById(int sceneId) {
// Get scene normally
Scene scene = getScenes().get(sceneId);
if (scene != null) {
return scene;
}
// Create scene from scene data if it doesnt exist
SceneData sceneData = GameData.getSceneDataMap().get(sceneId);
if (sceneData != null) {
scene = new Scene(this, sceneData);
this.registerScene(scene);
return scene;
}
return null;
}
public int getPlayerCount() {
return getPlayers().size();
}
public boolean isMultiplayer() {
return isMultiplayer;
}
public int getNextEntityId(EntityIdType idType) {
return (idType.getId() << 24) + ++this.nextEntityId;
}
public synchronized void addPlayer(Player player) {
// Check if player already in
if (getPlayers().contains(player)) {
return;
}
// Remove player from prev world
if (player.getWorld() != null) {
player.getWorld().removePlayer(player);
}
// Register
player.setWorld(this);
getPlayers().add(player);
public GameServer getServer() {
return server;
}
// Set player variables
player.setPeerId(this.getNextPeerId());
player.getTeamManager().setEntityId(getNextEntityId(EntityIdType.TEAM));
// Copy main team to mp team
if (this.isMultiplayer()) {
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getCurrentSinglePlayerTeamInfo(), player.getTeamManager().getMaxTeamSize());
player.getTeamManager().setCurrentCharacterIndex(0);
}
// Add to scene
Scene scene = this.getSceneById(player.getSceneId());
scene.addPlayer(player);
public int getLevelEntityId() {
return levelEntityId;
}
// Info packet for other players
if (this.getPlayers().size() > 1) {
this.updatePlayerInfos(player);
}
}
public int getHostPeerId() {
if (this.getHost() == null) {
return 0;
}
return this.getHost().getPeerId();
}
public synchronized void removePlayer(Player player) {
// Remove team entities
player.sendPacket(
new PacketDelTeamEntityNotify(
player.getSceneId(),
getPlayers().stream().map(p -> p.getTeamManager().getEntityId()).collect(Collectors.toList())
)
);
// Deregister
getPlayers().remove(player);
player.setWorld(null);
// Remove from scene
Scene scene = this.getSceneById(player.getSceneId());
scene.removePlayer(player);
public int getNextPeerId() {
return ++this.nextPeerId;
}
// Info packet for other players
if (this.getPlayers().size() > 0) {
this.updatePlayerInfos(player);
}
public int getWorldLevel() {
return worldLevel;
}
// Disband world if host leaves
if (getHost() == player) {
List<Player> kicked = new ArrayList<>(this.getPlayers());
for (Player victim : kicked) {
World world = new World(victim);
world.addPlayer(victim);
victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getSceneId(), victim.getPosition()));
}
}
}
public void registerScene(Scene scene) {
this.getScenes().put(scene.getId(), scene);
}
public void deregisterScene(Scene scene) {
this.getScenes().remove(scene.getId());
}
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) {
return transferPlayerToScene(player, sceneId, null, pos);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData data) {
return transferPlayerToScene(player, sceneId, data, null);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData dungeonData, Position pos) {
if (GameData.getSceneDataMap().get(sceneId) == null) {
return false;
}
Scene oldScene = null;
public void setWorldLevel(int worldLevel) {
this.worldLevel = worldLevel;
}
if (player.getScene() != null) {
oldScene = player.getScene();
public List<Player> getPlayers() {
return players;
}
// Dont deregister scenes if the player is going to tp back into them
if (oldScene.getId() == sceneId) {
oldScene.setDontDestroyWhenEmpty(true);
}
public Int2ObjectMap<Scene> getScenes() {
return this.scenes;
}
oldScene.removePlayer(player);
}
Scene newScene = this.getSceneById(sceneId);
newScene.setDungeonData(dungeonData);
newScene.addPlayer(player);
// Dungeon
SceneConfig config = newScene.getScriptManager().getConfig();
if (pos == null && config != null) {
if (config.born_pos != null) {
pos = newScene.getScriptManager().getConfig().born_pos;
}
if (config.born_rot != null) {
player.getRotation().set(config.born_rot);
}
}
// Set player position
if (pos == null) {
pos = player.getPosition();
}
player.getPosition().set(pos);
public Scene getSceneById(int sceneId) {
// Get scene normally
Scene scene = getScenes().get(sceneId);
if (scene != null) {
return scene;
}
if (oldScene != null) {
newScene.setPrevScene(oldScene.getId());
oldScene.setDontDestroyWhenEmpty(false);
}
// Create scene from scene data if it doesnt exist
SceneData sceneData = GameData.getSceneDataMap().get(sceneId);
if (sceneData != null) {
scene = new Scene(this, sceneData);
this.registerScene(scene);
return scene;
}
// Get enter types
EnterType enterType = EnterType.ENTER_TYPE_JUMP;
EnterReason enterReason = EnterReason.TransPoint;
if (dungeonData != null) {
enterType = EnterType.ENTER_TYPE_DUNGEON;
enterReason = EnterReason.DungeonEnter;
} else if (oldScene == newScene) {
enterType = EnterType.ENTER_TYPE_GOTO;
} else if (newScene.getSceneType() == SceneType.SCENE_HOME_WORLD) {
// Home
enterReason = EnterReason.EnterHome;
enterType = EnterType.ENTER_TYPE_SELF_HOME;
return null;
}
}
// Teleport packet
player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, pos));
return true;
}
private void updatePlayerInfos(Player paramPlayer) {
for (Player player : getPlayers()) {
// Dont send packets if player is loading in and filter out joining player
if (!player.hasSentAvatarDataNotify() || player.getSceneLoadState().getValue() < SceneLoadState.INIT.getValue() || player == paramPlayer) {
continue;
}
// Update team of all players since max players has been changed - Probably not the best way to do it
if (this.isMultiplayer()) {
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getMpTeam(), player.getTeamManager().getMaxTeamSize());
player.getTeamManager().updateTeamEntities(null);
}
public int getPlayerCount() {
return getPlayers().size();
}
// World player info packets
player.getSession().send(new PacketWorldPlayerInfoNotify(this));
player.getSession().send(new PacketScenePlayerInfoNotify(this));
player.getSession().send(new PacketWorldPlayerRTTNotify(this));
// Team packets
player.getSession().send(new PacketSyncTeamEntityNotify(player));
player.getSession().send(new PacketSyncScenePlayTeamEntityNotify(player));
}
}
public void broadcastPacket(BasePacket packet) {
// Send to all players - might have to check if player has been sent data packets
for (Player player : this.getPlayers()) {
player.getSession().send(packet);
}
}
public void onTick() {
for (Scene scene : this.getScenes().values()) {
scene.onTick();
}
}
public boolean isMultiplayer() {
return isMultiplayer;
}
public void close() {
}
public int getNextEntityId(EntityIdType idType) {
return (idType.getId() << 24) + ++this.nextEntityId;
}
@Override
public Iterator<Player> iterator() {
return getPlayers().iterator();
}
public synchronized void addPlayer(Player player) {
// Check if player already in
if (getPlayers().contains(player)) {
return;
}
// Remove player from prev world
if (player.getWorld() != null) {
player.getWorld().removePlayer(player);
}
// Register
player.setWorld(this);
getPlayers().add(player);
// Set player variables
player.setPeerId(this.getNextPeerId());
player.getTeamManager().setEntityId(getNextEntityId(EntityIdType.TEAM));
// Copy main team to mp team
if (this.isMultiplayer()) {
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getCurrentSinglePlayerTeamInfo(), player.getTeamManager().getMaxTeamSize());
player.getTeamManager().setCurrentCharacterIndex(0);
}
// Add to scene
Scene scene = this.getSceneById(player.getSceneId());
scene.addPlayer(player);
// Info packet for other players
if (this.getPlayers().size() > 1) {
this.updatePlayerInfos(player);
}
}
public synchronized void removePlayer(Player player) {
// Remove team entities
player.sendPacket(
new PacketDelTeamEntityNotify(
player.getSceneId(),
getPlayers().stream().map(p -> p.getTeamManager().getEntityId()).collect(Collectors.toList())
)
);
// Deregister
getPlayers().remove(player);
player.setWorld(null);
// Remove from scene
Scene scene = this.getSceneById(player.getSceneId());
scene.removePlayer(player);
// Info packet for other players
if (this.getPlayers().size() > 0) {
this.updatePlayerInfos(player);
}
// Disband world if host leaves
if (getHost() == player) {
List<Player> kicked = new ArrayList<>(this.getPlayers());
for (Player victim : kicked) {
World world = new World(victim);
world.addPlayer(victim);
victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getSceneId(), victim.getPosition()));
}
}
}
public void registerScene(Scene scene) {
this.getScenes().put(scene.getId(), scene);
}
public void deregisterScene(Scene scene) {
this.getScenes().remove(scene.getId());
}
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) {
return transferPlayerToScene(player, sceneId, null, pos);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData data) {
return transferPlayerToScene(player, sceneId, data, null);
}
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData dungeonData, Position pos) {
if (GameData.getSceneDataMap().get(sceneId) == null) {
return false;
}
Scene oldScene = null;
if (player.getScene() != null) {
oldScene = player.getScene();
// Dont deregister scenes if the player is going to tp back into them
if (oldScene.getId() == sceneId) {
oldScene.setDontDestroyWhenEmpty(true);
}
oldScene.removePlayer(player);
}
Scene newScene = this.getSceneById(sceneId);
newScene.setDungeonData(dungeonData);
newScene.addPlayer(player);
// Dungeon
SceneConfig config = newScene.getScriptManager().getConfig();
if (pos == null && config != null) {
if (config.born_pos != null) {
pos = newScene.getScriptManager().getConfig().born_pos;
}
if (config.born_rot != null) {
player.getRotation().set(config.born_rot);
}
}
// Set player position
if (pos == null) {
pos = player.getPosition();
}
player.getPosition().set(pos);
if (oldScene != null) {
newScene.setPrevScene(oldScene.getId());
oldScene.setDontDestroyWhenEmpty(false);
}
// Get enter types
EnterType enterType = EnterType.ENTER_TYPE_JUMP;
EnterReason enterReason = EnterReason.TransPoint;
if (dungeonData != null) {
enterType = EnterType.ENTER_TYPE_DUNGEON;
enterReason = EnterReason.DungeonEnter;
} else if (oldScene == newScene) {
enterType = EnterType.ENTER_TYPE_GOTO;
} else if (newScene.getSceneType() == SceneType.SCENE_HOME_WORLD) {
// Home
enterReason = EnterReason.EnterHome;
enterType = EnterType.ENTER_TYPE_SELF_HOME;
}
// Teleport packet
player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, pos));
return true;
}
private void updatePlayerInfos(Player paramPlayer) {
for (Player player : getPlayers()) {
// Dont send packets if player is loading in and filter out joining player
if (!player.hasSentAvatarDataNotify() || player.getSceneLoadState().getValue() < SceneLoadState.INIT.getValue() || player == paramPlayer) {
continue;
}
// Update team of all players since max players has been changed - Probably not the best way to do it
if (this.isMultiplayer()) {
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getMpTeam(), player.getTeamManager().getMaxTeamSize());
player.getTeamManager().updateTeamEntities(null);
}
// World player info packets
player.getSession().send(new PacketWorldPlayerInfoNotify(this));
player.getSession().send(new PacketScenePlayerInfoNotify(this));
player.getSession().send(new PacketWorldPlayerRTTNotify(this));
// Team packets
player.getSession().send(new PacketSyncTeamEntityNotify(player));
player.getSession().send(new PacketSyncScenePlayTeamEntityNotify(player));
}
}
public void broadcastPacket(BasePacket packet) {
// Send to all players - might have to check if player has been sent data packets
for (Player player : this.getPlayers()) {
player.getSession().send(packet);
}
}
public void onTick() {
for (Scene scene : this.getScenes().values()) {
scene.onTick();
}
}
public void close() {
}
@Override
public Iterator<Player> iterator() {
return getPlayers().iterator();
}
}

View File

@ -30,7 +30,7 @@ public class WorldDataSystem extends BaseGameSystem {
private final Map<String, ChestInteractHandler> chestInteractHandlerMap; // chestType-Handler
private final Map<String, SceneGroup> sceneInvestigationGroupMap; // <sceneId_groupId, Group>
public WorldDataSystem(GameServer server){
public WorldDataSystem(GameServer server) {
super(server);
this.chestInteractHandlerMap = new HashMap<>();
this.sceneInvestigationGroupMap = new ConcurrentHashMap<>();
@ -38,15 +38,15 @@ public class WorldDataSystem extends BaseGameSystem {
loadChestConfig();
}
public synchronized void loadChestConfig(){
public synchronized void loadChestConfig() {
// set the special chest first
chestInteractHandlerMap.put("SceneObj_Chest_Flora", new BossChestInteractHandler());
try(Reader reader = DataLoader.loadReader("ChestReward.json")) {
try (Reader reader = DataLoader.loadReader("ChestReward.json")) {
List<ChestReward> chestReward = Grasscutter.getGsonFactory().fromJson(
reader,
reader,
TypeToken.getParameterized(List.class, ChestReward.class).getType());
chestReward.forEach(reward ->
reward.getObjNames().forEach(
name -> chestInteractHandlerMap.putIfAbsent(name, new NormalChestInteractHandler(reward))));
@ -60,21 +60,21 @@ public class WorldDataSystem extends BaseGameSystem {
return chestInteractHandlerMap;
}
public RewardPreviewData getRewardByBossId(int monsterId){
public RewardPreviewData getRewardByBossId(int monsterId) {
var investigationMonsterData = GameData.getInvestigationMonsterDataMap().values().parallelStream()
.filter(imd -> imd.getMonsterIdList() != null && !imd.getMonsterIdList().isEmpty())
.filter(imd -> imd.getMonsterIdList().get(0) == monsterId)
.findFirst();
if(investigationMonsterData.isEmpty()){
if (investigationMonsterData.isEmpty()) {
return null;
}
return GameData.getRewardPreviewDataMap().get(investigationMonsterData.get().getRewardPreviewId());
}
private SceneGroup getInvestigationGroup(int sceneId, int groupId){
private SceneGroup getInvestigationGroup(int sceneId, int groupId) {
var key = sceneId + "_" + groupId;
if(!sceneInvestigationGroupMap.containsKey(key)){
if (!sceneInvestigationGroupMap.containsKey(key)) {
var group = SceneGroup.of(groupId).load(sceneId);
sceneInvestigationGroupMap.putIfAbsent(key, group);
return group;
@ -82,7 +82,7 @@ public class WorldDataSystem extends BaseGameSystem {
return sceneInvestigationGroupMap.get(key);
}
public int getMonsterLevel(SceneMonster monster, World world){
public int getMonsterLevel(SceneMonster monster, World world) {
// Calculate level
int level = monster.level;
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(world.getWorldLevel());
@ -98,14 +98,14 @@ public class WorldDataSystem extends BaseGameSystem {
var sceneId = imd.getCityData().getSceneId();
var group = getInvestigationGroup(sceneId, groupId);
if(group == null || group.monsters == null){
if (group == null || group.monsters == null) {
return null;
}
var monster = group.monsters.values().stream()
.filter(x -> x.monster_id == monsterId)
.findFirst();
if(monster.isEmpty()){
if (monster.isEmpty()) {
return null;
}
@ -122,9 +122,9 @@ public class WorldDataSystem extends BaseGameSystem {
.setRefreshInterval(Integer.MAX_VALUE)
.setPos(monster.get().pos.toProto());
if("Boss".equals(imd.getMonsterCategory())){
if ("Boss".equals(imd.getMonsterCategory())) {
var bossChest = group.searchBossChestInGroup();
if(bossChest.isPresent()){
if (bossChest.isPresent()) {
builder.setResin(bossChest.get().resin);
builder.setBossChestNum(bossChest.get().take_num);
}
@ -132,9 +132,9 @@ public class WorldDataSystem extends BaseGameSystem {
return builder.build();
}
public List<InvestigationMonsterOuterClass.InvestigationMonster> getInvestigationMonstersByCityId(Player player, int cityId){
public List<InvestigationMonsterOuterClass.InvestigationMonster> getInvestigationMonstersByCityId(Player player, int cityId) {
var cityData = GameData.getCityDataMap().get(cityId);
if(cityData == null){
if (cityData == null) {
Grasscutter.getLogger().warn("City not exist {}", cityId);
return List.of();
}

View File

@ -8,134 +8,134 @@ import emu.grasscutter.net.proto.PacketHeadOuterClass.PacketHead;
import emu.grasscutter.utils.Crypto;
public class BasePacket {
private static final int const1 = 17767; // 0x4567
private static final int const2 = -30293; // 0x89ab
private int opcode;
private boolean shouldBuildHeader = false;
private static final int const1 = 17767; // 0x4567
private static final int const2 = -30293; // 0x89ab
private byte[] header;
private byte[] data;
// Encryption
private boolean useDispatchKey;
public boolean shouldEncrypt = true;
public BasePacket(int opcode) {
this.opcode = opcode;
}
public BasePacket(int opcode, int clientSequence) {
this.opcode = opcode;
this.buildHeader(clientSequence);
}
public BasePacket(int opcode, boolean buildHeader) {
this.opcode = opcode;
this.shouldBuildHeader = buildHeader;
}
private int opcode;
private boolean shouldBuildHeader = false;
public int getOpcode() {
return opcode;
}
private byte[] header;
private byte[] data;
public void setOpcode(int opcode) {
this.opcode = opcode;
}
// Encryption
private boolean useDispatchKey;
public boolean shouldEncrypt = true;
public boolean useDispatchKey() {
return useDispatchKey;
}
public BasePacket(int opcode) {
this.opcode = opcode;
}
public void setUseDispatchKey(boolean useDispatchKey) {
this.useDispatchKey = useDispatchKey;
}
public BasePacket(int opcode, int clientSequence) {
this.opcode = opcode;
this.buildHeader(clientSequence);
}
public byte[] getHeader() {
return header;
}
public BasePacket(int opcode, boolean buildHeader) {
this.opcode = opcode;
this.shouldBuildHeader = buildHeader;
}
public void setHeader(byte[] header) {
this.header = header;
}
public int getOpcode() {
return opcode;
}
public boolean shouldBuildHeader() {
return shouldBuildHeader;
}
public void setOpcode(int opcode) {
this.opcode = opcode;
}
public byte[] getData() {
return data;
}
public boolean useDispatchKey() {
return useDispatchKey;
}
public void setData(byte[] data) {
this.data = data;
}
public void setData(GeneratedMessageV3 proto) {
this.data = proto.toByteArray();
}
@SuppressWarnings("rawtypes")
public void setData(GeneratedMessageV3.Builder proto) {
this.data = proto.build().toByteArray();
}
public BasePacket buildHeader(int clientSequence) {
if (this.getHeader() != null && clientSequence == 0) {
return this;
}
setHeader(PacketHead.newBuilder().setClientSequenceId(clientSequence).setSentMs(System.currentTimeMillis()).build().toByteArray());
return this;
}
public byte[] build() {
if (getHeader() == null) {
this.header = new byte[0];
}
if (getData() == null) {
this.data = new byte[0];
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(2 + 2 + 2 + 4 + getHeader().length + getData().length + 2);
this.writeUint16(baos, const1);
this.writeUint16(baos, opcode);
this.writeUint16(baos, header.length);
this.writeUint32(baos, data.length);
this.writeBytes(baos, header);
this.writeBytes(baos, data);
this.writeUint16(baos, const2);
byte[] packet = baos.toByteArray();
if (this.shouldEncrypt) {
Crypto.xor(packet, this.useDispatchKey() ? Crypto.DISPATCH_KEY : Crypto.ENCRYPT_KEY);
}
public void setUseDispatchKey(boolean useDispatchKey) {
this.useDispatchKey = useDispatchKey;
}
return packet;
}
public void writeUint16(ByteArrayOutputStream baos, int i) {
// Unsigned short
public byte[] getHeader() {
return header;
}
public void setHeader(byte[] header) {
this.header = header;
}
public boolean shouldBuildHeader() {
return shouldBuildHeader;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public void setData(GeneratedMessageV3 proto) {
this.data = proto.toByteArray();
}
@SuppressWarnings("rawtypes")
public void setData(GeneratedMessageV3.Builder proto) {
this.data = proto.build().toByteArray();
}
public BasePacket buildHeader(int clientSequence) {
if (this.getHeader() != null && clientSequence == 0) {
return this;
}
setHeader(PacketHead.newBuilder().setClientSequenceId(clientSequence).setSentMs(System.currentTimeMillis()).build().toByteArray());
return this;
}
public byte[] build() {
if (getHeader() == null) {
this.header = new byte[0];
}
if (getData() == null) {
this.data = new byte[0];
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(2 + 2 + 2 + 4 + getHeader().length + getData().length + 2);
this.writeUint16(baos, const1);
this.writeUint16(baos, opcode);
this.writeUint16(baos, header.length);
this.writeUint32(baos, data.length);
this.writeBytes(baos, header);
this.writeBytes(baos, data);
this.writeUint16(baos, const2);
byte[] packet = baos.toByteArray();
if (this.shouldEncrypt) {
Crypto.xor(packet, this.useDispatchKey() ? Crypto.DISPATCH_KEY : Crypto.ENCRYPT_KEY);
}
return packet;
}
public void writeUint16(ByteArrayOutputStream baos, int i) {
// Unsigned short
baos.write((byte) ((i >>> 8) & 0xFF));
baos.write((byte) (i & 0xFF));
}
public void writeUint32(ByteArrayOutputStream baos, int i) {
// Unsigned int (long)
public void writeUint32(ByteArrayOutputStream baos, int i) {
// Unsigned int (long)
baos.write((byte) ((i >>> 24) & 0xFF));
baos.write((byte) ((i >>> 16) & 0xFF));
baos.write((byte) ((i >>> 8) & 0xFF));
baos.write((byte) (i & 0xFF));
}
public void writeBytes(ByteArrayOutputStream baos, byte[] bytes) {
try {
baos.write(bytes);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
public void writeBytes(ByteArrayOutputStream baos, byte[] bytes) {
try {
baos.write(bytes);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@ -1854,4 +1854,4 @@ public class PacketOpcodes {
public static final int WorldRoutineTypeCloseNotify = 3502;
public static final int WorldRoutineTypeRefreshNotify = 3525;
}
}

View File

@ -22,7 +22,7 @@ public class PacketOpcodesUtils {
PacketOpcodes.WindSeedClientNotify,
PacketOpcodes.PlayerLuaShellNotify
);
public static final Set<Integer> LOOP_PACKETS = Set.of(
PacketOpcodes.PingReq,
PacketOpcodes.PingRsp,
@ -30,39 +30,39 @@ public class PacketOpcodesUtils {
PacketOpcodes.UnionCmdNotify,
PacketOpcodes.QueryPathReq
);
static {
opcodeMap = new Int2ObjectOpenHashMap<String>();
Field[] fields = PacketOpcodes.class.getFields();
static {
opcodeMap = new Int2ObjectOpenHashMap<String>();
for (Field f : fields) {
if(f.getType().equals(int.class)) {
try {
opcodeMap.put(f.getInt(null), f.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Field[] fields = PacketOpcodes.class.getFields();
public static String getOpcodeName(int opcode) {
if (opcode <= 0) return "UNKNOWN";
return opcodeMap.getOrDefault(opcode, "UNKNOWN");
}
for (Field f : fields) {
if (f.getType().equals(int.class)) {
try {
opcodeMap.put(f.getInt(null), f.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void dumpPacketIds() {
try (FileWriter writer = new FileWriter("./PacketIds_" + GameConstants.VERSION + ".json")) {
// Create sorted tree map
Map<Integer, String> packetIds = opcodeMap.int2ObjectEntrySet().stream()
.filter(e -> e.getIntKey() > 0)
.collect(Collectors.toMap(Int2ObjectMap.Entry::getIntKey, Int2ObjectMap.Entry::getValue, (k, v) -> v, TreeMap::new));
// Write to file
writer.write(Grasscutter.getGsonFactory().toJson(packetIds));
Grasscutter.getLogger().info("Dumped packet ids.");
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getOpcodeName(int opcode) {
if (opcode <= 0) return "UNKNOWN";
return opcodeMap.getOrDefault(opcode, "UNKNOWN");
}
public static void dumpPacketIds() {
try (FileWriter writer = new FileWriter("./PacketIds_" + GameConstants.VERSION + ".json")) {
// Create sorted tree map
Map<Integer, String> packetIds = opcodeMap.int2ObjectEntrySet().stream()
.filter(e -> e.getIntKey() > 0)
.collect(Collectors.toMap(Int2ObjectMap.Entry::getIntKey, Int2ObjectMap.Entry::getValue, (k, v) -> v, TreeMap::new));
// Write to file
writer.write(Grasscutter.getGsonFactory().toJson(packetIds));
Grasscutter.getLogger().info("Dumped packet ids.");
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -17,7 +17,7 @@ import java.net.URLClassLoader;
*/
public abstract class Plugin {
private final ServerHook server = ServerHook.getInstance();
private PluginIdentifier identifier;
private URLClassLoader classLoader;
private File dataFolder;
@ -25,22 +25,22 @@ public abstract class Plugin {
/**
* This method is reflected into.
*
*
* Set plugin variables.
* @param identifier The plugin's identifier.
*/
private void initializePlugin(PluginIdentifier identifier, URLClassLoader classLoader) {
if(this.identifier != null) {
if (this.identifier != null) {
Grasscutter.getLogger().warn(this.identifier.name + " had a reinitialization attempt.");
return;
}
this.identifier = identifier;
this.classLoader = classLoader;
this.dataFolder = new File(PLUGIN(), identifier.name);
this.logger = LoggerFactory.getLogger(identifier.name);
if(!this.dataFolder.exists() && !this.dataFolder.mkdirs()) {
if (!this.dataFolder.exists() && !this.dataFolder.mkdirs()) {
Grasscutter.getLogger().warn("Failed to create plugin data folder for " + this.identifier.name);
return;
}
@ -50,7 +50,7 @@ public abstract class Plugin {
* The plugin's identifier instance.
* @return An instance of {@link PluginIdentifier}.
*/
public final PluginIdentifier getIdentifier(){
public final PluginIdentifier getIdentifier() {
return this.identifier;
}
@ -115,7 +115,7 @@ public abstract class Plugin {
public final Logger getLogger() {
return this.logger;
}
/* Called when the plugin is first loaded. */
public void onLoad() { }
/* Called after (most of) the server enables. */

View File

@ -72,7 +72,7 @@ public final class PluginManager {
List<PluginData> dependencies = new ArrayList<>();
// Initialize all plugins.
for(var plugin : plugins) {
for (var plugin : plugins) {
try {
URL url = plugin.toURI().toURL();
try (URLClassLoader loader = new URLClassLoader(new URL[]{url})) {
@ -109,7 +109,7 @@ public final class PluginManager {
fileReader.close();
// Check if the plugin has alternate dependencies.
if(pluginConfig.loadAfter != null && pluginConfig.loadAfter.length > 0) {
if (pluginConfig.loadAfter != null && pluginConfig.loadAfter.length > 0) {
// Add the plugin to a "load later" list.
dependencies.add(new PluginData(
pluginInstance, PluginIdentifier.fromPluginConfig(pluginConfig),
@ -131,9 +131,9 @@ public final class PluginManager {
// Load plugins with dependencies.
int depth = 0; final int maxDepth = 30;
while(!dependencies.isEmpty()) {
while (!dependencies.isEmpty()) {
// Check if the depth is too high.
if(depth >= maxDepth) {
if (depth >= maxDepth) {
Grasscutter.getLogger().error("Failed to load plugins with dependencies.");
break;
}
@ -143,7 +143,7 @@ public final class PluginManager {
var pluginData = dependencies.get(0);
// Check if the plugin's dependencies are loaded.
if(!this.plugins.keySet().containsAll(List.of(pluginData.getDependencies()))) {
if (!this.plugins.keySet().containsAll(List.of(pluginData.getDependencies()))) {
depth++; // Increase depth counter.
continue; // Continue to next plugin.
}

View File

@ -19,13 +19,13 @@ public final class PlayerHook {
private final Player player;
/**
* Hooks into the player.
* Hooks into the player.
* @param player The player to hook into.
*/
public PlayerHook(Player player) {
this.player = player;
}
/**
* Kicks a player from the server.
* TODO: Refactor to kick using a packet.
@ -82,7 +82,7 @@ public final class PlayerHook {
*/
public void teleport(Position position) {
this.player.getPosition().set(position);
this.player.sendPacket(new PacketPlayerEnterSceneNotify(this.player,
this.player.sendPacket(new PacketPlayerEnterSceneNotify(this.player,
EnterType.ENTER_TYPE_JUMP, EnterReason.TransPoint,
this.player.getSceneId(), position
));
@ -111,4 +111,4 @@ public final class PlayerHook {
public Avatar getCurrentAvatar() {
return this.getCurrentAvatarEntity().getAvatar();
}
}
}

View File

@ -22,61 +22,61 @@ import java.util.stream.Collectors;
@ToString
@Setter
public class SceneBlock {
public int id;
public Position max;
public Position min;
public int id;
public Position max;
public Position min;
public int sceneId;
public Map<Integer,SceneGroup> groups;
public RTree<SceneGroup, Geometry> sceneGroupIndex;
public int sceneId;
public Map<Integer,SceneGroup> groups;
public RTree<SceneGroup, Geometry> sceneGroupIndex;
private transient boolean loaded; // Not an actual variable in the scripts either
private transient boolean loaded; // Not an actual variable in the scripts either
public boolean isLoaded() {
return this.loaded;
}
public boolean isLoaded() {
return this.loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public boolean contains(Position pos) {
return pos.getX() <= this.max.getX() && pos.getX() >= this.min.getX() &&
pos.getZ() <= this.max.getZ() && pos.getZ() >= this.min.getZ();
}
public boolean contains(Position pos) {
return pos.getX() <= this.max.getX() && pos.getX() >= this.min.getX() &&
pos.getZ() <= this.max.getZ() && pos.getZ() >= this.min.getZ();
}
public SceneBlock load(int sceneId, Bindings bindings){
if(this.loaded){
return this;
}
this.sceneId = sceneId;
public SceneBlock load(int sceneId, Bindings bindings) {
if (this.loaded) {
return this;
}
this.sceneId = sceneId;
this.setLoaded(true);
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_block" + this.id + "." + ScriptLoader.getScriptType()));
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_block" + this.id + "." + ScriptLoader.getScriptType()));
if (cs == null) {
return null;
}
if (cs == null) {
return null;
}
// Eval script
try {
cs.eval(bindings);
// Eval script
try {
cs.eval(bindings);
// Set groups
// Set groups
this.groups = ScriptLoader.getSerializer().toList(SceneGroup.class, bindings.get("groups")).stream()
.collect(Collectors.toMap(x -> x.id, y -> y));
.collect(Collectors.toMap(x -> x.id, y -> y));
this.groups.values().forEach(g -> g.block_id = this.id);
this.sceneGroupIndex = SceneIndexManager.buildIndex(3, this.groups.values(), g -> g.pos.toPoint());
} catch (ScriptException exception) {
this.sceneGroupIndex = SceneIndexManager.buildIndex(3, this.groups.values(), g -> g.pos.toPoint());
} catch (ScriptException exception) {
Grasscutter.getLogger().error("An error occurred while loading block " + this.id + " in scene " + sceneId, exception);
}
Grasscutter.getLogger().debug("Successfully loaded block {} in scene {}.", this.id, sceneId);
return this;
}
}
Grasscutter.getLogger().debug("Successfully loaded block {} in scene {}.", this.id, sceneId);
return this;
}
public Rectangle toRectangle() {
return Rectangle.create(this.min.toXZDoubleArray(), this.max.toXZDoubleArray());
}
}
public Rectangle toRectangle() {
return Rectangle.create(this.min.toXZDoubleArray(), this.max.toXZDoubleArray());
}
}

View File

@ -21,93 +21,93 @@ import java.util.stream.Collectors;
@ToString
@Setter
public class SceneGroup {
public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference
public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference
public int id;
public int refresh_id;
public Position pos;
public int id;
public int refresh_id;
public Position pos;
public Map<Integer,SceneMonster> monsters; // <ConfigId, Monster>
public Map<Integer, SceneGadget> gadgets; // <ConfigId, Gadgets>
public Map<String, SceneTrigger> triggers;
public Map<Integer,SceneMonster> monsters; // <ConfigId, Monster>
public Map<Integer, SceneGadget> gadgets; // <ConfigId, Gadgets>
public Map<String, SceneTrigger> triggers;
public Map<Integer, SceneRegion> regions;
public List<SceneSuite> suites;
public List<SceneVar> variables;
public List<SceneSuite> suites;
public List<SceneVar> variables;
public SceneBusiness business;
public SceneGarbage garbages;
public SceneInitConfig init_config;
public SceneBusiness business;
public SceneGarbage garbages;
public SceneInitConfig init_config;
private transient boolean loaded; // Not an actual variable in the scripts either
private transient CompiledScript script;
private transient Bindings bindings;
public static SceneGroup of(int groupId) {
var group = new SceneGroup();
group.id = groupId;
return group;
}
private transient boolean loaded; // Not an actual variable in the scripts either
private transient CompiledScript script;
private transient Bindings bindings;
public static SceneGroup of(int groupId) {
var group = new SceneGroup();
group.id = groupId;
return group;
}
public boolean isLoaded() {
return this.loaded;
}
public boolean isLoaded() {
return this.loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public int getBusinessType() {
return this.business == null ? 0 : this.business.type;
}
public int getBusinessType() {
return this.business == null ? 0 : this.business.type;
}
public List<SceneGadget> getGarbageGadgets() {
return this.garbages == null ? null : this.garbages.gadgets;
}
public List<SceneGadget> getGarbageGadgets() {
return this.garbages == null ? null : this.garbages.gadgets;
}
public CompiledScript getScript() {
return this.script;
}
public CompiledScript getScript() {
return this.script;
}
public SceneSuite getSuiteByIndex(int index) {
return this.suites.get(index - 1);
}
public SceneSuite getSuiteByIndex(int index) {
return this.suites.get(index - 1);
}
public Bindings getBindings() {
return this.bindings;
}
public Bindings getBindings() {
return this.bindings;
}
public synchronized SceneGroup load(int sceneId){
if(this.loaded){
return this;
}
// Set flag here so if there is no script, we don't call this function over and over again.
public synchronized SceneGroup load(int sceneId) {
if (this.loaded) {
return this;
}
// Set flag here so if there is no script, we don't call this function over and over again.
this.setLoaded(true);
this.bindings = ScriptLoader.getEngine().createBindings();
this.bindings = ScriptLoader.getEngine().createBindings();
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_group" + this.id + "." + ScriptLoader.getScriptType()));
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_group" + this.id + "." + ScriptLoader.getScriptType()));
if (cs == null) {
return this;
}
if (cs == null) {
return this;
}
this.script = cs;
this.script = cs;
// Eval script
try {
cs.eval(this.bindings);
// Eval script
try {
cs.eval(this.bindings);
// Set
// Set
this.monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, this.bindings.get("monsters")).stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y));
.collect(Collectors.toMap(x -> x.config_id, y -> y));
this.monsters.values().forEach(m -> m.group = this);
this.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, this.bindings.get("gadgets")).stream()
.collect(Collectors.toMap(x -> x.config_id, y -> y));
.collect(Collectors.toMap(x -> x.config_id, y -> y));
this.gadgets.values().forEach(m -> m.group = this);
this.triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, this.bindings.get("triggers")).stream()
.collect(Collectors.toMap(x -> x.name, y -> y));
.collect(Collectors.toMap(x -> x.name, y -> y));
this.triggers.values().forEach(t -> t.currentGroup = this);
this.suites = ScriptLoader.getSerializer().toList(SceneSuite.class, this.bindings.get("suites"));
@ -117,35 +117,35 @@ public class SceneGroup {
this.init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, this.bindings.get("init_config"));
// Garbages // TODO: fix properly later
Object garbagesValue = this.bindings.get("garbages");
if (garbagesValue instanceof LuaValue garbagesTable) {
// Garbages // TODO: fix properly later
Object garbagesValue = this.bindings.get("garbages");
if (garbagesValue instanceof LuaValue garbagesTable) {
this.garbages = new SceneGarbage();
if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) {
if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) {
this.garbages.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, garbagesTable.checktable().get("gadgets").checktable());
this.garbages.gadgets.forEach(m -> m.group = this);
}
}
}
}
// Add variables to suite
this.variables = ScriptLoader.getSerializer().toList(SceneVar.class, this.bindings.get("variables"));
// Add variables to suite
this.variables = ScriptLoader.getSerializer().toList(SceneVar.class, this.bindings.get("variables"));
// Add monsters and gadgets to suite
// Add monsters and gadgets to suite
this.suites.forEach(i -> i.init(this));
} catch (ScriptException e) {
Grasscutter.getLogger().error("An error occurred while loading group " + this.id + " in scene " + sceneId + ".", e);
}
} catch (ScriptException e) {
Grasscutter.getLogger().error("An error occurred while loading group " + this.id + " in scene " + sceneId + ".", e);
}
Grasscutter.getLogger().debug("Successfully loaded group {} in scene {}.", this.id, sceneId);
return this;
}
Grasscutter.getLogger().debug("Successfully loaded group {} in scene {}.", this.id, sceneId);
return this;
}
public Optional<SceneBossChest> searchBossChestInGroup() {
return this.gadgets.values().stream()
.filter(g -> g.boss_chest != null && g.boss_chest.monster_config_id > 0)
.map(g -> g.boss_chest)
.findFirst();
}
public Optional<SceneBossChest> searchBossChestInGroup() {
return this.gadgets.values().stream()
.filter(g -> g.boss_chest != null && g.boss_chest.monster_config_id > 0)
.map(g -> g.boss_chest)
.findFirst();
}
}

View File

@ -33,7 +33,7 @@ public class SceneMeta {
return new SceneMeta().load(sceneId);
}
public SceneMeta load(int sceneId){
public SceneMeta load(int sceneId) {
// Get compiled script if cached
CompiledScript cs = ScriptLoader.getScriptByPath(
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "." + ScriptLoader.getScriptType()));

View File

@ -2,11 +2,11 @@ package emu.grasscutter.server.game;
public abstract class BaseGameSystem {
protected final GameServer server;
public BaseGameSystem(GameServer server) {
this.server = server;
}
public GameServer getServer() {
return this.server;
}

View File

@ -51,97 +51,97 @@ import static emu.grasscutter.utils.Language.translate;
public final class GameServer extends KcpServer {
// Game server base
private final InetSocketAddress address;
private final GameServerPacketHandler packetHandler;
private final GameServerPacketHandler packetHandler;
private final Map<Integer, Player> players;
private final Set<World> worlds;
// Server systems
private final InventorySystem inventorySystem;
private final GachaSystem gachaSystem;
private final ShopSystem shopSystem;
private final MultiplayerSystem multiplayerSystem;
private final DungeonSystem dungeonSystem;
private final ExpeditionSystem expeditionSystem;
private final DropSystem dropSystem;
private final WorldDataSystem worldDataSystem;
private final BattlePassSystem battlePassSystem;
private final CombineManger combineSystem;
private final TowerSystem towerSystem;
private final AnnouncementSystem announcementSystem;
private final QuestSystem questSystem;
// Extra
private final ServerTaskScheduler scheduler;
private final InventorySystem inventorySystem;
private final GachaSystem gachaSystem;
private final ShopSystem shopSystem;
private final MultiplayerSystem multiplayerSystem;
private final DungeonSystem dungeonSystem;
private final ExpeditionSystem expeditionSystem;
private final DropSystem dropSystem;
private final WorldDataSystem worldDataSystem;
private final BattlePassSystem battlePassSystem;
private final CombineManger combineSystem;
private final TowerSystem towerSystem;
private final AnnouncementSystem announcementSystem;
private final QuestSystem questSystem;
// Extra
private final ServerTaskScheduler scheduler;
private final CommandMap commandMap;
private final TaskMap taskMap;
private ChatManagerHandler chatManager;
public GameServer() {
this(getAdapterInetSocketAddress());
}
private ChatManagerHandler chatManager;
public GameServer(InetSocketAddress address) {
ChannelConfig channelConfig = new ChannelConfig();
channelConfig.nodelay(true, 40, 2, true);
channelConfig.setMtu(1400);
channelConfig.setSndwnd(256);
channelConfig.setRcvwnd(256);
channelConfig.setTimeoutMillis(30 * 1000);//30s
channelConfig.setUseConvChannel(true);
channelConfig.setAckNoDelay(false);
public GameServer() {
this(getAdapterInetSocketAddress());
}
this.init(GameSessionManager.getListener(),channelConfig,address);
public GameServer(InetSocketAddress address) {
ChannelConfig channelConfig = new ChannelConfig();
channelConfig.nodelay(true, 40, 2, true);
channelConfig.setMtu(1400);
channelConfig.setSndwnd(256);
channelConfig.setRcvwnd(256);
channelConfig.setTimeoutMillis(30 * 1000);//30s
channelConfig.setUseConvChannel(true);
channelConfig.setAckNoDelay(false);
DungeonChallenge.initialize();
EnergyManager.initialize();
StaminaManager.initialize();
CookingManager.initialize();
CombineManger.initialize();
this.init(GameSessionManager.getListener(),channelConfig,address);
// Game Server base
this.address = address;
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
this.players = new ConcurrentHashMap<>();
this.worlds = Collections.synchronizedSet(new HashSet<>());
// Extra
this.scheduler = new ServerTaskScheduler();
this.commandMap = new CommandMap(true);
DungeonChallenge.initialize();
EnergyManager.initialize();
StaminaManager.initialize();
CookingManager.initialize();
CombineManger.initialize();
// Game Server base
this.address = address;
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
this.players = new ConcurrentHashMap<>();
this.worlds = Collections.synchronizedSet(new HashSet<>());
// Extra
this.scheduler = new ServerTaskScheduler();
this.commandMap = new CommandMap(true);
this.taskMap = new TaskMap(true);
// Create game systems
this.inventorySystem = new InventorySystem(this);
this.gachaSystem = new GachaSystem(this);
this.shopSystem = new ShopSystem(this);
this.multiplayerSystem = new MultiplayerSystem(this);
this.dungeonSystem = new DungeonSystem(this);
this.dropSystem = new DropSystem(this);
this.expeditionSystem = new ExpeditionSystem(this);
this.combineSystem = new CombineManger(this);
this.towerSystem = new TowerSystem(this);
this.worldDataSystem = new WorldDataSystem(this);
this.battlePassSystem = new BattlePassSystem(this);
this.announcementSystem = new AnnouncementSystem(this);
this.questSystem = new QuestSystem(this);
// Chata manager
this.chatManager = new ChatManager(this);
// Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
}
// Create game systems
this.inventorySystem = new InventorySystem(this);
this.gachaSystem = new GachaSystem(this);
this.shopSystem = new ShopSystem(this);
this.multiplayerSystem = new MultiplayerSystem(this);
this.dungeonSystem = new DungeonSystem(this);
this.dropSystem = new DropSystem(this);
this.expeditionSystem = new ExpeditionSystem(this);
this.combineSystem = new CombineManger(this);
this.towerSystem = new TowerSystem(this);
this.worldDataSystem = new WorldDataSystem(this);
this.battlePassSystem = new BattlePassSystem(this);
this.announcementSystem = new AnnouncementSystem(this);
this.questSystem = new QuestSystem(this);
// Chata manager
this.chatManager = new ChatManager(this);
// Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
}
@Deprecated
public ChatManagerHandler getChatManager() {
return chatManager;
}
@Deprecated
public void setChatManager(ChatManagerHandler chatManager) {
this.chatManager = chatManager;
}
public ChatManagerHandler getChatSystem() {
return chatManager;
}
@ -150,71 +150,71 @@ public final class GameServer extends KcpServer {
this.chatManager = chatManager;
}
private static InetSocketAddress getAdapterInetSocketAddress(){
InetSocketAddress inetSocketAddress;
if(GAME_INFO.bindAddress.equals("")){
inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort);
}else{
inetSocketAddress=new InetSocketAddress(
GAME_INFO.bindAddress,
GAME_INFO.bindPort
);
}
return inetSocketAddress;
}
private static InetSocketAddress getAdapterInetSocketAddress() {
InetSocketAddress inetSocketAddress;
if (GAME_INFO.bindAddress.equals("")) {
inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort);
}else {
inetSocketAddress=new InetSocketAddress(
GAME_INFO.bindAddress,
GAME_INFO.bindPort
);
}
return inetSocketAddress;
}
public void registerPlayer(Player player) {
getPlayers().put(player.getUid(), player);
}
public void registerPlayer(Player player) {
getPlayers().put(player.getUid(), player);
}
public Player getPlayerByUid(int id) {
return this.getPlayerByUid(id, false);
}
public Player getPlayerByUid(int id) {
return this.getPlayerByUid(id, false);
}
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
// Console check
if (id == GameConstants.SERVER_CONSOLE_UID) {
return null;
}
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
// Console check
if (id == GameConstants.SERVER_CONSOLE_UID) {
return null;
}
// Get from online players
Player player = this.getPlayers().get(id);
// Get from online players
Player player = this.getPlayers().get(id);
if (!allowOfflinePlayers) {
return player;
}
if (!allowOfflinePlayers) {
return player;
}
// Check database if character isnt here
if (player == null) {
player = DatabaseHelper.getPlayerByUid(id);
}
// Check database if character isnt here
if (player == null) {
player = DatabaseHelper.getPlayerByUid(id);
}
return player;
}
return player;
}
public Player getPlayerByAccountId(String accountId) {
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getId().equals(accountId)).findFirst();
return playerOpt.orElse(null);
}
public Player getPlayerByAccountId(String accountId) {
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getId().equals(accountId)).findFirst();
return playerOpt.orElse(null);
}
public SocialDetail.Builder getSocialDetailByUid(int id) {
// Get from online players
Player player = this.getPlayerByUid(id, true);
public SocialDetail.Builder getSocialDetailByUid(int id) {
// Get from online players
Player player = this.getPlayerByUid(id, true);
if (player == null) {
return null;
}
if (player == null) {
return null;
}
return player.getSocialDetail();
}
return player.getSocialDetail();
}
public Account getAccountByName(String username) {
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst();
if (playerOpt.isPresent()) {
return playerOpt.get().getAccount();
}
return DatabaseHelper.getAccountByName(username);
}
public Account getAccountByName(String username) {
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst();
if (playerOpt.isPresent()) {
return playerOpt.get().getAccount();
}
return DatabaseHelper.getAccountByName(username);
}
public synchronized void onTick() {
var tickStart = Instant.now();
@ -244,43 +244,43 @@ public final class GameServer extends KcpServer {
event.call();
}
public void registerWorld(World world) {
this.getWorlds().add(world);
}
public void registerWorld(World world) {
this.getWorlds().add(world);
}
public void deregisterWorld(World world) {
// TODO Auto-generated method stub
public void deregisterWorld(World world) {
// TODO Auto-generated method stub
}
}
public void start() {
// Schedule game loop.
Timer gameLoop = new Timer();
gameLoop.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
onTick();
} catch (Exception e) {
Grasscutter.getLogger().error(translate("messages.game.game_update_error"), e);
}
}
}, new Date(), 1000L);
Grasscutter.getLogger().info(translate("messages.status.free_software"));
Grasscutter.getLogger().info(translate("messages.game.port_bind", Integer.toString(address.getPort())));
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
event.call();
}
public void start() {
// Schedule game loop.
Timer gameLoop = new Timer();
gameLoop.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
onTick();
} catch (Exception e) {
Grasscutter.getLogger().error(translate("messages.game.game_update_error"), e);
}
}
}, new Date(), 1000L);
Grasscutter.getLogger().info(translate("messages.status.free_software"));
Grasscutter.getLogger().info(translate("messages.game.port_bind", Integer.toString(address.getPort())));
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
event.call();
}
public void onServerShutdown() {
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
public void onServerShutdown() {
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
// Kick and save all players
List<Player> list = new ArrayList<>(this.getPlayers().size());
list.addAll(this.getPlayers().values());
// Kick and save all players
List<Player> list = new ArrayList<>(this.getPlayers().size());
list.addAll(this.getPlayers().values());
for (Player player : list) {
player.getSession().close();
}
}
for (Player player : list) {
player.getSession().close();
}
}
}

View File

@ -18,84 +18,84 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@SuppressWarnings("unchecked")
public class GameServerPacketHandler {
private final Int2ObjectMap<PacketHandler> handlers;
private final Int2ObjectMap<PacketHandler> handlers;
public GameServerPacketHandler(Class<? extends PacketHandler> handlerClass) {
this.handlers = new Int2ObjectOpenHashMap<>();
public GameServerPacketHandler(Class<? extends PacketHandler> handlerClass) {
this.handlers = new Int2ObjectOpenHashMap<>();
this.registerHandlers(handlerClass);
}
this.registerHandlers(handlerClass);
}
public void registerPacketHandler(Class<? extends PacketHandler> handlerClass) {
try {
Opcodes opcode = handlerClass.getAnnotation(Opcodes.class);
public void registerPacketHandler(Class<? extends PacketHandler> handlerClass) {
try {
Opcodes opcode = handlerClass.getAnnotation(Opcodes.class);
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
return;
}
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
return;
}
PacketHandler packetHandler = handlerClass.getDeclaredConstructor().newInstance();
PacketHandler packetHandler = handlerClass.getDeclaredConstructor().newInstance();
this.handlers.put(opcode.value(), packetHandler);
} catch (Exception e) {
e.printStackTrace();
}
}
this.handlers.put(opcode.value(), packetHandler);
} catch (Exception e) {
e.printStackTrace();
}
}
public void registerHandlers(Class<? extends PacketHandler> handlerClass) {
Reflections reflections = new Reflections("emu.grasscutter.server.packet");
Set<?> handlerClasses = reflections.getSubTypesOf(handlerClass);
public void registerHandlers(Class<? extends PacketHandler> handlerClass) {
Reflections reflections = new Reflections("emu.grasscutter.server.packet");
Set<?> handlerClasses = reflections.getSubTypesOf(handlerClass);
for (Object obj : handlerClasses) {
this.registerPacketHandler((Class<? extends PacketHandler>) obj);
}
for (Object obj : handlerClasses) {
this.registerPacketHandler((Class<? extends PacketHandler>) obj);
}
// Debug
Grasscutter.getLogger().debug("Registered " + this.handlers.size() + " " + handlerClass.getSimpleName() + "s");
}
// Debug
Grasscutter.getLogger().debug("Registered " + this.handlers.size() + " " + handlerClass.getSimpleName() + "s");
}
public void handle(GameSession session, int opcode, byte[] header, byte[] payload) {
PacketHandler handler = this.handlers.get(opcode);
public void handle(GameSession session, int opcode, byte[] header, byte[] payload) {
PacketHandler handler = this.handlers.get(opcode);
if (handler != null) {
try {
// Make sure session is ready for packets
SessionState state = session.getState();
if (handler != null) {
try {
// Make sure session is ready for packets
SessionState state = session.getState();
if (opcode == PacketOpcodes.PingReq) {
// Always continue if packet is ping request
} else if (opcode == PacketOpcodes.GetPlayerTokenReq) {
if (state != SessionState.WAITING_FOR_TOKEN) {
return;
}
} else if (opcode == PacketOpcodes.PlayerLoginReq) {
if (state != SessionState.WAITING_FOR_LOGIN) {
return;
}
} else if (opcode == PacketOpcodes.SetPlayerBornDataReq) {
if (state != SessionState.PICKING_CHARACTER) {
return;
}
} else {
if (state != SessionState.ACTIVE) {
return;
}
}
if (opcode == PacketOpcodes.PingReq) {
// Always continue if packet is ping request
} else if (opcode == PacketOpcodes.GetPlayerTokenReq) {
if (state != SessionState.WAITING_FOR_TOKEN) {
return;
}
} else if (opcode == PacketOpcodes.PlayerLoginReq) {
if (state != SessionState.WAITING_FOR_LOGIN) {
return;
}
} else if (opcode == PacketOpcodes.SetPlayerBornDataReq) {
if (state != SessionState.PICKING_CHARACTER) {
return;
}
} else {
if (state != SessionState.ACTIVE) {
return;
}
}
// Invoke event.
ReceivePacketEvent event = new ReceivePacketEvent(session, opcode, payload); event.call();
if(!event.isCanceled()) // If event is not canceled, continue.
handler.handle(session, header, event.getPacketData());
} catch (Exception ex) {
// TODO Remove this when no more needed
ex.printStackTrace();
}
return; // Packet successfully handled
}
// Invoke event.
ReceivePacketEvent event = new ReceivePacketEvent(session, opcode, payload); event.call();
if (!event.isCanceled()) // If event is not canceled, continue.
handler.handle(session, header, event.getPacketData());
} catch (Exception ex) {
// TODO Remove this when no more needed
ex.printStackTrace();
}
return; // Packet successfully handled
}
// Log unhandled packets
if (GAME_INFO.logPackets == ServerDebugMode.MISSING) {
Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtils.getOpcodeName(opcode));
}
}
// Log unhandled packets
if (GAME_INFO.logPackets == ServerDebugMode.MISSING) {
Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtils.getOpcodeName(opcode));
}
}
}

View File

@ -21,108 +21,108 @@ import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.Language.translate;
public class GameSession implements GameSessionManager.KcpChannel {
private final GameServer server;
private GameSessionManager.KcpTunnel tunnel;
private final GameServer server;
private GameSessionManager.KcpTunnel tunnel;
private Account account;
private Player player;
private Account account;
private Player player;
private boolean useSecretKey;
private SessionState state;
private boolean useSecretKey;
private SessionState state;
private int clientTime;
private long lastPingTime;
private int lastClientSeq = 10;
private int clientTime;
private long lastPingTime;
private int lastClientSeq = 10;
public GameSession(GameServer server) {
this.server = server;
this.state = SessionState.WAITING_FOR_TOKEN;
this.lastPingTime = System.currentTimeMillis();
}
public GameSession(GameServer server) {
this.server = server;
this.state = SessionState.WAITING_FOR_TOKEN;
this.lastPingTime = System.currentTimeMillis();
}
public GameServer getServer() {
return server;
}
public GameServer getServer() {
return server;
}
public InetSocketAddress getAddress() {
try{
return tunnel.getAddress();
}catch (Throwable ignore){
return null;
}
}
public InetSocketAddress getAddress() {
try {
return tunnel.getAddress();
}catch (Throwable ignore) {
return null;
}
}
public boolean useSecretKey() {
return useSecretKey;
}
public boolean useSecretKey() {
return useSecretKey;
}
public Account getAccount() {
return account;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public void setAccount(Account account) {
this.account = account;
}
public String getAccountId() {
return this.getAccount().getId();
}
public String getAccountId() {
return this.getAccount().getId();
}
public Player getPlayer() {
return player;
}
public Player getPlayer() {
return player;
}
public synchronized void setPlayer(Player player) {
this.player = player;
this.player.setSession(this);
this.player.setAccount(this.getAccount());
}
public synchronized void setPlayer(Player player) {
this.player = player;
this.player.setSession(this);
this.player.setAccount(this.getAccount());
}
public SessionState getState() {
return state;
}
public SessionState getState() {
return state;
}
public void setState(SessionState state) {
this.state = state;
}
public void setState(SessionState state) {
this.state = state;
}
public boolean isLoggedIn() {
return this.getPlayer() != null;
}
public boolean isLoggedIn() {
return this.getPlayer() != null;
}
public void setUseSecretKey(boolean useSecretKey) {
this.useSecretKey = useSecretKey;
}
public void setUseSecretKey(boolean useSecretKey) {
this.useSecretKey = useSecretKey;
}
public int getClientTime() {
return this.clientTime;
}
public int getClientTime() {
return this.clientTime;
}
public long getLastPingTime() {
return lastPingTime;
}
public long getLastPingTime() {
return lastPingTime;
}
public void updateLastPingTime(int clientTime) {
this.clientTime = clientTime;
this.lastPingTime = System.currentTimeMillis();
}
public void updateLastPingTime(int clientTime) {
this.clientTime = clientTime;
this.lastPingTime = System.currentTimeMillis();
}
public int getNextClientSequence() {
return ++lastClientSeq;
}
public int getNextClientSequence() {
return ++lastClientSeq;
}
public void replayPacket(int opcode, String name) {
String filePath = PACKET(name);
File p = new File(filePath);
String filePath = PACKET(name);
File p = new File(filePath);
if (!p.exists()) return;
if (!p.exists()) return;
byte[] packet = FileUtils.read(p);
byte[] packet = FileUtils.read(p);
BasePacket basePacket = new BasePacket(opcode);
basePacket.setData(packet);
BasePacket basePacket = new BasePacket(opcode);
basePacket.setData(packet);
send(basePacket);
send(basePacket);
}
public void logPacket( String sendOrRecv, int opcode, byte[] payload) {
@ -130,162 +130,162 @@ public class GameSession implements GameSessionManager.KcpChannel {
System.out.println(Utils.bytesToHex(payload));
}
public void send(BasePacket packet) {
// Test
if (packet.getOpcode() <= 0) {
Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!");
return;
}
// Test
if (packet.getOpcode() <= 0) {
Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!");
return;
}
// DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can)
// Stop WindSeedClientNotify from being sent for security purposes.
if(PacketOpcodesUtils.BANNED_PACKETS.contains(packet.getOpcode())) {
return;
}
// DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can)
// Stop WindSeedClientNotify from being sent for security purposes.
if (PacketOpcodesUtils.BANNED_PACKETS.contains(packet.getOpcode())) {
return;
}
// Header
if (packet.shouldBuildHeader()) {
packet.buildHeader(this.getNextClientSequence());
}
// Header
if (packet.shouldBuildHeader()) {
packet.buildHeader(this.getNextClientSequence());
}
// Log
switch (GAME_INFO.logPackets) {
case ALL -> {
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(packet.getOpcode())) {
// Log
switch (GAME_INFO.logPackets) {
case ALL -> {
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(packet.getOpcode())) {
logPacket("SEND", packet.getOpcode(), packet.getData());
}
}
case WHITELIST-> {
if (SERVER.debugWhitelist.contains(packet.getOpcode())) {
logPacket("SEND", packet.getOpcode(), packet.getData());
}
}
case BLACKLIST-> {
}
case WHITELIST-> {
if (SERVER.debugWhitelist.contains(packet.getOpcode())) {
logPacket("SEND", packet.getOpcode(), packet.getData());
}
}
case BLACKLIST-> {
if (!SERVER.debugBlacklist.contains(packet.getOpcode())) {
logPacket("SEND", packet.getOpcode(), packet.getData());
}
}
default -> {}
}
default -> {}
}
// Invoke event.
SendPacketEvent event = new SendPacketEvent(this, packet); event.call();
if(!event.isCanceled()) { // If event is not cancelled, continue.
tunnel.writeData(event.getPacket().build());
}
// Invoke event.
SendPacketEvent event = new SendPacketEvent(this, packet); event.call();
if (!event.isCanceled()) { // If event is not cancelled, continue.
tunnel.writeData(event.getPacket().build());
}
}
@Override
public void onConnected(GameSessionManager.KcpTunnel tunnel) {
this.tunnel = tunnel;
Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().toString()));
}
@Override
public void onConnected(GameSessionManager.KcpTunnel tunnel) {
this.tunnel = tunnel;
Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().toString()));
}
@Override
public void handleReceive(byte[] bytes) {
// Decrypt and turn back into a packet
Crypto.xor(bytes, useSecretKey() ? Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
ByteBuf packet = Unpooled.wrappedBuffer(bytes);
@Override
public void handleReceive(byte[] bytes) {
// Decrypt and turn back into a packet
Crypto.xor(bytes, useSecretKey() ? Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
ByteBuf packet = Unpooled.wrappedBuffer(bytes);
// Log
//logPacket(packet);
// Handle
try {
boolean allDebug = GAME_INFO.logPackets == ServerDebugMode.ALL;
while (packet.readableBytes() > 0) {
// Length
if (packet.readableBytes() < 12) {
return;
}
// Packet sanity check
int const1 = packet.readShort();
if (const1 != 17767) {
if(allDebug){
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect 17767",const1);
}
return; // Bad packet
}
// Data
int opcode = packet.readShort();
int headerLength = packet.readShort();
int payloadLength = packet.readInt();
byte[] header = new byte[headerLength];
byte[] payload = new byte[payloadLength];
// Log
//logPacket(packet);
// Handle
try {
boolean allDebug = GAME_INFO.logPackets == ServerDebugMode.ALL;
while (packet.readableBytes() > 0) {
// Length
if (packet.readableBytes() < 12) {
return;
}
// Packet sanity check
int const1 = packet.readShort();
if (const1 != 17767) {
if (allDebug) {
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect 17767",const1);
}
return; // Bad packet
}
// Data
int opcode = packet.readShort();
int headerLength = packet.readShort();
int payloadLength = packet.readInt();
byte[] header = new byte[headerLength];
byte[] payload = new byte[payloadLength];
packet.readBytes(header);
packet.readBytes(payload);
// Sanity check #2
int const2 = packet.readShort();
if (const2 != -30293) {
if(allDebug){
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect -30293",const2);
}
return; // Bad packet
}
// Log packet
switch (GAME_INFO.logPackets) {
case ALL -> {
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(opcode)) {
packet.readBytes(header);
packet.readBytes(payload);
// Sanity check #2
int const2 = packet.readShort();
if (const2 != -30293) {
if (allDebug) {
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect -30293",const2);
}
return; // Bad packet
}
// Log packet
switch (GAME_INFO.logPackets) {
case ALL -> {
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(opcode)) {
logPacket("RECV",opcode, payload);
}
}
case WHITELIST-> {
if (SERVER.debugWhitelist.contains(opcode)) {
logPacket("RECV",opcode, payload);
}
}
case BLACKLIST-> {
if (!(SERVER.debugBlacklist.contains(opcode))) {
logPacket("RECV",opcode, payload);
}
}
default -> {}
}
}
case WHITELIST-> {
if (SERVER.debugWhitelist.contains(opcode)) {
logPacket("RECV",opcode, payload);
}
}
case BLACKLIST-> {
if (!(SERVER.debugBlacklist.contains(opcode))) {
logPacket("RECV",opcode, payload);
}
}
default -> {}
}
// Handle
getServer().getPacketHandler().handle(this, opcode, header, payload);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//byteBuf.release(); //Needn't
packet.release();
}
}
// Handle
getServer().getPacketHandler().handle(this, opcode, header, payload);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//byteBuf.release(); //Needn't
packet.release();
}
}
@Override
public void handleClose() {
setState(SessionState.INACTIVE);
//send disconnection pack in case of reconnection
Grasscutter.getLogger().info(translate("messages.game.disconnect", this.getAddress().toString()));
// Save after disconnecting
if (this.isLoggedIn()) {
Player player = getPlayer();
// Call logout event.
player.onLogout();
}
try {
send(new BasePacket(PacketOpcodes.ServerDisconnectClientNotify));
}catch (Throwable ignore){
Grasscutter.getLogger().warn("closing {} error",getAddress().getAddress().getHostAddress());
}
tunnel = null;
}
@Override
public void handleClose() {
setState(SessionState.INACTIVE);
//send disconnection pack in case of reconnection
Grasscutter.getLogger().info(translate("messages.game.disconnect", this.getAddress().toString()));
// Save after disconnecting
if (this.isLoggedIn()) {
Player player = getPlayer();
// Call logout event.
player.onLogout();
}
try {
send(new BasePacket(PacketOpcodes.ServerDisconnectClientNotify));
}catch (Throwable ignore) {
Grasscutter.getLogger().warn("closing {} error",getAddress().getAddress().getHostAddress());
}
tunnel = null;
}
public void close() {
tunnel.close();
}
public void close() {
tunnel.close();
}
public boolean isActive() {
return getState() == SessionState.ACTIVE;
}
public boolean isActive() {
return getState() == SessionState.ACTIVE;
}
public enum SessionState {
INACTIVE,
WAITING_FOR_TOKEN,
WAITING_FOR_LOGIN,
PICKING_CHARACTER,
ACTIVE
}
public enum SessionState {
INACTIVE,
WAITING_FOR_TOKEN,
WAITING_FOR_LOGIN,
PICKING_CHARACTER,
ACTIVE
}
}

View File

@ -30,22 +30,22 @@ public final class HttpServer {
this.express = new Express(config -> {
// Set the Express HTTP server.
config.server(HttpServer::createServer);
// Configure encryption/HTTPS/SSL.
config.enforceSsl = HTTP_ENCRYPTION.useEncryption;
// Configure HTTP policies.
if(HTTP_POLICIES.cors.enabled) {
if (HTTP_POLICIES.cors.enabled) {
var allowedOrigins = HTTP_POLICIES.cors.allowedOrigins;
if (allowedOrigins.length > 0)
config.enableCorsForOrigin(allowedOrigins);
else config.enableCorsForAllOrigins();
}
// Configure debug logging.
if(DISPATCH_INFO.logRequests == ServerDebugMode.ALL)
if (DISPATCH_INFO.logRequests == ServerDebugMode.ALL)
config.enableDevLogging();
// Disable compression on static files.
config.precompressStaticFiles = false;
});
@ -60,26 +60,26 @@ public final class HttpServer {
Server server = new Server();
ServerConnector serverConnector
= new ServerConnector(server);
if(HTTP_ENCRYPTION.useEncryption) {
if (HTTP_ENCRYPTION.useEncryption) {
var sslContextFactory = new SslContextFactory.Server();
var keystoreFile = new File(HTTP_ENCRYPTION.keystore);
if(!keystoreFile.exists()) {
if (!keystoreFile.exists()) {
HTTP_ENCRYPTION.useEncryption = false;
HTTP_ENCRYPTION.useInRouting = false;
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.no_keystore_error"));
} else try {
sslContextFactory.setKeyStorePath(keystoreFile.getPath());
sslContextFactory.setKeyStorePassword(HTTP_ENCRYPTION.keystorePassword);
} catch (Exception ignored) {
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.password_error"));
try {
sslContextFactory.setKeyStorePath(keystoreFile.getPath());
sslContextFactory.setKeyStorePassword("123456");
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.default_password"));
} catch (Exception exception) {
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.general_error"), exception);
@ -88,10 +88,10 @@ public final class HttpServer {
serverConnector = new ServerConnector(server, sslContextFactory);
}
}
serverConnector.setPort(HTTP_INFO.bindPort);
server.setConnectors(new ServerConnector[]{serverConnector});
return server;
}
@ -112,9 +112,9 @@ public final class HttpServer {
public HttpServer addRouter(Class<? extends Router> router, Object... args) {
// Get all constructor parameters.
Class<?>[] types = new Class<?>[args.length];
for(var argument : args)
for (var argument : args)
types[args.length - 1] = argument.getClass();
try { // Create a router instance & apply routes.
var constructor = router.getDeclaredConstructor(types); // Get the constructor.
var routerInstance = constructor.newInstance(args); // Create instance.
@ -130,9 +130,9 @@ public final class HttpServer {
*/
public void start() throws UnsupportedEncodingException {
// Attempt to start the HTTP server.
if(HTTP_INFO.bindAddress.equals("")){
if (HTTP_INFO.bindAddress.equals("")) {
this.express.listen(HTTP_INFO.bindPort);
}else{
}else {
this.express.listen(HTTP_INFO.bindAddress, HTTP_INFO.bindPort);
}
@ -147,7 +147,7 @@ public final class HttpServer {
@Override public void applyRoutes(Express express, Javalin handle) {
express.get("/", (request, response) -> {
File file = new File(HTTP_STATIC_FILES.indexFile);
if(!file.exists())
if (!file.exists())
response.send("""
<!DOCTYPE html>
<html>
@ -173,19 +173,19 @@ public final class HttpServer {
public static class UnhandledRequestRouter implements Router {
@Override public void applyRoutes(Express express, Javalin handle) {
handle.error(404, context -> {
if(DISPATCH_INFO.logRequests == ServerDebugMode.MISSING)
if (DISPATCH_INFO.logRequests == ServerDebugMode.MISSING)
Grasscutter.getLogger().info(translate("messages.dispatch.unhandled_request_error", context.method(), context.url()));
context.contentType("text/html");
File file = new File(HTTP_STATIC_FILES.errorFile);
if(!file.exists())
if (!file.exists())
context.result("""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf8">
</head>
<body>
<img src="https://http.cat/404" />
</body>

View File

@ -60,7 +60,7 @@ public final class RegionHandler implements Router {
List<String> usedNames = new ArrayList<>(); // List to check for potential naming conflicts.
var configuredRegions = new ArrayList<>(List.of(DISPATCH_INFO.regions));
if(SERVER.runMode != ServerRunMode.HYBRID && configuredRegions.size() == 0) {
if (SERVER.runMode != ServerRunMode.HYBRID && configuredRegions.size() == 0) {
Grasscutter.getLogger().error("[Dispatch] There are no game servers available. Exiting due to unplayable state.");
System.exit(1);
} else if (configuredRegions.size() == 0)
@ -136,11 +136,11 @@ public final class RegionHandler implements Router {
// Get region data.
String regionData = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw==";
if (request.query().values().size() > 0) {
if(region != null)
if (region != null)
regionData = region.getBase64();
}
if( versionName.contains("2.7.5") || versionName.contains("2.8.")) {
if ( versionName.contains("2.7.5") || versionName.contains("2.8.")) {
try {
QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(regionData); event.call();
@ -227,4 +227,4 @@ public final class RegionHandler implements Router {
public static QueryCurrRegionHttpRsp getCurrentRegion() {
return SERVER.runMode == ServerRunMode.HYBRID ? regions.get("os_usa").getRegionQuery() : null;
}
}
}

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