diff --git a/src/main/java/emu/grasscutter/GameConstants.java b/src/main/java/emu/grasscutter/GameConstants.java index fccc59ed1..c1f0ac9e0 100644 --- a/src/main/java/emu/grasscutter/GameConstants.java +++ b/src/main/java/emu/grasscutter/GameConstants.java @@ -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"); } diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index 2e9523e29..8a8c36e18 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -217,7 +217,7 @@ public final class Grasscutter { */ private static void onShutdown() { // Disable all plugins. - if(pluginManager != null) + if (pluginManager != null) pluginManager.disablePlugins(); } diff --git a/src/main/java/emu/grasscutter/auth/DefaultAuthentication.java b/src/main/java/emu/grasscutter/auth/DefaultAuthentication.java index 62d512ebd..9fa1ec4aa 100644 --- a/src/main/java/emu/grasscutter/auth/DefaultAuthentication.java +++ b/src/main/java/emu/grasscutter/auth/DefaultAuthentication.java @@ -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(); diff --git a/src/main/java/emu/grasscutter/command/CommandMap.java b/src/main/java/emu/grasscutter/command/CommandMap.java index fe19075bf..da37f0b37 100644 --- a/src/main/java/emu/grasscutter/command/CommandMap.java +++ b/src/main/java/emu/grasscutter/command/CommandMap.java @@ -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; diff --git a/src/main/java/emu/grasscutter/command/commands/AccountCommand.java b/src/main/java/emu/grasscutter/command/commands/AccountCommand.java index 3d622a505..843b78913 100644 --- a/src/main/java/emu/grasscutter/command/commands/AccountCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/AccountCommand.java @@ -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 [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 [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 "); return; diff --git a/src/main/java/emu/grasscutter/command/commands/AnnounceCommand.java b/src/main/java/emu/grasscutter/command/commands/AnnounceCommand.java index 455e79453..136db705d 100644 --- a/src/main/java/emu/grasscutter/command/commands/AnnounceCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/AnnounceCommand.java @@ -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; } diff --git a/src/main/java/emu/grasscutter/command/commands/CoopCommand.java b/src/main/java/emu/grasscutter/command/commands/CoopCommand.java index 8c1479718..e36c47ce2 100644 --- a/src/main/java/emu/grasscutter/command/commands/CoopCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/CoopCommand.java @@ -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); diff --git a/src/main/java/emu/grasscutter/command/commands/EnterDungeonCommand.java b/src/main/java/emu/grasscutter/command/commands/EnterDungeonCommand.java index 94de4a294..e940e8e36 100644 --- a/src/main/java/emu/grasscutter/command/commands/EnterDungeonCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/EnterDungeonCommand.java @@ -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) { diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java index dab45378d..d86cbb19a 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java @@ -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 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, diff --git a/src/main/java/emu/grasscutter/command/commands/HelpCommand.java b/src/main/java/emu/grasscutter/command/commands/HelpCommand.java index 65e7228cc..418a5fd76 100644 --- a/src/main/java/emu/grasscutter/command/commands/HelpCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/HelpCommand.java @@ -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)); } diff --git a/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java b/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java index 0d052da3b..6f1dd6a91 100644 --- a/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java @@ -15,13 +15,13 @@ public final class ReloadCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List 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")); } } diff --git a/src/main/java/emu/grasscutter/command/commands/SendMailCommand.java b/src/main/java/emu/grasscutter/command/commands/SendMailCommand.java index 79f13e208..34c2b2bf4 100644 --- a/src/main/java/emu/grasscutter/command/commands/SendMailCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SendMailCommand.java @@ -27,7 +27,7 @@ public final class SendMailCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List 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"); diff --git a/src/main/java/emu/grasscutter/command/commands/SetPropCommand.java b/src/main/java/emu/grasscutter/command/commands/SetPropCommand.java index 122eb782e..64989c86b 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetPropCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetPropCommand.java @@ -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 ", 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 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 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 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 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 ", 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 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 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 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 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; + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java b/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java index 1306991ef..b6a5040c0 100644 --- a/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java @@ -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); diff --git a/src/main/java/emu/grasscutter/command/commands/TeamCommand.java b/src/main/java/emu/grasscutter/command/commands/TeamCommand.java index afcaa4a74..bd591d25b 100644 --- a/src/main/java/emu/grasscutter/command/commands/TeamCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TeamCommand.java @@ -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 [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 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 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 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(); - var ignoreList = new ArrayList(); - 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 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 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(); - 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 [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 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 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 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(); + var ignoreList = new ArrayList(); + 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 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 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(); + 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; + } + } + +} diff --git a/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java b/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java index 5e046a207..31597ae83 100644 --- a/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java @@ -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")); } } diff --git a/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java b/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java index 21d898af9..4153a83c2 100644 --- a/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java @@ -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)) ); } diff --git a/src/main/java/emu/grasscutter/config/ConfigContainer.java b/src/main/java/emu/grasscutter/config/ConfigContainer.java index 619775dc8..1817ddc2a 100644 --- a/src/main/java/emu/grasscutter/config/ConfigContainer.java +++ b/src/main/java/emu/grasscutter/config/ConfigContainer.java @@ -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; } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/config/Configuration.java b/src/main/java/emu/grasscutter/config/Configuration.java index 82c7b3f71..7c769b3a3 100644 --- a/src/main/java/emu/grasscutter/config/Configuration.java +++ b/src/main/java/emu/grasscutter/config/Configuration.java @@ -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; } diff --git a/src/main/java/emu/grasscutter/data/DataLoader.java b/src/main/java/emu/grasscutter/data/DataLoader.java index a006448f3..902996593 100644 --- a/src/main/java/emu/grasscutter/data/DataLoader.java +++ b/src/main/java/emu/grasscutter/data/DataLoader.java @@ -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 * diff --git a/src/main/java/emu/grasscutter/data/GameDepot.java b/src/main/java/emu/grasscutter/data/GameDepot.java index 76b9b7c6f..a809105fa 100644 --- a/src/main/java/emu/grasscutter/data/GameDepot.java +++ b/src/main/java/emu/grasscutter/data/GameDepot.java @@ -22,47 +22,47 @@ public class GameDepot { private static Int2ObjectMap> relicMainPropDepot = new Int2ObjectOpenHashMap<>(); private static Int2ObjectMap> relicAffixDepot = new Int2ObjectOpenHashMap<>(); - private static Map playerAbilities = new HashMap<>(); + private static Map playerAbilities = new HashMap<>(); private static HashMap> 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 list = relicMainPropDepot.computeIfAbsent(data.getPropDepotId(), k -> new ArrayList<>()); list.add(data); WeightedList 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 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 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 depotList = relicRandomMainPropDepot.get(depot); - if (depotList == null) { - return null; - } - return depotList.next(); - } + if (depotList == null) { + return null; + } + return depotList.next(); + } public static List getRelicMainPropList(int depot) { return relicMainPropDepot.get(depot); } public static List getRelicAffixList(int depot) { - return relicAffixDepot.get(depot); - } + return relicAffixDepot.get(depot); + } public static HashMap> getSpawnLists() { return spawnLists; @@ -72,9 +72,9 @@ public class GameDepot { spawnLists.putAll(data); } - public static void setPlayerAbilities(Map playerAbilities) { - GameDepot.playerAbilities = playerAbilities; - } + public static void setPlayerAbilities(Map playerAbilities) { + GameDepot.playerAbilities = playerAbilities; + } public static Map getPlayerAbilities() { return playerAbilities; diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index 70b006567..61b329250 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -34,281 +34,281 @@ import static emu.grasscutter.utils.Language.translate; public class ResourceLoader { - private static final List loadedResources = new ArrayList<>(); + private static final List loadedResources = new ArrayList<>(); - public static List> getResourceDefClasses() { - Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName()); - Set classes = reflections.getSubTypesOf(GameResource.class); + public static List> getResourceDefClasses() { + Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName()); + Set classes = reflections.getSubTypesOf(GameResource.class); - List> classList = new ArrayList<>(classes.size()); - classes.forEach(o -> { - Class c = (Class) o; - if (c.getAnnotation(ResourceType.class) != null) { - classList.add(c); - } - }); + List> 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 scenePointList = new ArrayList<>(); - for (File file : Objects.requireNonNull(folder.listFiles())) { - ScenePointConfig config; Integer sceneId; + List 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 entry : config.points.entrySet()) { - PointData pointData = Grasscutter.getGsonFactory().fromJson(entry.getValue(), PointData.class); - pointData.setId(Integer.parseInt(entry.getKey())); + for (Map.Entry 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 embryoList = null; + private static void loadAbilityEmbryos() { + List 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>(){}.getType())); - } catch (Exception e) { - e.printStackTrace(); - } - } - } + if (playerElementsFile.exists()) { + try (FileReader fileReader = new FileReader(playerElementsFile)) { + GameDepot.setPlayerAbilities(Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken>(){}.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 abilityConfigList; + for (File file : files) { + List 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 entry : data.Default.modifiers.entrySet()) { - AbilityModifier modifier = entry.getValue(); + for (Entry 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 spawnEntryMap = new ArrayList<>(); + private static void loadSpawnData() { + String[] spawnDataNames = {"Spawns.json", "GadgetSpawns.json"}; + ArrayList 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> areaSort = new HashMap<>(); //key = sceneId,x,z , value = ArrayList @@ -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 list = null; + private static void loadOpenConfig() { + // Read from cached file if exists + List 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 map = new TreeMap<>(); - java.lang.reflect.Type type = new TypeToken>() {}.getType(); - String[] folderNames = {"BinOutput/Talent/EquipTalents/", "BinOutput/Talent/AvatarTalents/"}; + if (list == null) { + Map map = new TreeMap<>(); + java.lang.reflect.Type type = new TypeToken>() {}.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 config; + Map 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 e : config.entrySet()) { - OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), e.getValue()); - map.put(entry.getName(), entry); - } - } - } + for (Entry 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 abilities; - } + public static class AvatarConfig { + @SerializedName(value="abilities", alternate={"targetAbilities"}) + public ArrayList 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; + } } diff --git a/src/main/java/emu/grasscutter/data/common/ItemParamData.java b/src/main/java/emu/grasscutter/data/common/ItemParamData.java index 229cbf7b9..77c168138 100644 --- a/src/main/java/emu/grasscutter/data/common/ItemParamData.java +++ b/src/main/java/emu/grasscutter/data/common/ItemParamData.java @@ -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; + } } diff --git a/src/main/java/emu/grasscutter/data/excels/EnvAnimalGatherConfigData.java b/src/main/java/emu/grasscutter/data/excels/EnvAnimalGatherConfigData.java index d169b3ef7..d2e5313d3 100644 --- a/src/main/java/emu/grasscutter/data/excels/EnvAnimalGatherConfigData.java +++ b/src/main/java/emu/grasscutter/data/excels/EnvAnimalGatherConfigData.java @@ -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 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 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; + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/ForgeData.java b/src/main/java/emu/grasscutter/data/excels/ForgeData.java index 3e6bf3e83..fab403170 100644 --- a/src/main/java/emu/grasscutter/data/excels/ForgeData.java +++ b/src/main/java/emu/grasscutter/data/excels/ForgeData.java @@ -23,9 +23,9 @@ public class ForgeData extends GameResource { private List materialItems; @Override - public int getId() { - return this.id; - } + public int getId() { + return this.id; + } public int getPlayerLevel() { return playerLevel; diff --git a/src/main/java/emu/grasscutter/data/excels/ReliquaryLevelData.java b/src/main/java/emu/grasscutter/data/excels/ReliquaryLevelData.java index f9580ef7d..2a3dc8e2b 100644 --- a/src/main/java/emu/grasscutter/data/excels/ReliquaryLevelData.java +++ b/src/main/java/emu/grasscutter/data/excels/ReliquaryLevelData.java @@ -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 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 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; + } + } } diff --git a/src/main/java/emu/grasscutter/database/DatabaseManager.java b/src/main/java/emu/grasscutter/database/DatabaseManager.java index 15d78eb51..5bf5d0820 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseManager.java +++ b/src/main/java/emu/grasscutter/database/DatabaseManager.java @@ -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 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 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 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()); - } -} \ No newline at end of file + 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 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()); + } +} diff --git a/src/main/java/emu/grasscutter/game/Account.java b/src/main/java/emu/grasscutter/game/Account.java index 43f4efe5c..0f5c02f9d 100644 --- a/src/main/java/emu/grasscutter/game/Account.java +++ b/src/main/java/emu/grasscutter/game/Account.java @@ -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 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 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 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 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= (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= (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 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 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; } - } + } } diff --git a/src/main/java/emu/grasscutter/game/ability/AbilityManager.java b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java index d59b8b0f1..7f2b16485 100644 --- a/src/main/java/emu/grasscutter/game/ability/AbilityManager.java +++ b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java @@ -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); + } + } + } + } } diff --git a/src/main/java/emu/grasscutter/game/activity/ActivityManager.java b/src/main/java/emu/grasscutter/game/activity/ActivityManager.java index f1c86b812..f4cd55f0c 100644 --- a/src/main/java/emu/grasscutter/game/activity/ActivityManager.java +++ b/src/main/java/emu/grasscutter/game/activity/ActivityManager.java @@ -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 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 getActivityHandler(ActivityType type){ + public Optional getActivityHandler(ActivityType type) { return activityConfigItemMap.values().stream() .map(ActivityConfigItem::getActivityHandler) .filter(x -> type == x.getClass().getAnnotation(GameActivity.class).value()) .findFirst(); } - public Optional getActivityHandlerAs(ActivityType type, Class clazz){ + public Optional getActivityHandlerAs(ActivityType type, Class clazz) { return getActivityHandler(type).map(x -> (T)x); } - public Optional getActivityIdByActivityType(ActivityType type){ + public Optional getActivityIdByActivityType(ActivityType type) { return getActivityHandler(type) .map(ActivityHandler::getActivityConfigItem) .map(ActivityConfigItem::getActivityId); } - public Optional getPlayerActivityDataByActivityType(ActivityType type){ + public Optional getPlayerActivityDataByActivityType(ActivityType type) { return getActivityIdByActivityType(type) .map(playerActivityDataMap::get); } - public Optional getInfoProtoByActivityType(ActivityType type){ + public Optional getInfoProtoByActivityType(ActivityType type) { return getActivityIdByActivityType(type) .map(this::getInfoProtoByActivityId); } diff --git a/src/main/java/emu/grasscutter/game/activity/musicgame/MusicGameActivityHandler.java b/src/main/java/emu/grasscutter/game/activity/musicgame/MusicGameActivityHandler.java index 18f937c9e..7b8b81004 100644 --- a/src/main/java/emu/grasscutter/game/activity/musicgame/MusicGameActivityHandler.java +++ b/src/main/java/emu/grasscutter/game/activity/musicgame/MusicGameActivityHandler.java @@ -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); diff --git a/src/main/java/emu/grasscutter/game/activity/musicgame/MusicGamePlayerData.java b/src/main/java/emu/grasscutter/game/activity/musicgame/MusicGamePlayerData.java index 4fd25c5bb..e99b71cb6 100644 --- a/src/main/java/emu/grasscutter/game/activity/musicgame/MusicGamePlayerData.java +++ b/src/main/java/emu/grasscutter/game/activity/musicgame/MusicGamePlayerData.java @@ -21,7 +21,7 @@ public class MusicGamePlayerData { Map personalCustomBeatmapRecord; Map 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() diff --git a/src/main/java/emu/grasscutter/game/avatar/Avatar.java b/src/main/java/emu/grasscutter/game/avatar/Avatar.java index 3ab2e1dc8..b8adfdbd0 100644 --- a/src/main/java/emu/grasscutter/game/avatar/Avatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/Avatar.java @@ -66,896 +66,896 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @Entity(value = "avatars", useDiscriminator = false) public class Avatar { - @Id private ObjectId id; - @Indexed private int ownerId; // Id of player that this avatar belongs to - - @Transient private Player owner; - @Transient private AvatarData data; - @Transient private AvatarSkillDepotData skillDepot; - @Transient private long guid; // Player unique id - private int avatarId; // Id of avatar - - private int level = 1; - private int exp; - private int promoteLevel; - private int satiation; // ? - private int satiationPenalty; // ? - private float currentHp; - private float currentEnergy; - - @Transient private final Int2ObjectMap equips; - @Transient private final Int2FloatOpenHashMap fightProp; - @Transient private Set extraAbilityEmbryos; - - private List fetters; + @Id private ObjectId id; + @Indexed private int ownerId; // Id of player that this avatar belongs to - private Map skillLevelMap; // Talent levels - private Map skillExtraChargeMap; // Charges - private Map proudSkillBonusMap; // Talent bonus levels (from const) - private int skillDepotId; - private int coreProudSkillLevel; // Constellation level - private Set talentIdList; // Constellation id list - private Set proudSkillList; // Character passives - - private int flyCloak; - private int costume; - private int bornTime; + @Transient private Player owner; + @Transient private AvatarData data; + @Transient private AvatarSkillDepotData skillDepot; + @Transient private long guid; // Player unique id + private int avatarId; // Id of avatar - private int fetterLevel = 1; - private int fetterExp; + private int level = 1; + private int exp; + private int promoteLevel; + private int satiation; // ? + private int satiationPenalty; // ? + private float currentHp; + private float currentEnergy; - private int nameCardRewardId; - private int nameCardId; - - @Deprecated // Do not use. Morhpia only! - public Avatar() { - this.equips = new Int2ObjectOpenHashMap<>(); - this.fightProp = new Int2FloatOpenHashMap(); - this.extraAbilityEmbryos = new HashSet<>(); - this.proudSkillBonusMap = new HashMap<>(); - this.fetters = new ArrayList<>(); // TODO Move to avatar - } - - // On creation - public Avatar(int avatarId) { - this(GameData.getAvatarDataMap().get(avatarId)); - } - - public Avatar(AvatarData data) { - this(); - this.avatarId = data.getId(); - this.nameCardRewardId = data.getNameCardRewardId(); - this.nameCardId = data.getNameCardId(); - this.data = data; - this.bornTime = (int) (System.currentTimeMillis() / 1000); - this.flyCloak = 140001; - - this.skillLevelMap = new HashMap<>(); - this.skillExtraChargeMap = new HashMap<>(); - this.talentIdList = new HashSet<>(); - this.proudSkillList = new HashSet<>(); - - // Combat properties - for (FightProperty prop : FightProperty.values()) { - if (prop.getId() <= 0 || prop.getId() >= 3000) { - continue; - } - this.setFightProperty(prop, 0f); - } - - // Skill depot - this.setSkillDepotData(getAvatarData().getSkillDepot()); - - // Set stats - this.recalcStats(); - this.currentHp = getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); - setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.currentHp); - this.currentEnergy = 0f; - // Load handler - this.onLoad(); - } - - public Player getPlayer() { - return this.owner; - } + @Transient private final Int2ObjectMap equips; + @Transient private final Int2FloatOpenHashMap fightProp; + @Transient private Set extraAbilityEmbryos; - public ObjectId getObjectId() { - return id; - } + private List fetters; - public AvatarData getAvatarData() { - return data; - } + private Map skillLevelMap; // Talent levels + private Map skillExtraChargeMap; // Charges + private Map proudSkillBonusMap; // Talent bonus levels (from const) + private int skillDepotId; + private int coreProudSkillLevel; // Constellation level + private Set talentIdList; // Constellation id list + private Set proudSkillList; // Character passives - protected void setAvatarData(AvatarData data) { - if (this.data != null) return; - this.data = data; // Used while loading this from the database - } + private int flyCloak; + private int costume; + private int bornTime; - public int getOwnerId() { - return ownerId; - } + private int fetterLevel = 1; + private int fetterExp; - public void setOwner(Player player) { - this.owner = player; - this.ownerId = player.getUid(); - this.guid = player.getNextGameGuid(); - } - - public int getSatiation() { - return satiation; - } + private int nameCardRewardId; + private int nameCardId; - public void setSatiation(int satiation) { - this.satiation = satiation; - } + @Deprecated // Do not use. Morhpia only! + public Avatar() { + this.equips = new Int2ObjectOpenHashMap<>(); + this.fightProp = new Int2FloatOpenHashMap(); + this.extraAbilityEmbryos = new HashSet<>(); + this.proudSkillBonusMap = new HashMap<>(); + this.fetters = new ArrayList<>(); // TODO Move to avatar + } - public int getNameCardRewardId() { - return nameCardRewardId; - } + // On creation + public Avatar(int avatarId) { + this(GameData.getAvatarDataMap().get(avatarId)); + } - public void setNameCardRewardId(int nameCardRewardId) { - this.nameCardRewardId = nameCardRewardId; - } + public Avatar(AvatarData data) { + this(); + this.avatarId = data.getId(); + this.nameCardRewardId = data.getNameCardRewardId(); + this.nameCardId = data.getNameCardId(); + this.data = data; + this.bornTime = (int) (System.currentTimeMillis() / 1000); + this.flyCloak = 140001; - public int getSatiationPenalty() { - return satiationPenalty; - } + this.skillLevelMap = new HashMap<>(); + this.skillExtraChargeMap = new HashMap<>(); + this.talentIdList = new HashSet<>(); + this.proudSkillList = new HashSet<>(); - public void setSatiationPenalty(int satiationPenalty) { - this.satiationPenalty = satiationPenalty; - } + // Combat properties + for (FightProperty prop : FightProperty.values()) { + if (prop.getId() <= 0 || prop.getId() >= 3000) { + continue; + } + this.setFightProperty(prop, 0f); + } - public AvatarData getData() { - return data; - } + // Skill depot + this.setSkillDepotData(getAvatarData().getSkillDepot()); - public long getGuid() { - return guid; - } + // Set stats + this.recalcStats(); + this.currentHp = getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); + setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.currentHp); + this.currentEnergy = 0f; + // Load handler + this.onLoad(); + } - public int getAvatarId() { - return avatarId; - } + public Player getPlayer() { + return this.owner; + } - public int getLevel() { - return level; - } - - public void setLevel(int level) { - this.level = level; - } + public ObjectId getObjectId() { + return id; + } - public int getExp() { - return exp; - } - - public void setExp(int exp) { - this.exp = exp; - } + public AvatarData getAvatarData() { + return data; + } - public int getPromoteLevel() { - return promoteLevel; - } + protected void setAvatarData(AvatarData data) { + if (this.data != null) return; + this.data = data; // Used while loading this from the database + } - public void setPromoteLevel(int promoteLevel) { - this.promoteLevel = promoteLevel; - } + public int getOwnerId() { + return ownerId; + } - static public int getMinPromoteLevel(int level) { - if (level > 80) { - return 6; - } else if (level > 70) { - return 5; - } else if (level > 60) { - return 4; - } else if (level > 50) { - return 3; - } else if (level > 40) { - return 2; - } else if (level > 20) { - return 1; - } - return 0; - } + public void setOwner(Player player) { + this.owner = player; + this.ownerId = player.getUid(); + this.guid = player.getNextGameGuid(); + } - public Int2ObjectMap getEquips() { - return equips; - } - - public GameItem getEquipBySlot(EquipType slot) { - return this.getEquips().get(slot.getValue()); - } - - private GameItem getEquipBySlot(int slotId) { - return this.getEquips().get(slotId); - } - - public GameItem getWeapon() { - return this.getEquipBySlot(EquipType.EQUIP_WEAPON); - } + public int getSatiation() { + return satiation; + } - public int getSkillDepotId() { - return skillDepotId; - } + public void setSatiation(int satiation) { + this.satiation = satiation; + } - public AvatarSkillDepotData getSkillDepot() { - return skillDepot; - } - - protected void setSkillDepot(AvatarSkillDepotData skillDepot) { - if (this.skillDepot != null) return; - this.skillDepot = skillDepot; // Used while loading this from the database - } + public int getNameCardRewardId() { + return nameCardRewardId; + } - public void setSkillDepotData(AvatarSkillDepotData skillDepot) { - // Set id and depot - this.skillDepotId = skillDepot.getId(); - this.skillDepot = skillDepot; - // Clear, then add skills - getSkillLevelMap().clear(); - if (skillDepot.getEnergySkill() > 0) { - getSkillLevelMap().put(skillDepot.getEnergySkill(), 1); - } - for (int skillId : skillDepot.getSkills()) { - if (skillId > 0) { - getSkillLevelMap().put(skillId, 1); - } - } - // Add proud skills - this.getProudSkillList().clear(); - for (InherentProudSkillOpens openData : skillDepot.getInherentProudSkillOpens()) { - if (openData.getProudSkillGroupId() == 0) { - continue; - } - if (openData.getNeedAvatarPromoteLevel() <= this.getPromoteLevel()) { - int proudSkillId = (openData.getProudSkillGroupId() * 100) + 1; - if (GameData.getProudSkillDataMap().containsKey(proudSkillId)) { - this.getProudSkillList().add(proudSkillId); - } - } - } - } + public void setNameCardRewardId(int nameCardRewardId) { + this.nameCardRewardId = nameCardRewardId; + } - public Map getSkillLevelMap() { - return skillLevelMap; - } - - public Map getSkillExtraChargeMap() { - if (skillExtraChargeMap == null) { - skillExtraChargeMap = new HashMap<>(); - } - return skillExtraChargeMap; - } + public int getSatiationPenalty() { + return satiationPenalty; + } - public Map getProudSkillBonusMap() { - return proudSkillBonusMap; - } + public void setSatiationPenalty(int satiationPenalty) { + this.satiationPenalty = satiationPenalty; + } - public Set getExtraAbilityEmbryos() { - return extraAbilityEmbryos; - } + public AvatarData getData() { + return data; + } - public void setFetterList(List fetterList) { - this.fetters = fetterList; - } + public long getGuid() { + return guid; + } - public List getFetterList() { - return fetters; - } + public int getAvatarId() { + return avatarId; + } - public int getFetterLevel() { - return fetterLevel; - } + public int getLevel() { + return level; + } - public void setFetterLevel(int fetterLevel) { - this.fetterLevel = fetterLevel; - } + public void setLevel(int level) { + this.level = level; + } - public int getFetterExp() { - return fetterExp; - } + public int getExp() { + return exp; + } - public void setFetterExp(int fetterExp) { - this.fetterExp = fetterExp; - } + public void setExp(int exp) { + this.exp = exp; + } - public int getNameCardId() { - return nameCardId; - } + public int getPromoteLevel() { + return promoteLevel; + } - public void setNameCardId(int nameCardId) { - this.nameCardId = nameCardId; - } + public void setPromoteLevel(int promoteLevel) { + this.promoteLevel = promoteLevel; + } - public float getCurrentHp() { - return currentHp; - } + static public int getMinPromoteLevel(int level) { + if (level > 80) { + return 6; + } else if (level > 70) { + return 5; + } else if (level > 60) { + return 4; + } else if (level > 50) { + return 3; + } else if (level > 40) { + return 2; + } else if (level > 20) { + return 1; + } + return 0; + } - public void setCurrentHp(float currentHp) { - this.currentHp = currentHp; - } + public Int2ObjectMap getEquips() { + return equips; + } - public void setCurrentEnergy() { - if (GAME_OPTIONS.energyUsage) { - this.setCurrentEnergy(this.currentEnergy); - } - } - - public void setCurrentEnergy(float currentEnergy) { - if (this.getSkillDepot() != null && this.getSkillDepot().getEnergySkillData() != null) { - ElementType element = this.getSkillDepot().getElementType(); - this.setFightProperty(element.getMaxEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal()); - - if (GAME_OPTIONS.energyUsage) { - this.setFightProperty(element.getCurEnergyProp(), currentEnergy); - } - else { - this.setFightProperty(element.getCurEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal()); - } - } - } + public GameItem getEquipBySlot(EquipType slot) { + return this.getEquips().get(slot.getValue()); + } - public void setCurrentEnergy(FightProperty curEnergyProp, float currentEnergy) { - if (GAME_OPTIONS.energyUsage) { - this.setFightProperty(curEnergyProp, currentEnergy); - this.currentEnergy = currentEnergy; - this.save(); - } - } + private GameItem getEquipBySlot(int slotId) { + return this.getEquips().get(slotId); + } - public Int2FloatOpenHashMap getFightProperties() { - return fightProp; - } - - 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 GameItem getWeapon() { + return this.getEquipBySlot(EquipType.EQUIP_WEAPON); + } - public Set getTalentIdList() { - return talentIdList; - } + public int getSkillDepotId() { + return skillDepotId; + } - public int getCoreProudSkillLevel() { - return coreProudSkillLevel; - } + public AvatarSkillDepotData getSkillDepot() { + return skillDepot; + } - public void setCoreProudSkillLevel(int constLevel) { - this.coreProudSkillLevel = constLevel; - } + protected void setSkillDepot(AvatarSkillDepotData skillDepot) { + if (this.skillDepot != null) return; + this.skillDepot = skillDepot; // Used while loading this from the database + } - public Set getProudSkillList() { - return proudSkillList; - } - - public int getFlyCloak() { - return flyCloak; - } - - public void setFlyCloak(int flyCloak) { - this.flyCloak = flyCloak; - } + public void setSkillDepotData(AvatarSkillDepotData skillDepot) { + // Set id and depot + this.skillDepotId = skillDepot.getId(); + this.skillDepot = skillDepot; + // Clear, then add skills + getSkillLevelMap().clear(); + if (skillDepot.getEnergySkill() > 0) { + getSkillLevelMap().put(skillDepot.getEnergySkill(), 1); + } + for (int skillId : skillDepot.getSkills()) { + if (skillId > 0) { + getSkillLevelMap().put(skillId, 1); + } + } + // Add proud skills + this.getProudSkillList().clear(); + for (InherentProudSkillOpens openData : skillDepot.getInherentProudSkillOpens()) { + if (openData.getProudSkillGroupId() == 0) { + continue; + } + if (openData.getNeedAvatarPromoteLevel() <= this.getPromoteLevel()) { + int proudSkillId = (openData.getProudSkillGroupId() * 100) + 1; + if (GameData.getProudSkillDataMap().containsKey(proudSkillId)) { + this.getProudSkillList().add(proudSkillId); + } + } + } + } - public int getCostume() { - return costume; - } + public Map getSkillLevelMap() { + return skillLevelMap; + } - public void setCostume(int costume) { - this.costume = costume; - } + public Map getSkillExtraChargeMap() { + if (skillExtraChargeMap == null) { + skillExtraChargeMap = new HashMap<>(); + } + return skillExtraChargeMap; + } - public int getBornTime() { - return bornTime; - } - - public boolean equipItem(GameItem item, boolean shouldRecalc) { - // Sanity check equip type - EquipType itemEquipType = item.getItemData().getEquipType(); - if (itemEquipType == EquipType.EQUIP_NONE) { - return false; - } + public Map getProudSkillBonusMap() { + return proudSkillBonusMap; + } - // Check if other avatars have this item equipped - Avatar otherAvatar = getPlayer().getAvatars().getAvatarById(item.getEquipCharacter()); - if (otherAvatar != null) { - // Unequip other avatar's item - if (otherAvatar.unequipItem(item.getItemData().getEquipType())) { - getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(otherAvatar, item.getItemData().getEquipType())); - } - // Swap with other avatar - if (getEquips().containsKey(itemEquipType.getValue())) { - GameItem toSwap = this.getEquipBySlot(itemEquipType); - otherAvatar.equipItem(toSwap, false); - } - // Recalc - otherAvatar.recalcStats(); - } else if (getEquips().containsKey(itemEquipType.getValue())) { - // Unequip item in current slot if it exists - unequipItem(itemEquipType); - } - - // Set equip - getEquips().put(itemEquipType.getValue(), item); - - if (itemEquipType == EquipType.EQUIP_WEAPON && getPlayer().getWorld() != null) { - item.setWeaponEntityId(this.getPlayer().getWorld().getNextEntityId(EntityIdType.WEAPON)); - } - - item.setEquipCharacter(this.getAvatarId()); - item.save(); - - if (this.getPlayer().hasSentAvatarDataNotify()) { - this.getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(this, item)); - } - - if (shouldRecalc) { - this.recalcStats(); - } - - return true; - } - - public boolean unequipItem(EquipType slot) { - GameItem item = getEquips().remove(slot.getValue()); - - if (item != null) { - item.setEquipCharacter(0); - item.save(); - return true; - } - - return false; - } - - public void recalcStats() { - recalcStats(false); - } - - public void recalcStats(boolean forceSendAbilityChange) { - // Setup - AvatarData data = this.getAvatarData(); - AvatarPromoteData promoteData = GameData.getAvatarPromoteData(data.getAvatarPromoteId(), this.getPromoteLevel()); - Int2IntOpenHashMap setMap = new Int2IntOpenHashMap(); - - // Extra ability embryos - Set prevExtraAbilityEmbryos = this.getExtraAbilityEmbryos(); - this.extraAbilityEmbryos = new HashSet<>(); + public Set getExtraAbilityEmbryos() { + return extraAbilityEmbryos; + } - // Fetters - this.setFetterList(data.getFetters()); - this.setNameCardRewardId(data.getNameCardRewardId()); - this.setNameCardId(data.getNameCardId()); - - // 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); - - // Store current energy value for later - float currentEnergy = (this.getSkillDepot() != null) ? this.getFightProperty(this.getSkillDepot().getElementType().getCurEnergyProp()) : 0f; + public void setFetterList(List fetterList) { + this.fetters = fetterList; + } - // Clear properties - this.getFightProperties().clear(); - - // Base stats - this.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, data.getBaseHp(this.getLevel())); - this.setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, data.getBaseAttack(this.getLevel())); - this.setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, data.getBaseDefense(this.getLevel())); - this.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL, data.getBaseCritical()); - this.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL_HURT, data.getBaseCriticalHurt()); - this.setFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 1f); - - if (promoteData != null) { - for (FightPropData fightPropData : promoteData.getAddProps()) { - this.addFightProperty(fightPropData.getProp(), fightPropData.getValue()); - } - } - - // Set energy usage - setCurrentEnergy(currentEnergy); - - // Artifacts - for (int slotId = 1; slotId <= 5; slotId++) { - // Get artifact - GameItem equip = this.getEquipBySlot(slotId); - if (equip == null) { - continue; - } - // Artifact main stat - ReliquaryMainPropData mainPropData = GameData.getReliquaryMainPropDataMap().get(equip.getMainPropId()); - if (mainPropData != null) { - ReliquaryLevelData levelData = GameData.getRelicLevelData(equip.getItemData().getRankLevel(), equip.getLevel()); - if (levelData != null) { - this.addFightProperty(mainPropData.getFightProp(), levelData.getPropValue(mainPropData.getFightProp())); - } - } - // Artifact sub stats - for (int appendPropId : equip.getAppendPropIdList()) { - ReliquaryAffixData affixData = GameData.getReliquaryAffixDataMap().get(appendPropId); - if (affixData != null) { - this.addFightProperty(affixData.getFightProp(), affixData.getPropValue()); - } - } - // Set bonus - if (equip.getItemData().getSetId() > 0) { - setMap.addTo(equip.getItemData().getSetId(), 1); - } - } + public List getFetterList() { + return fetters; + } - // Set stuff - for (Int2IntOpenHashMap.Entry e : setMap.int2IntEntrySet()) { - ReliquarySetData setData = GameData.getReliquarySetDataMap().get(e.getIntKey()); - if (setData == null) { - continue; - } - - // Calculate how many items are from the set - int amount = e.getIntValue(); - - // Add affix data from set bonus - for (int setIndex = 0; setIndex < setData.getSetNeedNum().length; setIndex++) { - if (amount >= setData.getSetNeedNum()[setIndex]) { - int affixId = (setData.getEquipAffixId() * 10) + setIndex; - - EquipAffixData affix = GameData.getEquipAffixDataMap().get(affixId); - if (affix == null) { - continue; - } - - // Add properties from this affix to our avatar - for (FightPropData prop : affix.getAddProps()) { - this.addFightProperty(prop.getProp(), prop.getValue()); - } - - // Add any skill strings from this affix - this.addToExtraAbilityEmbryos(affix.getOpenConfig(), true); - } else { - break; - } - } - } - - // Weapon - GameItem weapon = this.getWeapon(); - if (weapon != null) { - // Add stats - WeaponCurveData curveData = GameData.getWeaponCurveDataMap().get(weapon.getLevel()); - if (curveData != null) { - for (WeaponProperty weaponProperty : weapon.getItemData().getWeaponProperties()) { - this.addFightProperty(weaponProperty.getFightProp(), weaponProperty.getInitValue() * curveData.getMultByProp(weaponProperty.getType())); - } - } - // Weapon promotion stats - WeaponPromoteData wepPromoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel()); - if (wepPromoteData != null) { - for (FightPropData prop : wepPromoteData.getAddProps()) { - if (prop.getValue() == 0f || prop.getProp() == null) { - continue; - } - this.addFightProperty(prop.getProp(), prop.getValue()); - } - } - // Add weapon skill from affixes - if (weapon.getAffixes() != null && weapon.getAffixes().size() > 0) { - // Weapons usually dont have more than one affix but just in case... - for (int af : weapon.getAffixes()) { - if (af == 0) { - continue; - } - // Calculate affix id - int affixId = (af * 10) + weapon.getRefinement(); - EquipAffixData affix = GameData.getEquipAffixDataMap().get(affixId); - if (affix == null) { - continue; - } - - // Add properties from this affix to our avatar - for (FightPropData prop : affix.getAddProps()) { - this.addFightProperty(prop.getProp(), prop.getValue()); - } - - // Add any skill strings from this affix - this.addToExtraAbilityEmbryos(affix.getOpenConfig(), true); - } - } - } - - // Add proud skills and unlock them if needed - AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.getSkillDepotId()); - this.getProudSkillList().clear(); - for (InherentProudSkillOpens openData : skillDepot.getInherentProudSkillOpens()) { - if (openData.getProudSkillGroupId() == 0) { - continue; - } - if (openData.getNeedAvatarPromoteLevel() <= this.getPromoteLevel()) { - int proudSkillId = (openData.getProudSkillGroupId() * 100) + 1; - if (GameData.getProudSkillDataMap().containsKey(proudSkillId)) { - this.getProudSkillList().add(proudSkillId); - } - } - } + public int getFetterLevel() { + return fetterLevel; + } - // Proud skills - for (int proudSkillId : this.getProudSkillList()) { - ProudSkillData proudSkillData = GameData.getProudSkillDataMap().get(proudSkillId); - if (proudSkillData == null) { - continue; - } - - // Add properties from this proud skill to our avatar - for (FightPropData prop : proudSkillData.getAddProps()) { - this.addFightProperty(prop.getProp(), prop.getValue()); - } - - // Add any skill strings from this proud skill - this.addToExtraAbilityEmbryos(proudSkillData.getOpenConfig(), true); - } - - // Constellations - if (this.getTalentIdList().size() > 0) { - for (int talentId : this.getTalentIdList()) { - AvatarTalentData avatarTalentData = GameData.getAvatarTalentDataMap().get(talentId); - if (avatarTalentData == null) { - return; - } - - // Add any skill strings from this constellation - this.addToExtraAbilityEmbryos(avatarTalentData.getOpenConfig(), false); - } - } + public void setFetterLevel(int fetterLevel) { + this.fetterLevel = fetterLevel; + } - // 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); - - // Packet - if (getPlayer() != null && getPlayer().hasSentAvatarDataNotify()) { - // Update stats for client - getPlayer().sendPacket(new PacketAvatarFightPropNotify(this)); - // Update client abilities - EntityAvatar entity = this.getAsEntity(); - if (entity != null && (!this.getExtraAbilityEmbryos().equals(prevExtraAbilityEmbryos) || forceSendAbilityChange)) { - getPlayer().sendPacket(new PacketAbilityChangeNotify(entity)); - } - } - } - - public void addToExtraAbilityEmbryos(String openConfig, boolean forceAdd) { - if (openConfig == null || openConfig.length() == 0) { - return; - } - - OpenConfigEntry entry = GameData.getOpenConfigEntries().get(openConfig); - if (entry == null) { - if (forceAdd) { - // Add config string to ability skill list anyways - this.getExtraAbilityEmbryos().add(openConfig); - } - return; - } - - if (entry.getAddAbilities() != null) { - for (String ability : entry.getAddAbilities()) { - this.getExtraAbilityEmbryos().add(ability); - } - } - } - - public void recalcConstellations() { - // Clear first - this.getProudSkillBonusMap().clear(); - this.getSkillExtraChargeMap().clear(); - - // Sanity checks - if (getData() == null || getData().getSkillDepot() == null) { - return; - } - - if (this.getTalentIdList().size() > 0) { - for (int talentId : this.getTalentIdList()) { - AvatarTalentData avatarTalentData = GameData.getAvatarTalentDataMap().get(talentId); - - if (avatarTalentData == null || avatarTalentData.getOpenConfig() == null || avatarTalentData.getOpenConfig().length() == 0) { - continue; - } - - // Get open config to find which skill should be boosted - OpenConfigEntry entry = GameData.getOpenConfigEntries().get(avatarTalentData.getOpenConfig()); - if (entry == null) { - continue; - } - - // Check if we can add charges to a skill - if (entry.getSkillPointModifiers() != null) { - for (SkillPointModifier mod : entry.getSkillPointModifiers()) { - AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(mod.getSkillId()); - - if (skillData == null) continue; - - int charges = skillData.getMaxChargeNum() + mod.getDelta(); - - this.getSkillExtraChargeMap().put(mod.getSkillId(), charges); - } - continue; - } - - // Check if a skill can be boosted by +3 levels - int skillId = 0; - - if (entry.getExtraTalentIndex() == 2 && this.getData().getSkillDepot().getSkills().size() >= 2) { - // E skill - skillId = this.getData().getSkillDepot().getSkills().get(1); - } else if (entry.getExtraTalentIndex() == 9) { - // Ult skill - skillId = this.getData().getSkillDepot().getEnergySkill(); - } - - // Sanity check - if (skillId == 0) { - continue; - } - - // Get proud skill group id - AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(skillId); - - if (skillData == null) { - continue; - } - - // Add to bonus list - this.getProudSkillBonusMap().put(skillData.getProudSkillGroupId(), 3); - } - } - } - - public EntityAvatar getAsEntity() { - for (EntityAvatar entity : getPlayer().getTeamManager().getActiveTeam()) { - if (entity.getAvatar() == this) { - return entity; - } - } - return null; - } - - public int getEntityId() { - EntityAvatar entity = getAsEntity(); - return entity != null ? entity.getId() : 0; - } + public int getFetterExp() { + return fetterExp; + } - public void save() { - DatabaseHelper.saveAvatar(this); - } - - public AvatarInfo toProto() { - int fetterLevel = this.getFetterLevel(); - AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder() - .setExpLevel(fetterLevel); - - if (fetterLevel != 10) { - avatarFetter.setExpNumber(this.getFetterExp()); - } - - - if (this.getFetterList() != null) { - for (int i = 0; i < this.getFetterList().size(); i++) { - avatarFetter.addFetterList( - FetterData.newBuilder() - .setFetterId(this.getFetterList().get(i)) - .setFetterState(FetterState.FINISH.getValue()) - ); - } - } + public void setFetterExp(int fetterExp) { + this.fetterExp = fetterExp; + } - int cardId = this.getNameCardId(); + public int getNameCardId() { + return nameCardId; + } - if (this.getPlayer().getNameCardList().contains(cardId)) { - avatarFetter.addRewardedFetterLevelList(10); - } + public void setNameCardId(int nameCardId) { + this.nameCardId = nameCardId; + } - AvatarInfo.Builder avatarInfo = AvatarInfo.newBuilder() - .setAvatarId(this.getAvatarId()) - .setGuid(this.getGuid()) - .setLifeState(1) - .addAllTalentIdList(this.getTalentIdList()) - .putAllFightPropMap(this.getFightProperties()) - .setSkillDepotId(this.getSkillDepotId()) - .setCoreProudSkillLevel(this.getCoreProudSkillLevel()) - .putAllSkillLevelMap(this.getSkillLevelMap()) - .addAllInherentProudSkillList(this.getProudSkillList()) - .putAllProudSkillExtraLevelMap(getProudSkillBonusMap()) - .setAvatarType(1) - .setBornTime(this.getBornTime()) - .setFetterInfo(avatarFetter) - .setWearingFlycloakId(this.getFlyCloak()) - .setCostumeId(this.getCostume()); - - for (Entry entry : this.getSkillExtraChargeMap().entrySet()) { - avatarInfo.putSkillMap(entry.getKey(), AvatarSkillInfo.newBuilder().setMaxChargeCount(entry.getValue()).build()); - } - - for (GameItem item : this.getEquips().values()) { - avatarInfo.addEquipGuidList(item.getGuid()); - } - - avatarInfo.putPropMap(PlayerProperty.PROP_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, this.getLevel())); - avatarInfo.putPropMap(PlayerProperty.PROP_EXP.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_EXP, this.getExp())); - avatarInfo.putPropMap(PlayerProperty.PROP_BREAK_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_BREAK_LEVEL, this.getPromoteLevel())); - avatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_VAL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_VAL, 0)); - avatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_PENALTY_TIME.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_PENALTY_TIME, 0)); - - return avatarInfo.build(); - } + public float getCurrentHp() { + return currentHp; + } - // used only in character showcase - public ShowAvatarInfo toShowAvatarInfoProto() { - AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder() - .setExpLevel(this.getFetterLevel()); + public void setCurrentHp(float currentHp) { + this.currentHp = currentHp; + } - ShowAvatarInfo.Builder showAvatarInfo = ShowAvatarInfoOuterClass.ShowAvatarInfo.newBuilder() - .setAvatarId(avatarId) - .addAllTalentIdList(this.getTalentIdList()) - .putAllFightPropMap(this.getFightProperties()) - .setSkillDepotId(this.getSkillDepotId()) - .setCoreProudSkillLevel(this.getCoreProudSkillLevel()) - .addAllInherentProudSkillList(this.getProudSkillList()) - .putAllSkillLevelMap(this.getSkillLevelMap()) - .putAllProudSkillExtraLevelMap(this.getProudSkillBonusMap()) - .setFetterInfo(avatarFetter) - .setCostumeId(this.getCostume()); + public void setCurrentEnergy() { + if (GAME_OPTIONS.energyUsage) { + this.setCurrentEnergy(this.currentEnergy); + } + } - showAvatarInfo.putPropMap(PlayerProperty.PROP_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, this.getLevel())); - showAvatarInfo.putPropMap(PlayerProperty.PROP_EXP.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_EXP, this.getExp())); - showAvatarInfo.putPropMap(PlayerProperty.PROP_BREAK_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_BREAK_LEVEL, this.getPromoteLevel())); - showAvatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_VAL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_VAL, this.getSatiation())); - showAvatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_PENALTY_TIME.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_VAL, this.getSatiationPenalty())); - int maxStamina = this.getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA); - showAvatarInfo.putPropMap(PlayerProperty.PROP_MAX_STAMINA.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_MAX_STAMINA, maxStamina)); + public void setCurrentEnergy(float currentEnergy) { + if (this.getSkillDepot() != null && this.getSkillDepot().getEnergySkillData() != null) { + ElementType element = this.getSkillDepot().getElementType(); + this.setFightProperty(element.getMaxEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal()); - for (GameItem item : this.getEquips().values()) { - if (item.getItemType() == ItemType.ITEM_RELIQUARY) { - showAvatarInfo.addEquipList(ShowEquip.newBuilder() - .setItemId(item.getItemId()) - .setReliquary(item.toReliquaryProto())); - } else if (item.getItemType() == ItemType.ITEM_WEAPON) { - showAvatarInfo.addEquipList(ShowEquip.newBuilder() - .setItemId(item.getItemId()) - .setWeapon(item.toWeaponProto())); - } - } + if (GAME_OPTIONS.energyUsage) { + this.setFightProperty(element.getCurEnergyProp(), currentEnergy); + } + else { + this.setFightProperty(element.getCurEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal()); + } + } + } - return showAvatarInfo.build(); - } - - @PostLoad - private void onLoad() { - - } - - @PrePersist - private void prePersist() { - this.currentHp = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); - } + public void setCurrentEnergy(FightProperty curEnergyProp, float currentEnergy) { + if (GAME_OPTIONS.energyUsage) { + this.setFightProperty(curEnergyProp, currentEnergy); + this.currentEnergy = currentEnergy; + this.save(); + } + } + + public Int2FloatOpenHashMap getFightProperties() { + return fightProp; + } + + 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 Set getTalentIdList() { + return talentIdList; + } + + public int getCoreProudSkillLevel() { + return coreProudSkillLevel; + } + + public void setCoreProudSkillLevel(int constLevel) { + this.coreProudSkillLevel = constLevel; + } + + public Set getProudSkillList() { + return proudSkillList; + } + + public int getFlyCloak() { + return flyCloak; + } + + public void setFlyCloak(int flyCloak) { + this.flyCloak = flyCloak; + } + + public int getCostume() { + return costume; + } + + public void setCostume(int costume) { + this.costume = costume; + } + + public int getBornTime() { + return bornTime; + } + + public boolean equipItem(GameItem item, boolean shouldRecalc) { + // Sanity check equip type + EquipType itemEquipType = item.getItemData().getEquipType(); + if (itemEquipType == EquipType.EQUIP_NONE) { + return false; + } + + // Check if other avatars have this item equipped + Avatar otherAvatar = getPlayer().getAvatars().getAvatarById(item.getEquipCharacter()); + if (otherAvatar != null) { + // Unequip other avatar's item + if (otherAvatar.unequipItem(item.getItemData().getEquipType())) { + getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(otherAvatar, item.getItemData().getEquipType())); + } + // Swap with other avatar + if (getEquips().containsKey(itemEquipType.getValue())) { + GameItem toSwap = this.getEquipBySlot(itemEquipType); + otherAvatar.equipItem(toSwap, false); + } + // Recalc + otherAvatar.recalcStats(); + } else if (getEquips().containsKey(itemEquipType.getValue())) { + // Unequip item in current slot if it exists + unequipItem(itemEquipType); + } + + // Set equip + getEquips().put(itemEquipType.getValue(), item); + + if (itemEquipType == EquipType.EQUIP_WEAPON && getPlayer().getWorld() != null) { + item.setWeaponEntityId(this.getPlayer().getWorld().getNextEntityId(EntityIdType.WEAPON)); + } + + item.setEquipCharacter(this.getAvatarId()); + item.save(); + + if (this.getPlayer().hasSentAvatarDataNotify()) { + this.getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(this, item)); + } + + if (shouldRecalc) { + this.recalcStats(); + } + + return true; + } + + public boolean unequipItem(EquipType slot) { + GameItem item = getEquips().remove(slot.getValue()); + + if (item != null) { + item.setEquipCharacter(0); + item.save(); + return true; + } + + return false; + } + + public void recalcStats() { + recalcStats(false); + } + + public void recalcStats(boolean forceSendAbilityChange) { + // Setup + AvatarData data = this.getAvatarData(); + AvatarPromoteData promoteData = GameData.getAvatarPromoteData(data.getAvatarPromoteId(), this.getPromoteLevel()); + Int2IntOpenHashMap setMap = new Int2IntOpenHashMap(); + + // Extra ability embryos + Set prevExtraAbilityEmbryos = this.getExtraAbilityEmbryos(); + this.extraAbilityEmbryos = new HashSet<>(); + + // Fetters + this.setFetterList(data.getFetters()); + this.setNameCardRewardId(data.getNameCardRewardId()); + this.setNameCardId(data.getNameCardId()); + + // 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); + + // Store current energy value for later + float currentEnergy = (this.getSkillDepot() != null) ? this.getFightProperty(this.getSkillDepot().getElementType().getCurEnergyProp()) : 0f; + + // Clear properties + this.getFightProperties().clear(); + + // Base stats + this.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, data.getBaseHp(this.getLevel())); + this.setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, data.getBaseAttack(this.getLevel())); + this.setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, data.getBaseDefense(this.getLevel())); + this.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL, data.getBaseCritical()); + this.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL_HURT, data.getBaseCriticalHurt()); + this.setFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 1f); + + if (promoteData != null) { + for (FightPropData fightPropData : promoteData.getAddProps()) { + this.addFightProperty(fightPropData.getProp(), fightPropData.getValue()); + } + } + + // Set energy usage + setCurrentEnergy(currentEnergy); + + // Artifacts + for (int slotId = 1; slotId <= 5; slotId++) { + // Get artifact + GameItem equip = this.getEquipBySlot(slotId); + if (equip == null) { + continue; + } + // Artifact main stat + ReliquaryMainPropData mainPropData = GameData.getReliquaryMainPropDataMap().get(equip.getMainPropId()); + if (mainPropData != null) { + ReliquaryLevelData levelData = GameData.getRelicLevelData(equip.getItemData().getRankLevel(), equip.getLevel()); + if (levelData != null) { + this.addFightProperty(mainPropData.getFightProp(), levelData.getPropValue(mainPropData.getFightProp())); + } + } + // Artifact sub stats + for (int appendPropId : equip.getAppendPropIdList()) { + ReliquaryAffixData affixData = GameData.getReliquaryAffixDataMap().get(appendPropId); + if (affixData != null) { + this.addFightProperty(affixData.getFightProp(), affixData.getPropValue()); + } + } + // Set bonus + if (equip.getItemData().getSetId() > 0) { + setMap.addTo(equip.getItemData().getSetId(), 1); + } + } + + // Set stuff + for (Int2IntOpenHashMap.Entry e : setMap.int2IntEntrySet()) { + ReliquarySetData setData = GameData.getReliquarySetDataMap().get(e.getIntKey()); + if (setData == null) { + continue; + } + + // Calculate how many items are from the set + int amount = e.getIntValue(); + + // Add affix data from set bonus + for (int setIndex = 0; setIndex < setData.getSetNeedNum().length; setIndex++) { + if (amount >= setData.getSetNeedNum()[setIndex]) { + int affixId = (setData.getEquipAffixId() * 10) + setIndex; + + EquipAffixData affix = GameData.getEquipAffixDataMap().get(affixId); + if (affix == null) { + continue; + } + + // Add properties from this affix to our avatar + for (FightPropData prop : affix.getAddProps()) { + this.addFightProperty(prop.getProp(), prop.getValue()); + } + + // Add any skill strings from this affix + this.addToExtraAbilityEmbryos(affix.getOpenConfig(), true); + } else { + break; + } + } + } + + // Weapon + GameItem weapon = this.getWeapon(); + if (weapon != null) { + // Add stats + WeaponCurveData curveData = GameData.getWeaponCurveDataMap().get(weapon.getLevel()); + if (curveData != null) { + for (WeaponProperty weaponProperty : weapon.getItemData().getWeaponProperties()) { + this.addFightProperty(weaponProperty.getFightProp(), weaponProperty.getInitValue() * curveData.getMultByProp(weaponProperty.getType())); + } + } + // Weapon promotion stats + WeaponPromoteData wepPromoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel()); + if (wepPromoteData != null) { + for (FightPropData prop : wepPromoteData.getAddProps()) { + if (prop.getValue() == 0f || prop.getProp() == null) { + continue; + } + this.addFightProperty(prop.getProp(), prop.getValue()); + } + } + // Add weapon skill from affixes + if (weapon.getAffixes() != null && weapon.getAffixes().size() > 0) { + // Weapons usually dont have more than one affix but just in case... + for (int af : weapon.getAffixes()) { + if (af == 0) { + continue; + } + // Calculate affix id + int affixId = (af * 10) + weapon.getRefinement(); + EquipAffixData affix = GameData.getEquipAffixDataMap().get(affixId); + if (affix == null) { + continue; + } + + // Add properties from this affix to our avatar + for (FightPropData prop : affix.getAddProps()) { + this.addFightProperty(prop.getProp(), prop.getValue()); + } + + // Add any skill strings from this affix + this.addToExtraAbilityEmbryos(affix.getOpenConfig(), true); + } + } + } + + // Add proud skills and unlock them if needed + AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.getSkillDepotId()); + this.getProudSkillList().clear(); + for (InherentProudSkillOpens openData : skillDepot.getInherentProudSkillOpens()) { + if (openData.getProudSkillGroupId() == 0) { + continue; + } + if (openData.getNeedAvatarPromoteLevel() <= this.getPromoteLevel()) { + int proudSkillId = (openData.getProudSkillGroupId() * 100) + 1; + if (GameData.getProudSkillDataMap().containsKey(proudSkillId)) { + this.getProudSkillList().add(proudSkillId); + } + } + } + + // Proud skills + for (int proudSkillId : this.getProudSkillList()) { + ProudSkillData proudSkillData = GameData.getProudSkillDataMap().get(proudSkillId); + if (proudSkillData == null) { + continue; + } + + // Add properties from this proud skill to our avatar + for (FightPropData prop : proudSkillData.getAddProps()) { + this.addFightProperty(prop.getProp(), prop.getValue()); + } + + // Add any skill strings from this proud skill + this.addToExtraAbilityEmbryos(proudSkillData.getOpenConfig(), true); + } + + // Constellations + if (this.getTalentIdList().size() > 0) { + for (int talentId : this.getTalentIdList()) { + AvatarTalentData avatarTalentData = GameData.getAvatarTalentDataMap().get(talentId); + if (avatarTalentData == null) { + return; + } + + // Add any skill strings from this constellation + this.addToExtraAbilityEmbryos(avatarTalentData.getOpenConfig(), false); + } + } + + // 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); + + // Packet + if (getPlayer() != null && getPlayer().hasSentAvatarDataNotify()) { + // Update stats for client + getPlayer().sendPacket(new PacketAvatarFightPropNotify(this)); + // Update client abilities + EntityAvatar entity = this.getAsEntity(); + if (entity != null && (!this.getExtraAbilityEmbryos().equals(prevExtraAbilityEmbryos) || forceSendAbilityChange)) { + getPlayer().sendPacket(new PacketAbilityChangeNotify(entity)); + } + } + } + + public void addToExtraAbilityEmbryos(String openConfig, boolean forceAdd) { + if (openConfig == null || openConfig.length() == 0) { + return; + } + + OpenConfigEntry entry = GameData.getOpenConfigEntries().get(openConfig); + if (entry == null) { + if (forceAdd) { + // Add config string to ability skill list anyways + this.getExtraAbilityEmbryos().add(openConfig); + } + return; + } + + if (entry.getAddAbilities() != null) { + for (String ability : entry.getAddAbilities()) { + this.getExtraAbilityEmbryos().add(ability); + } + } + } + + public void recalcConstellations() { + // Clear first + this.getProudSkillBonusMap().clear(); + this.getSkillExtraChargeMap().clear(); + + // Sanity checks + if (getData() == null || getData().getSkillDepot() == null) { + return; + } + + if (this.getTalentIdList().size() > 0) { + for (int talentId : this.getTalentIdList()) { + AvatarTalentData avatarTalentData = GameData.getAvatarTalentDataMap().get(talentId); + + if (avatarTalentData == null || avatarTalentData.getOpenConfig() == null || avatarTalentData.getOpenConfig().length() == 0) { + continue; + } + + // Get open config to find which skill should be boosted + OpenConfigEntry entry = GameData.getOpenConfigEntries().get(avatarTalentData.getOpenConfig()); + if (entry == null) { + continue; + } + + // Check if we can add charges to a skill + if (entry.getSkillPointModifiers() != null) { + for (SkillPointModifier mod : entry.getSkillPointModifiers()) { + AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(mod.getSkillId()); + + if (skillData == null) continue; + + int charges = skillData.getMaxChargeNum() + mod.getDelta(); + + this.getSkillExtraChargeMap().put(mod.getSkillId(), charges); + } + continue; + } + + // Check if a skill can be boosted by +3 levels + int skillId = 0; + + if (entry.getExtraTalentIndex() == 2 && this.getData().getSkillDepot().getSkills().size() >= 2) { + // E skill + skillId = this.getData().getSkillDepot().getSkills().get(1); + } else if (entry.getExtraTalentIndex() == 9) { + // Ult skill + skillId = this.getData().getSkillDepot().getEnergySkill(); + } + + // Sanity check + if (skillId == 0) { + continue; + } + + // Get proud skill group id + AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(skillId); + + if (skillData == null) { + continue; + } + + // Add to bonus list + this.getProudSkillBonusMap().put(skillData.getProudSkillGroupId(), 3); + } + } + } + + public EntityAvatar getAsEntity() { + for (EntityAvatar entity : getPlayer().getTeamManager().getActiveTeam()) { + if (entity.getAvatar() == this) { + return entity; + } + } + return null; + } + + public int getEntityId() { + EntityAvatar entity = getAsEntity(); + return entity != null ? entity.getId() : 0; + } + + public void save() { + DatabaseHelper.saveAvatar(this); + } + + public AvatarInfo toProto() { + int fetterLevel = this.getFetterLevel(); + AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder() + .setExpLevel(fetterLevel); + + if (fetterLevel != 10) { + avatarFetter.setExpNumber(this.getFetterExp()); + } + + + if (this.getFetterList() != null) { + for (int i = 0; i < this.getFetterList().size(); i++) { + avatarFetter.addFetterList( + FetterData.newBuilder() + .setFetterId(this.getFetterList().get(i)) + .setFetterState(FetterState.FINISH.getValue()) + ); + } + } + + int cardId = this.getNameCardId(); + + if (this.getPlayer().getNameCardList().contains(cardId)) { + avatarFetter.addRewardedFetterLevelList(10); + } + + AvatarInfo.Builder avatarInfo = AvatarInfo.newBuilder() + .setAvatarId(this.getAvatarId()) + .setGuid(this.getGuid()) + .setLifeState(1) + .addAllTalentIdList(this.getTalentIdList()) + .putAllFightPropMap(this.getFightProperties()) + .setSkillDepotId(this.getSkillDepotId()) + .setCoreProudSkillLevel(this.getCoreProudSkillLevel()) + .putAllSkillLevelMap(this.getSkillLevelMap()) + .addAllInherentProudSkillList(this.getProudSkillList()) + .putAllProudSkillExtraLevelMap(getProudSkillBonusMap()) + .setAvatarType(1) + .setBornTime(this.getBornTime()) + .setFetterInfo(avatarFetter) + .setWearingFlycloakId(this.getFlyCloak()) + .setCostumeId(this.getCostume()); + + for (Entry entry : this.getSkillExtraChargeMap().entrySet()) { + avatarInfo.putSkillMap(entry.getKey(), AvatarSkillInfo.newBuilder().setMaxChargeCount(entry.getValue()).build()); + } + + for (GameItem item : this.getEquips().values()) { + avatarInfo.addEquipGuidList(item.getGuid()); + } + + avatarInfo.putPropMap(PlayerProperty.PROP_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, this.getLevel())); + avatarInfo.putPropMap(PlayerProperty.PROP_EXP.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_EXP, this.getExp())); + avatarInfo.putPropMap(PlayerProperty.PROP_BREAK_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_BREAK_LEVEL, this.getPromoteLevel())); + avatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_VAL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_VAL, 0)); + avatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_PENALTY_TIME.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_PENALTY_TIME, 0)); + + return avatarInfo.build(); + } + + // used only in character showcase + public ShowAvatarInfo toShowAvatarInfoProto() { + AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder() + .setExpLevel(this.getFetterLevel()); + + ShowAvatarInfo.Builder showAvatarInfo = ShowAvatarInfoOuterClass.ShowAvatarInfo.newBuilder() + .setAvatarId(avatarId) + .addAllTalentIdList(this.getTalentIdList()) + .putAllFightPropMap(this.getFightProperties()) + .setSkillDepotId(this.getSkillDepotId()) + .setCoreProudSkillLevel(this.getCoreProudSkillLevel()) + .addAllInherentProudSkillList(this.getProudSkillList()) + .putAllSkillLevelMap(this.getSkillLevelMap()) + .putAllProudSkillExtraLevelMap(this.getProudSkillBonusMap()) + .setFetterInfo(avatarFetter) + .setCostumeId(this.getCostume()); + + showAvatarInfo.putPropMap(PlayerProperty.PROP_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, this.getLevel())); + showAvatarInfo.putPropMap(PlayerProperty.PROP_EXP.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_EXP, this.getExp())); + showAvatarInfo.putPropMap(PlayerProperty.PROP_BREAK_LEVEL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_BREAK_LEVEL, this.getPromoteLevel())); + showAvatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_VAL.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_VAL, this.getSatiation())); + showAvatarInfo.putPropMap(PlayerProperty.PROP_SATIATION_PENALTY_TIME.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_SATIATION_VAL, this.getSatiationPenalty())); + int maxStamina = this.getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA); + showAvatarInfo.putPropMap(PlayerProperty.PROP_MAX_STAMINA.getId(), ProtoHelper.newPropValue(PlayerProperty.PROP_MAX_STAMINA, maxStamina)); + + for (GameItem item : this.getEquips().values()) { + if (item.getItemType() == ItemType.ITEM_RELIQUARY) { + showAvatarInfo.addEquipList(ShowEquip.newBuilder() + .setItemId(item.getItemId()) + .setReliquary(item.toReliquaryProto())); + } else if (item.getItemType() == ItemType.ITEM_WEAPON) { + showAvatarInfo.addEquipList(ShowEquip.newBuilder() + .setItemId(item.getItemId()) + .setWeapon(item.toWeaponProto())); + } + } + + return showAvatarInfo.build(); + } + + @PostLoad + private void onLoad() { + + } + + @PrePersist + private void prePersist() { + this.currentHp = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); + } } diff --git a/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java b/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java index 3c3596277..d7f6272f0 100644 --- a/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java +++ b/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java @@ -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 { - private final Int2ObjectMap avatars; - private final Long2ObjectMap avatarsGuid; - - public AvatarStorage(Player player) { - super(player); - this.avatars = new Int2ObjectOpenHashMap<>(); - this.avatarsGuid = new Long2ObjectOpenHashMap<>(); - } + private final Int2ObjectMap avatars; + private final Long2ObjectMap avatarsGuid; - public Int2ObjectMap 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 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 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 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 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 iterator() { + return getAvatars().values().iterator(); + } } diff --git a/src/main/java/emu/grasscutter/game/battlepass/BattlePassManager.java b/src/main/java/emu/grasscutter/game/battlepass/BattlePassManager.java index 6c95db479..ac5608503 100644 --- a/src/main/java/emu/grasscutter/game/battlepass/BattlePassManager.java +++ b/src/main/java/emu/grasscutter/game/battlepass/BattlePassManager.java @@ -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 missions; private Map 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 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 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 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 missionIdList) { - // Obvious exploit check - if (missionIdList.size() > GameData.getBattlePassMissionDataMap().size()) { - return; - } - - List 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 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 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 takeOptionList) { - List 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 rewardItems = null; - - if (rewardList.size() > 0) { + // Handlers + public void takeMissionPoint(List 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 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(); + 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(); + 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 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 takeOptionList) { + List 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 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(); + + 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(); + + 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); } } diff --git a/src/main/java/emu/grasscutter/game/battlepass/BattlePassSystem.java b/src/main/java/emu/grasscutter/game/battlepass/BattlePassSystem.java index 240bcb473..c89fc09fa 100644 --- a/src/main/java/emu/grasscutter/game/battlepass/BattlePassSystem.java +++ b/src/main/java/emu/grasscutter/game/battlepass/BattlePassSystem.java @@ -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> 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 triggerList = getTriggers().computeIfAbsent(missionData.getTriggerType(), e -> new ArrayList<>()); - triggerList.add(missionData); - } - } - } + private final Map> cachedTriggers; - public GameServer getServer() { - return server; - } - - private Map> 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 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 triggerList = getTriggers().computeIfAbsent(missionData.getTriggerType(), e -> new ArrayList<>()); + triggerList.add(missionData); + } + } + } + + public GameServer getServer() { + return server; + } + + private Map> 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 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)); + } + } } diff --git a/src/main/java/emu/grasscutter/game/combine/CombineManger.java b/src/main/java/emu/grasscutter/game/combine/CombineManger.java index 0ec9750dc..90920abd1 100644 --- a/src/main/java/emu/grasscutter/game/combine/CombineManger.java +++ b/src/main/java/emu/grasscutter/game/combine/CombineManger.java @@ -31,59 +31,59 @@ import java.util.List; import com.google.gson.reflect.TypeToken; public class CombineManger extends BaseGameSystem { - private final static Int2ObjectMap> reliquaryDecomposeData = new Int2ObjectOpenHashMap<>(); + private final static Int2ObjectMap> 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 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 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 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()); diff --git a/src/main/java/emu/grasscutter/game/drop/DropSystem.java b/src/main/java/emu/grasscutter/game/drop/DropSystem.java index 2917df436..b2b28852a 100644 --- a/src/main/java/emu/grasscutter/game/drop/DropSystem.java +++ b/src/main/java/emu/grasscutter/game/drop/DropSystem.java @@ -32,7 +32,7 @@ public class DropSystem extends BaseGameSystem { this.dropData = new Int2ObjectOpenHashMap<>(); this.load(); } - + public Int2ObjectMap> getDropData() { return dropData; } @@ -41,7 +41,7 @@ public class DropSystem extends BaseGameSystem { try (Reader fileReader = DataLoader.loadReader("Drop.json")) { getDropData().clear(); List 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()); } diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java index 7ed773baa..a0b0978be 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java @@ -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 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 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(); + } + } } diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/DungeonChallenge.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/DungeonChallenge.java index 5093b8665..844f23c5c 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/challenge/DungeonChallenge.java +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/DungeonChallenge.java @@ -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> dungeonDropData = new Int2ObjectOpenHashMap<>(); + private final static Int2ObjectMap> dungeonDropData = new Int2ObjectOpenHashMap<>(); - public static void initialize() { - // Read the data we need for dungeon rewards drops. - try (Reader fileReader = DataLoader.loadReader("DungeonDrop.json")) { - List 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 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 paramList, - int timeLimit, int goal, - List 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 paramList, + int timeLimit, int goal, + List 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 rollRewards(boolean useCondensed) { - List 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 dropEntries = dungeonDropData.get(dungeonId); + private List rollRewards(boolean useCondensed) { + List 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 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 rewards = new ArrayList<>(); + // Get rewards. + List 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()); + } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java index 63d94b866..431054d14 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java @@ -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(); + } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java index 2d84cafa4..1f88f451a 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java @@ -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(); + } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityItem.java b/src/main/java/emu/grasscutter/game/entity/EntityItem.java index 63817f7c1..c4befb408 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityItem.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityItem.java @@ -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(); + } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java index 2ff99bef4..4c1426c02 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java @@ -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(); + } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java index 16d8b1f6f..e862469fd 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java @@ -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 vehicleMembers; + private float curStamina; + private List 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(); - } + 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(); + } - @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 getVehicleMembers() { return vehicleMembers; } + public List 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(); + } } diff --git a/src/main/java/emu/grasscutter/game/entity/GameEntity.java b/src/main/java/emu/grasscutter/game/entity/GameEntity.java index c571f54f0..e505969c0 100644 --- a/src/main/java/emu/grasscutter/game/entity/GameEntity.java +++ b/src/main/java/emu/grasscutter/game/entity/GameEntity.java @@ -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 metaOverrideMap; - private Int2ObjectMap 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 getMetaOverrideMap() { - if (this.metaOverrideMap == null) { - this.metaOverrideMap = new HashMap<>(); - } - return this.metaOverrideMap; - } - - public Int2ObjectMap 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 metaOverrideMap; + private Int2ObjectMap 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 getMetaOverrideMap() { + if (this.metaOverrideMap == null) { + this.metaOverrideMap = new HashMap<>(); + } + return this.metaOverrideMap; + } - public SpawnDataEntry getSpawnEntry() { - return spawnEntry; - } + public Int2ObjectMap 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(); } diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java index 1fb146240..43e6e0b80 100644 --- a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java +++ b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java @@ -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()); + } + + } } diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/chest/BossChestInteractHandler.java b/src/main/java/emu/grasscutter/game/entity/gadget/chest/BossChestInteractHandler.java index 3bc62f4f9..d02a3760d 100644 --- a/src/main/java/emu/grasscutter/game/entity/gadget/chest/BossChestInteractHandler.java +++ b/src/main/java/emu/grasscutter/game/entity/gadget/chest/BossChestInteractHandler.java @@ -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; } diff --git a/src/main/java/emu/grasscutter/game/expedition/ExpeditionSystem.java b/src/main/java/emu/grasscutter/game/expedition/ExpeditionSystem.java index 0faca23b7..ddc0535d5 100644 --- a/src/main/java/emu/grasscutter/game/expedition/ExpeditionSystem.java +++ b/src/main/java/emu/grasscutter/game/expedition/ExpeditionSystem.java @@ -24,16 +24,16 @@ public class ExpeditionSystem extends BaseGameSystem { this.expeditionRewardData = new Int2ObjectOpenHashMap<>(); this.load(); } - - public Int2ObjectMap> getExpeditionRewardDataList() { - return expeditionRewardData; + + public Int2ObjectMap> getExpeditionRewardDataList() { + return expeditionRewardData; } public synchronized void load() { try (Reader fileReader = DataLoader.loadReader("ExpeditionReward.json")) { getExpeditionRewardDataList().clear(); List 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()); } diff --git a/src/main/java/emu/grasscutter/game/friends/FriendsList.java b/src/main/java/emu/grasscutter/game/friends/FriendsList.java index 399406295..1b09b6cd2 100644 --- a/src/main/java/emu/grasscutter/game/friends/FriendsList.java +++ b/src/main/java/emu/grasscutter/game/friends/FriendsList.java @@ -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 friends; - private final Int2ObjectMap pendingFriends; - - private boolean loaded = false; - - public FriendsList(Player player) { - super(player); - this.friends = new Int2ObjectOpenHashMap(); - this.pendingFriends = new Int2ObjectOpenHashMap(); - } - - public boolean hasLoaded() { - return loaded; - } - - public synchronized Int2ObjectMap getFriends() { - return friends; - } - - public synchronized Int2ObjectMap 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 friends; + private final Int2ObjectMap 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(); + this.pendingFriends = new Int2ObjectOpenHashMap(); + } - 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 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 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 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 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 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 friendships = DatabaseHelper.getReverseFriends(getPlayer()); + for (Friendship friend : friendships) { + friend.setFriendProfile(this.getPlayer()); + friend.save(); + } + } } diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java index 4b8bd95c2..17d957387 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java @@ -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; + } } diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaSystem.java b/src/main/java/emu/grasscutter/game/gacha/GachaSystem.java index c1c591584..1d24c0b79 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaSystem.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaSystem.java @@ -48,400 +48,400 @@ import it.unimi.dsi.fastutil.ints.IntList; import org.greenrobot.eventbus.Subscribe; public class GachaSystem extends BaseGameSystem { - private final Int2ObjectMap gachaBanners; - private WatchService watchService; + private final Int2ObjectMap 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 getGachaBanners() { - return gachaBanners; - } + public Int2ObjectMap 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 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 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= 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= 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 list = new ArrayList<>(); - int stardust = 0, starglitter = 0; + // Add to character + gachaInfo.addTotalPulls(times); + BannerPools pools = new BannerPools(banner); + List 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); + } } diff --git a/src/main/java/emu/grasscutter/game/inventory/Inventory.java b/src/main/java/emu/grasscutter/game/inventory/Inventory.java index b2ee9aebf..23a833edb 100644 --- a/src/main/java/emu/grasscutter/game/inventory/Inventory.java +++ b/src/main/java/emu/grasscutter/game/inventory/Inventory.java @@ -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 { - private final Long2ObjectMap store; - private final Int2ObjectMap 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 store; + private final Int2ObjectMap inventoryTypes; - public AvatarStorage getAvatarStorage() { - return this.getPlayer().getAvatars(); - } + public Inventory(Player player) { + super(player); - public Long2ObjectMap getItems() { - return store; - } - - public Int2ObjectMap 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 getItems() { + return store; + } + + public Int2ObjectMap 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 items) { - this.addItems(items, null); - } - - public void addItems(Collection items, ActionReason reason) { - List 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 items) { - addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), null); - } - - public void addItemParamDatas(Collection items) { - addItemParamDatas(items, null); - } - - public void addItemParamDatas(Collection 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 items) { + this.addItems(items, null); + } + + public void addItems(Collection items, ActionReason reason) { + List 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 items) { + addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), null); + } + + public void addItemParamDatas(Collection items) { + addItemParamDatas(items, null); + } + + public void addItemParamDatas(Collection 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 { 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 { 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 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 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 items) { + // TODO Bulk delete + for (GameItem item : items) { + this.removeItem(item, item.getCount()); + } + } - @Override - public Iterator 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 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 iterator() { + return this.getItems().values().iterator(); + } } diff --git a/src/main/java/emu/grasscutter/game/mail/MailHandler.java b/src/main/java/emu/grasscutter/game/mail/MailHandler.java index 12e741816..7d0880c27 100644 --- a/src/main/java/emu/grasscutter/game/mail/MailHandler.java +++ b/src/main/java/emu/grasscutter/game/mail/MailHandler.java @@ -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; - - public MailHandler(Player player) { - super(player); - - this.mail = new ArrayList<>(); - } + private final List mail; - public List 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 getMail() { + return mail; + } - if (message != null) { - this.getMail().remove(mailId); - message.expireTime = 0; - message.save(); - - return true; - } + // ---------------------MAIL------------------------ - return false; - } - - public void deleteMail(List mailList) { - List sortedMailList = new ArrayList<>(); - sortedMailList.addAll(mailList); - Collections.sort(sortedMailList, Collections.reverseOrder()); - - List 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 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 mailList) { + List sortedMailList = new ArrayList<>(); + sortedMailList.addAll(mailList); + Collections.sort(sortedMailList, Collections.reverseOrder()); + + List 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 mailList = DatabaseHelper.getAllMail(this.getPlayer()); + + for (Mail mail : mailList) { + this.getMail().add(mail); + } + } } diff --git a/src/main/java/emu/grasscutter/game/managers/CookingManager.java b/src/main/java/emu/grasscutter/game/managers/CookingManager.java index 5964d4f19..4ede72403 100644 --- a/src/main/java/emu/grasscutter/game/managers/CookingManager.java +++ b/src/main/java/emu/grasscutter/game/managers/CookingManager.java @@ -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); diff --git a/src/main/java/emu/grasscutter/game/managers/FurnitureManager.java b/src/main/java/emu/grasscutter/game/managers/FurnitureManager.java index d18119405..38e2dcd7e 100644 --- a/src/main/java/emu/grasscutter/game/managers/FurnitureManager.java +++ b/src/main/java/emu/grasscutter/game/managers/FurnitureManager.java @@ -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; // } diff --git a/src/main/java/emu/grasscutter/game/managers/ResinManager.java b/src/main/java/emu/grasscutter/game/managers/ResinManager.java index 78f4b09fd..aa85f61ac 100644 --- a/src/main/java/emu/grasscutter/game/managers/ResinManager.java +++ b/src/main/java/emu/grasscutter/game/managers/ResinManager.java @@ -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); } diff --git a/src/main/java/emu/grasscutter/game/managers/SotSManager.java b/src/main/java/emu/grasscutter/game/managers/SotSManager.java index 8002fafa8..4ecb30ff2 100644 --- a/src/main/java/emu/grasscutter/game/managers/SotSManager.java +++ b/src/main/java/emu/grasscutter/game/managers/SotSManager.java @@ -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 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 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); + } + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/chat/ChatManager.java b/src/main/java/emu/grasscutter/game/managers/chat/ChatManager.java index 66acd2e9a..3bf46e03a 100644 --- a/src/main/java/emu/grasscutter/game/managers/chat/ChatManager.java +++ b/src/main/java/emu/grasscutter/game/managers/chat/ChatManager.java @@ -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>> history = new HashMap<>(); + // We store the chat history for ongoing sessions in the form + // user id -> chat partner id -> [messages] + private final Map>> 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"); + } } diff --git a/src/main/java/emu/grasscutter/game/managers/deforestation/DeforestationManager.java b/src/main/java/emu/grasscutter/game/managers/deforestation/DeforestationManager.java index a0fb46588..e49cad486 100644 --- a/src/main/java/emu/grasscutter/game/managers/deforestation/DeforestationManager.java +++ b/src/main/java/emu/grasscutter/game/managers/deforestation/DeforestationManager.java @@ -21,7 +21,7 @@ public class DeforestationManager extends BasePlayerManager { private final ArrayList currentRecord; private final static HashMap 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; diff --git a/src/main/java/emu/grasscutter/game/managers/energy/EnergyManager.java b/src/main/java/emu/grasscutter/game/managers/energy/EnergyManager.java index 710ec31a4..e94a219fc 100644 --- a/src/main/java/emu/grasscutter/game/managers/energy/EnergyManager.java +++ b/src/main/java/emu/grasscutter/game/managers/energy/EnergyManager.java @@ -448,4 +448,4 @@ public class EnergyManager extends BasePlayerManager { } } } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/game/managers/forging/ForgingManager.java b/src/main/java/emu/grasscutter/game/managers/forging/ForgingManager.java index 9cd3dc63b..50aeb94a8 100644 --- a/src/main/java/emu/grasscutter/game/managers/forging/ForgingManager.java +++ b/src/main/java/emu/grasscutter/game/managers/forging/ForgingManager.java @@ -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 determineCurrentForgeQueueData() { - Map res = new HashMap<>(); - int currentTime = Utils.getCurrentSeconds(); + private synchronized Map determineCurrentForgeQueueData() { + Map 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 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 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(); - 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(); + 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)); + } } diff --git a/src/main/java/emu/grasscutter/game/managers/mapmark/MapMarksManager.java b/src/main/java/emu/grasscutter/game/managers/mapmark/MapMarksManager.java index 703385bc6..713cd69c0 100644 --- a/src/main/java/emu/grasscutter/game/managers/mapmark/MapMarksManager.java +++ b/src/main/java/emu/grasscutter/game/managers/mapmark/MapMarksManager.java @@ -18,7 +18,7 @@ public class MapMarksManager extends BasePlayerManager { public MapMarksManager(Player player) { super(player); } - + public Map 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(); } diff --git a/src/main/java/emu/grasscutter/game/managers/stamina/StaminaManager.java b/src/main/java/emu/grasscutter/game/managers/stamina/StaminaManager.java index b45fdac1f..b23ae0c28 100644 --- a/src/main/java/emu/grasscutter/game/managers/stamina/StaminaManager.java +++ b/src/main/java/emu/grasscutter/game/managers/stamina/StaminaManager.java @@ -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(); } diff --git a/src/main/java/emu/grasscutter/game/player/BasePlayerDataManager.java b/src/main/java/emu/grasscutter/game/player/BasePlayerDataManager.java index 68447e892..a8f70bfb4 100644 --- a/src/main/java/emu/grasscutter/game/player/BasePlayerDataManager.java +++ b/src/main/java/emu/grasscutter/game/player/BasePlayerDataManager.java @@ -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; diff --git a/src/main/java/emu/grasscutter/game/player/BasePlayerManager.java b/src/main/java/emu/grasscutter/game/player/BasePlayerManager.java index 7508995c0..d3cbf9f90 100644 --- a/src/main/java/emu/grasscutter/game/player/BasePlayerManager.java +++ b/src/main/java/emu/grasscutter/game/player/BasePlayerManager.java @@ -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 */ diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index df4bad584..769e79012 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -86,30 +86,30 @@ import java.util.concurrent.LinkedBlockingQueue; @Entity(value = "players", useDiscriminator = false) public class Player { - @Id private int id; - @Indexed(options = @IndexOptions(unique = true)) private String accountId; - private transient Account account; - private transient GameSession session; - - private String nickname; - private String signature; - private int headImage; - private int nameCardId = 210001; - private Position position; - private Position rotation; - private PlayerBirthday birthday; - private PlayerCodex codex; - private boolean showAvatars; - private List showAvatarList; - private Map properties; - private int currentRealmId; - private int widgetId; - private int sceneId; + @Id private int id; + @Indexed(options = @IndexOptions(unique = true)) private String accountId; + private transient Account account; + private transient GameSession session; + + private String nickname; + private String signature; + private int headImage; + private int nameCardId = 210001; + private Position position; + private Position rotation; + private PlayerBirthday birthday; + private PlayerCodex codex; + private boolean showAvatars; + private List showAvatarList; + private Map properties; + private int currentRealmId; + private int widgetId; + private int sceneId; private int regionId; private int mainCharacterId; private boolean godmode; private boolean stamina; - + @Getter private Set nameCardList; @Getter private Set flyCloakList; @Getter private Set costumeList; @@ -123,23 +123,23 @@ public class Player { @Getter private Map unlockedRecipies; @Getter private List activeForges; - @Transient private long nextGuid = 0; - @Transient private int peerId; - @Transient private World world; - @Transient private Scene scene; - @Transient @Getter private int weatherId = 0; - @Transient @Getter private ClimateType climate = ClimateType.CLIMATE_SUNNY; - - // Player managers go here - @Getter private transient AvatarStorage avatars; - @Getter private transient Inventory inventory; - @Getter private transient FriendsList friendsList; - @Getter private transient MailHandler mailHandler; - @Getter private transient MessageHandler messageHandler; - @Getter private transient AbilityManager abilityManager; - @Getter private transient QuestManager questManager; - @Getter private transient TowerManager towerManager; - @Getter private transient SotSManager sotsManager; + @Transient private long nextGuid = 0; + @Transient private int peerId; + @Transient private World world; + @Transient private Scene scene; + @Transient @Getter private int weatherId = 0; + @Transient @Getter private ClimateType climate = ClimateType.CLIMATE_SUNNY; + + // Player managers go here + @Getter private transient AvatarStorage avatars; + @Getter private transient Inventory inventory; + @Getter private transient FriendsList friendsList; + @Getter private transient MailHandler mailHandler; + @Getter private transient MessageHandler messageHandler; + @Getter private transient AbilityManager abilityManager; + @Getter private transient QuestManager questManager; + @Getter private transient TowerManager towerManager; + @Getter private transient SotSManager sotsManager; @Getter private transient MapMarksManager mapMarksManager; @Getter private transient StaminaManager staminaManager; @Getter private transient EnergyManager energyManager; @@ -154,1276 +154,1276 @@ public class Player { // Manager data (Save-able to the database) private PlayerProfile playerProfile; private TeamManager teamManager; - private TowerData towerData; - private PlayerGachaInfo gachaInfo; - private PlayerOpenStateManager openStateManager; - private PlayerCollectionRecords collectionRecordStore; - private ArrayList shopLimit; - - @Getter private transient GameHome home; + private TowerData towerData; + private PlayerGachaInfo gachaInfo; + private PlayerOpenStateManager openStateManager; + private PlayerCollectionRecords collectionRecordStore; + private ArrayList shopLimit; - private boolean moonCard; - private Date moonCardStartTime; - private int moonCardDuration; - private Set moonCardGetTimes; + @Getter private transient GameHome home; - @Transient private boolean paused; - @Transient private int enterSceneToken; - @Transient private SceneLoadState sceneState; - @Transient private boolean hasSentAvatarDataNotify; - @Transient private long nextSendPlayerLocTime = 0; + private boolean moonCard; + private Date moonCardStartTime; + private int moonCardDuration; + private Set moonCardGetTimes; - private transient final Int2ObjectMap coopRequests; - private transient final Queue attackResults; - @Getter private transient final InvokeHandler combatInvokeHandler; - @Getter private transient final InvokeHandler abilityInvokeHandler; - @Getter private transient final InvokeHandler clientAbilityInitFinishHandler; + @Transient private boolean paused; + @Transient private int enterSceneToken; + @Transient private SceneLoadState sceneState; + @Transient private boolean hasSentAvatarDataNotify; + @Transient private long nextSendPlayerLocTime = 0; - private long springLastUsed; - private HashMap mapMarks; - private int nextResinRefresh; - private int lastDailyReset; + private transient final Int2ObjectMap coopRequests; + private transient final Queue attackResults; + @Getter private transient final InvokeHandler combatInvokeHandler; + @Getter private transient final InvokeHandler abilityInvokeHandler; + @Getter private transient final InvokeHandler clientAbilityInitFinishHandler; - @Deprecated - @SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only! - public Player() { - this.inventory = new Inventory(this); - this.avatars = new AvatarStorage(this); - this.friendsList = new FriendsList(this); - this.mailHandler = new MailHandler(this); - this.towerManager = new TowerManager(this); - this.abilityManager = new AbilityManager(this); - this.deforestationManager = new DeforestationManager(this); - this.questManager = new QuestManager(this); - this.position = new Position(GameConstants.START_POSITION); - this.rotation = new Position(0, 307, 0); - this.sceneId = 3; + private long springLastUsed; + private HashMap mapMarks; + private int nextResinRefresh; + private int lastDailyReset; + + @Deprecated + @SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only! + public Player() { + this.inventory = new Inventory(this); + this.avatars = new AvatarStorage(this); + this.friendsList = new FriendsList(this); + this.mailHandler = new MailHandler(this); + this.towerManager = new TowerManager(this); + this.abilityManager = new AbilityManager(this); + this.deforestationManager = new DeforestationManager(this); + this.questManager = new QuestManager(this); + this.position = new Position(GameConstants.START_POSITION); + this.rotation = new Position(0, 307, 0); + this.sceneId = 3; this.regionId = 1; - this.properties = new HashMap<>(); - for (PlayerProperty prop : PlayerProperty.values()) { - if (prop.getId() < 10000) { - continue; - } - this.properties.put(prop.getId(), 0); - } + this.properties = new HashMap<>(); + for (PlayerProperty prop : PlayerProperty.values()) { + if (prop.getId() < 10000) { + continue; + } + this.properties.put(prop.getId(), 0); + } - this.gachaInfo = new PlayerGachaInfo(); - this.nameCardList = new HashSet<>(); - this.flyCloakList = new HashSet<>(); - this.costumeList = new HashSet<>(); - this.towerData = new TowerData(); - this.collectionRecordStore = new PlayerCollectionRecords(); - this.unlockedForgingBlueprints = new HashSet<>(); - this.unlockedCombines = new HashSet<>(); - this.unlockedFurniture = new HashSet<>(); - this.unlockedFurnitureSuite = new HashSet<>(); - this.activeForges = new ArrayList<>(); - this.unlockedRecipies = new HashMap<>(); - this.sceneState = SceneLoadState.NONE; + this.gachaInfo = new PlayerGachaInfo(); + this.nameCardList = new HashSet<>(); + this.flyCloakList = new HashSet<>(); + this.costumeList = new HashSet<>(); + this.towerData = new TowerData(); + this.collectionRecordStore = new PlayerCollectionRecords(); + this.unlockedForgingBlueprints = new HashSet<>(); + this.unlockedCombines = new HashSet<>(); + this.unlockedFurniture = new HashSet<>(); + this.unlockedFurnitureSuite = new HashSet<>(); + this.activeForges = new ArrayList<>(); + this.unlockedRecipies = new HashMap<>(); + this.sceneState = SceneLoadState.NONE; - this.attackResults = new LinkedBlockingQueue<>(); - this.coopRequests = new Int2ObjectOpenHashMap<>(); - this.combatInvokeHandler = new InvokeHandler(PacketCombatInvocationsNotify.class); - this.abilityInvokeHandler = new InvokeHandler(PacketAbilityInvocationsNotify.class); - this.clientAbilityInitFinishHandler = new InvokeHandler(PacketClientAbilityInitFinishNotify.class); + this.attackResults = new LinkedBlockingQueue<>(); + this.coopRequests = new Int2ObjectOpenHashMap<>(); + this.combatInvokeHandler = new InvokeHandler(PacketCombatInvocationsNotify.class); + this.abilityInvokeHandler = new InvokeHandler(PacketAbilityInvocationsNotify.class); + this.clientAbilityInitFinishHandler = new InvokeHandler(PacketClientAbilityInitFinishNotify.class); - this.birthday = new PlayerBirthday(); - this.rewardedLevels = new HashSet<>(); - this.moonCardGetTimes = new HashSet<>(); - this.codex = new PlayerCodex(this); + this.birthday = new PlayerBirthday(); + this.rewardedLevels = new HashSet<>(); + this.moonCardGetTimes = new HashSet<>(); + this.codex = new PlayerCodex(this); this.openStateManager = new PlayerOpenStateManager(this); - this.shopLimit = new ArrayList<>(); - this.expeditionInfo = new HashMap<>(); - this.messageHandler = null; - this.mapMarksManager = new MapMarksManager(this); - this.staminaManager = new StaminaManager(this); - this.sotsManager = new SotSManager(this); - this.energyManager = new EnergyManager(this); - this.resinManager = new ResinManager(this); - this.forgingManager = new ForgingManager(this); - this.furnitureManager = new FurnitureManager(this); - this.cookingManager = new CookingManager(this); - } - - // On player creation - public Player(GameSession session) { - this(); - this.account = session.getAccount(); - this.accountId = this.getAccount().getId(); - this.session = session; - this.nickname = "Traveler"; - this.signature = ""; - this.teamManager = new TeamManager(this); - this.birthday = new PlayerBirthday(); - this.codex = new PlayerCodex(this); - this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, 1, false); - this.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1, false); - this.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50, false); - this.setProperty(PlayerProperty.PROP_IS_FLYABLE, 1, false); - this.setProperty(PlayerProperty.PROP_IS_TRANSFERABLE, 1, false); - this.setProperty(PlayerProperty.PROP_MAX_STAMINA, 24000, false); - this.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, 24000, false); - this.setProperty(PlayerProperty.PROP_PLAYER_RESIN, 160, false); - this.getFlyCloakList().add(140001); - this.getNameCardList().add(210001); - this.messageHandler = null; - this.mapMarksManager = new MapMarksManager(this); - this.staminaManager = new StaminaManager(this); - this.sotsManager = new SotSManager(this); - this.energyManager = new EnergyManager(this); - this.resinManager = new ResinManager(this); - this.deforestationManager = new DeforestationManager(this); - this.forgingManager = new ForgingManager(this); - this.furnitureManager = new FurnitureManager(this); - this.cookingManager = new CookingManager(this); - } - - public int getUid() { - return id; - } - - public void setUid(int id) { - this.id = id; - } - - public long getNextGameGuid() { - long nextId = ++this.nextGuid; - return ((long) this.getUid() << 32) + nextId; - } - - public Account getAccount() { - if (this.account == null) - this.account = DatabaseHelper.getAccountById(Integer.toString(this.id)); - return this.account; - } - - public void setAccount(Account account) { - this.account = account; - } - - public GameSession getSession() { - return session; - } - - public void setSession(GameSession session) { - this.session = session; - } - - public boolean isOnline() { - return this.getSession() != null && this.getSession().isActive(); - } - - public GameServer getServer() { - return this.getSession().getServer(); - } - - public synchronized World getWorld() { - return this.world; - } - - public synchronized void setWorld(World world) { - this.world = world; - } - - public synchronized Scene getScene() { - return scene; - } - - public synchronized void setScene(Scene scene) { - this.scene = scene; - } - - synchronized public void setClimate(ClimateType climate) { - this.climate = climate; - this.session.send(new PacketSceneAreaWeatherNotify(this)); - } - - synchronized public void setWeather(int weather) { - this.setWeather(weather, ClimateType.CLIMATE_NONE); - } - - synchronized public void setWeather(int weatherId, ClimateType climate) { - // Lookup default climate for this weather - if (climate == ClimateType.CLIMATE_NONE) { - WeatherData w = GameData.getWeatherDataMap().get(weatherId); - if (w != null) { - climate = w.getDefaultClimate(); - } - } - this.weatherId = weatherId; - this.climate = climate; - this.session.send(new PacketSceneAreaWeatherNotify(this)); - } - - public String getNickname() { - return nickname; - } - - public void setNickname(String nickName) { - this.nickname = nickName; - this.updateProfile(); - } - - public int getHeadImage() { - return headImage; - } - - public void setHeadImage(int picture) { - this.headImage = picture; - this.updateProfile(); - } - - public String getSignature() { - return signature; - } - - public void setSignature(String signature) { - this.signature = signature; - this.updateProfile(); - } - - public Integer getWidgetId() { - return widgetId; - } - - public void setWidgetId(Integer widgetId) { - this.widgetId = widgetId; - } - - public void setRealmList(Set realmList) { - this.realmList = realmList; - } - - public void addRealmList(int realmId) { - if (this.realmList == null) { - this.realmList = new HashSet<>(); - } else if (this.realmList.contains(realmId)) { - return; - } - this.realmList.add(realmId); - } - - public int getCurrentRealmId() { - return currentRealmId; - } - - public void setCurrentRealmId(int currentRealmId) { - this.currentRealmId = currentRealmId; - } - - public Position getPosition() { - return position; - } - - public Position getRotation() { - return rotation; - } - - public int getLevel() { - return this.getProperty(PlayerProperty.PROP_PLAYER_LEVEL); - } - - public boolean setLevel(int level) { - if (this.getLevel() == level) { - return true; - } - - if (this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, level)) { - // Update world level and profile. - this.updateWorldLevel(); - this.updateProfile(); - - // Handle OpenState unlocks from level-up. - this.getOpenStateManager().unlockLevelDependentStates(); - - return true; - } - return false; - } - - public int getExp() { - return this.getProperty(PlayerProperty.PROP_PLAYER_EXP); - } - - public int getWorldLevel() { - return this.getProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL); - } - - public boolean setWorldLevel(int level) { - if (this.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level)) { - if (this.world.getHost() == this) // Don't update World's WL if we are in someone else's world - this.world.setWorldLevel(level); - this.updateProfile(); - return true; - } - return false; - } - - public int getForgePoints() { - return this.getProperty(PlayerProperty.PROP_PLAYER_FORGE_POINT); - } - - public boolean setForgePoints(int value) { - if (value == this.getForgePoints()) { - return true; - } - - return this.setProperty(PlayerProperty.PROP_PLAYER_FORGE_POINT, value); - } - - public int getPrimogems() { - return this.getProperty(PlayerProperty.PROP_PLAYER_HCOIN); - } - - public boolean setPrimogems(int primogem) { - return this.setProperty(PlayerProperty.PROP_PLAYER_HCOIN, primogem); - } - - public int getMora() { - return this.getProperty(PlayerProperty.PROP_PLAYER_SCOIN); - } - - public boolean setMora(int mora) { - return this.setProperty(PlayerProperty.PROP_PLAYER_SCOIN, mora); - } - - public int getCrystals() { - return this.getProperty(PlayerProperty.PROP_PLAYER_MCOIN); - } - - public boolean setCrystals(int crystals) { - return this.setProperty(PlayerProperty.PROP_PLAYER_MCOIN, crystals); - } - - public int getHomeCoin() { - return this.getProperty(PlayerProperty.PROP_PLAYER_HOME_COIN); - } - - public boolean setHomeCoin(int coin) { - return this.setProperty(PlayerProperty.PROP_PLAYER_HOME_COIN, coin); - } - private int getExpRequired(int level) { - PlayerLevelData levelData = GameData.getPlayerLevelDataMap().get(level); - return levelData != null ? levelData.getExp() : 0; - } - - private float getExpModifier() { - return GAME_OPTIONS.rates.adventureExp; - } - - // Affected by exp rate - public void earnExp(int exp) { - addExpDirectly((int) (exp * getExpModifier())); - } - - // Directly give player exp - public void addExpDirectly(int gain) { - int level = getLevel(); - int exp = getExp(); - int reqExp = getExpRequired(level); - - exp += gain; - - while (exp >= reqExp && reqExp > 0) { - exp -= reqExp; - level += 1; - reqExp = getExpRequired(level); - - // Set level each time to allow level-up specific logic to run. - this.setLevel(level); - } - - // Set exp - this.setProperty(PlayerProperty.PROP_PLAYER_EXP, exp); - } - - private void updateWorldLevel() { - int currentWorldLevel = this.getWorldLevel(); - int currentLevel = this.getLevel(); - - int newWorldLevel = - (currentLevel >= 55) ? 8 : - (currentLevel >= 50) ? 7 : - (currentLevel >= 45) ? 6 : - (currentLevel >= 40) ? 5 : - (currentLevel >= 35) ? 4 : - (currentLevel >= 30) ? 3 : - (currentLevel >= 25) ? 2 : - (currentLevel >= 20) ? 1 : - 0; - - if (newWorldLevel != currentWorldLevel) { - this.setWorldLevel(newWorldLevel); - } - } - - private void updateProfile() { - getProfile().syncWithCharacter(this); - } - - public boolean isFirstLoginEnterScene() { - return !this.hasSentAvatarDataNotify; - } - - public TeamManager getTeamManager() { - return this.teamManager; - } - - public TowerData getTowerData() { - if (towerData == null) { - // because of mistake, null may be saved as storage at some machine, this if can be removed in future - towerData = new TowerData(); - } - return towerData; - } - - public PlayerGachaInfo getGachaInfo() { - return gachaInfo; - } - - public PlayerProfile getProfile() { - if (this.playerProfile == null) { - this.playerProfile = new PlayerProfile(this); - } - return playerProfile; - } - - // TODO: Based on the proto, property value could be int or float. - // Although there's no float value at this moment, our code should be prepared for float values. - public Map getProperties() { - return properties; - } - - public boolean setProperty(PlayerProperty prop, int value) { - return setPropertyWithSanityCheck(prop, value, true); - } - - public boolean setProperty(PlayerProperty prop, int value, boolean sendPacket) { - return setPropertyWithSanityCheck(prop, value, sendPacket); - } - - public int getProperty(PlayerProperty prop) { - return getProperties().get(prop.getId()); - } - - public MpSettingType getMpSetting() { - return MpSettingType.MP_SETTING_TYPE_ENTER_AFTER_APPLY; // TEMP - } - - public Queue getAttackResults() { - return this.attackResults; - } - - public synchronized Int2ObjectMap getCoopRequests() { - return coopRequests; - } - - public int getEnterSceneToken() { - return enterSceneToken; - } - - public void setEnterSceneToken(int enterSceneToken) { - this.enterSceneToken = enterSceneToken; - } - - public int getNameCardId() { - return nameCardId; - } - - public void setNameCardId(int nameCardId) { - this.nameCardId = nameCardId; - this.updateProfile(); - } - - public int getMainCharacterId() { - return mainCharacterId; - } - - public void setMainCharacterId(int mainCharacterId) { - this.mainCharacterId = mainCharacterId; - } - - public int getPeerId() { - return peerId; - } - - public void setPeerId(int peerId) { - this.peerId = peerId; - } - - public int getClientTime() { - return session.getClientTime(); - } - - public long getLastPingTime() { - return session.getLastPingTime(); - } - - public boolean isPaused() { - return paused; - } - - public void setPaused(boolean newPauseState) { - boolean oldPauseState = this.paused; - this.paused = newPauseState; - - if (newPauseState && !oldPauseState) { - this.onPause(); - } else if (oldPauseState && !newPauseState) { - this.onUnpause(); - } - } - - public long getSpringLastUsed() { - return springLastUsed; - } - - public void setSpringLastUsed(long val) { - springLastUsed = val; - } - - public int getNextResinRefresh() { - return nextResinRefresh; - } - - public void setNextResinRefresh(int value) { - this.nextResinRefresh = value; - } - - public SceneLoadState getSceneLoadState() { - return sceneState; - } - - public void setSceneLoadState(SceneLoadState sceneState) { - this.sceneState = sceneState; - } - - public boolean isInMultiplayer() { - return this.getWorld() != null && this.getWorld().isMultiplayer(); - } - - public int getSceneId() { - return sceneId; - } - - public void setSceneId(int sceneId) { - this.sceneId = sceneId; - } - - public int getRegionId() { - return regionId; - } - - public void setRegionId(int regionId) { - this.regionId = regionId; - } - - public void setShowAvatars(boolean showAvatars) { - this.showAvatars = showAvatars; - } - - public boolean isShowAvatars() { - return showAvatars; - } - - public void setShowAvatarList(List showAvatarList) { - this.showAvatarList = showAvatarList; - } - - public List getShowAvatarList() { - return showAvatarList; - } - - public int getLastDailyReset() { - return this.lastDailyReset; - } - - public void setLastDailyReset(int value) { - this.lastDailyReset = value; - } - - public boolean inMoonCard() { - return moonCard; - } - - public void setMoonCard(boolean moonCard) { - this.moonCard = moonCard; - } - - public void addMoonCardDays(int days) { - this.moonCardDuration += days; - } - - public int getMoonCardDuration() { - return moonCardDuration; - } - - public void setMoonCardDuration(int moonCardDuration) { - this.moonCardDuration = moonCardDuration; - } - - public Date getMoonCardStartTime() { - return moonCardStartTime; - } - - public void setMoonCardStartTime(Date moonCardStartTime) { - this.moonCardStartTime = moonCardStartTime; - } - - public Set getMoonCardGetTimes() { - return moonCardGetTimes; - } - - public void setMoonCardGetTimes(Set moonCardGetTimes) { - this.moonCardGetTimes = moonCardGetTimes; - } - - public int getMoonCardRemainDays() { - Calendar remainCalendar = Calendar.getInstance(); - remainCalendar.setTime(moonCardStartTime); - remainCalendar.add(Calendar.DATE, moonCardDuration); - Date theLastDay = remainCalendar.getTime(); - Date now = DateHelper.onlyYearMonthDay(new Date()); - return (int) ((theLastDay.getTime() - now.getTime()) / (24 * 60 * 60 * 1000)); // By copilot - } - - public void rechargeMoonCard() { - inventory.addItem(new GameItem(203, 300)); - if (!moonCard) { - moonCard = true; - Date now = new Date(); - moonCardStartTime = DateHelper.onlyYearMonthDay(now); - moonCardDuration = 30; - } else { - moonCardDuration += 30; - } - if (!moonCardGetTimes.contains(moonCardStartTime)) { - moonCardGetTimes.add(moonCardStartTime); - } - } - - public void getTodayMoonCard() { - if (!moonCard) { - return; - } - Date now = DateHelper.onlyYearMonthDay(new Date()); - if (moonCardGetTimes.contains(now)) { - return; - } - Date stopTime = new Date(); - Calendar stopCalendar = Calendar.getInstance(); - stopCalendar.setTime(stopTime); - stopCalendar.add(Calendar.DATE, moonCardDuration); - stopTime = stopCalendar.getTime(); - if (now.after(stopTime)) { - moonCard = false; - return; - } - moonCardGetTimes.add(now); - addMoonCardDays(1); - GameItem item = new GameItem(201, 90); - getInventory().addItem(item, ActionReason.BlessingRedeemReward); - session.send(new PacketCardProductRewardNotify(getMoonCardRemainDays())); - } - - public void addExpeditionInfo(long avaterGuid, int expId, int hourTime, int startTime){ - ExpeditionInfo exp = new ExpeditionInfo(); - exp.setExpId(expId); - exp.setHourTime(hourTime); - exp.setState(1); - exp.setStartTime(startTime); - expeditionInfo.put(avaterGuid, exp); - } - - public void removeExpeditionInfo(long avaterGuid){ - expeditionInfo.remove(avaterGuid); - } - - public ExpeditionInfo getExpeditionInfo(long avaterGuid){ - return expeditionInfo.get(avaterGuid); - } - - public List getShopLimit() { - return shopLimit; - } - - public ShopLimit getGoodsLimit(int goodsId) { - Optional shopLimit = this.shopLimit.stream().filter(x -> x.getShopGoodId() == goodsId).findFirst(); - if (shopLimit.isEmpty()) - return null; - return shopLimit.get(); - } - - public void addShopLimit(int goodsId, int boughtCount, int nextRefreshTime) { - ShopLimit target = getGoodsLimit(goodsId); - if (target != null) { - target.setHasBought(target.getHasBought() + boughtCount); - target.setHasBoughtInPeriod(target.getHasBoughtInPeriod() + boughtCount); - target.setNextRefreshTime(nextRefreshTime); - } else { - ShopLimit sl = new ShopLimit(); - sl.setShopGoodId(goodsId); - sl.setHasBought(boughtCount); - sl.setHasBoughtInPeriod(boughtCount); - sl.setNextRefreshTime(nextRefreshTime); - getShopLimit().add(sl); - } - this.save(); - } - public boolean getUnlimitedStamina() { - return stamina; - } - public void setUnlimitedStamina(boolean stamina) { - this.stamina = stamina; - } - public boolean inGodmode() { - return godmode; - } - - public void setGodmode(boolean godmode) { - this.godmode = godmode; - } - - public boolean hasSentAvatarDataNotify() { - return hasSentAvatarDataNotify; - } - - public void setHasSentAvatarDataNotify(boolean hasSentAvatarDataNotify) { - this.hasSentAvatarDataNotify = hasSentAvatarDataNotify; - } - - public void addAvatar(Avatar avatar, boolean addToCurrentTeam) { - boolean result = getAvatars().addAvatar(avatar); - - if (result) { - // Add starting weapon - getAvatars().addStartingWeapon(avatar); - - // Done - if (hasSentAvatarDataNotify()) { - // Recalc stats - avatar.recalcStats(); - // Packet, show notice on left if the avatar will be added to the team - sendPacket(new PacketAvatarAddNotify(avatar, addToCurrentTeam && this.getTeamManager().canAddAvatarToCurrentTeam())); - if (addToCurrentTeam) { - // If space in team, add - this.getTeamManager().addAvatarToCurrentTeam(avatar); - } - } - } else { - // Failed adding avatar - } - } - - public void addAvatar(Avatar avatar) { - addAvatar(avatar, true); - } - - public void addFlycloak(int flycloakId) { - this.getFlyCloakList().add(flycloakId); - this.sendPacket(new PacketAvatarGainFlycloakNotify(flycloakId)); - } - - public void addCostume(int costumeId) { - this.getCostumeList().add(costumeId); - this.sendPacket(new PacketAvatarGainCostumeNotify(costumeId)); - } - - public void addNameCard(int nameCardId) { - this.getNameCardList().add(nameCardId); - this.sendPacket(new PacketUnlockNameCardNotify(nameCardId)); - } - - public void setNameCard(int nameCardId) { - if (!this.getNameCardList().contains(nameCardId)) { - return; - } - - this.setNameCardId(nameCardId); - - this.sendPacket(new PacketSetNameCardRsp(nameCardId)); - } - - public void dropMessage(Object message) { - if (this.messageHandler != null) { - this.messageHandler.append(message.toString()); - return; - } - - this.getServer().getChatManager().sendPrivateMessageFromServer(getUid(), message.toString()); - // this.sendPacket(new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, getUid(), message.toString())); - } - - /** - * Sends a message to another player. - * - * @param sender The sender of the message. - * @param message The message to send. - */ - public void sendMessage(Player sender, Object message) { - // this.sendPacket(new PacketPrivateChatNotify(sender.getUid(), this.getUid(), message.toString())); - this.getServer().getChatManager().sendPrivateMessage(sender, this.getUid(), message.toString()); - } - - // ---------------------MAIL------------------------ - - public List getAllMail() { return this.getMailHandler().getMail(); } - - public void sendMail(Mail message) { - this.getMailHandler().sendMail(message); - } - - public boolean deleteMail(int mailId) { - return this.getMailHandler().deleteMail(mailId); - } - - public Mail getMail(int index) { return this.getMailHandler().getMailById(index); } - - public int getMailId(Mail message) { - return this.getMailHandler().getMailIndex(message); - } - - public boolean replaceMailByIndex(int index, Mail message) { - return this.getMailHandler().replaceMailByIndex(index, message); - } - - public void interactWith(int gadgetEntityId, GadgetInteractReq interactReq) { - GameEntity target = getScene().getEntityById(gadgetEntityId); - - if (target == null) { - return; - } - - target.onInteract(this, interactReq); - } - - public void onPause() { - getStaminaManager().stopSustainedStaminaHandler(); - } - - public void onUnpause() { - getStaminaManager().startSustainedStaminaHandler(); - } - - public void sendPacket(BasePacket packet) { - this.getSession().send(packet); - } - - public OnlinePlayerInfo getOnlinePlayerInfo() { - OnlinePlayerInfo.Builder onlineInfo = OnlinePlayerInfo.newBuilder() - .setUid(this.getUid()) - .setNickname(this.getNickname()) - .setPlayerLevel(this.getLevel()) - .setMpSettingType(this.getMpSetting()) - .setNameCardId(this.getNameCardId()) - .setSignature(this.getSignature()) - .setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage())); - - if (this.getWorld() != null) { - onlineInfo.setCurPlayerNumInWorld(getWorld().getPlayerCount()); - } else { - onlineInfo.setCurPlayerNumInWorld(1); - } - - return onlineInfo.build(); - } - - public PlayerBirthday getBirthday() { - return this.birthday; - } - - public void setBirthday(int d, int m) { - this.birthday = new PlayerBirthday(d, m); - this.updateProfile(); - } - - public boolean hasBirthday() { - return this.birthday.getDay() > 0; - } - - public PlayerCodex getCodex() { - return this.codex; - } - - public void setRewardedLevels(Set rewardedLevels) { - this.rewardedLevels = rewardedLevels; - } - - public SocialDetail.Builder getSocialDetail() { - List socialShowAvatarInfoList = new ArrayList<>(); - if (this.isOnline()) { - if (this.getShowAvatarList() != null) { - for (int avatarId : this.getShowAvatarList()) { - socialShowAvatarInfoList.add( - socialShowAvatarInfoList.size(), - SocialShowAvatarInfoOuterClass.SocialShowAvatarInfo.newBuilder() - .setAvatarId(avatarId) - .setLevel(getAvatars().getAvatarById(avatarId).getLevel()) - .setCostumeId(getAvatars().getAvatarById(avatarId).getCostume()) - .build() - ); - } - } - } else { - List showAvatarList = DatabaseHelper.getPlayerByUid(id).getShowAvatarList(); - AvatarStorage avatars = DatabaseHelper.getPlayerByUid(id).getAvatars(); - avatars.loadFromDatabase(); - if (showAvatarList != null) { - for (int avatarId : showAvatarList) { - socialShowAvatarInfoList.add( - socialShowAvatarInfoList.size(), - SocialShowAvatarInfoOuterClass.SocialShowAvatarInfo.newBuilder() - .setAvatarId(avatarId) - .setLevel(avatars.getAvatarById(avatarId).getLevel()) - .setCostumeId(avatars.getAvatarById(avatarId).getCostume()) - .build() - ); - } - } - } - - SocialDetail.Builder social = SocialDetail.newBuilder() - .setUid(this.getUid()) - .setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage())) - .setNickname(this.getNickname()) - .setSignature(this.getSignature()) - .setLevel(this.getLevel()) - .setBirthday(this.getBirthday().getFilledProtoWhenNotEmpty()) - .setWorldLevel(this.getWorldLevel()) - .setNameCardId(this.getNameCardId()) - .setIsShowAvatar(this.isShowAvatars()) - .addAllShowAvatarInfoList(socialShowAvatarInfoList) - .setFinishAchievementNum(0); - return social; - } - - public List getShowAvatarInfoList() { - List showAvatarInfoList = new ArrayList<>(); - - Player player; - boolean shouldRecalc; - if (this.isOnline()) { - player = this; - shouldRecalc = false; - } else { - player = DatabaseHelper.getPlayerByUid(id); - player.getAvatars().loadFromDatabase(); - player.getInventory().loadFromDatabase(); - shouldRecalc = true; - } - - List showAvatarList = player.getShowAvatarList(); - AvatarStorage avatars = player.getAvatars(); - if (showAvatarList != null) { - for (int avatarId : showAvatarList) { - Avatar avatar = avatars.getAvatarById(avatarId); - if (shouldRecalc) { - avatar.recalcStats(); - } - showAvatarInfoList.add(avatar.toShowAvatarInfoProto()); - } - } - return showAvatarInfoList; - } - - public PlayerWorldLocationInfoOuterClass.PlayerWorldLocationInfo getWorldPlayerLocationInfo() { - return PlayerWorldLocationInfoOuterClass.PlayerWorldLocationInfo.newBuilder() - .setSceneId(this.getSceneId()) - .setPlayerLoc(this.getPlayerLocationInfo()) - .build(); - } - - public PlayerLocationInfo getPlayerLocationInfo() { - return PlayerLocationInfo.newBuilder() - .setUid(this.getUid()) - .setPos(this.getPosition().toProto()) - .setRot(this.getRotation().toProto()) - .build(); - } - - public void loadBattlePassManager() { - if (this.battlePassManager != null) return; - this.battlePassManager = DatabaseHelper.loadBattlePass(this); - this.battlePassManager.getMissions().values().removeIf(mission -> mission.getData() == null); - } - - public PlayerCollectionRecords getCollectionRecordStore() { - if(this.collectionRecordStore==null){ - this.collectionRecordStore = new PlayerCollectionRecords(); - } - return collectionRecordStore; - } - - public Map getMapMarks() { - if (this.mapMarks == null) { - this.mapMarks = new HashMap(); - } - return mapMarks; - } - - public PlayerOpenStateManager getOpenStateManager() { - if (this.openStateManager == null) { + this.shopLimit = new ArrayList<>(); + this.expeditionInfo = new HashMap<>(); + this.messageHandler = null; + this.mapMarksManager = new MapMarksManager(this); + this.staminaManager = new StaminaManager(this); + this.sotsManager = new SotSManager(this); + this.energyManager = new EnergyManager(this); + this.resinManager = new ResinManager(this); + this.forgingManager = new ForgingManager(this); + this.furnitureManager = new FurnitureManager(this); + this.cookingManager = new CookingManager(this); + } + + // On player creation + public Player(GameSession session) { + this(); + this.account = session.getAccount(); + this.accountId = this.getAccount().getId(); + this.session = session; + this.nickname = "Traveler"; + this.signature = ""; + this.teamManager = new TeamManager(this); + this.birthday = new PlayerBirthday(); + this.codex = new PlayerCodex(this); + this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, 1, false); + this.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1, false); + this.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50, false); + this.setProperty(PlayerProperty.PROP_IS_FLYABLE, 1, false); + this.setProperty(PlayerProperty.PROP_IS_TRANSFERABLE, 1, false); + this.setProperty(PlayerProperty.PROP_MAX_STAMINA, 24000, false); + this.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, 24000, false); + this.setProperty(PlayerProperty.PROP_PLAYER_RESIN, 160, false); + this.getFlyCloakList().add(140001); + this.getNameCardList().add(210001); + this.messageHandler = null; + this.mapMarksManager = new MapMarksManager(this); + this.staminaManager = new StaminaManager(this); + this.sotsManager = new SotSManager(this); + this.energyManager = new EnergyManager(this); + this.resinManager = new ResinManager(this); + this.deforestationManager = new DeforestationManager(this); + this.forgingManager = new ForgingManager(this); + this.furnitureManager = new FurnitureManager(this); + this.cookingManager = new CookingManager(this); + } + + public int getUid() { + return id; + } + + public void setUid(int id) { + this.id = id; + } + + public long getNextGameGuid() { + long nextId = ++this.nextGuid; + return ((long) this.getUid() << 32) + nextId; + } + + public Account getAccount() { + if (this.account == null) + this.account = DatabaseHelper.getAccountById(Integer.toString(this.id)); + return this.account; + } + + public void setAccount(Account account) { + this.account = account; + } + + public GameSession getSession() { + return session; + } + + public void setSession(GameSession session) { + this.session = session; + } + + public boolean isOnline() { + return this.getSession() != null && this.getSession().isActive(); + } + + public GameServer getServer() { + return this.getSession().getServer(); + } + + public synchronized World getWorld() { + return this.world; + } + + public synchronized void setWorld(World world) { + this.world = world; + } + + public synchronized Scene getScene() { + return scene; + } + + public synchronized void setScene(Scene scene) { + this.scene = scene; + } + + synchronized public void setClimate(ClimateType climate) { + this.climate = climate; + this.session.send(new PacketSceneAreaWeatherNotify(this)); + } + + synchronized public void setWeather(int weather) { + this.setWeather(weather, ClimateType.CLIMATE_NONE); + } + + synchronized public void setWeather(int weatherId, ClimateType climate) { + // Lookup default climate for this weather + if (climate == ClimateType.CLIMATE_NONE) { + WeatherData w = GameData.getWeatherDataMap().get(weatherId); + if (w != null) { + climate = w.getDefaultClimate(); + } + } + this.weatherId = weatherId; + this.climate = climate; + this.session.send(new PacketSceneAreaWeatherNotify(this)); + } + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickName) { + this.nickname = nickName; + this.updateProfile(); + } + + public int getHeadImage() { + return headImage; + } + + public void setHeadImage(int picture) { + this.headImage = picture; + this.updateProfile(); + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + this.updateProfile(); + } + + public Integer getWidgetId() { + return widgetId; + } + + public void setWidgetId(Integer widgetId) { + this.widgetId = widgetId; + } + + public void setRealmList(Set realmList) { + this.realmList = realmList; + } + + public void addRealmList(int realmId) { + if (this.realmList == null) { + this.realmList = new HashSet<>(); + } else if (this.realmList.contains(realmId)) { + return; + } + this.realmList.add(realmId); + } + + public int getCurrentRealmId() { + return currentRealmId; + } + + public void setCurrentRealmId(int currentRealmId) { + this.currentRealmId = currentRealmId; + } + + public Position getPosition() { + return position; + } + + public Position getRotation() { + return rotation; + } + + public int getLevel() { + return this.getProperty(PlayerProperty.PROP_PLAYER_LEVEL); + } + + public boolean setLevel(int level) { + if (this.getLevel() == level) { + return true; + } + + if (this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, level)) { + // Update world level and profile. + this.updateWorldLevel(); + this.updateProfile(); + + // Handle OpenState unlocks from level-up. + this.getOpenStateManager().unlockLevelDependentStates(); + + return true; + } + return false; + } + + public int getExp() { + return this.getProperty(PlayerProperty.PROP_PLAYER_EXP); + } + + public int getWorldLevel() { + return this.getProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL); + } + + public boolean setWorldLevel(int level) { + if (this.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level)) { + if (this.world.getHost() == this) // Don't update World's WL if we are in someone else's world + this.world.setWorldLevel(level); + this.updateProfile(); + return true; + } + return false; + } + + public int getForgePoints() { + return this.getProperty(PlayerProperty.PROP_PLAYER_FORGE_POINT); + } + + public boolean setForgePoints(int value) { + if (value == this.getForgePoints()) { + return true; + } + + return this.setProperty(PlayerProperty.PROP_PLAYER_FORGE_POINT, value); + } + + public int getPrimogems() { + return this.getProperty(PlayerProperty.PROP_PLAYER_HCOIN); + } + + public boolean setPrimogems(int primogem) { + return this.setProperty(PlayerProperty.PROP_PLAYER_HCOIN, primogem); + } + + public int getMora() { + return this.getProperty(PlayerProperty.PROP_PLAYER_SCOIN); + } + + public boolean setMora(int mora) { + return this.setProperty(PlayerProperty.PROP_PLAYER_SCOIN, mora); + } + + public int getCrystals() { + return this.getProperty(PlayerProperty.PROP_PLAYER_MCOIN); + } + + public boolean setCrystals(int crystals) { + return this.setProperty(PlayerProperty.PROP_PLAYER_MCOIN, crystals); + } + + public int getHomeCoin() { + return this.getProperty(PlayerProperty.PROP_PLAYER_HOME_COIN); + } + + public boolean setHomeCoin(int coin) { + return this.setProperty(PlayerProperty.PROP_PLAYER_HOME_COIN, coin); + } + private int getExpRequired(int level) { + PlayerLevelData levelData = GameData.getPlayerLevelDataMap().get(level); + return levelData != null ? levelData.getExp() : 0; + } + + private float getExpModifier() { + return GAME_OPTIONS.rates.adventureExp; + } + + // Affected by exp rate + public void earnExp(int exp) { + addExpDirectly((int) (exp * getExpModifier())); + } + + // Directly give player exp + public void addExpDirectly(int gain) { + int level = getLevel(); + int exp = getExp(); + int reqExp = getExpRequired(level); + + exp += gain; + + while (exp >= reqExp && reqExp > 0) { + exp -= reqExp; + level += 1; + reqExp = getExpRequired(level); + + // Set level each time to allow level-up specific logic to run. + this.setLevel(level); + } + + // Set exp + this.setProperty(PlayerProperty.PROP_PLAYER_EXP, exp); + } + + private void updateWorldLevel() { + int currentWorldLevel = this.getWorldLevel(); + int currentLevel = this.getLevel(); + + int newWorldLevel = + (currentLevel >= 55) ? 8 : + (currentLevel >= 50) ? 7 : + (currentLevel >= 45) ? 6 : + (currentLevel >= 40) ? 5 : + (currentLevel >= 35) ? 4 : + (currentLevel >= 30) ? 3 : + (currentLevel >= 25) ? 2 : + (currentLevel >= 20) ? 1 : + 0; + + if (newWorldLevel != currentWorldLevel) { + this.setWorldLevel(newWorldLevel); + } + } + + private void updateProfile() { + getProfile().syncWithCharacter(this); + } + + public boolean isFirstLoginEnterScene() { + return !this.hasSentAvatarDataNotify; + } + + public TeamManager getTeamManager() { + return this.teamManager; + } + + public TowerData getTowerData() { + if (towerData == null) { + // because of mistake, null may be saved as storage at some machine, this if can be removed in future + towerData = new TowerData(); + } + return towerData; + } + + public PlayerGachaInfo getGachaInfo() { + return gachaInfo; + } + + public PlayerProfile getProfile() { + if (this.playerProfile == null) { + this.playerProfile = new PlayerProfile(this); + } + return playerProfile; + } + + // TODO: Based on the proto, property value could be int or float. + // Although there's no float value at this moment, our code should be prepared for float values. + public Map getProperties() { + return properties; + } + + public boolean setProperty(PlayerProperty prop, int value) { + return setPropertyWithSanityCheck(prop, value, true); + } + + public boolean setProperty(PlayerProperty prop, int value, boolean sendPacket) { + return setPropertyWithSanityCheck(prop, value, sendPacket); + } + + public int getProperty(PlayerProperty prop) { + return getProperties().get(prop.getId()); + } + + public MpSettingType getMpSetting() { + return MpSettingType.MP_SETTING_TYPE_ENTER_AFTER_APPLY; // TEMP + } + + public Queue getAttackResults() { + return this.attackResults; + } + + public synchronized Int2ObjectMap getCoopRequests() { + return coopRequests; + } + + public int getEnterSceneToken() { + return enterSceneToken; + } + + public void setEnterSceneToken(int enterSceneToken) { + this.enterSceneToken = enterSceneToken; + } + + public int getNameCardId() { + return nameCardId; + } + + public void setNameCardId(int nameCardId) { + this.nameCardId = nameCardId; + this.updateProfile(); + } + + public int getMainCharacterId() { + return mainCharacterId; + } + + public void setMainCharacterId(int mainCharacterId) { + this.mainCharacterId = mainCharacterId; + } + + public int getPeerId() { + return peerId; + } + + public void setPeerId(int peerId) { + this.peerId = peerId; + } + + public int getClientTime() { + return session.getClientTime(); + } + + public long getLastPingTime() { + return session.getLastPingTime(); + } + + public boolean isPaused() { + return paused; + } + + public void setPaused(boolean newPauseState) { + boolean oldPauseState = this.paused; + this.paused = newPauseState; + + if (newPauseState && !oldPauseState) { + this.onPause(); + } else if (oldPauseState && !newPauseState) { + this.onUnpause(); + } + } + + public long getSpringLastUsed() { + return springLastUsed; + } + + public void setSpringLastUsed(long val) { + springLastUsed = val; + } + + public int getNextResinRefresh() { + return nextResinRefresh; + } + + public void setNextResinRefresh(int value) { + this.nextResinRefresh = value; + } + + public SceneLoadState getSceneLoadState() { + return sceneState; + } + + public void setSceneLoadState(SceneLoadState sceneState) { + this.sceneState = sceneState; + } + + public boolean isInMultiplayer() { + return this.getWorld() != null && this.getWorld().isMultiplayer(); + } + + public int getSceneId() { + return sceneId; + } + + public void setSceneId(int sceneId) { + this.sceneId = sceneId; + } + + public int getRegionId() { + return regionId; + } + + public void setRegionId(int regionId) { + this.regionId = regionId; + } + + public void setShowAvatars(boolean showAvatars) { + this.showAvatars = showAvatars; + } + + public boolean isShowAvatars() { + return showAvatars; + } + + public void setShowAvatarList(List showAvatarList) { + this.showAvatarList = showAvatarList; + } + + public List getShowAvatarList() { + return showAvatarList; + } + + public int getLastDailyReset() { + return this.lastDailyReset; + } + + public void setLastDailyReset(int value) { + this.lastDailyReset = value; + } + + public boolean inMoonCard() { + return moonCard; + } + + public void setMoonCard(boolean moonCard) { + this.moonCard = moonCard; + } + + public void addMoonCardDays(int days) { + this.moonCardDuration += days; + } + + public int getMoonCardDuration() { + return moonCardDuration; + } + + public void setMoonCardDuration(int moonCardDuration) { + this.moonCardDuration = moonCardDuration; + } + + public Date getMoonCardStartTime() { + return moonCardStartTime; + } + + public void setMoonCardStartTime(Date moonCardStartTime) { + this.moonCardStartTime = moonCardStartTime; + } + + public Set getMoonCardGetTimes() { + return moonCardGetTimes; + } + + public void setMoonCardGetTimes(Set moonCardGetTimes) { + this.moonCardGetTimes = moonCardGetTimes; + } + + public int getMoonCardRemainDays() { + Calendar remainCalendar = Calendar.getInstance(); + remainCalendar.setTime(moonCardStartTime); + remainCalendar.add(Calendar.DATE, moonCardDuration); + Date theLastDay = remainCalendar.getTime(); + Date now = DateHelper.onlyYearMonthDay(new Date()); + return (int) ((theLastDay.getTime() - now.getTime()) / (24 * 60 * 60 * 1000)); // By copilot + } + + public void rechargeMoonCard() { + inventory.addItem(new GameItem(203, 300)); + if (!moonCard) { + moonCard = true; + Date now = new Date(); + moonCardStartTime = DateHelper.onlyYearMonthDay(now); + moonCardDuration = 30; + } else { + moonCardDuration += 30; + } + if (!moonCardGetTimes.contains(moonCardStartTime)) { + moonCardGetTimes.add(moonCardStartTime); + } + } + + public void getTodayMoonCard() { + if (!moonCard) { + return; + } + Date now = DateHelper.onlyYearMonthDay(new Date()); + if (moonCardGetTimes.contains(now)) { + return; + } + Date stopTime = new Date(); + Calendar stopCalendar = Calendar.getInstance(); + stopCalendar.setTime(stopTime); + stopCalendar.add(Calendar.DATE, moonCardDuration); + stopTime = stopCalendar.getTime(); + if (now.after(stopTime)) { + moonCard = false; + return; + } + moonCardGetTimes.add(now); + addMoonCardDays(1); + GameItem item = new GameItem(201, 90); + getInventory().addItem(item, ActionReason.BlessingRedeemReward); + session.send(new PacketCardProductRewardNotify(getMoonCardRemainDays())); + } + + public void addExpeditionInfo(long avaterGuid, int expId, int hourTime, int startTime) { + ExpeditionInfo exp = new ExpeditionInfo(); + exp.setExpId(expId); + exp.setHourTime(hourTime); + exp.setState(1); + exp.setStartTime(startTime); + expeditionInfo.put(avaterGuid, exp); + } + + public void removeExpeditionInfo(long avaterGuid) { + expeditionInfo.remove(avaterGuid); + } + + public ExpeditionInfo getExpeditionInfo(long avaterGuid) { + return expeditionInfo.get(avaterGuid); + } + + public List getShopLimit() { + return shopLimit; + } + + public ShopLimit getGoodsLimit(int goodsId) { + Optional shopLimit = this.shopLimit.stream().filter(x -> x.getShopGoodId() == goodsId).findFirst(); + if (shopLimit.isEmpty()) + return null; + return shopLimit.get(); + } + + public void addShopLimit(int goodsId, int boughtCount, int nextRefreshTime) { + ShopLimit target = getGoodsLimit(goodsId); + if (target != null) { + target.setHasBought(target.getHasBought() + boughtCount); + target.setHasBoughtInPeriod(target.getHasBoughtInPeriod() + boughtCount); + target.setNextRefreshTime(nextRefreshTime); + } else { + ShopLimit sl = new ShopLimit(); + sl.setShopGoodId(goodsId); + sl.setHasBought(boughtCount); + sl.setHasBoughtInPeriod(boughtCount); + sl.setNextRefreshTime(nextRefreshTime); + getShopLimit().add(sl); + } + this.save(); + } + public boolean getUnlimitedStamina() { + return stamina; + } + public void setUnlimitedStamina(boolean stamina) { + this.stamina = stamina; + } + public boolean inGodmode() { + return godmode; + } + + public void setGodmode(boolean godmode) { + this.godmode = godmode; + } + + public boolean hasSentAvatarDataNotify() { + return hasSentAvatarDataNotify; + } + + public void setHasSentAvatarDataNotify(boolean hasSentAvatarDataNotify) { + this.hasSentAvatarDataNotify = hasSentAvatarDataNotify; + } + + public void addAvatar(Avatar avatar, boolean addToCurrentTeam) { + boolean result = getAvatars().addAvatar(avatar); + + if (result) { + // Add starting weapon + getAvatars().addStartingWeapon(avatar); + + // Done + if (hasSentAvatarDataNotify()) { + // Recalc stats + avatar.recalcStats(); + // Packet, show notice on left if the avatar will be added to the team + sendPacket(new PacketAvatarAddNotify(avatar, addToCurrentTeam && this.getTeamManager().canAddAvatarToCurrentTeam())); + if (addToCurrentTeam) { + // If space in team, add + this.getTeamManager().addAvatarToCurrentTeam(avatar); + } + } + } else { + // Failed adding avatar + } + } + + public void addAvatar(Avatar avatar) { + addAvatar(avatar, true); + } + + public void addFlycloak(int flycloakId) { + this.getFlyCloakList().add(flycloakId); + this.sendPacket(new PacketAvatarGainFlycloakNotify(flycloakId)); + } + + public void addCostume(int costumeId) { + this.getCostumeList().add(costumeId); + this.sendPacket(new PacketAvatarGainCostumeNotify(costumeId)); + } + + public void addNameCard(int nameCardId) { + this.getNameCardList().add(nameCardId); + this.sendPacket(new PacketUnlockNameCardNotify(nameCardId)); + } + + public void setNameCard(int nameCardId) { + if (!this.getNameCardList().contains(nameCardId)) { + return; + } + + this.setNameCardId(nameCardId); + + this.sendPacket(new PacketSetNameCardRsp(nameCardId)); + } + + public void dropMessage(Object message) { + if (this.messageHandler != null) { + this.messageHandler.append(message.toString()); + return; + } + + this.getServer().getChatManager().sendPrivateMessageFromServer(getUid(), message.toString()); + // this.sendPacket(new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, getUid(), message.toString())); + } + + /** + * Sends a message to another player. + * + * @param sender The sender of the message. + * @param message The message to send. + */ + public void sendMessage(Player sender, Object message) { + // this.sendPacket(new PacketPrivateChatNotify(sender.getUid(), this.getUid(), message.toString())); + this.getServer().getChatManager().sendPrivateMessage(sender, this.getUid(), message.toString()); + } + + // ---------------------MAIL------------------------ + + public List getAllMail() { return this.getMailHandler().getMail(); } + + public void sendMail(Mail message) { + this.getMailHandler().sendMail(message); + } + + public boolean deleteMail(int mailId) { + return this.getMailHandler().deleteMail(mailId); + } + + public Mail getMail(int index) { return this.getMailHandler().getMailById(index); } + + public int getMailId(Mail message) { + return this.getMailHandler().getMailIndex(message); + } + + public boolean replaceMailByIndex(int index, Mail message) { + return this.getMailHandler().replaceMailByIndex(index, message); + } + + public void interactWith(int gadgetEntityId, GadgetInteractReq interactReq) { + GameEntity target = getScene().getEntityById(gadgetEntityId); + + if (target == null) { + return; + } + + target.onInteract(this, interactReq); + } + + public void onPause() { + getStaminaManager().stopSustainedStaminaHandler(); + } + + public void onUnpause() { + getStaminaManager().startSustainedStaminaHandler(); + } + + public void sendPacket(BasePacket packet) { + this.getSession().send(packet); + } + + public OnlinePlayerInfo getOnlinePlayerInfo() { + OnlinePlayerInfo.Builder onlineInfo = OnlinePlayerInfo.newBuilder() + .setUid(this.getUid()) + .setNickname(this.getNickname()) + .setPlayerLevel(this.getLevel()) + .setMpSettingType(this.getMpSetting()) + .setNameCardId(this.getNameCardId()) + .setSignature(this.getSignature()) + .setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage())); + + if (this.getWorld() != null) { + onlineInfo.setCurPlayerNumInWorld(getWorld().getPlayerCount()); + } else { + onlineInfo.setCurPlayerNumInWorld(1); + } + + return onlineInfo.build(); + } + + public PlayerBirthday getBirthday() { + return this.birthday; + } + + public void setBirthday(int d, int m) { + this.birthday = new PlayerBirthday(d, m); + this.updateProfile(); + } + + public boolean hasBirthday() { + return this.birthday.getDay() > 0; + } + + public PlayerCodex getCodex() { + return this.codex; + } + + public void setRewardedLevels(Set rewardedLevels) { + this.rewardedLevels = rewardedLevels; + } + + public SocialDetail.Builder getSocialDetail() { + List socialShowAvatarInfoList = new ArrayList<>(); + if (this.isOnline()) { + if (this.getShowAvatarList() != null) { + for (int avatarId : this.getShowAvatarList()) { + socialShowAvatarInfoList.add( + socialShowAvatarInfoList.size(), + SocialShowAvatarInfoOuterClass.SocialShowAvatarInfo.newBuilder() + .setAvatarId(avatarId) + .setLevel(getAvatars().getAvatarById(avatarId).getLevel()) + .setCostumeId(getAvatars().getAvatarById(avatarId).getCostume()) + .build() + ); + } + } + } else { + List showAvatarList = DatabaseHelper.getPlayerByUid(id).getShowAvatarList(); + AvatarStorage avatars = DatabaseHelper.getPlayerByUid(id).getAvatars(); + avatars.loadFromDatabase(); + if (showAvatarList != null) { + for (int avatarId : showAvatarList) { + socialShowAvatarInfoList.add( + socialShowAvatarInfoList.size(), + SocialShowAvatarInfoOuterClass.SocialShowAvatarInfo.newBuilder() + .setAvatarId(avatarId) + .setLevel(avatars.getAvatarById(avatarId).getLevel()) + .setCostumeId(avatars.getAvatarById(avatarId).getCostume()) + .build() + ); + } + } + } + + SocialDetail.Builder social = SocialDetail.newBuilder() + .setUid(this.getUid()) + .setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage())) + .setNickname(this.getNickname()) + .setSignature(this.getSignature()) + .setLevel(this.getLevel()) + .setBirthday(this.getBirthday().getFilledProtoWhenNotEmpty()) + .setWorldLevel(this.getWorldLevel()) + .setNameCardId(this.getNameCardId()) + .setIsShowAvatar(this.isShowAvatars()) + .addAllShowAvatarInfoList(socialShowAvatarInfoList) + .setFinishAchievementNum(0); + return social; + } + + public List getShowAvatarInfoList() { + List showAvatarInfoList = new ArrayList<>(); + + Player player; + boolean shouldRecalc; + if (this.isOnline()) { + player = this; + shouldRecalc = false; + } else { + player = DatabaseHelper.getPlayerByUid(id); + player.getAvatars().loadFromDatabase(); + player.getInventory().loadFromDatabase(); + shouldRecalc = true; + } + + List showAvatarList = player.getShowAvatarList(); + AvatarStorage avatars = player.getAvatars(); + if (showAvatarList != null) { + for (int avatarId : showAvatarList) { + Avatar avatar = avatars.getAvatarById(avatarId); + if (shouldRecalc) { + avatar.recalcStats(); + } + showAvatarInfoList.add(avatar.toShowAvatarInfoProto()); + } + } + return showAvatarInfoList; + } + + public PlayerWorldLocationInfoOuterClass.PlayerWorldLocationInfo getWorldPlayerLocationInfo() { + return PlayerWorldLocationInfoOuterClass.PlayerWorldLocationInfo.newBuilder() + .setSceneId(this.getSceneId()) + .setPlayerLoc(this.getPlayerLocationInfo()) + .build(); + } + + public PlayerLocationInfo getPlayerLocationInfo() { + return PlayerLocationInfo.newBuilder() + .setUid(this.getUid()) + .setPos(this.getPosition().toProto()) + .setRot(this.getRotation().toProto()) + .build(); + } + + public void loadBattlePassManager() { + if (this.battlePassManager != null) return; + this.battlePassManager = DatabaseHelper.loadBattlePass(this); + this.battlePassManager.getMissions().values().removeIf(mission -> mission.getData() == null); + } + + public PlayerCollectionRecords getCollectionRecordStore() { + if (this.collectionRecordStore==null) { + this.collectionRecordStore = new PlayerCollectionRecords(); + } + return collectionRecordStore; + } + + public Map getMapMarks() { + if (this.mapMarks == null) { + this.mapMarks = new HashMap(); + } + return mapMarks; + } + + public PlayerOpenStateManager getOpenStateManager() { + if (this.openStateManager == null) { this.openStateManager = new PlayerOpenStateManager(this); } return openStateManager; } public synchronized void onTick() { - // Check ping - if (this.getLastPingTime() > System.currentTimeMillis() + 60000) { - this.getSession().close(); - return; - } - // Check co-op requests - Iterator it = this.getCoopRequests().values().iterator(); - while (it.hasNext()) { - CoopRequest req = it.next(); - if (req.isExpired()) { - req.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify( - this, - false, - PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_SYSTEM_JUDGE)); - it.remove(); - } - } - // Ping - if (this.getWorld() != null) { - // RTT notify - very important to send this often - this.sendPacket(new PacketWorldPlayerRTTNotify(this.getWorld())); + // Check ping + if (this.getLastPingTime() > System.currentTimeMillis() + 60000) { + this.getSession().close(); + return; + } + // Check co-op requests + Iterator it = this.getCoopRequests().values().iterator(); + while (it.hasNext()) { + CoopRequest req = it.next(); + if (req.isExpired()) { + req.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify( + this, + false, + PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_SYSTEM_JUDGE)); + it.remove(); + } + } + // Ping + if (this.getWorld() != null) { + // RTT notify - very important to send this often + this.sendPacket(new PacketWorldPlayerRTTNotify(this.getWorld())); - // Update player locations if in multiplayer every 5 seconds - long time = System.currentTimeMillis(); - if (this.getWorld().isMultiplayer() && this.getScene() != null && time > nextSendPlayerLocTime) { - this.sendPacket(new PacketWorldPlayerLocationNotify(this.getWorld())); - this.sendPacket(new PacketScenePlayerLocationNotify(this.getScene())); - this.resetSendPlayerLocTime(); - } - } + // Update player locations if in multiplayer every 5 seconds + long time = System.currentTimeMillis(); + if (this.getWorld().isMultiplayer() && this.getScene() != null && time > nextSendPlayerLocTime) { + this.sendPacket(new PacketWorldPlayerLocationNotify(this.getWorld())); + this.sendPacket(new PacketScenePlayerLocationNotify(this.getScene())); + this.resetSendPlayerLocTime(); + } + } - // Handle daily reset. - this.doDailyReset(); + // Handle daily reset. + this.doDailyReset(); - // Expedition - var timeNow = Utils.getCurrentSeconds(); - var needNotify = false; - for (Long key : expeditionInfo.keySet()) { - ExpeditionInfo e = expeditionInfo.get(key); - if(e.getState() == 1){ - if(timeNow - e.getStartTime() >= e.getHourTime() * 60 * 60){ - e.setState(2); - needNotify = true; - } - } - } - if(needNotify){ - this.save(); - this.sendPacket(new PacketAvatarExpeditionDataNotify(this)); - } + // Expedition + var timeNow = Utils.getCurrentSeconds(); + var needNotify = false; + for (Long key : expeditionInfo.keySet()) { + ExpeditionInfo e = expeditionInfo.get(key); + if (e.getState() == 1) { + if (timeNow - e.getStartTime() >= e.getHourTime() * 60 * 60) { + e.setState(2); + needNotify = true; + } + } + } + if (needNotify) { + this.save(); + this.sendPacket(new PacketAvatarExpeditionDataNotify(this)); + } - // Send updated forge queue data, if necessary. - this.getForgingManager().sendPlayerForgingUpdate(); + // Send updated forge queue data, if necessary. + this.getForgingManager().sendPlayerForgingUpdate(); - // Recharge resin. - this.getResinManager().rechargeResin(); - } + // Recharge resin. + this.getResinManager().rechargeResin(); + } - private synchronized void doDailyReset() { - // Check if we should execute a daily reset on this tick. - int currentTime = Utils.getCurrentSeconds(); + private synchronized void doDailyReset() { + // Check if we should execute a daily reset on this tick. + int currentTime = Utils.getCurrentSeconds(); - var currentDate = LocalDate.ofInstant(Instant.ofEpochSecond(currentTime), ZoneId.systemDefault()); - var lastResetDate = LocalDate.ofInstant(Instant.ofEpochSecond(this.getLastDailyReset()), ZoneId.systemDefault()); + var currentDate = LocalDate.ofInstant(Instant.ofEpochSecond(currentTime), ZoneId.systemDefault()); + var lastResetDate = LocalDate.ofInstant(Instant.ofEpochSecond(this.getLastDailyReset()), ZoneId.systemDefault()); - if (!currentDate.isAfter(lastResetDate)) { - return; - } + if (!currentDate.isAfter(lastResetDate)) { + return; + } - // We should - now execute all the resetting logic we need. - // Reset forge points. - this.setForgePoints(300_000); + // We should - now execute all the resetting logic we need. + // Reset forge points. + this.setForgePoints(300_000); - // Reset daily BP missions. - this.getBattlePassManager().resetDailyMissions(); + // Reset daily BP missions. + this.getBattlePassManager().resetDailyMissions(); - // Trigger login BP mission, so players who are online during the reset - // don't have to relog to clear the mission. - this.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_LOGIN); + // Trigger login BP mission, so players who are online during the reset + // don't have to relog to clear the mission. + this.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_LOGIN); - // Reset weekly BP missions. - if (currentDate.getDayOfWeek() == DayOfWeek.MONDAY) { - this.getBattlePassManager().resetWeeklyMissions(); - } + // Reset weekly BP missions. + if (currentDate.getDayOfWeek() == DayOfWeek.MONDAY) { + this.getBattlePassManager().resetWeeklyMissions(); + } - // Done. Update last reset time. - this.setLastDailyReset(currentTime); - } + // Done. Update last reset time. + this.setLastDailyReset(currentTime); + } - public void resetSendPlayerLocTime() { - this.nextSendPlayerLocTime = System.currentTimeMillis() + 5000; - } + public void resetSendPlayerLocTime() { + this.nextSendPlayerLocTime = System.currentTimeMillis() + 5000; + } - @PostLoad - private void onLoad() { - this.getCodex().setPlayer(this); + @PostLoad + private void onLoad() { + this.getCodex().setPlayer(this); this.getOpenStateManager().setPlayer(this); - this.getTeamManager().setPlayer(this); - } + this.getTeamManager().setPlayer(this); + } - public void save() { - DatabaseHelper.savePlayer(this); - } + public void save() { + DatabaseHelper.savePlayer(this); + } - // Called from tokenrsp - public void loadFromDatabase() { - // Make sure these exist - if (this.getTeamManager() == null) { - this.teamManager = new TeamManager(this); - } - if (this.getCodex() == null) { - this.codex = new PlayerCodex(this); - } - if (this.getProfile().getUid() == 0) { - this.getProfile().syncWithCharacter(this); - } + // Called from tokenrsp + public void loadFromDatabase() { + // Make sure these exist + if (this.getTeamManager() == null) { + this.teamManager = new TeamManager(this); + } + if (this.getCodex() == null) { + this.codex = new PlayerCodex(this); + } + if (this.getProfile().getUid() == 0) { + this.getProfile().syncWithCharacter(this); + } - // Load from db - this.getAvatars().loadFromDatabase(); - this.getInventory().loadFromDatabase(); - this.getAvatars().postLoad(); // Needs to be called after inventory is handled + // Load from db + this.getAvatars().loadFromDatabase(); + this.getInventory().loadFromDatabase(); + this.getAvatars().postLoad(); // Needs to be called after inventory is handled - this.getFriendsList().loadFromDatabase(); - this.getMailHandler().loadFromDatabase(); - this.getQuestManager().loadFromDatabase(); + this.getFriendsList().loadFromDatabase(); + this.getMailHandler().loadFromDatabase(); + this.getQuestManager().loadFromDatabase(); - this.loadBattlePassManager(); - } + this.loadBattlePassManager(); + } - public void onLogin() { - // Quest - Commented out because a problem is caused if you log out while this quest is active - /* - if (getQuestManager().getMainQuestById(351) == null) { - GameQuest quest = getQuestManager().addQuest(35104); - if (quest != null) { - quest.finish(); - } - getQuestManager().addQuest(35101); + public void onLogin() { + // Quest - Commented out because a problem is caused if you log out while this quest is active + /* + if (getQuestManager().getMainQuestById(351) == null) { + GameQuest quest = getQuestManager().addQuest(35104); + if (quest != null) { + quest.finish(); + } + getQuestManager().addQuest(35101); - this.setSceneId(3); - this.getPos().set(GameConstants.START_POSITION); - } - */ + this.setSceneId(3); + this.getPos().set(GameConstants.START_POSITION); + } + */ - // Create world - World world = new World(this); - world.addPlayer(this); + // Create world + World world = new World(this); + world.addPlayer(this); - // Multiplayer setting - this.setProperty(PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE, this.getMpSetting().getNumber(), false); - this.setProperty(PlayerProperty.PROP_IS_MP_MODE_AVAILABLE, 1, false); + // Multiplayer setting + this.setProperty(PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE, this.getMpSetting().getNumber(), false); + this.setProperty(PlayerProperty.PROP_IS_MP_MODE_AVAILABLE, 1, false); - // Execute daily reset logic if this is a new day. - this.doDailyReset(); + // Execute daily reset logic if this is a new day. + this.doDailyReset(); - // Packets - session.send(new PacketPlayerDataNotify(this)); // Player data - session.send(new PacketStoreWeightLimitNotify()); - session.send(new PacketPlayerStoreNotify(this)); - session.send(new PacketAvatarDataNotify(this)); - session.send(new PacketFinishedParentQuestNotify(this)); - session.send(new PacketBattlePassAllDataNotify(this)); - session.send(new PacketQuestListNotify(this)); - session.send(new PacketCodexDataFullNotify(this)); - session.send(new PacketAllWidgetDataNotify(this)); - session.send(new PacketWidgetGadgetAllDataNotify()); - session.send(new PacketCombineDataNotify(this.unlockedCombines)); - this.forgingManager.sendForgeDataNotify(); - this.resinManager.onPlayerLogin(); - this.cookingManager.sendCookDataNofity(); + // Packets + session.send(new PacketPlayerDataNotify(this)); // Player data + session.send(new PacketStoreWeightLimitNotify()); + session.send(new PacketPlayerStoreNotify(this)); + session.send(new PacketAvatarDataNotify(this)); + session.send(new PacketFinishedParentQuestNotify(this)); + session.send(new PacketBattlePassAllDataNotify(this)); + session.send(new PacketQuestListNotify(this)); + session.send(new PacketCodexDataFullNotify(this)); + session.send(new PacketAllWidgetDataNotify(this)); + session.send(new PacketWidgetGadgetAllDataNotify()); + session.send(new PacketCombineDataNotify(this.unlockedCombines)); + this.forgingManager.sendForgeDataNotify(); + this.resinManager.onPlayerLogin(); + this.cookingManager.sendCookDataNofity(); - // Unlock in case this is an existing user that reached a level before we implemented unlocking. - this.getOpenStateManager().unlockLevelDependentStates(); + // Unlock in case this is an existing user that reached a level before we implemented unlocking. + this.getOpenStateManager().unlockLevelDependentStates(); this.getOpenStateManager().onPlayerLogin(); - getTodayMoonCard(); // The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward. + getTodayMoonCard(); // The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward. - // Battle Pass trigger - this.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_LOGIN); + // Battle Pass trigger + this.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_LOGIN); - this.furnitureManager.onLogin(); - // Home - home = GameHome.getByUid(getUid()); - home.onOwnerLogin(this); + this.furnitureManager.onLogin(); + // Home + home = GameHome.getByUid(getUid()); + home.onOwnerLogin(this); // Activity activityManager = new ActivityManager(this); - session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world - session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels)); + session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world + session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels)); - // First notify packets sent - this.setHasSentAvatarDataNotify(true); + // First notify packets sent + this.setHasSentAvatarDataNotify(true); - // Send server welcome chat. - this.getServer().getChatManager().sendServerWelcomeMessages(this); - - // Set session state - session.setState(SessionState.ACTIVE); + // Send server welcome chat. + this.getServer().getChatManager().sendServerWelcomeMessages(this); - // Call join event. - PlayerJoinEvent event = new PlayerJoinEvent(this); event.call(); - if(event.isCanceled()){ // If event is not cancelled, continue. - session.close(); - return; - } + // Set session state + session.setState(SessionState.ACTIVE); - // register - getServer().registerPlayer(this); - getProfile().setPlayer(this); // Set online - } + // Call join event. + PlayerJoinEvent event = new PlayerJoinEvent(this); event.call(); + if (event.isCanceled()) { // If event is not cancelled, continue. + session.close(); + return; + } - public void onLogout() { - try{ - // Clear chat history. - this.getServer().getChatManager().clearHistoryOnLogout(this); + // register + getServer().registerPlayer(this); + getProfile().setPlayer(this); // Set online + } - // stop stamina calculation - getStaminaManager().stopSustainedStaminaHandler(); + public void onLogout() { + try { + // Clear chat history. + this.getServer().getChatManager().clearHistoryOnLogout(this); - // force to leave the dungeon (inside has a "if") - this.getServer().getDungeonSystem().exitDungeon(this); + // stop stamina calculation + getStaminaManager().stopSustainedStaminaHandler(); - // Leave world - if (this.getWorld() != null) { - this.getWorld().removePlayer(this); - } + // force to leave the dungeon (inside has a "if") + this.getServer().getDungeonSystem().exitDungeon(this); - // Status stuff - this.getProfile().syncWithCharacter(this); - this.getProfile().setPlayer(null); // Set offline + // Leave world + if (this.getWorld() != null) { + this.getWorld().removePlayer(this); + } - this.getCoopRequests().clear(); + // Status stuff + this.getProfile().syncWithCharacter(this); + this.getProfile().setPlayer(null); // Set offline - // Save to db - this.save(); - this.getTeamManager().saveAvatars(); - this.getFriendsList().save(); + this.getCoopRequests().clear(); - // Call quit event. - PlayerQuitEvent event = new PlayerQuitEvent(this); event.call(); - }catch (Throwable e){ - e.printStackTrace(); - Grasscutter.getLogger().warn("Player (UID {}) save failure", getUid()); - }finally { - removeFromServer(); - } - } + // Save to db + this.save(); + this.getTeamManager().saveAvatars(); + this.getFriendsList().save(); - public void removeFromServer() { - // Remove from server. - //Note: DON'T DELETE BY UID,BECAUSE THERE ARE MULTIPLE SAME UID PLAYERS WHEN DUPLICATED LOGIN! - //so I decide to delete by object rather than uid - getServer().getPlayers().values().removeIf(player1 -> player1 == this); - } + // Call quit event. + PlayerQuitEvent event = new PlayerQuitEvent(this); event.call(); + }catch (Throwable e) { + e.printStackTrace(); + Grasscutter.getLogger().warn("Player (UID {}) save failure", getUid()); + }finally { + removeFromServer(); + } + } + + public void removeFromServer() { + // Remove from server. + //Note: DON'T DELETE BY UID,BECAUSE THERE ARE MULTIPLE SAME UID PLAYERS WHEN DUPLICATED LOGIN! + //so I decide to delete by object rather than uid + getServer().getPlayers().values().removeIf(player1 -> player1 == this); + } public int getLegendaryKey() { return this.getProperty(PlayerProperty.PROP_PLAYER_LEGENDARY_KEY); @@ -1436,58 +1436,58 @@ public class Player { } public enum SceneLoadState { - NONE(0), LOADING(1), INIT(2), LOADED(3); + NONE(0), LOADING(1), INIT(2), LOADED(3); - private final int value; + private final int value; - private SceneLoadState(int value) { - this.value = value; - } + private SceneLoadState(int value) { + this.value = value; + } - public int getValue() { - return this.value; - } - } + public int getValue() { + return this.value; + } + } - public void setMessageHandler(MessageHandler messageHandler) { - this.messageHandler = messageHandler; - } + public void setMessageHandler(MessageHandler messageHandler) { + this.messageHandler = messageHandler; + } - public int getPropertyMin(PlayerProperty prop) { - if (prop.getDynamicRange()) { - return switch (prop) { - default -> 0; - }; - } else { - return prop.getMin(); - } - } + public int getPropertyMin(PlayerProperty prop) { + if (prop.getDynamicRange()) { + return switch (prop) { + default -> 0; + }; + } else { + return prop.getMin(); + } + } - public int getPropertyMax(PlayerProperty prop) { - if (prop.getDynamicRange()) { - return switch (prop) { - case PROP_CUR_SPRING_VOLUME -> getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME); - case PROP_CUR_PERSIST_STAMINA -> getProperty(PlayerProperty.PROP_MAX_STAMINA); - default -> 0; - }; - } else { - return prop.getMax(); - } - } + public int getPropertyMax(PlayerProperty prop) { + if (prop.getDynamicRange()) { + return switch (prop) { + case PROP_CUR_SPRING_VOLUME -> getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME); + case PROP_CUR_PERSIST_STAMINA -> getProperty(PlayerProperty.PROP_MAX_STAMINA); + default -> 0; + }; + } else { + return prop.getMax(); + } + } - private boolean setPropertyWithSanityCheck(PlayerProperty prop, int value, boolean sendPacket) { - int min = this.getPropertyMin(prop); - int max = this.getPropertyMax(prop); - if (min <= value && value <= max) { - this.properties.put(prop.getId(), value); - if (sendPacket) { - // Update player with packet - this.sendPacket(new PacketPlayerPropNotify(this, prop)); - } - return true; - } else { - return false; - } - } + private boolean setPropertyWithSanityCheck(PlayerProperty prop, int value, boolean sendPacket) { + int min = this.getPropertyMin(prop); + int max = this.getPropertyMax(prop); + if (min <= value && value <= max) { + this.properties.put(prop.getId(), value); + if (sendPacket) { + // Update player with packet + this.sendPacket(new PacketPlayerPropNotify(this, prop)); + } + return true; + } else { + return false; + } + } } diff --git a/src/main/java/emu/grasscutter/game/player/PlayerCollectionRecords.java b/src/main/java/emu/grasscutter/game/player/PlayerCollectionRecords.java index 676caee40..ce7bf68ec 100644 --- a/src/main/java/emu/grasscutter/game/player/PlayerCollectionRecords.java +++ b/src/main/java/emu/grasscutter/game/player/PlayerCollectionRecords.java @@ -8,60 +8,60 @@ import dev.morphia.annotations.Entity; @Entity(useDiscriminator = false) public class PlayerCollectionRecords { private Map records; - + private Map getRecords() { if (records == null) { - records = new HashMap<>(); + records = new HashMap<>(); } return records; } - - public void addRecord(int configId, long expiredMillisecond){ - Map records; + + public void addRecord(int configId, long expiredMillisecond) { + Map records; synchronized (records = getRecords()) { records.put(configId, new CollectionRecord(configId, expiredMillisecond + System.currentTimeMillis())); } } - + public boolean findRecord(int configId) { - Map records; + Map 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; + } } } diff --git a/src/main/java/emu/grasscutter/game/player/PlayerOpenStateManager.java b/src/main/java/emu/grasscutter/game/player/PlayerOpenStateManager.java index 8effaadac..8757639f4 100644 --- a/src/main/java/emu/grasscutter/game/player/PlayerOpenStateManager.java +++ b/src/main/java/emu/grasscutter/game/player/PlayerOpenStateManager.java @@ -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 map; - + /* //DO NOT MODIFY. Based on conversation of official server and client, game version 2.7 private static Set 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 DEV_OPEN_STATES = Stream.of(OpenState.values()) @@ -38,25 +38,25 @@ public class PlayerOpenStateManager extends BasePlayerDataManager { super(player); } - public Map getOpenStateMap() { - if (this.map == null) this.map = new HashMap<>(); - return this.map; - } + public Map 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 openStatesChanged) { - for(Map.Entry entry : openStatesChanged.entrySet()) { + for (Map.Entry 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)); } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/game/player/TeamInfo.java b/src/main/java/emu/grasscutter/game/player/TeamInfo.java index b1f265339..692bf05c3 100644 --- a/src/main/java/emu/grasscutter/game/player/TeamInfo.java +++ b/src/main/java/emu/grasscutter/game/player/TeamInfo.java @@ -10,75 +10,75 @@ import emu.grasscutter.game.avatar.Avatar; @Entity public class TeamInfo { - private String name; - private List avatars; - - public TeamInfo() { - this.name = ""; - this.avatars = new ArrayList<>(GAME_OPTIONS.avatarLimits.singlePlayerTeam); - } + private String name; + private List avatars; - public TeamInfo(List 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 avatars) { + this.name = ""; + this.avatars = avatars; + } - public void setName(String name) { - this.name = name; - } + public String getName() { + return name; + } - public List 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 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 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 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); + } + } } diff --git a/src/main/java/emu/grasscutter/game/player/TeamManager.java b/src/main/java/emu/grasscutter/game/player/TeamManager.java index 5281f85f5..9fd19abdf 100644 --- a/src/main/java/emu/grasscutter/game/player/TeamManager.java +++ b/src/main/java/emu/grasscutter/game/player/TeamManager.java @@ -42,636 +42,636 @@ import it.unimi.dsi.fastutil.ints.IntSet; @Entity public class TeamManager extends BasePlayerDataManager { - @Transient private Player player; - - private Map teams; - private int currentTeamIndex; - private int currentCharacterIndex; - - @Transient private TeamInfo mpTeam; - @Transient private int entityId; - @Transient private final List avatars; - @Transient private final Set gadgets; - @Transient private final IntSet teamResonances; - @Transient private final IntSet teamResonancesConfig; + @Transient private Player player; - @Transient private int useTemporarilyTeamIndex = -1; - @Transient private List temporaryTeam; // Temporary Team for tower + private Map teams; + private int currentTeamIndex; + private int currentCharacterIndex; - public TeamManager() { - this.mpTeam = new TeamInfo(); - this.avatars = new ArrayList<>(); - this.gadgets = new HashSet<>(); - this.teamResonances = new IntOpenHashSet(); - this.teamResonancesConfig = new IntOpenHashSet(); - } - - public TeamManager(Player player) { - this(); - this.setPlayer(player); - - this.teams = new HashMap<>(); - this.currentTeamIndex = 1; - for (int i = 1; i <= GameConstants.MAX_TEAMS; i++) { - this.teams.put(i, new TeamInfo()); - } - } - - public World getWorld() { - return player.getWorld(); - } - - public Map getTeams() { - return this.teams; - } + @Transient private TeamInfo mpTeam; + @Transient private int entityId; + @Transient private final List avatars; + @Transient private final Set gadgets; + @Transient private final IntSet teamResonances; + @Transient private final IntSet teamResonancesConfig; - public TeamInfo getMpTeam() { - return mpTeam; - } + @Transient private int useTemporarilyTeamIndex = -1; + @Transient private List temporaryTeam; // Temporary Team for tower - public void setMpTeam(TeamInfo mpTeam) { - this.mpTeam = mpTeam; - } + public TeamManager() { + this.mpTeam = new TeamInfo(); + this.avatars = new ArrayList<>(); + this.gadgets = new HashSet<>(); + this.teamResonances = new IntOpenHashSet(); + this.teamResonancesConfig = new IntOpenHashSet(); + } - /** - * Search through all teams and if the team matches, return that index. - * Otherwise, return -1. - * No match could mean that the team does not currently belong to the player. - */ - public int getTeamId(TeamInfo team) { - for (int i = 1; i <= this.teams.size(); i++) { - if (this.teams.get(i).equals(team)) { - return i; - } - } - return -1; - } + public TeamManager(Player player) { + this(); + this.setPlayer(player); - public int getCurrentTeamId() { - // Starts from 1 - return currentTeamIndex; - } + this.teams = new HashMap<>(); + this.currentTeamIndex = 1; + for (int i = 1; i <= GameConstants.MAX_TEAMS; i++) { + this.teams.put(i, new TeamInfo()); + } + } - private void setCurrentTeamId(int currentTeamIndex) { - this.currentTeamIndex = currentTeamIndex; - } + public World getWorld() { + return player.getWorld(); + } - public int getCurrentCharacterIndex() { - return currentCharacterIndex; - } + public Map getTeams() { + return this.teams; + } - public void setCurrentCharacterIndex(int currentCharacterIndex) { - this.currentCharacterIndex = currentCharacterIndex; - } + public TeamInfo getMpTeam() { + return mpTeam; + } - public long getCurrentCharacterGuid() { - return getCurrentAvatarEntity().getAvatar().getGuid(); - } - - public TeamInfo getCurrentTeamInfo() { - if (useTemporarilyTeamIndex >= 0 && - useTemporarilyTeamIndex < temporaryTeam.size()){ - return temporaryTeam.get(useTemporarilyTeamIndex); - } - if (this.getPlayer().isInMultiplayer()) { - return this.getMpTeam(); - } - return this.getTeams().get(this.currentTeamIndex); - } - - public TeamInfo getCurrentSinglePlayerTeamInfo() { - return this.getTeams().get(this.currentTeamIndex); - } + public void setMpTeam(TeamInfo mpTeam) { + this.mpTeam = mpTeam; + } - public int getEntityId() { - return entityId; - } + /** + * Search through all teams and if the team matches, return that index. + * Otherwise, return -1. + * No match could mean that the team does not currently belong to the player. + */ + public int getTeamId(TeamInfo team) { + for (int i = 1; i <= this.teams.size(); i++) { + if (this.teams.get(i).equals(team)) { + return i; + } + } + return -1; + } - public void setEntityId(int entityId) { - this.entityId = entityId; - } + public int getCurrentTeamId() { + // Starts from 1 + return currentTeamIndex; + } - public Set getGadgets() { - return gadgets; - } + private void setCurrentTeamId(int currentTeamIndex) { + this.currentTeamIndex = currentTeamIndex; + } - public IntSet getTeamResonances() { - return teamResonances; - } + public int getCurrentCharacterIndex() { + return currentCharacterIndex; + } - public IntSet getTeamResonancesConfig() { - return teamResonancesConfig; - } + public void setCurrentCharacterIndex(int currentCharacterIndex) { + this.currentCharacterIndex = currentCharacterIndex; + } - public List getActiveTeam() { - return avatars; - } - - public EntityAvatar getCurrentAvatarEntity() { - return getActiveTeam().get(currentCharacterIndex); - } - - public boolean isSpawned() { - return getPlayer().getScene() != null && getPlayer().getScene().getEntities().containsKey(getCurrentAvatarEntity().getId()); - } - - public int getMaxTeamSize() { - if (getPlayer().isInMultiplayer()) { - int max = GAME_OPTIONS.avatarLimits.multiplayerTeam; - if (getPlayer().getWorld().getHost() == this.getPlayer()) { - return Math.max(1, (int) Math.ceil(max / (double) getWorld().getPlayerCount())); - } - return Math.max(1, (int) Math.floor(max / (double) getWorld().getPlayerCount())); - } - - return GAME_OPTIONS.avatarLimits.singlePlayerTeam; - } - - // Methods + public long getCurrentCharacterGuid() { + return getCurrentAvatarEntity().getAvatar().getGuid(); + } - /** - * Returns true if there is space to add the number of avatars to the team. - */ - public boolean canAddAvatarsToTeam(TeamInfo team, int avatars) { - return team.size() + avatars <= getMaxTeamSize(); - } + public TeamInfo getCurrentTeamInfo() { + if (useTemporarilyTeamIndex >= 0 && + useTemporarilyTeamIndex < temporaryTeam.size()) { + return temporaryTeam.get(useTemporarilyTeamIndex); + } + if (this.getPlayer().isInMultiplayer()) { + return this.getMpTeam(); + } + return this.getTeams().get(this.currentTeamIndex); + } - /** - * Returns true if there is space to add to the team. - */ - public boolean canAddAvatarToTeam(TeamInfo team) { - return canAddAvatarsToTeam(team, 1); - } + public TeamInfo getCurrentSinglePlayerTeamInfo() { + return this.getTeams().get(this.currentTeamIndex); + } - /** - * Returns true if there is space to add the number of avatars to the current team. - * If the current team is temporary, returns false. - */ - public boolean canAddAvatarsToCurrentTeam(int avatars) { - if (this.useTemporarilyTeamIndex != -1){ - return false; - } - return canAddAvatarsToTeam(this.getCurrentTeamInfo(), avatars); - } + public int getEntityId() { + return entityId; + } - /** - * Returns true if there is space to add to the current team. - * If the current team is temporary, returns false. - */ - public boolean canAddAvatarToCurrentTeam() { - return canAddAvatarsToCurrentTeam(1); - } + public void setEntityId(int entityId) { + this.entityId = entityId; + } - /** - * Try to add the collection of avatars to the team. - * Returns true if all were successfully added. - * If some can not be added, returns false and does not add any. - */ - public boolean addAvatarsToTeam(TeamInfo team, Collection avatars) { - if (!canAddAvatarsToTeam(team, avatars.size())) { - return false; - } + public Set getGadgets() { + return gadgets; + } - // Convert avatars into a collection of avatar IDs, then add - team.getAvatars().addAll(avatars.stream().map(a -> a.getAvatarId()).toList()); + public IntSet getTeamResonances() { + return teamResonances; + } - // Update team - if (this.getPlayer().isInMultiplayer()) { - if (team.equals(this.getMpTeam())) { - // MP team Packet - this.updateTeamEntities(new PacketChangeMpTeamAvatarRsp(getPlayer(), team)); - } - } else { - // SP team update packet - getPlayer().sendPacket(new PacketAvatarTeamUpdateNotify(getPlayer())); + public IntSet getTeamResonancesConfig() { + return teamResonancesConfig; + } - int teamId = this.getTeamId(team); - if (teamId != -1) { - // This is one of the player's teams - // Update entites - if (teamId == this.getCurrentTeamId()) { - this.updateTeamEntities(new PacketSetUpAvatarTeamRsp(getPlayer(), teamId, team)); - } else { - getPlayer().sendPacket(new PacketSetUpAvatarTeamRsp(getPlayer(), teamId, team)); - } - } - } + public List getActiveTeam() { + return avatars; + } - return true; - } + public EntityAvatar getCurrentAvatarEntity() { + return getActiveTeam().get(currentCharacterIndex); + } - /** - * Try to add an avatar to a team. - * Returns true if successful. - */ - public boolean addAvatarToTeam(TeamInfo team, Avatar avatar){ - return addAvatarsToTeam(team, Collections.singleton(avatar)); - } + public boolean isSpawned() { + return getPlayer().getScene() != null && getPlayer().getScene().getEntities().containsKey(getCurrentAvatarEntity().getId()); + } - /** - * Try to add the collection of avatars to the current team. - * Will not modify a temporary team. - * Returns true if all were successfully added. - * If some can not be added, returns false and does not add any. - */ - public boolean addAvatarsToCurrentTeam(Collection avatars) { - if (this.useTemporarilyTeamIndex != -1){ - return false; - } - return addAvatarsToTeam(this.getCurrentTeamInfo(), avatars); - } + public int getMaxTeamSize() { + if (getPlayer().isInMultiplayer()) { + int max = GAME_OPTIONS.avatarLimits.multiplayerTeam; + if (getPlayer().getWorld().getHost() == this.getPlayer()) { + return Math.max(1, (int) Math.ceil(max / (double) getWorld().getPlayerCount())); + } + return Math.max(1, (int) Math.floor(max / (double) getWorld().getPlayerCount())); + } - /** - * Try to add an avatar to the current team. - * Will not modify a temporary team. - * Returns true if successful. - */ - public boolean addAvatarToCurrentTeam(Avatar avatar) { - return addAvatarsToCurrentTeam(Collections.singleton(avatar)); - } + return GAME_OPTIONS.avatarLimits.singlePlayerTeam; + } - private void updateTeamResonances() { - Int2IntOpenHashMap map = new Int2IntOpenHashMap(); - - this.getTeamResonances().clear(); - this.getTeamResonancesConfig().clear(); - - for (EntityAvatar entity : getActiveTeam()) { - AvatarSkillDepotData skillData = entity.getAvatar().getAvatarData().getSkillDepot(); - if (skillData != null) { - map.addTo(skillData.getElementType().getValue(), 1); - } - } - - for (Int2IntMap.Entry e : map.int2IntEntrySet()) { - if (e.getIntValue() >= 2) { - ElementType element = ElementType.getTypeByValue(e.getIntKey()); - if (element.getTeamResonanceId() != 0) { - this.getTeamResonances().add(element.getTeamResonanceId()); - this.getTeamResonancesConfig().add(element.getConfigHash()); - } - } - } - - // No resonances - if (this.getTeamResonances().size() == 0) { - this.getTeamResonances().add(ElementType.Default.getTeamResonanceId()); - this.getTeamResonancesConfig().add(ElementType.Default.getTeamResonanceId()); - } - } - - public void updateTeamEntities(BasePacket responsePacket) { - // Sanity check - Should never happen - if (this.getCurrentTeamInfo().getAvatars().size() <= 0) { - return; - } - - // If current team has changed - EntityAvatar currentEntity = this.getCurrentAvatarEntity(); - Int2ObjectMap existingAvatars = new Int2ObjectOpenHashMap<>(); - int prevSelectedAvatarIndex = -1; - - for (EntityAvatar entity : getActiveTeam()) { - existingAvatars.put(entity.getAvatar().getAvatarId(), entity); - } - - // Clear active team entity list - this.getActiveTeam().clear(); - - // Add back entities into team - for (int i = 0; i < this.getCurrentTeamInfo().getAvatars().size(); i++) { - int avatarId = this.getCurrentTeamInfo().getAvatars().get(i); - EntityAvatar entity; - - if (existingAvatars.containsKey(avatarId)) { - entity = existingAvatars.get(avatarId); - existingAvatars.remove(avatarId); - if (entity == currentEntity) { - prevSelectedAvatarIndex = i; - } - } else { - entity = new EntityAvatar(getPlayer().getScene(), getPlayer().getAvatars().getAvatarById(avatarId)); - } - - this.getActiveTeam().add(entity); - } - - // Unload removed entities - for (EntityAvatar entity : existingAvatars.values()) { - getPlayer().getScene().removeEntity(entity); - entity.getAvatar().save(); - } - - // Set new selected character index - if (prevSelectedAvatarIndex == -1) { - // Previous selected avatar is not in the same spot, we will select the current one in the prev slot - prevSelectedAvatarIndex = Math.min(this.currentCharacterIndex, this.getActiveTeam().size() - 1); - } - this.currentCharacterIndex = prevSelectedAvatarIndex; - - // Update team resonances - updateTeamResonances(); - - // Packets - getPlayer().getWorld().broadcastPacket(new PacketSceneTeamUpdateNotify(getPlayer())); - - // Skill charges packet - Yes, this is official server behavior as of 2.6.0 - for (EntityAvatar entity : getActiveTeam()) { - if (entity.getAvatar().getSkillExtraChargeMap().size() > 0) { - getPlayer().sendPacket(new PacketAvatarSkillInfoNotify(entity.getAvatar())); - } - } - - // Run callback - if (responsePacket != null) { - getPlayer().sendPacket(responsePacket); - } + // Methods - // Check if character changed - if (currentEntity != getCurrentAvatarEntity()) { - // Remove and Add - getPlayer().getScene().replaceEntity(currentEntity, getCurrentAvatarEntity()); - } - } - - public synchronized void setupAvatarTeam(int teamId, List list) { - // Sanity checks - if (list.size() == 0 || list.size() > getMaxTeamSize() || getPlayer().isInMultiplayer()) { - return; - } - - // Get team - TeamInfo teamInfo = this.getTeams().get(teamId); - if (teamInfo == null) { - return; - } - - // Set team data - LinkedHashSet newTeam = new LinkedHashSet<>(); - for (Long aLong : list) { - Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(aLong); - if (avatar == null || newTeam.contains(avatar)) { - // Should never happen - return; - } - newTeam.add(avatar); - } - - // Clear current team info and add avatars from our new team - teamInfo.getAvatars().clear(); - this.addAvatarsToTeam(teamInfo, newTeam); - } - - public void setupMpTeam(List list) { - // Sanity checks - if (list.size() == 0 || list.size() > getMaxTeamSize() || !getPlayer().isInMultiplayer()) { - return; - } + /** + * Returns true if there is space to add the number of avatars to the team. + */ + public boolean canAddAvatarsToTeam(TeamInfo team, int avatars) { + return team.size() + avatars <= getMaxTeamSize(); + } - TeamInfo teamInfo = this.getMpTeam(); - - // Set team data - LinkedHashSet newTeam = new LinkedHashSet<>(); - for (Long aLong : list) { - Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(aLong); - if (avatar == null || newTeam.contains(avatar)) { - // Should never happen - return; - } - newTeam.add(avatar); - } - - // Clear current team info and add avatars from our new team - teamInfo.getAvatars().clear(); - this.addAvatarsToTeam(teamInfo, newTeam); - } + /** + * Returns true if there is space to add to the team. + */ + public boolean canAddAvatarToTeam(TeamInfo team) { + return canAddAvatarsToTeam(team, 1); + } - public void setupTemporaryTeam(List> guidList) { - this.temporaryTeam = guidList.stream().map(list -> { - // Sanity checks - if (list.size() == 0 || list.size() > getMaxTeamSize()) { - return null; - } + /** + * Returns true if there is space to add the number of avatars to the current team. + * If the current team is temporary, returns false. + */ + public boolean canAddAvatarsToCurrentTeam(int avatars) { + if (this.useTemporarilyTeamIndex != -1) { + return false; + } + return canAddAvatarsToTeam(this.getCurrentTeamInfo(), avatars); + } - // Set team data - LinkedHashSet newTeam = new LinkedHashSet<>(); - for (Long aLong : list) { - Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(aLong); - if (avatar == null || newTeam.contains(avatar)) { - // Should never happen - return null; - } - newTeam.add(avatar); - } + /** + * Returns true if there is space to add to the current team. + * If the current team is temporary, returns false. + */ + public boolean canAddAvatarToCurrentTeam() { + return canAddAvatarsToCurrentTeam(1); + } - // convert to avatar ids - return newTeam.stream() - .map(Avatar::getAvatarId) - .toList(); - }) - .filter(Objects::nonNull) - .map(TeamInfo::new) - .toList(); - } + /** + * Try to add the collection of avatars to the team. + * Returns true if all were successfully added. + * If some can not be added, returns false and does not add any. + */ + public boolean addAvatarsToTeam(TeamInfo team, Collection avatars) { + if (!canAddAvatarsToTeam(team, avatars.size())) { + return false; + } - public void useTemporaryTeam(int index) { - this.useTemporarilyTeamIndex = index; - updateTeamEntities(null); - } + // Convert avatars into a collection of avatar IDs, then add + team.getAvatars().addAll(avatars.stream().map(a -> a.getAvatarId()).toList()); - public void cleanTemporaryTeam() { - // check if using temporary team - if(useTemporarilyTeamIndex < 0){ - return; - } + // Update team + if (this.getPlayer().isInMultiplayer()) { + if (team.equals(this.getMpTeam())) { + // MP team Packet + this.updateTeamEntities(new PacketChangeMpTeamAvatarRsp(getPlayer(), team)); + } + } else { + // SP team update packet + getPlayer().sendPacket(new PacketAvatarTeamUpdateNotify(getPlayer())); - this.useTemporarilyTeamIndex = -1; - this.temporaryTeam = null; - updateTeamEntities(null); - } - public synchronized void setCurrentTeam(int teamId) { - // - if (getPlayer().isInMultiplayer()) { - return; - } - - // Get team - TeamInfo teamInfo = this.getTeams().get(teamId); - if (teamInfo == null || teamInfo.getAvatars().size() == 0) { - return; - } - - // Set - this.setCurrentTeamId(teamId); - this.updateTeamEntities(new PacketChooseCurAvatarTeamRsp(teamId)); - } + int teamId = this.getTeamId(team); + if (teamId != -1) { + // This is one of the player's teams + // Update entites + if (teamId == this.getCurrentTeamId()) { + this.updateTeamEntities(new PacketSetUpAvatarTeamRsp(getPlayer(), teamId, team)); + } else { + getPlayer().sendPacket(new PacketSetUpAvatarTeamRsp(getPlayer(), teamId, team)); + } + } + } - public synchronized void setTeamName(int teamId, String teamName) { - // Get team - TeamInfo teamInfo = this.getTeams().get(teamId); - if (teamInfo == null) { - return; - } - - teamInfo.setName(teamName); - - // Packet - getPlayer().sendPacket(new PacketChangeTeamNameRsp(teamId, teamName)); - } + return true; + } - public synchronized void changeAvatar(long guid) { - EntityAvatar oldEntity = this.getCurrentAvatarEntity(); - - if (guid == oldEntity.getAvatar().getGuid()) { - return; - } + /** + * Try to add an avatar to a team. + * Returns true if successful. + */ + public boolean addAvatarToTeam(TeamInfo team, Avatar avatar) { + return addAvatarsToTeam(team, Collections.singleton(avatar)); + } - EntityAvatar newEntity = null; - int index = -1; - for (int i = 0; i < getActiveTeam().size(); i++) { - if (guid == getActiveTeam().get(i).getAvatar().getGuid()) { - index = i; - newEntity = getActiveTeam().get(i); - } - } - - if (index < 0 || newEntity == oldEntity) { - return; - } - - // Set index - this.setCurrentCharacterIndex(index); - - // Old entity motion state - oldEntity.setMotionState(MotionState.MOTION_STATE_STANDBY); + /** + * Try to add the collection of avatars to the current team. + * Will not modify a temporary team. + * Returns true if all were successfully added. + * If some can not be added, returns false and does not add any. + */ + public boolean addAvatarsToCurrentTeam(Collection avatars) { + if (this.useTemporarilyTeamIndex != -1) { + return false; + } + return addAvatarsToTeam(this.getCurrentTeamInfo(), avatars); + } - // Remove and Add - getPlayer().getScene().replaceEntity(oldEntity, newEntity); - getPlayer().sendPacket(new PacketChangeAvatarRsp(guid)); - } - - public void onAvatarDie(long dieGuid) { - EntityAvatar deadAvatar = this.getCurrentAvatarEntity(); - - if (deadAvatar.isAlive() || deadAvatar.getId() != dieGuid) { - return; - } + /** + * Try to add an avatar to the current team. + * Will not modify a temporary team. + * Returns true if successful. + */ + public boolean addAvatarToCurrentTeam(Avatar avatar) { + return addAvatarsToCurrentTeam(Collections.singleton(avatar)); + } - PlayerDieType dieType = deadAvatar.getKilledType(); - int killedBy = deadAvatar.getKilledBy(); + private void updateTeamResonances() { + Int2IntOpenHashMap map = new Int2IntOpenHashMap(); - if (dieType == PlayerDieType.PLAYER_DIE_TYPE_DRAWN) { - // Died in water. Do not replace - // The official server has skipped this notify and will just respawn the team immediately after the animation. - // TODO: Perhaps find a way to get vanilla experience? - getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy)); - } else { - // Replacement avatar - EntityAvatar replacement = null; - int replaceIndex = -1; + this.getTeamResonances().clear(); + this.getTeamResonancesConfig().clear(); - for (int i = 0; i < this.getActiveTeam().size(); i++) { - EntityAvatar entity = this.getActiveTeam().get(i); - if (entity.isAlive()) { - replaceIndex = i; - replacement = entity; - break; - } - } + for (EntityAvatar entity : getActiveTeam()) { + AvatarSkillDepotData skillData = entity.getAvatar().getAvatarData().getSkillDepot(); + if (skillData != null) { + map.addTo(skillData.getElementType().getValue(), 1); + } + } - if (replacement == null) { - // No more living team members... - getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy)); - } else { - // Set index and spawn replacement member - this.setCurrentCharacterIndex(replaceIndex); - getPlayer().getScene().addEntity(replacement); - } - } + for (Int2IntMap.Entry e : map.int2IntEntrySet()) { + if (e.getIntValue() >= 2) { + ElementType element = ElementType.getTypeByValue(e.getIntKey()); + if (element.getTeamResonanceId() != 0) { + this.getTeamResonances().add(element.getTeamResonanceId()); + this.getTeamResonancesConfig().add(element.getConfigHash()); + } + } + } - // Response packet - getPlayer().sendPacket(new PacketAvatarDieAnimationEndRsp(deadAvatar.getId(), 0)); - } - - public boolean reviveAvatar(Avatar avatar) { - for (EntityAvatar entity : getActiveTeam()) { - if (entity.getAvatar() == avatar) { - if (entity.isAlive()) { - return false; - } - - entity.setFightProperty( - FightProperty.FIGHT_PROP_CUR_HP, - entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .1f - ); - getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); - getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); - return true; - } - } - - return false; - } + // No resonances + if (this.getTeamResonances().size() == 0) { + this.getTeamResonances().add(ElementType.Default.getTeamResonanceId()); + this.getTeamResonancesConfig().add(ElementType.Default.getTeamResonanceId()); + } + } - public boolean healAvatar(Avatar avatar, int healRate, int healAmount) { - for (EntityAvatar entity : getActiveTeam()) { - if (entity.getAvatar() == avatar) { - if (!entity.isAlive()) { - return false; - } + public void updateTeamEntities(BasePacket responsePacket) { + // Sanity check - Should never happen + if (this.getCurrentTeamInfo().getAvatars().size() <= 0) { + return; + } - entity.setFightProperty( - FightProperty.FIGHT_PROP_CUR_HP, - (float) Math.min( - (entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) + - entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * (float) healRate / 100.0 + - (float) healAmount / 100.0), - entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) - ) - ); - getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); - getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); - return true; - } - } - return false; - } + // If current team has changed + EntityAvatar currentEntity = this.getCurrentAvatarEntity(); + Int2ObjectMap existingAvatars = new Int2ObjectOpenHashMap<>(); + int prevSelectedAvatarIndex = -1; - public void respawnTeam() { - // Make sure all team members are dead - // Drowning needs revive when there may be other team members still alive. - // for (EntityAvatar entity : getActiveTeam()) { - // if (entity.isAlive()) { - // return; - // } - // } - player.getStaminaManager().stopSustainedStaminaHandler(); // prevent drowning immediately after respawn - - // Revive all team members - for (EntityAvatar entity : getActiveTeam()) { - entity.setFightProperty( - FightProperty.FIGHT_PROP_CUR_HP, - entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .4f - ); - getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); - getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); - } - - // Teleport player - getPlayer().sendPacket(new PacketPlayerEnterSceneNotify(getPlayer(), EnterType.ENTER_TYPE_SELF, EnterReason.Revival, 3, GameConstants.START_POSITION)); - - // Set player position - player.setSceneId(3); - player.getPosition().set(GameConstants.START_POSITION); + for (EntityAvatar entity : getActiveTeam()) { + existingAvatars.put(entity.getAvatar().getAvatarId(), entity); + } - // Packets - getPlayer().sendPacket(new BasePacket(PacketOpcodes.WorldPlayerReviveRsp)); - } + // Clear active team entity list + this.getActiveTeam().clear(); - public void saveAvatars() { - // Save all avatars from active team - for (EntityAvatar entity : getActiveTeam()) { - entity.getAvatar().save(); - } - } + // Add back entities into team + for (int i = 0; i < this.getCurrentTeamInfo().getAvatars().size(); i++) { + int avatarId = this.getCurrentTeamInfo().getAvatars().get(i); + EntityAvatar entity; + + if (existingAvatars.containsKey(avatarId)) { + entity = existingAvatars.get(avatarId); + existingAvatars.remove(avatarId); + if (entity == currentEntity) { + prevSelectedAvatarIndex = i; + } + } else { + entity = new EntityAvatar(getPlayer().getScene(), getPlayer().getAvatars().getAvatarById(avatarId)); + } + + this.getActiveTeam().add(entity); + } + + // Unload removed entities + for (EntityAvatar entity : existingAvatars.values()) { + getPlayer().getScene().removeEntity(entity); + entity.getAvatar().save(); + } + + // Set new selected character index + if (prevSelectedAvatarIndex == -1) { + // Previous selected avatar is not in the same spot, we will select the current one in the prev slot + prevSelectedAvatarIndex = Math.min(this.currentCharacterIndex, this.getActiveTeam().size() - 1); + } + this.currentCharacterIndex = prevSelectedAvatarIndex; + + // Update team resonances + updateTeamResonances(); + + // Packets + getPlayer().getWorld().broadcastPacket(new PacketSceneTeamUpdateNotify(getPlayer())); + + // Skill charges packet - Yes, this is official server behavior as of 2.6.0 + for (EntityAvatar entity : getActiveTeam()) { + if (entity.getAvatar().getSkillExtraChargeMap().size() > 0) { + getPlayer().sendPacket(new PacketAvatarSkillInfoNotify(entity.getAvatar())); + } + } + + // Run callback + if (responsePacket != null) { + getPlayer().sendPacket(responsePacket); + } + + // Check if character changed + if (currentEntity != getCurrentAvatarEntity()) { + // Remove and Add + getPlayer().getScene().replaceEntity(currentEntity, getCurrentAvatarEntity()); + } + } + + public synchronized void setupAvatarTeam(int teamId, List list) { + // Sanity checks + if (list.size() == 0 || list.size() > getMaxTeamSize() || getPlayer().isInMultiplayer()) { + return; + } + + // Get team + TeamInfo teamInfo = this.getTeams().get(teamId); + if (teamInfo == null) { + return; + } + + // Set team data + LinkedHashSet newTeam = new LinkedHashSet<>(); + for (Long aLong : list) { + Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(aLong); + if (avatar == null || newTeam.contains(avatar)) { + // Should never happen + return; + } + newTeam.add(avatar); + } + + // Clear current team info and add avatars from our new team + teamInfo.getAvatars().clear(); + this.addAvatarsToTeam(teamInfo, newTeam); + } + + public void setupMpTeam(List list) { + // Sanity checks + if (list.size() == 0 || list.size() > getMaxTeamSize() || !getPlayer().isInMultiplayer()) { + return; + } + + TeamInfo teamInfo = this.getMpTeam(); + + // Set team data + LinkedHashSet newTeam = new LinkedHashSet<>(); + for (Long aLong : list) { + Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(aLong); + if (avatar == null || newTeam.contains(avatar)) { + // Should never happen + return; + } + newTeam.add(avatar); + } + + // Clear current team info and add avatars from our new team + teamInfo.getAvatars().clear(); + this.addAvatarsToTeam(teamInfo, newTeam); + } + + public void setupTemporaryTeam(List> guidList) { + this.temporaryTeam = guidList.stream().map(list -> { + // Sanity checks + if (list.size() == 0 || list.size() > getMaxTeamSize()) { + return null; + } + + // Set team data + LinkedHashSet newTeam = new LinkedHashSet<>(); + for (Long aLong : list) { + Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(aLong); + if (avatar == null || newTeam.contains(avatar)) { + // Should never happen + return null; + } + newTeam.add(avatar); + } + + // convert to avatar ids + return newTeam.stream() + .map(Avatar::getAvatarId) + .toList(); + }) + .filter(Objects::nonNull) + .map(TeamInfo::new) + .toList(); + } + + public void useTemporaryTeam(int index) { + this.useTemporarilyTeamIndex = index; + updateTeamEntities(null); + } + + public void cleanTemporaryTeam() { + // check if using temporary team + if (useTemporarilyTeamIndex < 0) { + return; + } + + this.useTemporarilyTeamIndex = -1; + this.temporaryTeam = null; + updateTeamEntities(null); + } + public synchronized void setCurrentTeam(int teamId) { + // + if (getPlayer().isInMultiplayer()) { + return; + } + + // Get team + TeamInfo teamInfo = this.getTeams().get(teamId); + if (teamInfo == null || teamInfo.getAvatars().size() == 0) { + return; + } + + // Set + this.setCurrentTeamId(teamId); + this.updateTeamEntities(new PacketChooseCurAvatarTeamRsp(teamId)); + } + + public synchronized void setTeamName(int teamId, String teamName) { + // Get team + TeamInfo teamInfo = this.getTeams().get(teamId); + if (teamInfo == null) { + return; + } + + teamInfo.setName(teamName); + + // Packet + getPlayer().sendPacket(new PacketChangeTeamNameRsp(teamId, teamName)); + } + + public synchronized void changeAvatar(long guid) { + EntityAvatar oldEntity = this.getCurrentAvatarEntity(); + + if (guid == oldEntity.getAvatar().getGuid()) { + return; + } + + EntityAvatar newEntity = null; + int index = -1; + for (int i = 0; i < getActiveTeam().size(); i++) { + if (guid == getActiveTeam().get(i).getAvatar().getGuid()) { + index = i; + newEntity = getActiveTeam().get(i); + } + } + + if (index < 0 || newEntity == oldEntity) { + return; + } + + // Set index + this.setCurrentCharacterIndex(index); + + // Old entity motion state + oldEntity.setMotionState(MotionState.MOTION_STATE_STANDBY); + + // Remove and Add + getPlayer().getScene().replaceEntity(oldEntity, newEntity); + getPlayer().sendPacket(new PacketChangeAvatarRsp(guid)); + } + + public void onAvatarDie(long dieGuid) { + EntityAvatar deadAvatar = this.getCurrentAvatarEntity(); + + if (deadAvatar.isAlive() || deadAvatar.getId() != dieGuid) { + return; + } + + PlayerDieType dieType = deadAvatar.getKilledType(); + int killedBy = deadAvatar.getKilledBy(); + + if (dieType == PlayerDieType.PLAYER_DIE_TYPE_DRAWN) { + // Died in water. Do not replace + // The official server has skipped this notify and will just respawn the team immediately after the animation. + // TODO: Perhaps find a way to get vanilla experience? + getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy)); + } else { + // Replacement avatar + EntityAvatar replacement = null; + int replaceIndex = -1; + + for (int i = 0; i < this.getActiveTeam().size(); i++) { + EntityAvatar entity = this.getActiveTeam().get(i); + if (entity.isAlive()) { + replaceIndex = i; + replacement = entity; + break; + } + } + + if (replacement == null) { + // No more living team members... + getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy)); + } else { + // Set index and spawn replacement member + this.setCurrentCharacterIndex(replaceIndex); + getPlayer().getScene().addEntity(replacement); + } + } + + // Response packet + getPlayer().sendPacket(new PacketAvatarDieAnimationEndRsp(deadAvatar.getId(), 0)); + } + + public boolean reviveAvatar(Avatar avatar) { + for (EntityAvatar entity : getActiveTeam()) { + if (entity.getAvatar() == avatar) { + if (entity.isAlive()) { + return false; + } + + entity.setFightProperty( + FightProperty.FIGHT_PROP_CUR_HP, + entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .1f + ); + getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); + getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); + return true; + } + } + + return false; + } + + public boolean healAvatar(Avatar avatar, int healRate, int healAmount) { + for (EntityAvatar entity : getActiveTeam()) { + if (entity.getAvatar() == avatar) { + if (!entity.isAlive()) { + return false; + } + + entity.setFightProperty( + FightProperty.FIGHT_PROP_CUR_HP, + (float) Math.min( + (entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) + + entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * (float) healRate / 100.0 + + (float) healAmount / 100.0), + entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) + ) + ); + getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); + getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); + return true; + } + } + return false; + } + + public void respawnTeam() { + // Make sure all team members are dead + // Drowning needs revive when there may be other team members still alive. + // for (EntityAvatar entity : getActiveTeam()) { + // if (entity.isAlive()) { + // return; + // } + // } + player.getStaminaManager().stopSustainedStaminaHandler(); // prevent drowning immediately after respawn + + // Revive all team members + for (EntityAvatar entity : getActiveTeam()) { + entity.setFightProperty( + FightProperty.FIGHT_PROP_CUR_HP, + entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .4f + ); + getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); + getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); + } + + // Teleport player + getPlayer().sendPacket(new PacketPlayerEnterSceneNotify(getPlayer(), EnterType.ENTER_TYPE_SELF, EnterReason.Revival, 3, GameConstants.START_POSITION)); + + // Set player position + player.setSceneId(3); + player.getPosition().set(GameConstants.START_POSITION); + + // Packets + getPlayer().sendPacket(new BasePacket(PacketOpcodes.WorldPlayerReviveRsp)); + } + + public void saveAvatars() { + // Save all avatars from active team + for (EntityAvatar entity : getActiveTeam()) { + entity.getAvatar().save(); + } + } } diff --git a/src/main/java/emu/grasscutter/game/props/OpenState.java b/src/main/java/emu/grasscutter/game/props/OpenState.java index 536c57d45..b908bd45b 100644 --- a/src/main/java/emu/grasscutter/game/props/OpenState.java +++ b/src/main/java/emu/grasscutter/game/props/OpenState.java @@ -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 map = new Int2ObjectOpenHashMap<>(); - private static final Map 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 map = new Int2ObjectOpenHashMap<>(); + private static final Map 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); + } } diff --git a/src/main/java/emu/grasscutter/game/quest/GameQuest.java b/src/main/java/emu/grasscutter/game/quest/GameQuest.java index 87e31a6fb..7d76c152e 100644 --- a/src/main/java/emu/grasscutter/game/quest/GameQuest.java +++ b/src/main/java/emu/grasscutter/game/quest/GameQuest.java @@ -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(); + } } diff --git a/src/main/java/emu/grasscutter/game/quest/QuestManager.java b/src/main/java/emu/grasscutter/game/quest/QuestManager.java index 28a17973f..c8049f362 100644 --- a/src/main/java/emu/grasscutter/game/quest/QuestManager.java +++ b/src/main/java/emu/grasscutter/game/quest/QuestManager.java @@ -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 quests; + private final Int2ObjectMap quests; - public QuestManager(Player player) { - super(player); - this.quests = new Int2ObjectOpenHashMap<>(); - } + public QuestManager(Player player) { + super(player); + this.quests = new Int2ObjectOpenHashMap<>(); + } - public Int2ObjectMap 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 callback) { - for (GameMainQuest mainQuest : getQuests().values()) { - for (GameQuest quest : mainQuest.getChildQuests().values()) { - callback.accept(quest); - } - } - } + public Int2ObjectMap getQuests() { + return quests; + } - public void forEachMainQuest(Consumer callback) { - for (GameMainQuest mainQuest : getQuests().values()) { - callback.accept(mainQuest); - } - } - - // TODO - public void forEachActiveQuest(Consumer 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 callback) { + for (GameMainQuest mainQuest : getQuests().values()) { + for (GameQuest quest : mainQuest.getChildQuests().values()) { + callback.accept(quest); + } + } + } + + public void forEachMainQuest(Consumer callback) { + for (GameMainQuest mainQuest : getQuests().values()) { + callback.accept(mainQuest); + } + } + + // TODO + public void forEachActiveQuest(Consumer 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 changedQuests = new HashSet<>(); + Set 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 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 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 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); + } + } } diff --git a/src/main/java/emu/grasscutter/game/quest/QuestSystem.java b/src/main/java/emu/grasscutter/game/quest/QuestSystem.java index 24f78b9d4..16167b010 100644 --- a/src/main/java/emu/grasscutter/game/quest/QuestSystem.java +++ b/src/main/java/emu/grasscutter/game/quest/QuestSystem.java @@ -17,81 +17,81 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @SuppressWarnings("unchecked") public class QuestSystem extends BaseGameSystem { - private final Int2ObjectMap condHandlers; - private final Int2ObjectMap contHandlers; - private final Int2ObjectMap execHandlers; + private final Int2ObjectMap condHandlers; + private final Int2ObjectMap contHandlers; + private final Int2ObjectMap 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 void registerHandlers(Int2ObjectMap map, String packageName, Class 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 void registerHandlers(Int2ObjectMap map, String packageName, Class clazz) { + Reflections reflections = new Reflections(packageName); + var handlerClasses = reflections.getSubTypesOf(clazz); - public void registerPacketHandler(Int2ObjectMap map, Class 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 void registerPacketHandler(Int2ObjectMap map, Class 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); + } } diff --git a/src/main/java/emu/grasscutter/game/shop/ShopSystem.java b/src/main/java/emu/grasscutter/game/shop/ShopSystem.java index 8ad4fa657..006af71d2 100644 --- a/src/main/java/emu/grasscutter/game/shop/ShopSystem.java +++ b/src/main/java/emu/grasscutter/game/shop/ShopSystem.java @@ -23,22 +23,22 @@ import java.util.Iterator; import java.util.List; public class ShopSystem extends BaseGameSystem { - private final Int2ObjectMap> shopData; - private final List shopChestData; - private final List shopChestBatchUseData; - + private final Int2ObjectMap> shopData; + private final List shopChestData; + private final List 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> getShopData() { + public ShopSystem(GameServer server) { + super(server); + this.shopData = new Int2ObjectOpenHashMap<>(); + this.shopChestData = new ArrayList<>(); + this.shopChestBatchUseData = new ArrayList<>(); + this.load(); + } + + public Int2ObjectMap> 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 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 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 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 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 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 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 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 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; + } } diff --git a/src/main/java/emu/grasscutter/game/systems/AnnouncementSystem.java b/src/main/java/emu/grasscutter/game/systems/AnnouncementSystem.java index 5d2161b94..510a3f61a 100644 --- a/src/main/java/emu/grasscutter/game/systems/AnnouncementSystem.java +++ b/src/main/java/emu/grasscutter/game/systems/AnnouncementSystem.java @@ -22,8 +22,8 @@ import java.util.*; @Getter public class AnnouncementSystem extends BaseGameSystem { private final Map 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 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) ; diff --git a/src/main/java/emu/grasscutter/game/systems/InventorySystem.java b/src/main/java/emu/grasscutter/game/systems/InventorySystem.java index 37b988ff8..2f7a0614b 100644 --- a/src/main/java/emu/grasscutter/game/systems/InventorySystem.java +++ b/src/main/java/emu/grasscutter/game/systems/InventorySystem.java @@ -35,922 +35,922 @@ import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; public class InventorySystem extends BaseGameSystem { - private final static int RELIC_MATERIAL_1 = 105002; // Sanctifying Unction - private final static int RELIC_MATERIAL_2 = 105003; // Sanctifying Essence - private final static int RELIC_MATERIAL_EXP_1 = 2500; // Sanctifying Unction - private final static int RELIC_MATERIAL_EXP_2 = 10000; // Sanctifying Essence - - private final static int WEAPON_ORE_1 = 104011; // Enhancement Ore - private final static int WEAPON_ORE_2 = 104012; // Fine Enhancement Ore - private final static int WEAPON_ORE_3 = 104013; // Mystic Enhancement Ore - private final static int WEAPON_ORE_EXP_1 = 400; // Enhancement Ore - private final static int WEAPON_ORE_EXP_2 = 2000; // Fine Enhancement Ore - private final static int WEAPON_ORE_EXP_3 = 10000; // Mystic Enhancement Ore - - private final static int AVATAR_BOOK_1 = 104001; // Wanderer's Advice - private final static int AVATAR_BOOK_2 = 104002; // Adventurer's Experience - private final static int AVATAR_BOOK_3 = 104003; // Hero's Wit - private final static int AVATAR_BOOK_EXP_1 = 1000; // Wanderer's Advice - private final static int AVATAR_BOOK_EXP_2 = 5000; // Adventurer's Experience - private final static int AVATAR_BOOK_EXP_3 = 20000; // Hero's Wit - - public InventorySystem(GameServer server) { - super(server); - } - - public void lockEquip(Player player, long targetEquipGuid, boolean isLocked) { - GameItem equip = player.getInventory().getItemByGuid(targetEquipGuid); - - if (equip == null || !equip.getItemData().isEquip()) { - return; - } - - equip.setLocked(isLocked); - equip.save(); - - player.sendPacket(new PacketStoreItemChangeNotify(equip)); - player.sendPacket(new PacketSetEquipLockStateRsp(equip)); - } + private final static int RELIC_MATERIAL_1 = 105002; // Sanctifying Unction + private final static int RELIC_MATERIAL_2 = 105003; // Sanctifying Essence + private final static int RELIC_MATERIAL_EXP_1 = 2500; // Sanctifying Unction + private final static int RELIC_MATERIAL_EXP_2 = 10000; // Sanctifying Essence - public void upgradeRelic(Player player, long targetGuid, List foodRelicList, List list) { - GameItem relic = player.getInventory().getItemByGuid(targetGuid); - - if (relic == null || relic.getItemType() != ItemType.ITEM_RELIQUARY) { - return; - } - - int moraCost = 0; - int expGain = 0; - - List foodRelics = new ArrayList(); - for (long guid : foodRelicList) { - // Add to delete queue - GameItem food = player.getInventory().getItemByGuid(guid); - if (food == null || !food.isDestroyable()) { - continue; - } - // Calculate mora cost - moraCost += food.getItemData().getBaseConvExp(); - expGain += food.getItemData().getBaseConvExp(); - // Feeding artifact with exp already - if (food.getTotalExp() > 0) { - expGain += (food.getTotalExp() * 4) / 5; - } - foodRelics.add(food); - } - List payList = new ArrayList(); - for (ItemParam itemParam : list) { - int amount = itemParam.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order - int gain = amount * switch(itemParam.getItemId()) { - case RELIC_MATERIAL_1 -> RELIC_MATERIAL_EXP_1; - case RELIC_MATERIAL_2 -> RELIC_MATERIAL_EXP_2; - default -> 0; - }; - expGain += gain; - moraCost += gain; - payList.add(new ItemParamData(itemParam.getItemId(), itemParam.getCount())); - } - - // Make sure exp gain is valid - if (expGain <= 0) { - return; - } - - // Confirm payment of materials and mora (assume food relics are payable afterwards) - payList.add(new ItemParamData(202, moraCost)); - if (!player.getInventory().payItems(payList.toArray(new ItemParamData[0]))) { - return; - } - - // Consume food relics - player.getInventory().removeItems(foodRelics); - - // Implement random rate boost - int rate = 1; - int boost = Utils.randomRange(1, 100); - if (boost == 100) { - rate = 5; - } else if (boost <= 9) { - rate = 2; - } - expGain *= rate; - - // Now we upgrade - int level = relic.getLevel(); - int oldLevel = level; - int exp = relic.getExp(); - int totalExp = relic.getTotalExp(); - int reqExp = GameData.getRelicExpRequired(relic.getItemData().getRankLevel(), level); - int upgrades = 0; + private final static int WEAPON_ORE_1 = 104011; // Enhancement Ore + private final static int WEAPON_ORE_2 = 104012; // Fine Enhancement Ore + private final static int WEAPON_ORE_3 = 104013; // Mystic Enhancement Ore + private final static int WEAPON_ORE_EXP_1 = 400; // Enhancement Ore + private final static int WEAPON_ORE_EXP_2 = 2000; // Fine Enhancement Ore + private final static int WEAPON_ORE_EXP_3 = 10000; // Mystic Enhancement Ore + + private final static int AVATAR_BOOK_1 = 104001; // Wanderer's Advice + private final static int AVATAR_BOOK_2 = 104002; // Adventurer's Experience + private final static int AVATAR_BOOK_3 = 104003; // Hero's Wit + private final static int AVATAR_BOOK_EXP_1 = 1000; // Wanderer's Advice + private final static int AVATAR_BOOK_EXP_2 = 5000; // Adventurer's Experience + private final static int AVATAR_BOOK_EXP_3 = 20000; // Hero's Wit + + public InventorySystem(GameServer server) { + super(server); + } + + public void lockEquip(Player player, long targetEquipGuid, boolean isLocked) { + GameItem equip = player.getInventory().getItemByGuid(targetEquipGuid); + + if (equip == null || !equip.getItemData().isEquip()) { + return; + } + + equip.setLocked(isLocked); + equip.save(); + + player.sendPacket(new PacketStoreItemChangeNotify(equip)); + player.sendPacket(new PacketSetEquipLockStateRsp(equip)); + } + + public void upgradeRelic(Player player, long targetGuid, List foodRelicList, List list) { + GameItem relic = player.getInventory().getItemByGuid(targetGuid); + + if (relic == null || relic.getItemType() != ItemType.ITEM_RELIQUARY) { + return; + } + + int moraCost = 0; + int expGain = 0; + + List foodRelics = new ArrayList(); + for (long guid : foodRelicList) { + // Add to delete queue + GameItem food = player.getInventory().getItemByGuid(guid); + if (food == null || !food.isDestroyable()) { + continue; + } + // Calculate mora cost + moraCost += food.getItemData().getBaseConvExp(); + expGain += food.getItemData().getBaseConvExp(); + // Feeding artifact with exp already + if (food.getTotalExp() > 0) { + expGain += (food.getTotalExp() * 4) / 5; + } + foodRelics.add(food); + } + List payList = new ArrayList(); + for (ItemParam itemParam : list) { + int amount = itemParam.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order + int gain = amount * switch (itemParam.getItemId()) { + case RELIC_MATERIAL_1 -> RELIC_MATERIAL_EXP_1; + case RELIC_MATERIAL_2 -> RELIC_MATERIAL_EXP_2; + default -> 0; + }; + expGain += gain; + moraCost += gain; + payList.add(new ItemParamData(itemParam.getItemId(), itemParam.getCount())); + } + + // Make sure exp gain is valid + if (expGain <= 0) { + return; + } + + // Confirm payment of materials and mora (assume food relics are payable afterwards) + payList.add(new ItemParamData(202, moraCost)); + if (!player.getInventory().payItems(payList.toArray(new ItemParamData[0]))) { + return; + } + + // Consume food relics + player.getInventory().removeItems(foodRelics); + + // Implement random rate boost + int rate = 1; + int boost = Utils.randomRange(1, 100); + if (boost == 100) { + rate = 5; + } else if (boost <= 9) { + rate = 2; + } + expGain *= rate; + + // Now we upgrade + int level = relic.getLevel(); + int oldLevel = level; + int exp = relic.getExp(); + int totalExp = relic.getTotalExp(); + int reqExp = GameData.getRelicExpRequired(relic.getItemData().getRankLevel(), level); + int upgrades = 0; List oldAppendPropIdList = new ArrayList<>(relic.getAppendPropIdList()); - - while (expGain > 0 && reqExp > 0 && level < relic.getItemData().getMaxLevel()) { - // Do calculations - int toGain = Math.min(expGain, reqExp - exp); - exp += toGain; - totalExp += toGain; - expGain -= toGain; - // Level up - if (exp >= reqExp) { - // Exp - exp = 0; - level += 1; - // On relic levelup - if (relic.getItemData().getAddPropLevelSet() != null && relic.getItemData().getAddPropLevelSet().contains(level)) { - upgrades += 1; - } - // Set req exp - reqExp = GameData.getRelicExpRequired(relic.getItemData().getRankLevel(), level); - } - } - + + while (expGain > 0 && reqExp > 0 && level < relic.getItemData().getMaxLevel()) { + // Do calculations + int toGain = Math.min(expGain, reqExp - exp); + exp += toGain; + totalExp += toGain; + expGain -= toGain; + // Level up + if (exp >= reqExp) { + // Exp + exp = 0; + level += 1; + // On relic levelup + if (relic.getItemData().getAddPropLevelSet() != null && relic.getItemData().getAddPropLevelSet().contains(level)) { + upgrades += 1; + } + // Set req exp + reqExp = GameData.getRelicExpRequired(relic.getItemData().getRankLevel(), level); + } + } + relic.addAppendProps(upgrades); - - // Save - relic.setLevel(level); - relic.setExp(exp); - relic.setTotalExp(totalExp); - relic.save(); - - // Avatar - if (oldLevel != level) { - Avatar avatar = relic.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(relic.getEquipCharacter()) : null; - if (avatar != null) { - avatar.recalcStats(); - } - } - // Packet - player.sendPacket(new PacketStoreItemChangeNotify(relic)); - player.sendPacket(new PacketReliquaryUpgradeRsp(relic, rate, oldLevel, oldAppendPropIdList)); - } + // Save + relic.setLevel(level); + relic.setExp(exp); + relic.setTotalExp(totalExp); + relic.save(); - public List calcWeaponUpgradeReturnItems(Player player, long targetGuid, List foodWeaponGuidList, List itemParamList) { - GameItem weapon = player.getInventory().getItemByGuid(targetGuid); - - // Sanity checks - if (weapon == null || weapon.getItemType() != ItemType.ITEM_WEAPON) { - return null; - } - - WeaponPromoteData promoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel()); - if (promoteData == null) { - return null; - } - - // Get exp gain - int expGain = 0; - for (long guid : foodWeaponGuidList) { - GameItem food = player.getInventory().getItemByGuid(guid); - if (food == null) { - continue; - } - expGain += food.getItemData().getWeaponBaseExp(); - if (food.getTotalExp() > 0) { - expGain += (food.getTotalExp() * 4) / 5; - } - } - for (ItemParam param : itemParamList) { - expGain += param.getCount() * switch(param.getItemId()) { - case WEAPON_ORE_1 -> WEAPON_ORE_EXP_1; - case WEAPON_ORE_2 -> WEAPON_ORE_EXP_2; - case WEAPON_ORE_3 -> WEAPON_ORE_EXP_3; - default -> 0; - }; - } - - // Try - int maxLevel = promoteData.getUnlockMaxLevel(); - int level = weapon.getLevel(); - int exp = weapon.getExp(); - int reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level); - - while (expGain > 0 && reqExp > 0 && level < maxLevel) { - // Do calculations - int toGain = Math.min(expGain, reqExp - exp); - exp += toGain; - expGain -= toGain; - // Level up - if (exp >= reqExp) { - // Exp - exp = 0; - level += 1; - // Set req exp - reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level); - } - } - - return getLeftoverOres(expGain); - } - + // Avatar + if (oldLevel != level) { + Avatar avatar = relic.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(relic.getEquipCharacter()) : null; + if (avatar != null) { + avatar.recalcStats(); + } + } - public void upgradeWeapon(Player player, long targetGuid, List foodWeaponGuidList, List itemParamList) { - GameItem weapon = player.getInventory().getItemByGuid(targetGuid); - - // Sanity checks - if (weapon == null || weapon.getItemType() != ItemType.ITEM_WEAPON) { - return; - } - - WeaponPromoteData promoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel()); - if (promoteData == null) { - return; - } - - // Get exp gain - int expGain = 0, expGainFree = 0; - List foodWeapons = new ArrayList(); - for (long guid : foodWeaponGuidList) { - GameItem food = player.getInventory().getItemByGuid(guid); - if (food == null || !food.isDestroyable()) { - continue; - } - expGain += food.getItemData().getWeaponBaseExp(); - if (food.getTotalExp() > 0) { - expGainFree += (food.getTotalExp() * 4) / 5; // No tax :D - } - foodWeapons.add(food); - } - List payList = new ArrayList(); - for (ItemParam param : itemParamList) { - int amount = param.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order - int gain = amount * switch(param.getItemId()) { - case WEAPON_ORE_1 -> WEAPON_ORE_EXP_1; - case WEAPON_ORE_2 -> WEAPON_ORE_EXP_2; - case WEAPON_ORE_3 -> WEAPON_ORE_EXP_3; - default -> 0; - }; - expGain += gain; - payList.add(new ItemParamData(param.getItemId(), amount)); - } - - // Make sure exp gain is valid - int moraCost = expGain / 10; - expGain += expGainFree; - if (expGain <= 0) { - return; - } + // Packet + player.sendPacket(new PacketStoreItemChangeNotify(relic)); + player.sendPacket(new PacketReliquaryUpgradeRsp(relic, rate, oldLevel, oldAppendPropIdList)); + } - // Confirm payment of materials and mora (assume food weapons are payable afterwards) - payList.add(new ItemParamData(202, moraCost)); - if (!player.getInventory().payItems(payList.toArray(new ItemParamData[0]))) { - return; - } - player.getInventory().removeItems(foodWeapons); - - // Level up - int maxLevel = promoteData.getUnlockMaxLevel(); - int level = weapon.getLevel(); - int oldLevel = level; - int exp = weapon.getExp(); - int totalExp = weapon.getTotalExp(); - int reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level); - - while (expGain > 0 && reqExp > 0 && level < maxLevel) { - // Do calculations - int toGain = Math.min(expGain, reqExp - exp); - exp += toGain; - totalExp += toGain; - expGain -= toGain; - // Level up - if (exp >= reqExp) { - // Exp - exp = 0; - level += 1; - // Set req exp - reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level); - } - } - - List leftovers = getLeftoverOres(expGain); - player.getInventory().addItemParams(leftovers); - - weapon.setLevel(level); - weapon.setExp(exp); - weapon.setTotalExp(totalExp); - weapon.save(); - - // Avatar - if (oldLevel != level) { - Avatar avatar = weapon.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(weapon.getEquipCharacter()) : null; - if (avatar != null) { - avatar.recalcStats(); - } - } - - // Packets - player.sendPacket(new PacketStoreItemChangeNotify(weapon)); - player.sendPacket(new PacketWeaponUpgradeRsp(weapon, oldLevel, leftovers)); - } - - private List getLeftoverOres(int leftover) { - List leftoverOreList = new ArrayList<>(3); - - if (leftover < WEAPON_ORE_EXP_1) { - return leftoverOreList; - } - - // Get leftovers - int ore3 = leftover / WEAPON_ORE_EXP_3; - leftover = leftover % WEAPON_ORE_EXP_3; - int ore2 = leftover / WEAPON_ORE_EXP_2; - leftover = leftover % WEAPON_ORE_EXP_2; - int ore1 = leftover / WEAPON_ORE_EXP_1; - - if (ore3 > 0) { - leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_3).setCount(ore3).build()); - } if (ore2 > 0) { - leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_2).setCount(ore2).build()); - } if (ore1 > 0) { - leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_1).setCount(ore1).build()); - } - - return leftoverOreList; - } + public List calcWeaponUpgradeReturnItems(Player player, long targetGuid, List foodWeaponGuidList, List itemParamList) { + GameItem weapon = player.getInventory().getItemByGuid(targetGuid); - public void refineWeapon(Player player, long targetGuid, long feedGuid) { - GameItem weapon = player.getInventory().getItemByGuid(targetGuid); - GameItem feed = player.getInventory().getItemByGuid(feedGuid); - - // Sanity checks - if (weapon == null || feed == null || !feed.isDestroyable()) { - return; - } - - if (weapon.getItemData().getAwakenMaterial() == 0) { - if (weapon.getItemType() != ItemType.ITEM_WEAPON || weapon.getItemId() != feed.getItemId()) { - return; - } - } else { - if (weapon.getItemType() != ItemType.ITEM_WEAPON || weapon.getItemData().getAwakenMaterial() != feed.getItemId()) { - return; - } - } - - if (weapon.getRefinement() >= 4 || weapon.getAffixes() == null || weapon.getAffixes().size() == 0) { - return; - } - - // Calculate - int oldRefineLevel = weapon.getRefinement(); - int targetRefineLevel = Math.min(oldRefineLevel + feed.getRefinement() + 1, 4); - int moraCost = 0; - - try { - moraCost = weapon.getItemData().getAwakenCosts()[weapon.getRefinement()]; - } catch (Exception e) { - return; - } - - // Mora check - if (player.getMora() >= moraCost) { - player.setMora(player.getMora() - moraCost); - } else { - return; - } - - // Consume weapon - player.getInventory().removeItem(feed, 1); - - // Get - weapon.setRefinement(targetRefineLevel); - weapon.save(); - - // Avatar - Avatar avatar = weapon.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(weapon.getEquipCharacter()) : null; - if (avatar != null) { - avatar.recalcStats(); - } - - // Packets - player.sendPacket(new PacketStoreItemChangeNotify(weapon)); - player.sendPacket(new PacketWeaponAwakenRsp(avatar, weapon, feed, oldRefineLevel)); - } + // Sanity checks + if (weapon == null || weapon.getItemType() != ItemType.ITEM_WEAPON) { + return null; + } - public void promoteWeapon(Player player, long targetGuid) { - GameItem weapon = player.getInventory().getItemByGuid(targetGuid); - - if (weapon == null || weapon.getItemType() != ItemType.ITEM_WEAPON) { - return; - } - - int nextPromoteLevel = weapon.getPromoteLevel() + 1; - WeaponPromoteData currentPromoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel()); - WeaponPromoteData nextPromoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), nextPromoteLevel); - if (currentPromoteData == null || nextPromoteData == null) { - return; - } - - // Level check - if (weapon.getLevel() != currentPromoteData.getUnlockMaxLevel()) { - return; - } - - // Pay materials and mora if possible - ItemParamData[] costs = nextPromoteData.getCostItems(); // Can this be null? - if (nextPromoteData.getCoinCost() > 0) { - costs = Arrays.copyOf(costs, costs.length + 1); - costs[costs.length-1] = new ItemParamData(202, nextPromoteData.getCoinCost()); - } - if (!player.getInventory().payItems(costs)) { - return; - } - - int oldPromoteLevel = weapon.getPromoteLevel(); - weapon.setPromoteLevel(nextPromoteLevel); - weapon.save(); - - // Avatar - Avatar avatar = weapon.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(weapon.getEquipCharacter()) : null; - if (avatar != null) { - avatar.recalcStats(); - } - - // Packets - player.sendPacket(new PacketStoreItemChangeNotify(weapon)); - player.sendPacket(new PacketWeaponPromoteRsp(weapon, oldPromoteLevel)); - } + WeaponPromoteData promoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel()); + if (promoteData == null) { + return null; + } - public void promoteAvatar(Player player, long guid) { - Avatar avatar = player.getAvatars().getAvatarByGuid(guid); - - // Sanity checks - if (avatar == null) { - return; - } - - int nextPromoteLevel = avatar.getPromoteLevel() + 1; - AvatarPromoteData currentPromoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), avatar.getPromoteLevel()); - AvatarPromoteData nextPromoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), nextPromoteLevel); - if (currentPromoteData == null || nextPromoteData == null) { - return; - } - - // Level check - if (avatar.getLevel() != currentPromoteData.getUnlockMaxLevel()) { - return; - } - - // Pay materials and mora if possible - ItemParamData[] costs = nextPromoteData.getCostItems(); // Can this be null? - if (nextPromoteData.getCoinCost() > 0) { - costs = Arrays.copyOf(costs, costs.length + 1); - costs[costs.length-1] = new ItemParamData(202, nextPromoteData.getCoinCost()); - } - if (!player.getInventory().payItems(costs)) { - return; - } - - // Update promote level - avatar.setPromoteLevel(nextPromoteLevel); - - // Update proud skills - AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId()); - - if (skillDepot != null && skillDepot.getInherentProudSkillOpens() != null) { - for (InherentProudSkillOpens openData : skillDepot.getInherentProudSkillOpens()) { - if (openData.getProudSkillGroupId() == 0) { - continue; - } - if (openData.getNeedAvatarPromoteLevel() == avatar.getPromoteLevel()) { - int proudSkillId = (openData.getProudSkillGroupId() * 100) + 1; - if (GameData.getProudSkillDataMap().containsKey(proudSkillId)) { - avatar.getProudSkillList().add(proudSkillId); - player.sendPacket(new PacketProudSkillChangeNotify(avatar)); - } - } - } - } - - // Packets - player.sendPacket(new PacketAvatarPropNotify(avatar)); - player.sendPacket(new PacketAvatarPromoteRsp(avatar)); - - // TODO Send entity prop update packet to world - avatar.recalcStats(true); - avatar.save(); - } + // Get exp gain + int expGain = 0; + for (long guid : foodWeaponGuidList) { + GameItem food = player.getInventory().getItemByGuid(guid); + if (food == null) { + continue; + } + expGain += food.getItemData().getWeaponBaseExp(); + if (food.getTotalExp() > 0) { + expGain += (food.getTotalExp() * 4) / 5; + } + } + for (ItemParam param : itemParamList) { + expGain += param.getCount() * switch (param.getItemId()) { + case WEAPON_ORE_1 -> WEAPON_ORE_EXP_1; + case WEAPON_ORE_2 -> WEAPON_ORE_EXP_2; + case WEAPON_ORE_3 -> WEAPON_ORE_EXP_3; + default -> 0; + }; + } - public void upgradeAvatar(Player player, long guid, int itemId, int count) { - Avatar avatar = player.getAvatars().getAvatarByGuid(guid); - - // Sanity checks - if (avatar == null) { - return; - } - - AvatarPromoteData promoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), avatar.getPromoteLevel()); - if (promoteData == null) { - return; - } - - // Calc exp - int expGain = switch(itemId) { - case AVATAR_BOOK_1 -> AVATAR_BOOK_EXP_1 * count; - case AVATAR_BOOK_2 -> AVATAR_BOOK_EXP_2 * count; - case AVATAR_BOOK_3 -> AVATAR_BOOK_EXP_3 * count; - default -> 0; - }; - - // Sanity check - if (expGain <= 0) { - return; - } + // Try + int maxLevel = promoteData.getUnlockMaxLevel(); + int level = weapon.getLevel(); + int exp = weapon.getExp(); + int reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level); - // Payment check - int moraCost = expGain / 5; - ItemParamData[] costItems = new ItemParamData[] {new ItemParamData(itemId, count), new ItemParamData(202, moraCost)}; - if (!player.getInventory().payItems(costItems)) { - return; - } - - // Level up - upgradeAvatar(player, avatar, promoteData, expGain); - } + while (expGain > 0 && reqExp > 0 && level < maxLevel) { + // Do calculations + int toGain = Math.min(expGain, reqExp - exp); + exp += toGain; + expGain -= toGain; + // Level up + if (exp >= reqExp) { + // Exp + exp = 0; + level += 1; + // Set req exp + reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level); + } + } - public void upgradeAvatar(Player player, Avatar avatar, int expGain) { - AvatarPromoteData promoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), avatar.getPromoteLevel()); - if (promoteData == null) { - return; - } - - upgradeAvatar(player, avatar, promoteData, expGain); - } + return getLeftoverOres(expGain); + } - public void upgradeAvatar(Player player, Avatar avatar, AvatarPromoteData promoteData, int expGain) { - int maxLevel = promoteData.getUnlockMaxLevel(); - int level = avatar.getLevel(); - int oldLevel = level; - int exp = avatar.getExp(); - int reqExp = GameData.getAvatarLevelExpRequired(level); - while (expGain > 0 && reqExp > 0 && level < maxLevel) { - // Do calculations - int toGain = Math.min(expGain, reqExp - exp); - exp += toGain; - expGain -= toGain; - // Level up - if (exp >= reqExp) { - // Exp - exp = 0; - level += 1; - // Set req exp - reqExp = GameData.getAvatarLevelExpRequired(level); - } - } + public void upgradeWeapon(Player player, long targetGuid, List foodWeaponGuidList, List itemParamList) { + GameItem weapon = player.getInventory().getItemByGuid(targetGuid); - // Old map for packet - Map oldPropMap = avatar.getFightProperties(); - if (oldLevel != level) { - // Deep copy if level has changed - oldPropMap = avatar.getFightProperties().int2FloatEntrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - // Done - avatar.setLevel(level); - avatar.setExp(exp); - avatar.recalcStats(); - avatar.save(); - - // TODO Send entity prop update packet to world - - // Packets - player.sendPacket(new PacketAvatarPropNotify(avatar)); - player.sendPacket(new PacketAvatarUpgradeRsp(avatar, oldLevel, oldPropMap)); - } + // Sanity checks + if (weapon == null || weapon.getItemType() != ItemType.ITEM_WEAPON) { + return; + } - public void upgradeAvatarFetterLevel(Player player, Avatar avatar, int expGain) { - // May work. Not test. - int maxLevel = 10; // Keep it until I think of a more "elegant" way - int level = avatar.getFetterLevel(); - int exp = avatar.getFetterExp(); - int reqExp = GameData.getAvatarFetterLevelExpRequired(level); + WeaponPromoteData promoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel()); + if (promoteData == null) { + return; + } - while (expGain > 0 && reqExp > 0 && level < maxLevel) { - int toGain = Math.min(expGain, reqExp - exp); - exp += toGain; - expGain -= toGain; - if (exp >= reqExp) { - exp = 0; - level += 1; - reqExp = GameData.getAvatarFetterLevelExpRequired(level); - } - } - - avatar.setFetterLevel(level); - avatar.setFetterExp(exp); - avatar.save(); - - player.sendPacket(new PacketAvatarPropNotify(avatar)); - player.sendPacket(new PacketAvatarFetterDataNotify(avatar)); - } + // Get exp gain + int expGain = 0, expGainFree = 0; + List foodWeapons = new ArrayList(); + for (long guid : foodWeaponGuidList) { + GameItem food = player.getInventory().getItemByGuid(guid); + if (food == null || !food.isDestroyable()) { + continue; + } + expGain += food.getItemData().getWeaponBaseExp(); + if (food.getTotalExp() > 0) { + expGainFree += (food.getTotalExp() * 4) / 5; // No tax :D + } + foodWeapons.add(food); + } + List payList = new ArrayList(); + for (ItemParam param : itemParamList) { + int amount = param.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order + int gain = amount * switch (param.getItemId()) { + case WEAPON_ORE_1 -> WEAPON_ORE_EXP_1; + case WEAPON_ORE_2 -> WEAPON_ORE_EXP_2; + case WEAPON_ORE_3 -> WEAPON_ORE_EXP_3; + default -> 0; + }; + expGain += gain; + payList.add(new ItemParamData(param.getItemId(), amount)); + } - public void upgradeAvatarSkill(Player player, long guid, int skillId) { - // Sanity checks - Avatar avatar = player.getAvatars().getAvatarByGuid(guid); - if (avatar == null) { - return; - } - - // Make sure avatar has skill - if (!avatar.getSkillLevelMap().containsKey(skillId)) { - return; - } - - AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(skillId); - if (skillData == null) { - return; - } - - // Get data for next skill level - int currentLevel = avatar.getSkillLevelMap().get(skillId); - int nextLevel = currentLevel + 1; - int proudSkillId = (skillData.getProudSkillGroupId() * 100) + nextLevel; - - // Capped at level 10 talent - if (nextLevel > 10) { - return; - } - - // Proud skill data - ProudSkillData proudSkill = GameData.getProudSkillDataMap().get(proudSkillId); - if (proudSkill == null) { - return; - } - - // Make sure break level is correct - if (avatar.getPromoteLevel() < proudSkill.getBreakLevel()) { - return; - } - - // Pay materials and mora if possible - List costs = new ArrayList(proudSkill.getCostItems()); // Can this be null? - if (proudSkill.getCoinCost() > 0) { - costs.add(new ItemParamData(202, proudSkill.getCoinCost())); - } - if (!player.getInventory().payItems(costs.toArray(new ItemParamData[0]))) { - return; - } - - // Upgrade skill - avatar.getSkillLevelMap().put(skillId, nextLevel); - avatar.save(); - - // Packet - player.sendPacket(new PacketAvatarSkillChangeNotify(avatar, skillId, currentLevel, nextLevel)); - player.sendPacket(new PacketAvatarSkillUpgradeRsp(avatar, skillId, currentLevel, nextLevel)); - } + // Make sure exp gain is valid + int moraCost = expGain / 10; + expGain += expGainFree; + if (expGain <= 0) { + return; + } - public void unlockAvatarConstellation(Player player, long guid) { - // Sanity checks - Avatar avatar = player.getAvatars().getAvatarByGuid(guid); - if (avatar == null) { - return; - } - - // Get talent - int currentTalentLevel = avatar.getCoreProudSkillLevel(); - int nextTalentId = ((avatar.getAvatarId() % 10000000) * 10) + currentTalentLevel + 1; - - if (avatar.getAvatarId() == 10000006) { - // Lisa is special in that her talentId starts with 4 instead of 6. - nextTalentId = 40 + currentTalentLevel + 1; - } - - AvatarTalentData talentData = GameData.getAvatarTalentDataMap().get(nextTalentId); - - if (talentData == null) { - return; - } - - // Pay constellation item if possible - if (!player.getInventory().payItem(talentData.getMainCostItemId(), 1)) { - return; - } - - // Apply + recalc - avatar.getTalentIdList().add(talentData.getId()); - avatar.setCoreProudSkillLevel(currentTalentLevel + 1); + // Confirm payment of materials and mora (assume food weapons are payable afterwards) + payList.add(new ItemParamData(202, moraCost)); + if (!player.getInventory().payItems(payList.toArray(new ItemParamData[0]))) { + return; + } + player.getInventory().removeItems(foodWeapons); - // Packet - player.sendPacket(new PacketAvatarUnlockTalentNotify(avatar, nextTalentId)); - player.sendPacket(new PacketUnlockAvatarTalentRsp(avatar, nextTalentId)); - - // Proud skill bonus map (Extra skills) - OpenConfigEntry entry = GameData.getOpenConfigEntries().get(talentData.getOpenConfig()); - if (entry != null) { - if (entry.getExtraTalentIndex() > 0) { - // Check if new constellation adds +3 to a skill level - avatar.recalcConstellations(); - // Packet - player.sendPacket(new PacketProudSkillExtraLevelNotify(avatar, entry.getExtraTalentIndex())); - } else if (entry.getSkillPointModifiers() != null) { - // Check if new constellation adds skill charges - avatar.recalcConstellations(); - // Packet - for (SkillPointModifier mod : entry.getSkillPointModifiers()) { - player.sendPacket( - new PacketAvatarSkillMaxChargeCountNotify(avatar, mod.getSkillId(), avatar.getSkillExtraChargeMap().getOrDefault(mod.getSkillId(), 0)) - ); - } - } - } - - // Recalc + save avatar - avatar.recalcStats(true); - avatar.save(); - } + // Level up + int maxLevel = promoteData.getUnlockMaxLevel(); + int level = weapon.getLevel(); + int oldLevel = level; + int exp = weapon.getExp(); + int totalExp = weapon.getTotalExp(); + int reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level); - public void destroyMaterial(Player player, List list) { - // Return materials - Int2IntOpenHashMap returnMaterialMap = new Int2IntOpenHashMap(); - - for (MaterialInfo info : list) { - // Sanity check - if (info.getCount() <= 0) { - continue; - } - - GameItem item = player.getInventory().getItemByGuid(info.getGuid()); - if (item == null || !item.isDestroyable()) { - continue; - } - - // Remove - int removeAmount = Math.min(info.getCount(), item.getCount()); - player.getInventory().removeItem(item, removeAmount); - - // Delete material return items - if (item.getItemData().getDestroyReturnMaterial().length > 0) { - for (int i = 0; i < item.getItemData().getDestroyReturnMaterial().length; i++) { - returnMaterialMap.addTo(item.getItemData().getDestroyReturnMaterial()[i], item.getItemData().getDestroyReturnMaterialCount()[i]); - } - } - } - - // Give back items - if (returnMaterialMap.size() > 0) { - for (Int2IntMap.Entry e : returnMaterialMap.int2IntEntrySet()) { - player.getInventory().addItem(new GameItem(e.getIntKey(), e.getIntValue())); - } - } - - // Packets - player.sendPacket(new PacketDestroyMaterialRsp(returnMaterialMap)); - } + while (expGain > 0 && reqExp > 0 && level < maxLevel) { + // Do calculations + int toGain = Math.min(expGain, reqExp - exp); + exp += toGain; + totalExp += toGain; + expGain -= toGain; + // Level up + if (exp >= reqExp) { + // Exp + exp = 0; + level += 1; + // Set req exp + reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level); + } + } - public GameItem useItem(Player player, long targetGuid, long itemGuid, int count, int optionId) { - Avatar target = player.getAvatars().getAvatarByGuid(targetGuid); - GameItem useItem = player.getInventory().getItemByGuid(itemGuid); - - if (useItem == null) { - return null; - } - - int used = 0; - boolean useSuccess = false; - - // Use - switch (useItem.getItemData().getMaterialType()) { - case MATERIAL_FOOD: - if (useItem.getItemData().getUseTarget().equals("ITEM_USE_TARGET_SPECIFY_DEAD_AVATAR")) { - if (target == null) { - break; - } + List leftovers = getLeftoverOres(expGain); + player.getInventory().addItemParams(leftovers); - used = player.getTeamManager().reviveAvatar(target) ? 1 : 0; - } - break; - case MATERIAL_NOTICE_ADD_HP: - if (useItem.getItemData().getUseTarget().equals("ITEM_USE_TARGET_SPECIFY_ALIVE_AVATAR")) { - if (target == null) { - break; - } + weapon.setLevel(level); + weapon.setExp(exp); + weapon.setTotalExp(totalExp); + weapon.save(); - int[] SatiationParams = useItem.getItemData().getSatiationParams(); - used = player.getTeamManager().healAvatar(target, SatiationParams[0], SatiationParams[1]) ? 1 : 0; - } - break; - case MATERIAL_CONSUME: - // Make sure we have usage data for this material. - if (useItem.getItemData().getItemUse() == null) { - break; - } + // Avatar + if (oldLevel != level) { + Avatar avatar = weapon.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(weapon.getEquipCharacter()) : null; + if (avatar != null) { + avatar.recalcStats(); + } + } - // Handle forging blueprints. - if (useItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_FORGE")) { - // Unlock. - useSuccess = player.getForgingManager().unlockForgingBlueprint(useItem); - } - // Handle combine diagrams. - if (useItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COMBINE")) { - // Unlock. - useSuccess = player.getServer().getCombineSystem().unlockCombineDiagram(player, useItem); - } - // Handle cooking recipies. - if (useItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COOK_RECIPE")) { - // Unlock. - useSuccess = player.getCookingManager().unlockRecipe(useItem); - } - break; - case MATERIAL_FURNITURE_FORMULA: - case MATERIAL_FURNITURE_SUITE_FORMULA: - if (useItem.getItemData().getItemUse() == null) { - break; - } - useSuccess = player.getFurnitureManager().unlockFurnitureOrSuite(useItem); + // Packets + player.sendPacket(new PacketStoreItemChangeNotify(weapon)); + player.sendPacket(new PacketWeaponUpgradeRsp(weapon, oldLevel, leftovers)); + } - break; - case MATERIAL_CONSUME_BATCH_USE: - // Make sure we have usage data for this material. - if (useItem.getItemData().getItemUse() == null) { - break; - } + private List getLeftoverOres(int leftover) { + List leftoverOreList = new ArrayList<>(3); - // Handle fragile/transient resin. - if (useItem.getItemId() == 107009 || useItem.getItemId() == 107012){ - // Add resin to the inventory. - ItemData resinItemData = GameData.getItemDataMap().get(106); - player.getInventory().addItem(new GameItem(resinItemData, 60 * count), ActionReason.PlayerUseItem); + if (leftover < WEAPON_ORE_EXP_1) { + return leftoverOreList; + } - // Set used amount. - used = count; - } - break; - case MATERIAL_CHEST: - List shopChestTableList = player.getServer().getShopSystem().getShopChestData(); - List rewardItemList = new ArrayList<>(); - for (ShopChestTable shopChestTable : shopChestTableList) { - if (shopChestTable.getItemId() != useItem.getItemId()) { - continue; - } + // Get leftovers + int ore3 = leftover / WEAPON_ORE_EXP_3; + leftover = leftover % WEAPON_ORE_EXP_3; + int ore2 = leftover / WEAPON_ORE_EXP_2; + leftover = leftover % WEAPON_ORE_EXP_2; + int ore1 = leftover / WEAPON_ORE_EXP_1; - if (shopChestTable.getContainsItem() == null) { - break; - } + if (ore3 > 0) { + leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_3).setCount(ore3).build()); + } if (ore2 > 0) { + leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_2).setCount(ore2).build()); + } if (ore1 > 0) { + leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_1).setCount(ore1).build()); + } - for (ItemParamData itemParamData : shopChestTable.getContainsItem()) { - ItemData itemData = GameData.getItemDataMap().get(itemParamData.getId()); - if (itemData == null) { - continue; - } - rewardItemList.add(new GameItem(itemData, itemParamData.getCount())); - } + return leftoverOreList; + } - if (!rewardItemList.isEmpty()) { - player.getInventory().addItems(rewardItemList, ActionReason.Shop); - } + public void refineWeapon(Player player, long targetGuid, long feedGuid) { + GameItem weapon = player.getInventory().getItemByGuid(targetGuid); + GameItem feed = player.getInventory().getItemByGuid(feedGuid); - used = 1; - break; - } - break; - case MATERIAL_CHEST_BATCH_USE: - if (optionId < 1) { - break; - } - List shopChestBatchUseTableList = player.getServer().getShopSystem().getShopChestBatchUseData(); - for (ShopChestBatchUseTable shopChestBatchUseTable : shopChestBatchUseTableList) { - if (shopChestBatchUseTable.getItemId() != useItem.getItemId()) { - continue; - } + // Sanity checks + if (weapon == null || feed == null || !feed.isDestroyable()) { + return; + } - if (shopChestBatchUseTable.getOptionItem() == null || optionId > shopChestBatchUseTable.getOptionItem().size()) { - break; - } + if (weapon.getItemData().getAwakenMaterial() == 0) { + if (weapon.getItemType() != ItemType.ITEM_WEAPON || weapon.getItemId() != feed.getItemId()) { + return; + } + } else { + if (weapon.getItemType() != ItemType.ITEM_WEAPON || weapon.getItemData().getAwakenMaterial() != feed.getItemId()) { + return; + } + } - int optionItemId = shopChestBatchUseTable.getOptionItem().get(optionId - 1); - ItemData itemData = GameData.getItemDataMap().get(optionItemId); - if (itemData == null) { - break; - } + if (weapon.getRefinement() >= 4 || weapon.getAffixes() == null || weapon.getAffixes().size() == 0) { + return; + } - player.getInventory().addItem(new GameItem(itemData, count), ActionReason.Shop); + // Calculate + int oldRefineLevel = weapon.getRefinement(); + int targetRefineLevel = Math.min(oldRefineLevel + feed.getRefinement() + 1, 4); + int moraCost = 0; - used = count; - break; - } - break; - default: - break; - } + try { + moraCost = weapon.getItemData().getAwakenCosts()[weapon.getRefinement()]; + } catch (Exception e) { + return; + } - // Welkin - if (useItem.getItemId() == 1202) { - player.rechargeMoonCard(); - used = 1; - } + // Mora check + if (player.getMora() >= moraCost) { + player.setMora(player.getMora() - moraCost); + } else { + return; + } - // If we used at least one item, or one of the methods called here reports using the item successfully, - // we return the item to make UseItemRsp a success. - if (used > 0) { - player.getInventory().removeItem(useItem, used); - return useItem; - } - if (useSuccess) { - return useItem; - } + // Consume weapon + player.getInventory().removeItem(feed, 1); - return null; - } + // Get + weapon.setRefinement(targetRefineLevel); + weapon.save(); + + // Avatar + Avatar avatar = weapon.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(weapon.getEquipCharacter()) : null; + if (avatar != null) { + avatar.recalcStats(); + } + + // Packets + player.sendPacket(new PacketStoreItemChangeNotify(weapon)); + player.sendPacket(new PacketWeaponAwakenRsp(avatar, weapon, feed, oldRefineLevel)); + } + + public void promoteWeapon(Player player, long targetGuid) { + GameItem weapon = player.getInventory().getItemByGuid(targetGuid); + + if (weapon == null || weapon.getItemType() != ItemType.ITEM_WEAPON) { + return; + } + + int nextPromoteLevel = weapon.getPromoteLevel() + 1; + WeaponPromoteData currentPromoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel()); + WeaponPromoteData nextPromoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), nextPromoteLevel); + if (currentPromoteData == null || nextPromoteData == null) { + return; + } + + // Level check + if (weapon.getLevel() != currentPromoteData.getUnlockMaxLevel()) { + return; + } + + // Pay materials and mora if possible + ItemParamData[] costs = nextPromoteData.getCostItems(); // Can this be null? + if (nextPromoteData.getCoinCost() > 0) { + costs = Arrays.copyOf(costs, costs.length + 1); + costs[costs.length-1] = new ItemParamData(202, nextPromoteData.getCoinCost()); + } + if (!player.getInventory().payItems(costs)) { + return; + } + + int oldPromoteLevel = weapon.getPromoteLevel(); + weapon.setPromoteLevel(nextPromoteLevel); + weapon.save(); + + // Avatar + Avatar avatar = weapon.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(weapon.getEquipCharacter()) : null; + if (avatar != null) { + avatar.recalcStats(); + } + + // Packets + player.sendPacket(new PacketStoreItemChangeNotify(weapon)); + player.sendPacket(new PacketWeaponPromoteRsp(weapon, oldPromoteLevel)); + } + + public void promoteAvatar(Player player, long guid) { + Avatar avatar = player.getAvatars().getAvatarByGuid(guid); + + // Sanity checks + if (avatar == null) { + return; + } + + int nextPromoteLevel = avatar.getPromoteLevel() + 1; + AvatarPromoteData currentPromoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), avatar.getPromoteLevel()); + AvatarPromoteData nextPromoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), nextPromoteLevel); + if (currentPromoteData == null || nextPromoteData == null) { + return; + } + + // Level check + if (avatar.getLevel() != currentPromoteData.getUnlockMaxLevel()) { + return; + } + + // Pay materials and mora if possible + ItemParamData[] costs = nextPromoteData.getCostItems(); // Can this be null? + if (nextPromoteData.getCoinCost() > 0) { + costs = Arrays.copyOf(costs, costs.length + 1); + costs[costs.length-1] = new ItemParamData(202, nextPromoteData.getCoinCost()); + } + if (!player.getInventory().payItems(costs)) { + return; + } + + // Update promote level + avatar.setPromoteLevel(nextPromoteLevel); + + // Update proud skills + AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId()); + + if (skillDepot != null && skillDepot.getInherentProudSkillOpens() != null) { + for (InherentProudSkillOpens openData : skillDepot.getInherentProudSkillOpens()) { + if (openData.getProudSkillGroupId() == 0) { + continue; + } + if (openData.getNeedAvatarPromoteLevel() == avatar.getPromoteLevel()) { + int proudSkillId = (openData.getProudSkillGroupId() * 100) + 1; + if (GameData.getProudSkillDataMap().containsKey(proudSkillId)) { + avatar.getProudSkillList().add(proudSkillId); + player.sendPacket(new PacketProudSkillChangeNotify(avatar)); + } + } + } + } + + // Packets + player.sendPacket(new PacketAvatarPropNotify(avatar)); + player.sendPacket(new PacketAvatarPromoteRsp(avatar)); + + // TODO Send entity prop update packet to world + avatar.recalcStats(true); + avatar.save(); + } + + public void upgradeAvatar(Player player, long guid, int itemId, int count) { + Avatar avatar = player.getAvatars().getAvatarByGuid(guid); + + // Sanity checks + if (avatar == null) { + return; + } + + AvatarPromoteData promoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), avatar.getPromoteLevel()); + if (promoteData == null) { + return; + } + + // Calc exp + int expGain = switch (itemId) { + case AVATAR_BOOK_1 -> AVATAR_BOOK_EXP_1 * count; + case AVATAR_BOOK_2 -> AVATAR_BOOK_EXP_2 * count; + case AVATAR_BOOK_3 -> AVATAR_BOOK_EXP_3 * count; + default -> 0; + }; + + // Sanity check + if (expGain <= 0) { + return; + } + + // Payment check + int moraCost = expGain / 5; + ItemParamData[] costItems = new ItemParamData[] {new ItemParamData(itemId, count), new ItemParamData(202, moraCost)}; + if (!player.getInventory().payItems(costItems)) { + return; + } + + // Level up + upgradeAvatar(player, avatar, promoteData, expGain); + } + + public void upgradeAvatar(Player player, Avatar avatar, int expGain) { + AvatarPromoteData promoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), avatar.getPromoteLevel()); + if (promoteData == null) { + return; + } + + upgradeAvatar(player, avatar, promoteData, expGain); + } + + public void upgradeAvatar(Player player, Avatar avatar, AvatarPromoteData promoteData, int expGain) { + int maxLevel = promoteData.getUnlockMaxLevel(); + int level = avatar.getLevel(); + int oldLevel = level; + int exp = avatar.getExp(); + int reqExp = GameData.getAvatarLevelExpRequired(level); + + while (expGain > 0 && reqExp > 0 && level < maxLevel) { + // Do calculations + int toGain = Math.min(expGain, reqExp - exp); + exp += toGain; + expGain -= toGain; + // Level up + if (exp >= reqExp) { + // Exp + exp = 0; + level += 1; + // Set req exp + reqExp = GameData.getAvatarLevelExpRequired(level); + } + } + + // Old map for packet + Map oldPropMap = avatar.getFightProperties(); + if (oldLevel != level) { + // Deep copy if level has changed + oldPropMap = avatar.getFightProperties().int2FloatEntrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + // Done + avatar.setLevel(level); + avatar.setExp(exp); + avatar.recalcStats(); + avatar.save(); + + // TODO Send entity prop update packet to world + + // Packets + player.sendPacket(new PacketAvatarPropNotify(avatar)); + player.sendPacket(new PacketAvatarUpgradeRsp(avatar, oldLevel, oldPropMap)); + } + + public void upgradeAvatarFetterLevel(Player player, Avatar avatar, int expGain) { + // May work. Not test. + int maxLevel = 10; // Keep it until I think of a more "elegant" way + int level = avatar.getFetterLevel(); + int exp = avatar.getFetterExp(); + int reqExp = GameData.getAvatarFetterLevelExpRequired(level); + + while (expGain > 0 && reqExp > 0 && level < maxLevel) { + int toGain = Math.min(expGain, reqExp - exp); + exp += toGain; + expGain -= toGain; + if (exp >= reqExp) { + exp = 0; + level += 1; + reqExp = GameData.getAvatarFetterLevelExpRequired(level); + } + } + + avatar.setFetterLevel(level); + avatar.setFetterExp(exp); + avatar.save(); + + player.sendPacket(new PacketAvatarPropNotify(avatar)); + player.sendPacket(new PacketAvatarFetterDataNotify(avatar)); + } + + public void upgradeAvatarSkill(Player player, long guid, int skillId) { + // Sanity checks + Avatar avatar = player.getAvatars().getAvatarByGuid(guid); + if (avatar == null) { + return; + } + + // Make sure avatar has skill + if (!avatar.getSkillLevelMap().containsKey(skillId)) { + return; + } + + AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(skillId); + if (skillData == null) { + return; + } + + // Get data for next skill level + int currentLevel = avatar.getSkillLevelMap().get(skillId); + int nextLevel = currentLevel + 1; + int proudSkillId = (skillData.getProudSkillGroupId() * 100) + nextLevel; + + // Capped at level 10 talent + if (nextLevel > 10) { + return; + } + + // Proud skill data + ProudSkillData proudSkill = GameData.getProudSkillDataMap().get(proudSkillId); + if (proudSkill == null) { + return; + } + + // Make sure break level is correct + if (avatar.getPromoteLevel() < proudSkill.getBreakLevel()) { + return; + } + + // Pay materials and mora if possible + List costs = new ArrayList(proudSkill.getCostItems()); // Can this be null? + if (proudSkill.getCoinCost() > 0) { + costs.add(new ItemParamData(202, proudSkill.getCoinCost())); + } + if (!player.getInventory().payItems(costs.toArray(new ItemParamData[0]))) { + return; + } + + // Upgrade skill + avatar.getSkillLevelMap().put(skillId, nextLevel); + avatar.save(); + + // Packet + player.sendPacket(new PacketAvatarSkillChangeNotify(avatar, skillId, currentLevel, nextLevel)); + player.sendPacket(new PacketAvatarSkillUpgradeRsp(avatar, skillId, currentLevel, nextLevel)); + } + + public void unlockAvatarConstellation(Player player, long guid) { + // Sanity checks + Avatar avatar = player.getAvatars().getAvatarByGuid(guid); + if (avatar == null) { + return; + } + + // Get talent + int currentTalentLevel = avatar.getCoreProudSkillLevel(); + int nextTalentId = ((avatar.getAvatarId() % 10000000) * 10) + currentTalentLevel + 1; + + if (avatar.getAvatarId() == 10000006) { + // Lisa is special in that her talentId starts with 4 instead of 6. + nextTalentId = 40 + currentTalentLevel + 1; + } + + AvatarTalentData talentData = GameData.getAvatarTalentDataMap().get(nextTalentId); + + if (talentData == null) { + return; + } + + // Pay constellation item if possible + if (!player.getInventory().payItem(talentData.getMainCostItemId(), 1)) { + return; + } + + // Apply + recalc + avatar.getTalentIdList().add(talentData.getId()); + avatar.setCoreProudSkillLevel(currentTalentLevel + 1); + + // Packet + player.sendPacket(new PacketAvatarUnlockTalentNotify(avatar, nextTalentId)); + player.sendPacket(new PacketUnlockAvatarTalentRsp(avatar, nextTalentId)); + + // Proud skill bonus map (Extra skills) + OpenConfigEntry entry = GameData.getOpenConfigEntries().get(talentData.getOpenConfig()); + if (entry != null) { + if (entry.getExtraTalentIndex() > 0) { + // Check if new constellation adds +3 to a skill level + avatar.recalcConstellations(); + // Packet + player.sendPacket(new PacketProudSkillExtraLevelNotify(avatar, entry.getExtraTalentIndex())); + } else if (entry.getSkillPointModifiers() != null) { + // Check if new constellation adds skill charges + avatar.recalcConstellations(); + // Packet + for (SkillPointModifier mod : entry.getSkillPointModifiers()) { + player.sendPacket( + new PacketAvatarSkillMaxChargeCountNotify(avatar, mod.getSkillId(), avatar.getSkillExtraChargeMap().getOrDefault(mod.getSkillId(), 0)) + ); + } + } + } + + // Recalc + save avatar + avatar.recalcStats(true); + avatar.save(); + } + + public void destroyMaterial(Player player, List list) { + // Return materials + Int2IntOpenHashMap returnMaterialMap = new Int2IntOpenHashMap(); + + for (MaterialInfo info : list) { + // Sanity check + if (info.getCount() <= 0) { + continue; + } + + GameItem item = player.getInventory().getItemByGuid(info.getGuid()); + if (item == null || !item.isDestroyable()) { + continue; + } + + // Remove + int removeAmount = Math.min(info.getCount(), item.getCount()); + player.getInventory().removeItem(item, removeAmount); + + // Delete material return items + if (item.getItemData().getDestroyReturnMaterial().length > 0) { + for (int i = 0; i < item.getItemData().getDestroyReturnMaterial().length; i++) { + returnMaterialMap.addTo(item.getItemData().getDestroyReturnMaterial()[i], item.getItemData().getDestroyReturnMaterialCount()[i]); + } + } + } + + // Give back items + if (returnMaterialMap.size() > 0) { + for (Int2IntMap.Entry e : returnMaterialMap.int2IntEntrySet()) { + player.getInventory().addItem(new GameItem(e.getIntKey(), e.getIntValue())); + } + } + + // Packets + player.sendPacket(new PacketDestroyMaterialRsp(returnMaterialMap)); + } + + public GameItem useItem(Player player, long targetGuid, long itemGuid, int count, int optionId) { + Avatar target = player.getAvatars().getAvatarByGuid(targetGuid); + GameItem useItem = player.getInventory().getItemByGuid(itemGuid); + + if (useItem == null) { + return null; + } + + int used = 0; + boolean useSuccess = false; + + // Use + switch (useItem.getItemData().getMaterialType()) { + case MATERIAL_FOOD: + if (useItem.getItemData().getUseTarget().equals("ITEM_USE_TARGET_SPECIFY_DEAD_AVATAR")) { + if (target == null) { + break; + } + + used = player.getTeamManager().reviveAvatar(target) ? 1 : 0; + } + break; + case MATERIAL_NOTICE_ADD_HP: + if (useItem.getItemData().getUseTarget().equals("ITEM_USE_TARGET_SPECIFY_ALIVE_AVATAR")) { + if (target == null) { + break; + } + + int[] SatiationParams = useItem.getItemData().getSatiationParams(); + used = player.getTeamManager().healAvatar(target, SatiationParams[0], SatiationParams[1]) ? 1 : 0; + } + break; + case MATERIAL_CONSUME: + // Make sure we have usage data for this material. + if (useItem.getItemData().getItemUse() == null) { + break; + } + + // Handle forging blueprints. + if (useItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_FORGE")) { + // Unlock. + useSuccess = player.getForgingManager().unlockForgingBlueprint(useItem); + } + // Handle combine diagrams. + if (useItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COMBINE")) { + // Unlock. + useSuccess = player.getServer().getCombineSystem().unlockCombineDiagram(player, useItem); + } + // Handle cooking recipies. + if (useItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COOK_RECIPE")) { + // Unlock. + useSuccess = player.getCookingManager().unlockRecipe(useItem); + } + break; + case MATERIAL_FURNITURE_FORMULA: + case MATERIAL_FURNITURE_SUITE_FORMULA: + if (useItem.getItemData().getItemUse() == null) { + break; + } + useSuccess = player.getFurnitureManager().unlockFurnitureOrSuite(useItem); + + break; + case MATERIAL_CONSUME_BATCH_USE: + // Make sure we have usage data for this material. + if (useItem.getItemData().getItemUse() == null) { + break; + } + + // Handle fragile/transient resin. + if (useItem.getItemId() == 107009 || useItem.getItemId() == 107012) { + // Add resin to the inventory. + ItemData resinItemData = GameData.getItemDataMap().get(106); + player.getInventory().addItem(new GameItem(resinItemData, 60 * count), ActionReason.PlayerUseItem); + + // Set used amount. + used = count; + } + break; + case MATERIAL_CHEST: + List shopChestTableList = player.getServer().getShopSystem().getShopChestData(); + List rewardItemList = new ArrayList<>(); + for (ShopChestTable shopChestTable : shopChestTableList) { + if (shopChestTable.getItemId() != useItem.getItemId()) { + continue; + } + + if (shopChestTable.getContainsItem() == null) { + break; + } + + for (ItemParamData itemParamData : shopChestTable.getContainsItem()) { + ItemData itemData = GameData.getItemDataMap().get(itemParamData.getId()); + if (itemData == null) { + continue; + } + rewardItemList.add(new GameItem(itemData, itemParamData.getCount())); + } + + if (!rewardItemList.isEmpty()) { + player.getInventory().addItems(rewardItemList, ActionReason.Shop); + } + + used = 1; + break; + } + break; + case MATERIAL_CHEST_BATCH_USE: + if (optionId < 1) { + break; + } + List shopChestBatchUseTableList = player.getServer().getShopSystem().getShopChestBatchUseData(); + for (ShopChestBatchUseTable shopChestBatchUseTable : shopChestBatchUseTableList) { + if (shopChestBatchUseTable.getItemId() != useItem.getItemId()) { + continue; + } + + if (shopChestBatchUseTable.getOptionItem() == null || optionId > shopChestBatchUseTable.getOptionItem().size()) { + break; + } + + int optionItemId = shopChestBatchUseTable.getOptionItem().get(optionId - 1); + ItemData itemData = GameData.getItemDataMap().get(optionItemId); + if (itemData == null) { + break; + } + + player.getInventory().addItem(new GameItem(itemData, count), ActionReason.Shop); + + used = count; + break; + } + break; + default: + break; + } + + // Welkin + if (useItem.getItemId() == 1202) { + player.rechargeMoonCard(); + used = 1; + } + + // If we used at least one item, or one of the methods called here reports using the item successfully, + // we return the item to make UseItemRsp a success. + if (used > 0) { + player.getInventory().removeItem(useItem, used); + return useItem; + } + if (useSuccess) { + return useItem; + } + + return null; + } } diff --git a/src/main/java/emu/grasscutter/game/systems/MultiplayerSystem.java b/src/main/java/emu/grasscutter/game/systems/MultiplayerSystem.java index a3f23a448..71daa2c1b 100644 --- a/src/main/java/emu/grasscutter/game/systems/MultiplayerSystem.java +++ b/src/main/java/emu/grasscutter/game/systems/MultiplayerSystem.java @@ -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; + } } diff --git a/src/main/java/emu/grasscutter/game/tower/TowerManager.java b/src/main/java/emu/grasscutter/game/tower/TowerManager.java index 19a6d860c..8da97358c 100644 --- a/src/main/java/emu/grasscutter/game/tower/TowerManager.java +++ b/src/main/java/emu/grasscutter/game/tower/TowerManager.java @@ -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 towerDungeonSettleListener = List.of(new TowerDungeonSettleListener()); - + public Map getRecordMap() { Map 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 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 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()) diff --git a/src/main/java/emu/grasscutter/game/tower/TowerSystem.java b/src/main/java/emu/grasscutter/game/tower/TowerSystem.java index 2923dfd0b..1282d710a 100644 --- a/src/main/java/emu/grasscutter/game/tower/TowerSystem.java +++ b/src/main/java/emu/grasscutter/game/tower/TowerSystem.java @@ -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 players; - private final Map entities; - private final Set spawnedEntities; - private final Set deadSpawnedEntities; - private final Set loadedBlocks; - private Set loadedGridBlocks; - private boolean dontDestroyWhenEmpty; - - private int autoCloseTime; - private int time; - - private SceneScriptManager scriptManager; - private WorldChallenge challenge; - private List dungeonSettleListeners; - private DungeonData dungeonData; - private int prevScene; // Id of the previous scene - private int prevScenePoint; - private Set npcBornEntrySet; - public Scene(World world, SceneData sceneData) { - this.world = world; - this.sceneData = sceneData; - this.players = new CopyOnWriteArrayList<>(); - this.entities = new ConcurrentHashMap<>(); - - this.time = 8 * 60; - this.prevScene = 3; - - this.spawnedEntities = ConcurrentHashMap.newKeySet(); - this.deadSpawnedEntities = ConcurrentHashMap.newKeySet(); - this.loadedBlocks = ConcurrentHashMap.newKeySet(); - this.loadedGridBlocks = new HashSet<>(); - this.npcBornEntrySet = ConcurrentHashMap.newKeySet(); - this.scriptManager = new SceneScriptManager(this); - } - - public int getId() { - return sceneData.getId(); - } - - public World getWorld() { - return world; - } - - public SceneData getSceneData() { - return this.sceneData; - } - - public SceneType getSceneType() { - return getSceneData().getSceneType(); - } - - public List getPlayers() { - return players; - } - - public int getPlayerCount() { - return this.getPlayers().size(); - } - - public Map getEntities() { - return entities; - } - - public GameEntity getEntityById(int id) { - return this.entities.get(id); - } - - public GameEntity getEntityByConfigId(int configId) { - return this.entities.values().stream() - .filter(x -> x.getConfigId() == configId) - .findFirst() - .orElse(null); - } - /** - * @return the autoCloseTime - */ - public int getAutoCloseTime() { - return autoCloseTime; - } - - /** - * @param autoCloseTime the autoCloseTime to set - */ - public void setAutoCloseTime(int autoCloseTime) { - this.autoCloseTime = autoCloseTime; - } - - public int getTime() { - return time; - } - - public void changeTime(int time) { - this.time = time % 1440; - } - - public int getPrevScene() { - return prevScene; - } - - public void setPrevScene(int prevScene) { - this.prevScene = prevScene; - } - - public int getPrevScenePoint() { - return prevScenePoint; - } - - public void setPrevScenePoint(int prevPoint) { - this.prevScenePoint = prevPoint; - } - - public boolean dontDestroyWhenEmpty() { - return dontDestroyWhenEmpty; - } - - public void setDontDestroyWhenEmpty(boolean dontDestroyWhenEmpty) { - this.dontDestroyWhenEmpty = dontDestroyWhenEmpty; - } - - public Set getLoadedBlocks() { - return loadedBlocks; - } - - public Set getSpawnedEntities() { - return spawnedEntities; - } - - public Set getDeadSpawnedEntities() { - return deadSpawnedEntities; - } - - public SceneScriptManager getScriptManager() { - return scriptManager; - } - - public DungeonData getDungeonData() { - return dungeonData; - } - - public void setDungeonData(DungeonData dungeonData) { - if (dungeonData == null || this.dungeonData != null || this.getSceneType() != SceneType.SCENE_DUNGEON || dungeonData.getSceneId() != this.getId()) { - return; - } - this.dungeonData = dungeonData; - } - - public WorldChallenge getChallenge() { - return challenge; - } - - public void setChallenge(WorldChallenge challenge) { - this.challenge = challenge; - } - - public void addDungeonSettleObserver(DungeonSettleListener dungeonSettleListener){ - if(dungeonSettleListeners == null){ - dungeonSettleListeners = new ArrayList<>(); - } - dungeonSettleListeners.add(dungeonSettleListener); - } - - public List getDungeonSettleObservers() { - return dungeonSettleListeners; - } - - public boolean isInScene(GameEntity entity) { - return this.entities.containsKey(entity.getId()); - } - - public synchronized void addPlayer(Player player) { - // Check if player already in - if (getPlayers().contains(player)) { - return; - } - - // Remove player from prev scene - if (player.getScene() != null) { - player.getScene().removePlayer(player); - } - - // Add - getPlayers().add(player); - player.setSceneId(this.getId()); - player.setScene(this); - - this.setupPlayerAvatars(player); - } - - public synchronized void removePlayer(Player player) { - // Remove from challenge if leaving - if (this.getChallenge() != null && this.getChallenge().inProgress()) { - player.sendPacket(new PacketDungeonChallengeFinishNotify(this.getChallenge())); - } - - // Remove player from scene - getPlayers().remove(player); - player.setScene(null); - - // Remove player avatars - this.removePlayerAvatars(player); - - // Remove player gadgets - for (EntityBaseGadget gadget : player.getTeamManager().getGadgets()) { - this.removeEntity(gadget); - } - - // Deregister scene if not in use - if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty()) { - this.getWorld().deregisterScene(this); - } - } - - private void setupPlayerAvatars(Player player) { - // Clear entities from old team - player.getTeamManager().getActiveTeam().clear(); - - // Add new entities for player - TeamInfo teamInfo = player.getTeamManager().getCurrentTeamInfo(); - for (int avatarId : teamInfo.getAvatars()) { - EntityAvatar entity = new EntityAvatar(player.getScene(), player.getAvatars().getAvatarById(avatarId)); - player.getTeamManager().getActiveTeam().add(entity); - } - - // Limit character index in case its out of bounds - if (player.getTeamManager().getCurrentCharacterIndex() >= player.getTeamManager().getActiveTeam().size() || player.getTeamManager().getCurrentCharacterIndex() < 0) { - player.getTeamManager().setCurrentCharacterIndex(player.getTeamManager().getCurrentCharacterIndex() - 1); - } - } - - private void removePlayerAvatars(Player player) { - Iterator it = player.getTeamManager().getActiveTeam().iterator(); - while (it.hasNext()) { - this.removeEntity(it.next(), VisionType.VISION_TYPE_REMOVE); - it.remove(); - } - } - - public void spawnPlayer(Player player) { - if (this.isInScene(player.getTeamManager().getCurrentAvatarEntity())) { - return; - } - - if (player.getTeamManager().getCurrentAvatarEntity().getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { - player.getTeamManager().getCurrentAvatarEntity().setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 1f); - } - - this.addEntity(player.getTeamManager().getCurrentAvatarEntity()); - - // Notify the client of any extra skill charges - for (EntityAvatar entity : player.getTeamManager().getActiveTeam()) { - if (entity.getAvatar().getSkillExtraChargeMap().size() > 0) { - player.sendPacket(new PacketAvatarSkillInfoNotify(entity.getAvatar())); - } - } - } - - private void addEntityDirectly(GameEntity entity) { - getEntities().put(entity.getId(), entity); - entity.onCreate(); // Call entity create event - } - - public synchronized void addEntity(GameEntity entity) { - this.addEntityDirectly(entity); - this.broadcastPacket(new PacketSceneEntityAppearNotify(entity)); - } - - public synchronized void addEntityToSingleClient(Player player, GameEntity entity) { - this.addEntityDirectly(entity); - player.sendPacket(new PacketSceneEntityAppearNotify(entity)); - - } - public void addEntities(Collection entities){ - addEntities(entities, VisionType.VISION_TYPE_BORN); - } - - public synchronized void addEntities(Collection entities, VisionType visionType) { - if(entities == null || entities.isEmpty()){ - return; - } - for (GameEntity entity : entities) { - this.addEntityDirectly(entity); - } - - this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, visionType)); - } - - private GameEntity removeEntityDirectly(GameEntity entity) { - return getEntities().remove(entity.getId()); - } - - public void removeEntity(GameEntity entity) { - this.removeEntity(entity, VisionType.VISION_TYPE_DIE); - } - - public synchronized void removeEntity(GameEntity entity, VisionType visionType) { - GameEntity removed = this.removeEntityDirectly(entity); - if (removed != null) { - this.broadcastPacket(new PacketSceneEntityDisappearNotify(removed, visionType)); - } - } - public synchronized void removeEntities(List entity, VisionType visionType) { - var toRemove = entity.stream() - .map(this::removeEntityDirectly) - .toList(); - if (toRemove.size() > 0) { - this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, visionType)); - } - } - public synchronized void replaceEntity(EntityAvatar oldEntity, EntityAvatar newEntity) { - this.removeEntityDirectly(oldEntity); - this.addEntityDirectly(newEntity); - this.broadcastPacket(new PacketSceneEntityDisappearNotify(oldEntity, VisionType.VISION_TYPE_REPLACE)); - this.broadcastPacket(new PacketSceneEntityAppearNotify(newEntity, VisionType.VISION_TYPE_REPLACE, oldEntity.getId())); - } - - public void showOtherEntities(Player player) { - List entities = new LinkedList<>(); - GameEntity currentEntity = player.getTeamManager().getCurrentAvatarEntity(); - - for (GameEntity entity : this.getEntities().values()) { - if (entity == currentEntity) { - continue; - } - entities.add(entity); - } - - player.sendPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_TYPE_MEET)); - } - - public void handleAttack(AttackResult result) { - //GameEntity attacker = getEntityById(result.getAttackerId()); - GameEntity target = getEntityById(result.getDefenseId()); - - if (target == null) { - return; - } - - // Godmode check - if (target instanceof EntityAvatar) { - if (((EntityAvatar) target).getPlayer().inGodmode()) { - return; - } - } - - // Sanity check - target.damage(result.getDamage(), result.getAttackerId()); - } - - public void killEntity(GameEntity target) { - killEntity(target, 0); - } - - public void killEntity(GameEntity target, int attackerId) { - GameEntity attacker = null; - - if (attackerId > 0) { - attacker = getEntityById(attackerId); - } - - if (attacker != null) { - // Check codex - if (attacker instanceof EntityClientGadget gadgetAttacker) { - var clientGadgetOwner = getEntityById(gadgetAttacker.getOwnerEntityId()); - if (clientGadgetOwner instanceof EntityAvatar) { - ((EntityClientGadget) attacker).getOwner().getCodex().checkAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL); - } - } else if (attacker instanceof EntityAvatar avatarAttacker) { - avatarAttacker.getPlayer().getCodex().checkAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL); - } - } - - // Packet - this.broadcastPacket(new PacketLifeStateChangeNotify(attackerId, target, LifeState.LIFE_DEAD)); - - // Reward drop - if (target instanceof EntityMonster && this.getSceneType() != SceneType.SCENE_DUNGEON) { - getWorld().getServer().getDropSystem().callDrop((EntityMonster) target); - } - - // Remove entity from world - this.removeEntity(target); - - // Death event - target.onDeath(attackerId); - } - - public void onTick() { - // disable script for home - if (this.getSceneType() == SceneType.SCENE_HOME_WORLD || this.getSceneType() == SceneType.SCENE_HOME_ROOM){ - return; - } - if (this.getScriptManager().isInit()) { - this.checkBlocks(); - } else { - // TEMPORARY - this.checkSpawns(); - } - // Triggers - this.scriptManager.checkRegions(); - - if(challenge != null){ - challenge.onCheckTimeOut(); - } + private final World world; + private final SceneData sceneData; + private final List players; + private final Map entities; + private final Set spawnedEntities; + private final Set deadSpawnedEntities; + private final Set loadedBlocks; + private Set loadedGridBlocks; + private boolean dontDestroyWhenEmpty; + + private int autoCloseTime; + private int time; + + private SceneScriptManager scriptManager; + private WorldChallenge challenge; + private List dungeonSettleListeners; + private DungeonData dungeonData; + private int prevScene; // Id of the previous scene + private int prevScenePoint; + private Set npcBornEntrySet; + public Scene(World world, SceneData sceneData) { + this.world = world; + this.sceneData = sceneData; + this.players = new CopyOnWriteArrayList<>(); + this.entities = new ConcurrentHashMap<>(); + + this.time = 8 * 60; + this.prevScene = 3; + + this.spawnedEntities = ConcurrentHashMap.newKeySet(); + this.deadSpawnedEntities = ConcurrentHashMap.newKeySet(); + this.loadedBlocks = ConcurrentHashMap.newKeySet(); + this.loadedGridBlocks = new HashSet<>(); + this.npcBornEntrySet = ConcurrentHashMap.newKeySet(); + this.scriptManager = new SceneScriptManager(this); + } + + public int getId() { + return sceneData.getId(); + } + + public World getWorld() { + return world; + } + + public SceneData getSceneData() { + return this.sceneData; + } + + public SceneType getSceneType() { + return getSceneData().getSceneType(); + } + + public List getPlayers() { + return players; + } + + public int getPlayerCount() { + return this.getPlayers().size(); + } + + public Map getEntities() { + return entities; + } + + public GameEntity getEntityById(int id) { + return this.entities.get(id); + } + + public GameEntity getEntityByConfigId(int configId) { + return this.entities.values().stream() + .filter(x -> x.getConfigId() == configId) + .findFirst() + .orElse(null); + } + /** + * @return the autoCloseTime + */ + public int getAutoCloseTime() { + return autoCloseTime; + } + + /** + * @param autoCloseTime the autoCloseTime to set + */ + public void setAutoCloseTime(int autoCloseTime) { + this.autoCloseTime = autoCloseTime; + } + + public int getTime() { + return time; + } + + public void changeTime(int time) { + this.time = time % 1440; + } + + public int getPrevScene() { + return prevScene; + } + + public void setPrevScene(int prevScene) { + this.prevScene = prevScene; + } + + public int getPrevScenePoint() { + return prevScenePoint; + } + + public void setPrevScenePoint(int prevPoint) { + this.prevScenePoint = prevPoint; + } + + public boolean dontDestroyWhenEmpty() { + return dontDestroyWhenEmpty; + } + + public void setDontDestroyWhenEmpty(boolean dontDestroyWhenEmpty) { + this.dontDestroyWhenEmpty = dontDestroyWhenEmpty; + } + + public Set getLoadedBlocks() { + return loadedBlocks; + } + + public Set getSpawnedEntities() { + return spawnedEntities; + } + + public Set getDeadSpawnedEntities() { + return deadSpawnedEntities; + } + + public SceneScriptManager getScriptManager() { + return scriptManager; + } + + public DungeonData getDungeonData() { + return dungeonData; + } + + public void setDungeonData(DungeonData dungeonData) { + if (dungeonData == null || this.dungeonData != null || this.getSceneType() != SceneType.SCENE_DUNGEON || dungeonData.getSceneId() != this.getId()) { + return; + } + this.dungeonData = dungeonData; + } + + public WorldChallenge getChallenge() { + return challenge; + } + + public void setChallenge(WorldChallenge challenge) { + this.challenge = challenge; + } + + public void addDungeonSettleObserver(DungeonSettleListener dungeonSettleListener) { + if (dungeonSettleListeners == null) { + dungeonSettleListeners = new ArrayList<>(); + } + dungeonSettleListeners.add(dungeonSettleListener); + } + + public List getDungeonSettleObservers() { + return dungeonSettleListeners; + } + + public boolean isInScene(GameEntity entity) { + return this.entities.containsKey(entity.getId()); + } + + public synchronized void addPlayer(Player player) { + // Check if player already in + if (getPlayers().contains(player)) { + return; + } + + // Remove player from prev scene + if (player.getScene() != null) { + player.getScene().removePlayer(player); + } + + // Add + getPlayers().add(player); + player.setSceneId(this.getId()); + player.setScene(this); + + this.setupPlayerAvatars(player); + } + + public synchronized void removePlayer(Player player) { + // Remove from challenge if leaving + if (this.getChallenge() != null && this.getChallenge().inProgress()) { + player.sendPacket(new PacketDungeonChallengeFinishNotify(this.getChallenge())); + } + + // Remove player from scene + getPlayers().remove(player); + player.setScene(null); + + // Remove player avatars + this.removePlayerAvatars(player); + + // Remove player gadgets + for (EntityBaseGadget gadget : player.getTeamManager().getGadgets()) { + this.removeEntity(gadget); + } + + // Deregister scene if not in use + if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty()) { + this.getWorld().deregisterScene(this); + } + } + + private void setupPlayerAvatars(Player player) { + // Clear entities from old team + player.getTeamManager().getActiveTeam().clear(); + + // Add new entities for player + TeamInfo teamInfo = player.getTeamManager().getCurrentTeamInfo(); + for (int avatarId : teamInfo.getAvatars()) { + EntityAvatar entity = new EntityAvatar(player.getScene(), player.getAvatars().getAvatarById(avatarId)); + player.getTeamManager().getActiveTeam().add(entity); + } + + // Limit character index in case its out of bounds + if (player.getTeamManager().getCurrentCharacterIndex() >= player.getTeamManager().getActiveTeam().size() || player.getTeamManager().getCurrentCharacterIndex() < 0) { + player.getTeamManager().setCurrentCharacterIndex(player.getTeamManager().getCurrentCharacterIndex() - 1); + } + } + + private void removePlayerAvatars(Player player) { + Iterator it = player.getTeamManager().getActiveTeam().iterator(); + while (it.hasNext()) { + this.removeEntity(it.next(), VisionType.VISION_TYPE_REMOVE); + it.remove(); + } + } + + public void spawnPlayer(Player player) { + if (this.isInScene(player.getTeamManager().getCurrentAvatarEntity())) { + return; + } + + if (player.getTeamManager().getCurrentAvatarEntity().getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { + player.getTeamManager().getCurrentAvatarEntity().setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 1f); + } + + this.addEntity(player.getTeamManager().getCurrentAvatarEntity()); + + // Notify the client of any extra skill charges + for (EntityAvatar entity : player.getTeamManager().getActiveTeam()) { + if (entity.getAvatar().getSkillExtraChargeMap().size() > 0) { + player.sendPacket(new PacketAvatarSkillInfoNotify(entity.getAvatar())); + } + } + } + + private void addEntityDirectly(GameEntity entity) { + getEntities().put(entity.getId(), entity); + entity.onCreate(); // Call entity create event + } + + public synchronized void addEntity(GameEntity entity) { + this.addEntityDirectly(entity); + this.broadcastPacket(new PacketSceneEntityAppearNotify(entity)); + } + + public synchronized void addEntityToSingleClient(Player player, GameEntity entity) { + this.addEntityDirectly(entity); + player.sendPacket(new PacketSceneEntityAppearNotify(entity)); + + } + public void addEntities(Collection entities) { + addEntities(entities, VisionType.VISION_TYPE_BORN); + } + + public synchronized void addEntities(Collection entities, VisionType visionType) { + if (entities == null || entities.isEmpty()) { + return; + } + for (GameEntity entity : entities) { + this.addEntityDirectly(entity); + } + + this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, visionType)); + } + + private GameEntity removeEntityDirectly(GameEntity entity) { + return getEntities().remove(entity.getId()); + } + + public void removeEntity(GameEntity entity) { + this.removeEntity(entity, VisionType.VISION_TYPE_DIE); + } + + public synchronized void removeEntity(GameEntity entity, VisionType visionType) { + GameEntity removed = this.removeEntityDirectly(entity); + if (removed != null) { + this.broadcastPacket(new PacketSceneEntityDisappearNotify(removed, visionType)); + } + } + public synchronized void removeEntities(List entity, VisionType visionType) { + var toRemove = entity.stream() + .map(this::removeEntityDirectly) + .toList(); + if (toRemove.size() > 0) { + this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, visionType)); + } + } + public synchronized void replaceEntity(EntityAvatar oldEntity, EntityAvatar newEntity) { + this.removeEntityDirectly(oldEntity); + this.addEntityDirectly(newEntity); + this.broadcastPacket(new PacketSceneEntityDisappearNotify(oldEntity, VisionType.VISION_TYPE_REPLACE)); + this.broadcastPacket(new PacketSceneEntityAppearNotify(newEntity, VisionType.VISION_TYPE_REPLACE, oldEntity.getId())); + } + + public void showOtherEntities(Player player) { + List entities = new LinkedList<>(); + GameEntity currentEntity = player.getTeamManager().getCurrentAvatarEntity(); + + for (GameEntity entity : this.getEntities().values()) { + if (entity == currentEntity) { + continue; + } + entities.add(entity); + } + + player.sendPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_TYPE_MEET)); + } + + public void handleAttack(AttackResult result) { + //GameEntity attacker = getEntityById(result.getAttackerId()); + GameEntity target = getEntityById(result.getDefenseId()); + + if (target == null) { + return; + } + + // Godmode check + if (target instanceof EntityAvatar) { + if (((EntityAvatar) target).getPlayer().inGodmode()) { + return; + } + } + + // Sanity check + target.damage(result.getDamage(), result.getAttackerId()); + } + + public void killEntity(GameEntity target) { + killEntity(target, 0); + } + + public void killEntity(GameEntity target, int attackerId) { + GameEntity attacker = null; + + if (attackerId > 0) { + attacker = getEntityById(attackerId); + } + + if (attacker != null) { + // Check codex + if (attacker instanceof EntityClientGadget gadgetAttacker) { + var clientGadgetOwner = getEntityById(gadgetAttacker.getOwnerEntityId()); + if (clientGadgetOwner instanceof EntityAvatar) { + ((EntityClientGadget) attacker).getOwner().getCodex().checkAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL); + } + } else if (attacker instanceof EntityAvatar avatarAttacker) { + avatarAttacker.getPlayer().getCodex().checkAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL); + } + } + + // Packet + this.broadcastPacket(new PacketLifeStateChangeNotify(attackerId, target, LifeState.LIFE_DEAD)); + + // Reward drop + if (target instanceof EntityMonster && this.getSceneType() != SceneType.SCENE_DUNGEON) { + getWorld().getServer().getDropSystem().callDrop((EntityMonster) target); + } + + // Remove entity from world + this.removeEntity(target); + + // Death event + target.onDeath(attackerId); + } + + public void onTick() { + // disable script for home + if (this.getSceneType() == SceneType.SCENE_HOME_WORLD || this.getSceneType() == SceneType.SCENE_HOME_ROOM) { + return; + } + if (this.getScriptManager().isInit()) { + this.checkBlocks(); + } else { + // TEMPORARY + this.checkSpawns(); + } + // Triggers + this.scriptManager.checkRegions(); + + if (challenge != null) { + challenge.onCheckTimeOut(); + } checkNpcGroup(); - } + } - public int getEntityLevel(int baseLevel, int worldLevelOverride) { - int level = worldLevelOverride > 0 ? worldLevelOverride + baseLevel - 22 : baseLevel; - level = level >= 100 ? 100 : level; - level = level <= 0 ? 1 : level; + public int getEntityLevel(int baseLevel, int worldLevelOverride) { + int level = worldLevelOverride > 0 ? worldLevelOverride + baseLevel - 22 : baseLevel; + level = level >= 100 ? 100 : level; + level = level <= 0 ? 1 : level; - return level; - } - public void checkNpcGroup(){ + return level; + } + public void checkNpcGroup() { Set npcBornEntries = ConcurrentHashMap.newKeySet(); for (Player player : this.getPlayers()) { npcBornEntries.addAll(loadNpcForPlayer(player)); @@ -458,7 +458,7 @@ public class Scene { .map(SceneNpcBornEntry::getGroupId) .toList(); - if(toUnload.size() > 0){ + if (toUnload.size() > 0) { broadcastPacket(new PacketGroupUnloadNotify(toUnload)); Grasscutter.getLogger().debug("Unload NPC Group {}", toUnload); } @@ -480,342 +480,342 @@ public class Scene { Set visible = new HashSet<>(); for (var block : loadedGridBlocks) { var spawns = spawnLists.get(block); - if(spawns!=null) { + if (spawns!=null) { visible.addAll(spawns); } } - // World level - WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(getWorld().getWorldLevel()); - int worldLevelOverride = 0; + // World level + WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(getWorld().getWorldLevel()); + int worldLevelOverride = 0; - if (worldLevelData != null) { - worldLevelOverride = worldLevelData.getMonsterLevel(); - } + if (worldLevelData != null) { + worldLevelOverride = worldLevelData.getMonsterLevel(); + } - // Todo - List toAdd = new LinkedList<>(); - List toRemove = new LinkedList<>(); + // Todo + List toAdd = new LinkedList<>(); + List toRemove = new LinkedList<>(); var spawnedEntities = this.getSpawnedEntities(); - for (SpawnDataEntry entry : visible) { - // If spawn entry is in our view and hasnt been spawned/killed yet, we should spawn it - if (!spawnedEntities.contains(entry) && !this.getDeadSpawnedEntities().contains(entry)) { - // Entity object holder - GameEntity entity = null; + for (SpawnDataEntry entry : visible) { + // If spawn entry is in our view and hasnt been spawned/killed yet, we should spawn it + if (!spawnedEntities.contains(entry) && !this.getDeadSpawnedEntities().contains(entry)) { + // Entity object holder + GameEntity entity = null; - // Check if spawn entry is monster or gadget - if (entry.getMonsterId() > 0) { - MonsterData data = GameData.getMonsterDataMap().get(entry.getMonsterId()); - if (data == null) continue; + // Check if spawn entry is monster or gadget + if (entry.getMonsterId() > 0) { + MonsterData data = GameData.getMonsterDataMap().get(entry.getMonsterId()); + if (data == null) continue; - int level = this.getEntityLevel(entry.getLevel(), worldLevelOverride); + int level = this.getEntityLevel(entry.getLevel(), worldLevelOverride); - EntityMonster monster = new EntityMonster(this, data, entry.getPos(), level); - monster.getRotation().set(entry.getRot()); - monster.setGroupId(entry.getGroup().getGroupId()); - monster.setPoseId(entry.getPoseId()); - monster.setConfigId(entry.getConfigId()); - monster.setSpawnEntry(entry); + EntityMonster monster = new EntityMonster(this, data, entry.getPos(), level); + monster.getRotation().set(entry.getRot()); + monster.setGroupId(entry.getGroup().getGroupId()); + monster.setPoseId(entry.getPoseId()); + monster.setConfigId(entry.getConfigId()); + monster.setSpawnEntry(entry); - entity = monster; - } else if (entry.getGadgetId() > 0) { - EntityGadget gadget = new EntityGadget(this, entry.getGadgetId(), entry.getPos(), entry.getRot()); - gadget.setGroupId(entry.getGroup().getGroupId()); - gadget.setConfigId(entry.getConfigId()); - gadget.setSpawnEntry(entry); - int state = entry.getGadgetState(); - if(state>0) { + entity = monster; + } else if (entry.getGadgetId() > 0) { + EntityGadget gadget = new EntityGadget(this, entry.getGadgetId(), entry.getPos(), entry.getRot()); + gadget.setGroupId(entry.getGroup().getGroupId()); + gadget.setConfigId(entry.getConfigId()); + gadget.setSpawnEntry(entry); + int state = entry.getGadgetState(); + if (state>0) { gadget.setState(state); } - gadget.buildContent(); + gadget.buildContent(); - gadget.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, 99999); - gadget.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 99999); - gadget.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, 99999); + gadget.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, 99999); + gadget.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 99999); + gadget.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, 99999); - entity = gadget; - } + entity = gadget; + } - if (entity == null) continue; + if (entity == null) continue; - // Add to scene and spawned list - toAdd.add(entity); + // Add to scene and spawned list + toAdd.add(entity); spawnedEntities.add(entry); - } - } + } + } - for (GameEntity entity : this.getEntities().values()) { - var spawnEntry = entity.getSpawnEntry(); - if (spawnEntry != null && !visible.contains(spawnEntry)) { - toRemove.add(entity); + for (GameEntity entity : this.getEntities().values()) { + var spawnEntry = entity.getSpawnEntry(); + if (spawnEntry != null && !visible.contains(spawnEntry)) { + toRemove.add(entity); spawnedEntities.remove(spawnEntry); - } - } + } + } - if (toAdd.size() > 0) { - toAdd.stream().forEach(this::addEntityDirectly); - this.broadcastPacket(new PacketSceneEntityAppearNotify(toAdd, VisionType.VISION_TYPE_BORN)); - } - if (toRemove.size() > 0) { - toRemove.stream().forEach(this::removeEntityDirectly); - this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE)); - } - } + if (toAdd.size() > 0) { + toAdd.stream().forEach(this::addEntityDirectly); + this.broadcastPacket(new PacketSceneEntityAppearNotify(toAdd, VisionType.VISION_TYPE_BORN)); + } + if (toRemove.size() > 0) { + toRemove.stream().forEach(this::removeEntityDirectly); + this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE)); + } + } - public List getPlayerActiveBlocks(Player player){ - // consider the borders' entities of blocks, so we check if contains by index - return SceneIndexManager.queryNeighbors(getScriptManager().getBlocksIndex(), - player.getPosition().toXZDoubleArray(), Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange); - } - public void checkBlocks() { - Set visible = new HashSet<>(); - for (Player player : this.getPlayers()) { - var blocks = getPlayerActiveBlocks(player); - visible.addAll(blocks); - } + public List getPlayerActiveBlocks(Player player) { + // consider the borders' entities of blocks, so we check if contains by index + return SceneIndexManager.queryNeighbors(getScriptManager().getBlocksIndex(), + player.getPosition().toXZDoubleArray(), Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange); + } + public void checkBlocks() { + Set visible = new HashSet<>(); + for (Player player : this.getPlayers()) { + var blocks = getPlayerActiveBlocks(player); + visible.addAll(blocks); + } - Iterator it = this.getLoadedBlocks().iterator(); - while (it.hasNext()) { - SceneBlock block = it.next(); + Iterator it = this.getLoadedBlocks().iterator(); + while (it.hasNext()) { + SceneBlock block = it.next(); - if (!visible.contains(block)) { - it.remove(); + if (!visible.contains(block)) { + it.remove(); - onUnloadBlock(block); - } - } + onUnloadBlock(block); + } + } - for(var block : visible){ - if (!this.getLoadedBlocks().contains(block)) { - this.onLoadBlock(block, this.getPlayers()); - this.getLoadedBlocks().add(block); - }else{ - // dynamic load the groups for players in a loaded block - var toLoad = this.getPlayers().stream() - .filter(p -> block.contains(p.getPosition())) - .map(p -> playerMeetGroups(p, block)) - .flatMap(Collection::stream) - .toList(); - onLoadGroup(toLoad); - } - } + for (var block : visible) { + if (!this.getLoadedBlocks().contains(block)) { + this.onLoadBlock(block, this.getPlayers()); + this.getLoadedBlocks().add(block); + }else { + // dynamic load the groups for players in a loaded block + var toLoad = this.getPlayers().stream() + .filter(p -> block.contains(p.getPosition())) + .map(p -> playerMeetGroups(p, block)) + .flatMap(Collection::stream) + .toList(); + onLoadGroup(toLoad); + } + } - } - public List playerMeetGroups(Player player, SceneBlock block){ - List sceneGroups = SceneIndexManager.queryNeighbors(block.sceneGroupIndex, player.getPosition().toDoubleArray(), - Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange); + } + public List playerMeetGroups(Player player, SceneBlock block) { + List sceneGroups = SceneIndexManager.queryNeighbors(block.sceneGroupIndex, player.getPosition().toDoubleArray(), + Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange); - List groups = sceneGroups.stream() - .filter(group -> !scriptManager.getLoadedGroupSetPerBlock().get(block.id).contains(group)) - .peek(group -> scriptManager.getLoadedGroupSetPerBlock().get(block.id).add(group)) - .toList(); + List groups = sceneGroups.stream() + .filter(group -> !scriptManager.getLoadedGroupSetPerBlock().get(block.id).contains(group)) + .peek(group -> scriptManager.getLoadedGroupSetPerBlock().get(block.id).add(group)) + .toList(); - if (groups.size() == 0) { - return List.of(); - } + if (groups.size() == 0) { + return List.of(); + } - return groups; - } - public void onLoadBlock(SceneBlock block, List players) { - this.getScriptManager().loadBlockFromScript(block); - scriptManager.getLoadedGroupSetPerBlock().put(block.id , new HashSet<>()); + return groups; + } + public void onLoadBlock(SceneBlock block, List players) { + this.getScriptManager().loadBlockFromScript(block); + scriptManager.getLoadedGroupSetPerBlock().put(block.id , new HashSet<>()); - // the groups form here is not added in current scene - var groups = players.stream() - .filter(player -> block.contains(player.getPosition())) - .map(p -> playerMeetGroups(p, block)) - .flatMap(Collection::stream) - .toList(); + // the groups form here is not added in current scene + var groups = players.stream() + .filter(player -> block.contains(player.getPosition())) + .map(p -> playerMeetGroups(p, block)) + .flatMap(Collection::stream) + .toList(); - onLoadGroup(groups); - Grasscutter.getLogger().info("Scene {} Block {} loaded.", this.getId(), block.id); - } + onLoadGroup(groups); + Grasscutter.getLogger().info("Scene {} Block {} loaded.", this.getId(), block.id); + } - public void onLoadGroup(List groups){ - if(groups == null || groups.isEmpty()){ - return; - } - for (SceneGroup group : groups) { - // We load the script files for the groups here - this.getScriptManager().loadGroupFromScript(group); - } + public void onLoadGroup(List groups) { + if (groups == null || groups.isEmpty()) { + return; + } + for (SceneGroup group : groups) { + // We load the script files for the groups here + this.getScriptManager().loadGroupFromScript(group); + } - // Spawn gadgets AFTER triggers are added - // TODO - var entities = new ArrayList(); - for (SceneGroup group : groups) { - if (group.init_config == null) { - continue; - } + // Spawn gadgets AFTER triggers are added + // TODO + var entities = new ArrayList(); + for (SceneGroup group : groups) { + if (group.init_config == null) { + continue; + } - // Load garbages - List garbageGadgets = group.getGarbageGadgets(); + // Load garbages + List garbageGadgets = group.getGarbageGadgets(); - if (garbageGadgets != null) { - entities.addAll(garbageGadgets.stream().map(g -> scriptManager.createGadget(group.id, group.block_id, g)) - .filter(Objects::nonNull) - .toList()); - } + if (garbageGadgets != null) { + entities.addAll(garbageGadgets.stream().map(g -> scriptManager.createGadget(group.id, group.block_id, g)) + .filter(Objects::nonNull) + .toList()); + } - // Load suites - int suite = group.init_config.suite; + // Load suites + int suite = group.init_config.suite; - if (suite == 0 || group.suites == null || group.suites.size() == 0) { - continue; - } + if (suite == 0 || group.suites == null || group.suites.size() == 0) { + continue; + } - // just load the 'init' suite, avoid spawn the suite added by AddExtraGroupSuite etc. - var suiteData = group.getSuiteByIndex(suite); - suiteData.sceneTriggers.forEach(getScriptManager()::registerTrigger); + // just load the 'init' suite, avoid spawn the suite added by AddExtraGroupSuite etc. + var suiteData = group.getSuiteByIndex(suite); + suiteData.sceneTriggers.forEach(getScriptManager()::registerTrigger); - entities.addAll(scriptManager.getGadgetsInGroupSuite(group, suiteData)); - entities.addAll(scriptManager.getMonstersInGroupSuite(group, suiteData)); + entities.addAll(scriptManager.getGadgetsInGroupSuite(group, suiteData)); + entities.addAll(scriptManager.getMonstersInGroupSuite(group, suiteData)); scriptManager.registerRegionInGroupSuite(group, suiteData); - } + } - scriptManager.meetEntities(entities); - //scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null); - //groups.forEach(g -> scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null)); - Grasscutter.getLogger().info("Scene {} loaded {} group(s)", this.getId(), groups.size()); - } + scriptManager.meetEntities(entities); + //scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null); + //groups.forEach(g -> scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null)); + Grasscutter.getLogger().info("Scene {} loaded {} group(s)", this.getId(), groups.size()); + } - public void onUnloadBlock(SceneBlock block) { - List toRemove = this.getEntities().values().stream() - .filter(e -> e.getBlockId() == block.id).toList(); + public void onUnloadBlock(SceneBlock block) { + List toRemove = this.getEntities().values().stream() + .filter(e -> e.getBlockId() == block.id).toList(); - if (toRemove.size() > 0) { - toRemove.forEach(this::removeEntityDirectly); - this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE)); - } + if (toRemove.size() > 0) { + toRemove.forEach(this::removeEntityDirectly); + this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE)); + } - for (SceneGroup group : block.groups.values()) { - if(group.triggers != null){ - group.triggers.values().forEach(getScriptManager()::deregisterTrigger); - } - if(group.regions != null){ + for (SceneGroup group : block.groups.values()) { + if (group.triggers != null) { + group.triggers.values().forEach(getScriptManager()::deregisterTrigger); + } + if (group.regions != null) { group.regions.values().forEach(getScriptManager()::deregisterRegion); - } - } - scriptManager.getLoadedGroupSetPerBlock().remove(block.id); - Grasscutter.getLogger().info("Scene {} Block {} is unloaded.", this.getId(), block.id); - } - // Gadgets + } + } + scriptManager.getLoadedGroupSetPerBlock().remove(block.id); + Grasscutter.getLogger().info("Scene {} Block {} is unloaded.", this.getId(), block.id); + } + // Gadgets - public void onPlayerCreateGadget(EntityClientGadget gadget) { - // Directly add - this.addEntityDirectly(gadget); + public void onPlayerCreateGadget(EntityClientGadget gadget) { + // Directly add + this.addEntityDirectly(gadget); - // Add to owner's gadget list - gadget.getOwner().getTeamManager().getGadgets().add(gadget); + // Add to owner's gadget list + gadget.getOwner().getTeamManager().getGadgets().add(gadget); - // Optimization - if (this.getPlayerCount() == 1 && this.getPlayers().get(0) == gadget.getOwner()) { - return; - } + // Optimization + if (this.getPlayerCount() == 1 && this.getPlayers().get(0) == gadget.getOwner()) { + return; + } - this.broadcastPacketToOthers(gadget.getOwner(), new PacketSceneEntityAppearNotify(gadget)); - } + this.broadcastPacketToOthers(gadget.getOwner(), new PacketSceneEntityAppearNotify(gadget)); + } - public void onPlayerDestroyGadget(int entityId) { - GameEntity entity = getEntities().get(entityId); + public void onPlayerDestroyGadget(int entityId) { + GameEntity entity = getEntities().get(entityId); - if (entity == null || !(entity instanceof EntityClientGadget)) { - return; - } + if (entity == null || !(entity instanceof EntityClientGadget)) { + return; + } - // Get and remove entity - EntityClientGadget gadget = (EntityClientGadget) entity; - this.removeEntityDirectly(gadget); + // Get and remove entity + EntityClientGadget gadget = (EntityClientGadget) entity; + this.removeEntityDirectly(gadget); - // Remove from owner's gadget list - gadget.getOwner().getTeamManager().getGadgets().remove(gadget); + // Remove from owner's gadget list + gadget.getOwner().getTeamManager().getGadgets().remove(gadget); - // Optimization - if (this.getPlayerCount() == 1 && this.getPlayers().get(0) == gadget.getOwner()) { - return; - } + // Optimization + if (this.getPlayerCount() == 1 && this.getPlayers().get(0) == gadget.getOwner()) { + return; + } - this.broadcastPacketToOthers(gadget.getOwner(), new PacketSceneEntityDisappearNotify(gadget, VisionType.VISION_TYPE_DIE)); - } + this.broadcastPacketToOthers(gadget.getOwner(), new PacketSceneEntityDisappearNotify(gadget, VisionType.VISION_TYPE_DIE)); + } - // Broadcasting + // Broadcasting - 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 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 broadcastPacketToOthers(Player excludedPlayer, BasePacket packet) { - // Optimization - if (this.getPlayerCount() == 1 && this.getPlayers().get(0) == excludedPlayer) { - return; - } - // Send to all players - might have to check if player has been sent data packets - for (Player player : this.getPlayers()) { - if (player == excludedPlayer) { - continue; - } - // Send - player.getSession().send(packet); - } - } + public void broadcastPacketToOthers(Player excludedPlayer, BasePacket packet) { + // Optimization + if (this.getPlayerCount() == 1 && this.getPlayers().get(0) == excludedPlayer) { + return; + } + // Send to all players - might have to check if player has been sent data packets + for (Player player : this.getPlayers()) { + if (player == excludedPlayer) { + continue; + } + // Send + player.getSession().send(packet); + } + } - public void addItemEntity(int itemId, int amount, GameEntity bornForm){ - ItemData itemData = GameData.getItemDataMap().get(itemId); - if (itemData == null) { - return; - } - if (itemData.isEquip()) { - float range = (3f + (.1f * amount)); - for (int i = 0; i < amount; i++) { - Position pos = bornForm.getPosition().clone().addX((float) (Math.random() * range) - (range / 2)).addZ((float) (Math.random() * range) - (range / 2)).addZ(.9f); - EntityItem entity = new EntityItem(this, null, itemData, pos, 1); - addEntity(entity); - } - } else { - EntityItem entity = new EntityItem(this, null, itemData, bornForm.getPosition().clone().addZ(.9f), amount); - addEntity(entity); - } - } - public void loadNpcForPlayerEnter(Player player){ + public void addItemEntity(int itemId, int amount, GameEntity bornForm) { + ItemData itemData = GameData.getItemDataMap().get(itemId); + if (itemData == null) { + return; + } + if (itemData.isEquip()) { + float range = (3f + (.1f * amount)); + for (int i = 0; i < amount; i++) { + Position pos = bornForm.getPosition().clone().addX((float) (Math.random() * range) - (range / 2)).addZ((float) (Math.random() * range) - (range / 2)).addZ(.9f); + EntityItem entity = new EntityItem(this, null, itemData, pos, 1); + addEntity(entity); + } + } else { + EntityItem entity = new EntityItem(this, null, itemData, bornForm.getPosition().clone().addZ(.9f), amount); + addEntity(entity); + } + } + public void loadNpcForPlayerEnter(Player player) { this.npcBornEntrySet.addAll(loadNpcForPlayer(player)); } - private List loadNpcForPlayer(Player player){ - var pos = player.getPosition(); - var data = GameData.getSceneNpcBornData().get(getId()); - if(data == null){ - return List.of(); - } + private List loadNpcForPlayer(Player player) { + var pos = player.getPosition(); + var data = GameData.getSceneNpcBornData().get(getId()); + if (data == null) { + return List.of(); + } - var npcList = SceneIndexManager.queryNeighbors(data.getIndex(), pos.toDoubleArray(), - Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange); + var npcList = SceneIndexManager.queryNeighbors(data.getIndex(), pos.toDoubleArray(), + Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange); - var sceneNpcBornEntries = npcList.stream() + var sceneNpcBornEntries = npcList.stream() .filter(i -> !this.npcBornEntrySet.contains(i)) .toList(); - if(sceneNpcBornEntries.size() > 0){ - this.broadcastPacket(new PacketGroupSuiteNotify(sceneNpcBornEntries)); + if (sceneNpcBornEntries.size() > 0) { + this.broadcastPacket(new PacketGroupSuiteNotify(sceneNpcBornEntries)); Grasscutter.getLogger().debug("Loaded Npc Group Suite {}", sceneNpcBornEntries); - } + } return npcList; - } + } public void loadGroupForQuest(List sceneGroupSuite) { - if(!scriptManager.isInit()){ + if (!scriptManager.isInit()) { return; } sceneGroupSuite.forEach(i -> { var group = scriptManager.getGroupById(i.getGroup()); - if(group == null){ + if (group == null) { return; } var suite = group.getSuiteByIndex(i.getSuite()); - if(suite == null){ + if (suite == null) { return; } scriptManager.addGroupSuite(group, suite); diff --git a/src/main/java/emu/grasscutter/game/world/SpawnDataEntry.java b/src/main/java/emu/grasscutter/game/world/SpawnDataEntry.java index 456197c7c..f4d5efd4f 100644 --- a/src/main/java/emu/grasscutter/game/world/SpawnDataEntry.java +++ b/src/main/java/emu/grasscutter/game/world/SpawnDataEntry.java @@ -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 spawns; + public static class SpawnGroupEntry { + private int sceneId; + private int groupId; + private int blockId; + private List 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 getSpawns() { - return spawns; - } - } + public List 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 } } diff --git a/src/main/java/emu/grasscutter/game/world/World.java b/src/main/java/emu/grasscutter/game/world/World.java index d4bdbfc83..456a07108 100644 --- a/src/main/java/emu/grasscutter/game/world/World.java +++ b/src/main/java/emu/grasscutter/game/world/World.java @@ -31,298 +31,298 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; public class World implements Iterable { - private final GameServer server; - private final Player owner; - private final List players; - private final Int2ObjectMap 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 players; + private final Int2ObjectMap 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 getPlayers() { - return players; - } - - public Int2ObjectMap 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 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 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 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 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 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 iterator() { + return getPlayers().iterator(); + } } diff --git a/src/main/java/emu/grasscutter/game/world/WorldDataSystem.java b/src/main/java/emu/grasscutter/game/world/WorldDataSystem.java index 5ec06de79..9251057aa 100644 --- a/src/main/java/emu/grasscutter/game/world/WorldDataSystem.java +++ b/src/main/java/emu/grasscutter/game/world/WorldDataSystem.java @@ -30,7 +30,7 @@ public class WorldDataSystem extends BaseGameSystem { private final Map chestInteractHandlerMap; // chestType-Handler private final Map sceneInvestigationGroupMap; // - 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 = 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 getInvestigationMonstersByCityId(Player player, int cityId){ + public List 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(); } diff --git a/src/main/java/emu/grasscutter/net/packet/BasePacket.java b/src/main/java/emu/grasscutter/net/packet/BasePacket.java index 12009ffd3..ff40b8ee0 100644 --- a/src/main/java/emu/grasscutter/net/packet/BasePacket.java +++ b/src/main/java/emu/grasscutter/net/packet/BasePacket.java @@ -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(); + } } } diff --git a/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java b/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java index 3d79a196d..6ae8f8d95 100644 --- a/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java +++ b/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java @@ -1854,4 +1854,4 @@ public class PacketOpcodes { public static final int WorldRoutineTypeCloseNotify = 3502; public static final int WorldRoutineTypeRefreshNotify = 3525; -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/net/packet/PacketOpcodesUtils.java b/src/main/java/emu/grasscutter/net/packet/PacketOpcodesUtils.java index fc6c5dba0..ac915b2a4 100644 --- a/src/main/java/emu/grasscutter/net/packet/PacketOpcodesUtils.java +++ b/src/main/java/emu/grasscutter/net/packet/PacketOpcodesUtils.java @@ -22,7 +22,7 @@ public class PacketOpcodesUtils { PacketOpcodes.WindSeedClientNotify, PacketOpcodes.PlayerLuaShellNotify ); - + public static final Set LOOP_PACKETS = Set.of( PacketOpcodes.PingReq, PacketOpcodes.PingRsp, @@ -30,39 +30,39 @@ public class PacketOpcodesUtils { PacketOpcodes.UnionCmdNotify, PacketOpcodes.QueryPathReq ); - - static { - opcodeMap = new Int2ObjectOpenHashMap(); - Field[] fields = PacketOpcodes.class.getFields(); + static { + opcodeMap = new Int2ObjectOpenHashMap(); - 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 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 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(); + } + } } diff --git a/src/main/java/emu/grasscutter/plugin/Plugin.java b/src/main/java/emu/grasscutter/plugin/Plugin.java index d14ad5c51..fdc27514a 100644 --- a/src/main/java/emu/grasscutter/plugin/Plugin.java +++ b/src/main/java/emu/grasscutter/plugin/Plugin.java @@ -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. */ diff --git a/src/main/java/emu/grasscutter/plugin/PluginManager.java b/src/main/java/emu/grasscutter/plugin/PluginManager.java index 6de30da38..f55e0a6fe 100644 --- a/src/main/java/emu/grasscutter/plugin/PluginManager.java +++ b/src/main/java/emu/grasscutter/plugin/PluginManager.java @@ -72,7 +72,7 @@ public final class PluginManager { List 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. } diff --git a/src/main/java/emu/grasscutter/plugin/api/PlayerHook.java b/src/main/java/emu/grasscutter/plugin/api/PlayerHook.java index 5410241a3..0b91ede33 100644 --- a/src/main/java/emu/grasscutter/plugin/api/PlayerHook.java +++ b/src/main/java/emu/grasscutter/plugin/api/PlayerHook.java @@ -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(); } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneBlock.java b/src/main/java/emu/grasscutter/scripts/data/SceneBlock.java index 48903f68b..d8f969e04 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneBlock.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneBlock.java @@ -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 groups; - public RTree sceneGroupIndex; + public int sceneId; + public Map groups; + public RTree 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()); - } -} \ No newline at end of file + public Rectangle toRectangle() { + return Rectangle.create(this.min.toXZDoubleArray(), this.max.toXZDoubleArray()); + } +} diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java index 507f80c34..163ce94c8 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java @@ -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 monsters; // - public Map gadgets; // - public Map triggers; + public Map monsters; // + public Map gadgets; // + public Map triggers; public Map regions; - public List suites; - public List variables; + public List suites; + public List 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 getGarbageGadgets() { - return this.garbages == null ? null : this.garbages.gadgets; - } + public List 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 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 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(); + } } diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneMeta.java b/src/main/java/emu/grasscutter/scripts/data/SceneMeta.java index efb6744fe..5d8a14dfd 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneMeta.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneMeta.java @@ -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())); diff --git a/src/main/java/emu/grasscutter/server/game/BaseGameSystem.java b/src/main/java/emu/grasscutter/server/game/BaseGameSystem.java index 536e90575..3fd3db05c 100644 --- a/src/main/java/emu/grasscutter/server/game/BaseGameSystem.java +++ b/src/main/java/emu/grasscutter/server/game/BaseGameSystem.java @@ -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; } diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index f115b2c3b..7eb25a67c 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -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 players; private final Set 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 playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getId().equals(accountId)).findFirst(); - return playerOpt.orElse(null); - } + public Player getPlayerByAccountId(String accountId) { + Optional 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 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 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 list = new ArrayList<>(this.getPlayers().size()); - list.addAll(this.getPlayers().values()); + // Kick and save all players + List 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(); + } + } } diff --git a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java index 7a8c4b8b1..94fe3e232 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java +++ b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java @@ -18,84 +18,84 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @SuppressWarnings("unchecked") public class GameServerPacketHandler { - private final Int2ObjectMap handlers; + private final Int2ObjectMap handlers; - public GameServerPacketHandler(Class handlerClass) { - this.handlers = new Int2ObjectOpenHashMap<>(); + public GameServerPacketHandler(Class handlerClass) { + this.handlers = new Int2ObjectOpenHashMap<>(); - this.registerHandlers(handlerClass); - } + this.registerHandlers(handlerClass); + } - public void registerPacketHandler(Class handlerClass) { - try { - Opcodes opcode = handlerClass.getAnnotation(Opcodes.class); + public void registerPacketHandler(Class 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 handlerClass) { - Reflections reflections = new Reflections("emu.grasscutter.server.packet"); - Set handlerClasses = reflections.getSubTypesOf(handlerClass); + public void registerHandlers(Class handlerClass) { + Reflections reflections = new Reflections("emu.grasscutter.server.packet"); + Set handlerClasses = reflections.getSubTypesOf(handlerClass); - for (Object obj : handlerClasses) { - this.registerPacketHandler((Class) obj); - } + for (Object obj : handlerClasses) { + this.registerPacketHandler((Class) 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)); + } + } } diff --git a/src/main/java/emu/grasscutter/server/game/GameSession.java b/src/main/java/emu/grasscutter/server/game/GameSession.java index 5d184853c..9d43b0324 100644 --- a/src/main/java/emu/grasscutter/server/game/GameSession.java +++ b/src/main/java/emu/grasscutter/server/game/GameSession.java @@ -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 + } } diff --git a/src/main/java/emu/grasscutter/server/http/HttpServer.java b/src/main/java/emu/grasscutter/server/http/HttpServer.java index a4a0e704a..7058493de 100644 --- a/src/main/java/emu/grasscutter/server/http/HttpServer.java +++ b/src/main/java/emu/grasscutter/server/http/HttpServer.java @@ -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 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(""" @@ -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(""" - + diff --git a/src/main/java/emu/grasscutter/server/http/dispatch/RegionHandler.java b/src/main/java/emu/grasscutter/server/http/dispatch/RegionHandler.java index e9af92cb3..790191540 100644 --- a/src/main/java/emu/grasscutter/server/http/dispatch/RegionHandler.java +++ b/src/main/java/emu/grasscutter/server/http/dispatch/RegionHandler.java @@ -60,7 +60,7 @@ public final class RegionHandler implements Router { List 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; } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/server/http/handlers/AnnouncementsHandler.java b/src/main/java/emu/grasscutter/server/http/handlers/AnnouncementsHandler.java index f2c04be7e..69ebc6b96 100644 --- a/src/main/java/emu/grasscutter/server/http/handlers/AnnouncementsHandler.java +++ b/src/main/java/emu/grasscutter/server/http/handlers/AnnouncementsHandler.java @@ -39,14 +39,14 @@ public final class AnnouncementsHandler implements Router { express.get("/hk4e/announcement/*", AnnouncementsHandler::getPageResources); } - + private static void getAnnouncement(Request request, Response response) { String data = ""; if (Objects.equals(request.baseUrl(), "/common/hk4e_global/announcement/api/getAnnContent")) { try { data = FileUtils.readToString(DataLoader.load("GameAnnouncement.json")); } catch (Exception e) { - if(e.getClass() == IOException.class) { + if (e.getClass() == IOException.class) { Grasscutter.getLogger().info("Unable to read file 'GameAnnouncementList.json'. \n" + e); } } @@ -54,7 +54,7 @@ public final class AnnouncementsHandler implements Router { try { data = FileUtils.readToString(DataLoader.load("GameAnnouncementList.json")); } catch (Exception e) { - if(e.getClass() == IOException.class) { + if (e.getClass() == IOException.class) { Grasscutter.getLogger().info("Unable to read file 'GameAnnouncementList.json'. \n" + e); } } @@ -76,9 +76,9 @@ public final class AnnouncementsHandler implements Router { .replace("{{SYSTEM_TIME}}", String.valueOf(System.currentTimeMillis())); response.send("{\"retcode\":0,\"message\":\"OK\",\"data\": " + data + "}"); } - + private static void getPageResources(Request request, Response response) { - try(InputStream filestream = DataLoader.load(request.path())) { + try (InputStream filestream = DataLoader.load(request.path())) { String possibleFilename = Utils.toFilePath(DATA(request.path())); MediaType fromExtension = MediaType.getByExtension(possibleFilename.substring(possibleFilename.lastIndexOf(".") + 1)); diff --git a/src/main/java/emu/grasscutter/server/http/handlers/GachaHandler.java b/src/main/java/emu/grasscutter/server/http/handlers/GachaHandler.java index 7c42555ae..38548b1d6 100644 --- a/src/main/java/emu/grasscutter/server/http/handlers/GachaHandler.java +++ b/src/main/java/emu/grasscutter/server/http/handlers/GachaHandler.java @@ -30,14 +30,14 @@ import static emu.grasscutter.utils.Language.translate; */ public final class GachaHandler implements Router { public static final String gachaMappings = DATA(Utils.toFilePath("gacha/mappings.js")); - + @Override public void applyRoutes(Express express, Javalin handle) { express.get("/gacha", GachaHandler::gachaRecords); express.get("/gacha/details", GachaHandler::gachaDetails); - + express.useStaticFallback("/gacha/mappings", gachaMappings, Location.EXTERNAL); } - + private static void gachaRecords(Request request, Response response) { File recordsTemplate = new File(Utils.toFilePath(DATA("gacha/records.html"))); if (!recordsTemplate.exists()) { @@ -48,7 +48,7 @@ public final class GachaHandler implements Router { String sessionKey = request.query("s"); Account account = DatabaseHelper.getAccountBySessionKey(sessionKey); - if(account == null) { + if (account == null) { response.status(403).send("Requested account was not found"); return; } @@ -59,9 +59,9 @@ public final class GachaHandler implements Router { } int page = 0, gachaType = 0; - if(request.query("p") != null) + if (request.query("p") != null) page = Integer.parseInt(request.query("p")); - if(request.query("gachaType") != null) + if (request.query("gachaType") != null) gachaType = Integer.parseInt(request.query("gachaType")); String records = DatabaseHelper.getGachaRecords(player.getUid(), page, gachaType).toString(); @@ -76,7 +76,7 @@ public final class GachaHandler implements Router { .replace("{{LANGUAGE}}", Utils.getLanguageCode(account.getLocale())); response.send(template); } - + private static void gachaDetails(Request request, Response response) { File detailsTemplate = new File(Utils.toFilePath(DATA("gacha/details.html"))); if (!detailsTemplate.exists()) { @@ -87,7 +87,7 @@ public final class GachaHandler implements Router { String sessionKey = request.query("s"); Account account = DatabaseHelper.getAccountBySessionKey(sessionKey); - if(account == null) { + if (account == null) { response.status(403).send("Requested account was not found"); return; } diff --git a/src/main/java/emu/grasscutter/server/http/handlers/GenericHandler.java b/src/main/java/emu/grasscutter/server/http/handlers/GenericHandler.java index 664bbed60..4c32d3d2a 100644 --- a/src/main/java/emu/grasscutter/server/http/handlers/GenericHandler.java +++ b/src/main/java/emu/grasscutter/server/http/handlers/GenericHandler.java @@ -25,7 +25,7 @@ public final class GenericHandler implements Router { // api-account-os.hoyoverse.com express.post("/account/risky/api/check", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"none\",\"action\":\"ACTION_NONE\",\"geetest\":null}}")); - + // sdk-os-static.hoyoverse.com express.get("/combo/box/api/config/sdk/combo", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"disable_email_bind_skip\":\"false\",\"email_bind_remind_interval\":\"7\",\"email_bind_remind\":\"true\"}}}")); // hk4e-sdk-os-static.hoyoverse.com @@ -35,7 +35,7 @@ public final class GenericHandler implements Router { // Test api? // abtest-api-data-sg.hoyoverse.com express.post("/data_abtest_api/config/experiment/list", new HttpJsonResponse("{\"retcode\":0,\"success\":true,\"message\":\"\",\"data\":[{\"code\":1000,\"type\":2,\"config_id\":\"14\",\"period_id\":\"6036_99\",\"version\":\"1\",\"configs\":{\"cardType\":\"old\"}}]}")); - + // log-upload-os.mihoyo.com express.all("/log/sdk/upload", new HttpJsonResponse("{\"code\":0}")); express.all("/sdk/upload", new HttpJsonResponse("{\"code\":0}")); @@ -45,10 +45,10 @@ public final class GenericHandler implements Router { // webstatic-sea.hoyoverse.com express.get("/admin/mi18n/plat_oversea/*", new WebStaticVersionResponse()); - + express.get("/status/server", GenericHandler::serverStatus); } - + private static void serverStatus(Request request, Response response) { int playerCount = Grasscutter.getGameServer().getPlayers().size(); int maxPlayer = ACCOUNT.maxPlayer; diff --git a/src/main/java/emu/grasscutter/server/http/objects/HttpJsonResponse.java b/src/main/java/emu/grasscutter/server/http/objects/HttpJsonResponse.java index 147259161..327445f22 100644 --- a/src/main/java/emu/grasscutter/server/http/objects/HttpJsonResponse.java +++ b/src/main/java/emu/grasscutter/server/http/objects/HttpJsonResponse.java @@ -14,30 +14,30 @@ import static emu.grasscutter.config.Configuration.*; import static emu.grasscutter.utils.Language.translate; public final class HttpJsonResponse implements HttpContextHandler { - private final String response; - private final String[] missingRoutes = { // TODO: When http requests for theses routes are found please remove it from this list and update the route request type in the DispatchServer - "/common/hk4e_global/announcement/api/getAlertPic", - "/common/hk4e_global/announcement/api/getAlertAnn", - "/common/hk4e_global/announcement/api/getAnnList", - "/common/hk4e_global/announcement/api/getAnnContent", - "/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier", - "/log/sdk/upload", - "/sdk/upload", - "/perf/config/verify", - "/log", - "/crash/dataUpload" - }; - - public HttpJsonResponse(String response) { - this.response = response; - } + private final String response; + private final String[] missingRoutes = { // TODO: When http requests for theses routes are found please remove it from this list and update the route request type in the DispatchServer + "/common/hk4e_global/announcement/api/getAlertPic", + "/common/hk4e_global/announcement/api/getAlertAnn", + "/common/hk4e_global/announcement/api/getAnnList", + "/common/hk4e_global/announcement/api/getAnnContent", + "/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier", + "/log/sdk/upload", + "/sdk/upload", + "/perf/config/verify", + "/log", + "/crash/dataUpload" + }; - @Override - public void handle(Request req, Response res) throws IOException { - // Checking for ALL here isn't required as when ALL is enabled enableDevLogging() gets enabled - if(DISPATCH_INFO.logRequests == ServerDebugMode.MISSING && Arrays.stream(missingRoutes).anyMatch(x -> Objects.equals(x, req.baseUrl()))) { - Grasscutter.getLogger().info(translate("messages.dispatch.request", req.ip(), req.method(), req.baseUrl()) + (DISPATCH_INFO.logRequests == ServerDebugMode.MISSING ? "(MISSING)" : "")); - } - res.send(response); - } + public HttpJsonResponse(String response) { + this.response = response; + } + + @Override + public void handle(Request req, Response res) throws IOException { + // Checking for ALL here isn't required as when ALL is enabled enableDevLogging() gets enabled + if (DISPATCH_INFO.logRequests == ServerDebugMode.MISSING && Arrays.stream(missingRoutes).anyMatch(x -> Objects.equals(x, req.baseUrl()))) { + Grasscutter.getLogger().info(translate("messages.dispatch.request", req.ip(), req.method(), req.baseUrl()) + (DISPATCH_INFO.logRequests == ServerDebugMode.MISSING ? "(MISSING)" : "")); + } + res.send(response); + } } diff --git a/src/main/java/emu/grasscutter/server/http/objects/WebStaticVersionResponse.java b/src/main/java/emu/grasscutter/server/http/objects/WebStaticVersionResponse.java index 111451bf8..ec0f973fb 100644 --- a/src/main/java/emu/grasscutter/server/http/objects/WebStaticVersionResponse.java +++ b/src/main/java/emu/grasscutter/server/http/objects/WebStaticVersionResponse.java @@ -27,13 +27,13 @@ public class WebStaticVersionResponse implements HttpContextHandler { } private static void getPageResources(String path, Response response) { - try(InputStream filestream = FileUtils.readResourceAsStream(path)) { + try (InputStream filestream = FileUtils.readResourceAsStream(path)) { MediaType fromExtension = MediaType.getByExtension(path.substring(path.lastIndexOf(".") + 1)); response.type((fromExtension != null) ? fromExtension.getMIME() : "application/octet-stream"); response.send(filestream.readAllBytes()); } catch (Exception e) { - if(DISPATCH_INFO.logRequests == Grasscutter.ServerDebugMode.MISSING) { + if (DISPATCH_INFO.logRequests == Grasscutter.ServerDebugMode.MISSING) { Grasscutter.getLogger().warn("Webstatic File Missing: " + path); } response.status(404); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarExpeditionGetRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarExpeditionGetRewardReq.java index aaacd7954..835017d59 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarExpeditionGetRewardReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarExpeditionGetRewardReq.java @@ -36,11 +36,11 @@ public class HandlerAvatarExpeditionGetRewardReq extends PacketHandler { if (session.getServer().getExpeditionSystem().getExpeditionRewardDataList().containsKey(expInfo.getExpId())) { for (ExpeditionRewardDataList RewardDataList : session.getServer().getExpeditionSystem().getExpeditionRewardDataList().get(expInfo.getExpId())) { - if(RewardDataList.getHourTime() == expInfo.getHourTime()){ - if(!RewardDataList.getExpeditionRewardData().isEmpty()){ + if (RewardDataList.getHourTime() == expInfo.getHourTime()) { + if (!RewardDataList.getExpeditionRewardData().isEmpty()) { for (ExpeditionRewardData RewardData :RewardDataList.getExpeditionRewardData()) { int num = RewardData.getMinCount(); - if(RewardData.getMinCount() != RewardData.getMaxCount()){ + if (RewardData.getMinCount() != RewardData.getMaxCount()) { num = Utils.randomRange(RewardData.getMinCount(), RewardData.getMaxCount()); } items.add(new GameItem(RewardData.getItemId(), num)); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarPromoteReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarPromoteReq.java index 5914f98c9..6b2540850 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarPromoteReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarPromoteReq.java @@ -8,13 +8,13 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.AvatarPromoteReq) public class HandlerAvatarPromoteReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - AvatarPromoteReq req = AvatarPromoteReq.parseFrom(payload); - - // Ascend avatar - session.getServer().getInventorySystem().promoteAvatar(session.getPlayer(), req.getGuid()); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + AvatarPromoteReq req = AvatarPromoteReq.parseFrom(payload); + + // Ascend avatar + session.getServer().getInventorySystem().promoteAvatar(session.getPlayer(), req.getGuid()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarSkillUpgradeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarSkillUpgradeReq.java index 188e754fe..a371a8e83 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarSkillUpgradeReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarSkillUpgradeReq.java @@ -8,13 +8,13 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.AvatarSkillUpgradeReq) public class HandlerAvatarSkillUpgradeReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - AvatarSkillUpgradeReq req = AvatarSkillUpgradeReq.parseFrom(payload); - - // Level up avatar talent - session.getServer().getInventorySystem().upgradeAvatarSkill(session.getPlayer(), req.getAvatarGuid(), req.getAvatarSkillId()); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + AvatarSkillUpgradeReq req = AvatarSkillUpgradeReq.parseFrom(payload); + + // Level up avatar talent + session.getServer().getInventorySystem().upgradeAvatarSkill(session.getPlayer(), req.getAvatarGuid(), req.getAvatarSkillId()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarUpgradeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarUpgradeReq.java index 93fa0a7a3..e13cec34c 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarUpgradeReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarUpgradeReq.java @@ -8,18 +8,18 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.AvatarUpgradeReq) public class HandlerAvatarUpgradeReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - AvatarUpgradeReq req = AvatarUpgradeReq.parseFrom(payload); - - // Level up avatar - session.getServer().getInventorySystem().upgradeAvatar( - session.getPlayer(), - req.getAvatarGuid(), - req.getItemId(), - req.getCount() - ); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + AvatarUpgradeReq req = AvatarUpgradeReq.parseFrom(payload); + + // Level up avatar + session.getServer().getInventorySystem().upgradeAvatar( + session.getPlayer(), + req.getAvatarGuid(), + req.getItemId(), + req.getCount() + ); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCalcWeaponUpgradeReturnItemsReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCalcWeaponUpgradeReturnItemsReq.java index cc7a06e7b..3d26b5432 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCalcWeaponUpgradeReturnItemsReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCalcWeaponUpgradeReturnItemsReq.java @@ -12,23 +12,23 @@ import emu.grasscutter.server.packet.send.PacketCalcWeaponUpgradeReturnItemsRsp; @Opcodes(PacketOpcodes.CalcWeaponUpgradeReturnItemsReq) public class HandlerCalcWeaponUpgradeReturnItemsReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - CalcWeaponUpgradeReturnItemsReq req = CalcWeaponUpgradeReturnItemsReq.parseFrom(payload); - - List returnOres = session.getServer().getInventorySystem().calcWeaponUpgradeReturnItems( - session.getPlayer(), - req.getTargetWeaponGuid(), - req.getFoodWeaponGuidListList(), - req.getItemParamListList() - ); - - if (returnOres != null) { - session.send(new PacketCalcWeaponUpgradeReturnItemsRsp(req.getTargetWeaponGuid(), returnOres)); - } else { - session.send(new PacketCalcWeaponUpgradeReturnItemsRsp()); - } - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + CalcWeaponUpgradeReturnItemsReq req = CalcWeaponUpgradeReturnItemsReq.parseFrom(payload); + + List returnOres = session.getServer().getInventorySystem().calcWeaponUpgradeReturnItems( + session.getPlayer(), + req.getTargetWeaponGuid(), + req.getFoodWeaponGuidListList(), + req.getItemParamListList() + ); + + if (returnOres != null) { + session.send(new PacketCalcWeaponUpgradeReturnItemsRsp(req.getTargetWeaponGuid(), returnOres)); + } else { + session.send(new PacketCalcWeaponUpgradeReturnItemsRsp()); + } + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombineReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombineReq.java index b8d80eb3d..cbcd1f038 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombineReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombineReq.java @@ -24,7 +24,7 @@ public class HandlerCombineReq extends PacketHandler { var result = session.getServer().getCombineSystem() .combineItem(session.getPlayer(), req.getCombineId(), req.getCombineCount()); - if(result == null){ + if (result == null) { return; } @@ -36,7 +36,7 @@ public class HandlerCombineReq extends PacketHandler { toItemParamList(result.getBack()))); } - private List toItemParamList(List list){ + private List toItemParamList(List list) { return list.stream() .map(item -> ItemParamOuterClass.ItemParam.newBuilder() .setItemId(item.getId()) diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDestroyMaterialReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDestroyMaterialReq.java index 35626e97a..9a5e63397 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDestroyMaterialReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDestroyMaterialReq.java @@ -8,11 +8,11 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.DestroyMaterialReq) public class HandlerDestroyMaterialReq extends PacketHandler { - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - DestroyMaterialReq req = DestroyMaterialReq.parseFrom(payload); - - // Delete items - session.getServer().getInventorySystem().destroyMaterial(session.getPlayer(), req.getMaterialListList()); - } + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + DestroyMaterialReq req = DestroyMaterialReq.parseFrom(payload); + + // Delete items + session.getServer().getInventorySystem().destroyMaterial(session.getPlayer(), req.getMaterialListList()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDoGachaReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDoGachaReq.java index 8a69f3edc..e8ad28e10 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDoGachaReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDoGachaReq.java @@ -8,10 +8,10 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.DoGachaReq) public class HandlerDoGachaReq extends PacketHandler { - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - DoGachaReq req = DoGachaReq.parseFrom(payload); - - session.getServer().getGachaSystem().doPulls(session.getPlayer(), req.getGachaScheduleId(), req.getGachaTimes()); - } + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + DoGachaReq req = DoGachaReq.parseFrom(payload); + + session.getServer().getGachaSystem().doPulls(session.getPlayer(), req.getGachaScheduleId(), req.getGachaTimes()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java index 006f3c73c..34ad24be7 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java @@ -8,12 +8,12 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.DungeonEntryInfoReq) public class HandlerDungeonEntryInfoReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - DungeonEntryInfoReq req = DungeonEntryInfoReq.parseFrom(payload); - - session.getServer().getDungeonSystem().getEntryInfo(session.getPlayer(), req.getPointId()); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + DungeonEntryInfoReq req = DungeonEntryInfoReq.parseFrom(payload); + + session.getServer().getDungeonSystem().getEntryInfo(session.getPlayer(), req.getPointId()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetGachaInfoReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetGachaInfoReq.java index afa4c9612..085952b93 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetGachaInfoReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetGachaInfoReq.java @@ -11,10 +11,10 @@ import emu.grasscutter.server.packet.send.PacketGetGachaInfoRsp; @Opcodes(PacketOpcodes.GetGachaInfoReq) public class HandlerGetGachaInfoReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - session.send(new PacketGetGachaInfoRsp(session.getServer().getGachaSystem(), session.getPlayer())); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + session.send(new PacketGetGachaInfoRsp(session.getServer().getGachaSystem(), session.getPlayer())); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetInvestigationMonsterReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetInvestigationMonsterReq.java index f3ca23ce0..27a2f6cf6 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetInvestigationMonsterReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetInvestigationMonsterReq.java @@ -9,16 +9,16 @@ import emu.grasscutter.server.packet.send.PacketGetInvestigationMonsterRsp; @Opcodes(PacketOpcodes.GetInvestigationMonsterReq) public class HandlerGetInvestigationMonsterReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - var req = GetInvestigationMonsterReqOuterClass.GetInvestigationMonsterReq.parseFrom(payload); - session.send(new PacketGetInvestigationMonsterRsp( - session.getPlayer(), - session.getServer().getWorldDataSystem(), - req.getCityIdListList())); + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + var req = GetInvestigationMonsterReqOuterClass.GetInvestigationMonsterReq.parseFrom(payload); - } + session.send(new PacketGetInvestigationMonsterRsp( + session.getPlayer(), + session.getServer().getWorldDataSystem(), + req.getCityIdListList())); + + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerTokenReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerTokenReq.java index c73019a9a..0b54a4f9a 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerTokenReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerTokenReq.java @@ -27,77 +27,77 @@ import java.security.Signature; @Opcodes(PacketOpcodes.GetPlayerTokenReq) public class HandlerGetPlayerTokenReq extends PacketHandler { - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - GetPlayerTokenReq req = GetPlayerTokenReq.parseFrom(payload); + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + GetPlayerTokenReq req = GetPlayerTokenReq.parseFrom(payload); - // Authenticate - Account account = DatabaseHelper.getAccountById(req.getAccountUid()); - if (account == null || !account.getToken().equals(req.getAccountToken())) { - return; - } + // Authenticate + Account account = DatabaseHelper.getAccountById(req.getAccountUid()); + if (account == null || !account.getToken().equals(req.getAccountToken())) { + return; + } - // Set account - session.setAccount(account); + // Set account + session.setAccount(account); - // Check if player object exists in server - // NOTE: CHECKING MUST SITUATED HERE (BEFORE getPlayerByUid)! because to save firstly ,to load secondly !!! - // TODO - optimize - boolean kicked = false; - Player exists = Grasscutter.getGameServer().getPlayerByAccountId(account.getId()); - if (exists != null) { - GameSession existsSession = exists.getSession(); - if (existsSession != session) {// No self-kicking - exists.onLogout();//must save immediately , or the below will load old data - existsSession.close(); - Grasscutter.getLogger().warn("Player {} was kicked due to duplicated login", account.getUsername()); - kicked = true; - } - } + // Check if player object exists in server + // NOTE: CHECKING MUST SITUATED HERE (BEFORE getPlayerByUid)! because to save firstly ,to load secondly !!! + // TODO - optimize + boolean kicked = false; + Player exists = Grasscutter.getGameServer().getPlayerByAccountId(account.getId()); + if (exists != null) { + GameSession existsSession = exists.getSession(); + if (existsSession != session) {// No self-kicking + exists.onLogout();//must save immediately , or the below will load old data + existsSession.close(); + Grasscutter.getLogger().warn("Player {} was kicked due to duplicated login", account.getUsername()); + kicked = true; + } + } - //NOTE: If there are 5 online players, max count of player is 5, - // a new client want to login by kicking one of them , - // I think it should be allowed - if(!kicked) { - // Max players limit - if (ACCOUNT.maxPlayer > -1 && Grasscutter.getGameServer().getPlayers().size() >= ACCOUNT.maxPlayer) { - session.close(); - return; - } - } + //NOTE: If there are 5 online players, max count of player is 5, + // a new client want to login by kicking one of them , + // I think it should be allowed + if (!kicked) { + // Max players limit + if (ACCOUNT.maxPlayer > -1 && Grasscutter.getGameServer().getPlayers().size() >= ACCOUNT.maxPlayer) { + session.close(); + return; + } + } // Call creation event. PlayerCreationEvent event = new PlayerCreationEvent(session, Player.class); event.call(); - // Get player. - Player player = DatabaseHelper.getPlayerByAccount(account, event.getPlayerClass()); + // Get player. + Player player = DatabaseHelper.getPlayerByAccount(account, event.getPlayerClass()); - if (player == null) { - int nextPlayerUid = DatabaseHelper.getNextPlayerId(session.getAccount().getReservedPlayerUid()); + if (player == null) { + int nextPlayerUid = DatabaseHelper.getNextPlayerId(session.getAccount().getReservedPlayerUid()); - // Create player instance from event. - player = event.getPlayerClass().getDeclaredConstructor(GameSession.class).newInstance(session); + // Create player instance from event. + player = event.getPlayerClass().getDeclaredConstructor(GameSession.class).newInstance(session); - // Save to db - DatabaseHelper.generatePlayerUid(player, nextPlayerUid); - } + // Save to db + DatabaseHelper.generatePlayerUid(player, nextPlayerUid); + } - // Set player object for session - session.setPlayer(player); + // Set player object for session + session.setPlayer(player); - // Checks if the player is banned - if (session.getAccount().isBanned()) { - session.send(new PacketGetPlayerTokenRsp(session, 21, "FORBID_CHEATING_PLUGINS", session.getAccount().getBanEndTime())); - session.close(); - return; - } + // Checks if the player is banned + if (session.getAccount().isBanned()) { + session.send(new PacketGetPlayerTokenRsp(session, 21, "FORBID_CHEATING_PLUGINS", session.getAccount().getBanEndTime())); + session.close(); + return; + } - // Load player from database - player.loadFromDatabase(); + // Load player from database + player.loadFromDatabase(); - // Set session state - session.setUseSecretKey(true); - session.setState(SessionState.WAITING_FOR_LOGIN); + // Set session state + session.setUseSecretKey(true); + session.setState(SessionState.WAITING_FOR_LOGIN); // Only >= 2.7.50 has this if (req.getKeyId() > 0) { diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUnknown2Req.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUnknown2Req.java index 517f9c17e..467449067 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUnknown2Req.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeUnknown2Req.java @@ -8,13 +8,13 @@ import emu.grasscutter.server.packet.send.PacketHomeUnknown2Rsp; @Opcodes(PacketOpcodes.Unk2700_ACILPONNGGK_ClientReq) public class HandlerHomeUnknown2Req extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - /* - * This packet is about the edit mode - */ - session.send(new PacketHomeUnknown2Rsp()); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + /* + * This packet is about the edit mode + */ + session.send(new PacketHomeUnknown2Rsp()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerApplyEnterMpReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerApplyEnterMpReq.java index e2e1309ad..3e1e7ffb1 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerApplyEnterMpReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerApplyEnterMpReq.java @@ -9,13 +9,13 @@ import emu.grasscutter.server.packet.send.PacketPlayerApplyEnterMpRsp; @Opcodes(PacketOpcodes.PlayerApplyEnterMpReq) public class HandlerPlayerApplyEnterMpReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - PlayerApplyEnterMpReq req = PlayerApplyEnterMpReq.parseFrom(payload); - - session.getServer().getMultiplayerSystem().applyEnterMp(session.getPlayer(), req.getTargetUid()); - session.send(new PacketPlayerApplyEnterMpRsp(req.getTargetUid())); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + PlayerApplyEnterMpReq req = PlayerApplyEnterMpReq.parseFrom(payload); + + session.getServer().getMultiplayerSystem().applyEnterMp(session.getPlayer(), req.getTargetUid()); + session.send(new PacketPlayerApplyEnterMpRsp(req.getTargetUid())); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerApplyEnterMpResultReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerApplyEnterMpResultReq.java index 7589190aa..418266468 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerApplyEnterMpResultReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerApplyEnterMpResultReq.java @@ -9,13 +9,13 @@ import emu.grasscutter.server.packet.send.PacketPlayerApplyEnterMpResultRsp; @Opcodes(PacketOpcodes.PlayerApplyEnterMpResultReq) public class HandlerPlayerApplyEnterMpResultReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - PlayerApplyEnterMpResultReq req = PlayerApplyEnterMpResultReq.parseFrom(payload); - - session.getServer().getMultiplayerSystem().applyEnterMpReply(session.getPlayer(), req.getApplyUid(), req.getIsAgreed()); - session.send(new PacketPlayerApplyEnterMpResultRsp(req.getApplyUid(), req.getIsAgreed())); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + PlayerApplyEnterMpResultReq req = PlayerApplyEnterMpResultReq.parseFrom(payload); + + session.getServer().getMultiplayerSystem().applyEnterMpReply(session.getPlayer(), req.getApplyUid(), req.getIsAgreed()); + session.send(new PacketPlayerApplyEnterMpResultRsp(req.getApplyUid(), req.getIsAgreed())); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerEnterDungeonReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerEnterDungeonReq.java index 162eec998..f76ff588c 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerEnterDungeonReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerEnterDungeonReq.java @@ -8,13 +8,13 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.PlayerEnterDungeonReq) public class HandlerPlayerEnterDungeonReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - // Auto template - PlayerEnterDungeonReq req = PlayerEnterDungeonReq.parseFrom(payload); - - session.getServer().getDungeonSystem().enterDungeon(session.getPlayer(), req.getPointId(), req.getDungeonId()); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + // Auto template + PlayerEnterDungeonReq req = PlayerEnterDungeonReq.parseFrom(payload); + + session.getServer().getDungeonSystem().enterDungeon(session.getPlayer(), req.getPointId(), req.getDungeonId()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerGetForceQuitBanInfoReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerGetForceQuitBanInfoReq.java index f0b5311a9..7da3bbcb8 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerGetForceQuitBanInfoReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerGetForceQuitBanInfoReq.java @@ -9,17 +9,17 @@ import emu.grasscutter.server.packet.send.PacketPlayerGetForceQuitBanInfoRsp; @Opcodes(PacketOpcodes.PlayerGetForceQuitBanInfoReq) public class HandlerPlayerGetForceQuitBanInfoReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - - if (session.getServer().getMultiplayerSystem().leaveCoop(session.getPlayer())) { - // Success - session.send(new PacketPlayerGetForceQuitBanInfoRsp(RetcodeOuterClass.Retcode.RET_SUCC_VALUE)); - } else { - // Fail - session.send(new PacketPlayerGetForceQuitBanInfoRsp(RetcodeOuterClass.Retcode.RET_SVR_ERROR_VALUE)); - } - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + + if (session.getServer().getMultiplayerSystem().leaveCoop(session.getPlayer())) { + // Success + session.send(new PacketPlayerGetForceQuitBanInfoRsp(RetcodeOuterClass.Retcode.RET_SUCC_VALUE)); + } else { + // Fail + session.send(new PacketPlayerGetForceQuitBanInfoRsp(RetcodeOuterClass.Retcode.RET_SVR_ERROR_VALUE)); + } + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerLoginReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerLoginReq.java index 714d3161b..3df671fa5 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerLoginReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerLoginReq.java @@ -18,39 +18,39 @@ import emu.grasscutter.server.packet.send.PacketTakeAchievementRewardReq; @Opcodes(PacketOpcodes.PlayerLoginReq) // Sends initial data packets public class HandlerPlayerLoginReq extends PacketHandler { - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - // Check - if (session.getAccount() == null) { - session.close(); - return; - } + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + // Check + if (session.getAccount() == null) { + session.close(); + return; + } - // Parse request - PlayerLoginReq req = PlayerLoginReq.parseFrom(payload); + // Parse request + PlayerLoginReq req = PlayerLoginReq.parseFrom(payload); - // Authenticate session - if (!req.getToken().equals(session.getAccount().getToken())) { - session.close(); - return; - } + // Authenticate session + if (!req.getToken().equals(session.getAccount().getToken())) { + session.close(); + return; + } - // Load character from db - Player player = session.getPlayer(); + // Load character from db + Player player = session.getPlayer(); - // Show opening cutscene if player has no avatars - if (player.getAvatars().getAvatarCount() == 0) { - // Pick character - session.setState(SessionState.PICKING_CHARACTER); - session.send(new BasePacket(PacketOpcodes.DoSetPlayerBornDataNotify)); - } else { - // Login done - session.getPlayer().onLogin(); - } + // Show opening cutscene if player has no avatars + if (player.getAvatars().getAvatarCount() == 0) { + // Pick character + session.setState(SessionState.PICKING_CHARACTER); + session.send(new BasePacket(PacketOpcodes.DoSetPlayerBornDataNotify)); + } else { + // Login done + session.getPlayer().onLogin(); + } - // Final packet to tell client logging in is done - session.send(new PacketPlayerLoginRsp(session)); - session.send(new PacketTakeAchievementRewardReq(session)); - } + // Final packet to tell client logging in is done + session.send(new PacketPlayerLoginRsp(session)); + session.send(new PacketTakeAchievementRewardReq(session)); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerQuitDungeonReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerQuitDungeonReq.java index 9bc450149..a8e483c50 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerQuitDungeonReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerQuitDungeonReq.java @@ -7,10 +7,10 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.PlayerQuitDungeonReq) public class HandlerPlayerQuitDungeonReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - session.getPlayer().getServer().getDungeonSystem().exitDungeon(session.getPlayer()); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + session.getPlayer().getServer().getDungeonSystem().exitDungeon(session.getPlayer()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullPrivateChatReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullPrivateChatReq.java index c579d20b0..191d96d31 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullPrivateChatReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullPrivateChatReq.java @@ -9,14 +9,14 @@ import emu.grasscutter.server.packet.send.PacketPullPrivateChatRsp; @Opcodes(PacketOpcodes.PullPrivateChatReq) public class HandlerPullPrivateChatReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - PullPrivateChatReq req = PullPrivateChatReq.parseFrom(payload); - session.getServer().getChatManager().handlePullPrivateChatReq(session.getPlayer(), req.getTargetUid()); - - // session.send(new PacketPullPrivateChatRsp(req.getTargetUid())); - } + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + PullPrivateChatReq req = PullPrivateChatReq.parseFrom(payload); + + session.getServer().getChatManager().handlePullPrivateChatReq(session.getPlayer(), req.getTargetUid()); + + // session.send(new PacketPullPrivateChatRsp(req.getTargetUid())); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullRecentChatReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullRecentChatReq.java index fa5e6ba8a..1db217e2d 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullRecentChatReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPullRecentChatReq.java @@ -8,8 +8,8 @@ import emu.grasscutter.server.packet.send.PacketPullRecentChatRsp; @Opcodes(PacketOpcodes.PullRecentChatReq) public class HandlerPullRecentChatReq extends PacketHandler { - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - session.getServer().getChatManager().handlePullRecentChatReq(session.getPlayer()); - } + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + session.getServer().getChatManager().handlePullRecentChatReq(session.getPlayer()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerQueryPathReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQueryPathReq.java index 62aad5da8..6ecadee5b 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerQueryPathReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQueryPathReq.java @@ -11,17 +11,17 @@ import emu.grasscutter.server.packet.send.PacketQueryPathRsp; @Opcodes(PacketOpcodes.QueryPathReq) public class HandlerQueryPathReq extends PacketHandler { - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - var req = QueryPathReq.parseFrom(payload); + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + var req = QueryPathReq.parseFrom(payload); /** * It is not the actual work */ - + if (req.getDestinationPosList().size() > 0) { - session.send(new PacketQueryPathRsp(req)); + session.send(new PacketQueryPathRsp(req)); } - } + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerReliquaryDecomposeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerReliquaryDecomposeReq.java index a098ef0d6..fae813a8d 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerReliquaryDecomposeReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerReliquaryDecomposeReq.java @@ -8,9 +8,9 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.ReliquaryDecomposeReq) public class HandlerReliquaryDecomposeReq extends PacketHandler { - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - ReliquaryDecomposeReq req = ReliquaryDecomposeReq.parseFrom(payload); - session.getServer().getCombineSystem().decomposeReliquaries(session.getPlayer(), req.getConfigId(), req.getTargetCount(), req.getGuidListList()); - } + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + ReliquaryDecomposeReq req = ReliquaryDecomposeReq.parseFrom(payload); + session.getServer().getCombineSystem().decomposeReliquaries(session.getPlayer(), req.getConfigId(), req.getTargetCount(), req.getGuidListList()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerReliquaryUpgradeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerReliquaryUpgradeReq.java index 6eb19b7ed..6115ef1fd 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerReliquaryUpgradeReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerReliquaryUpgradeReq.java @@ -8,12 +8,12 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.ReliquaryUpgradeReq) public class HandlerReliquaryUpgradeReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - ReliquaryUpgradeReq req = ReliquaryUpgradeReq.parseFrom(payload); - - session.getServer().getInventorySystem().upgradeRelic(session.getPlayer(), req.getTargetReliquaryGuid(), req.getFoodReliquaryGuidListList(), req.getItemParamListList()); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + ReliquaryUpgradeReq req = ReliquaryUpgradeReq.parseFrom(payload); + + session.getServer().getInventorySystem().upgradeRelic(session.getPlayer(), req.getTargetReliquaryGuid(), req.getFoodReliquaryGuidListList(), req.getItemParamListList()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneKickPlayerReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneKickPlayerReq.java index fec303b4b..731ead639 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneKickPlayerReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneKickPlayerReq.java @@ -9,18 +9,18 @@ import emu.grasscutter.server.packet.send.PacketSceneKickPlayerRsp; @Opcodes(PacketOpcodes.SceneKickPlayerReq) public class HandlerSceneKickPlayerReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - SceneKickPlayerReq req = SceneKickPlayerReq.parseFrom(payload); - - if (session.getServer().getMultiplayerSystem().kickPlayer(session.getPlayer(), req.getTargetUid())) { - // Success - session.send(new PacketSceneKickPlayerRsp(req.getTargetUid())); - } else { - // Fail - session.send(new PacketSceneKickPlayerRsp()); - } - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + SceneKickPlayerReq req = SceneKickPlayerReq.parseFrom(payload); + + if (session.getServer().getMultiplayerSystem().kickPlayer(session.getPlayer(), req.getTargetUid())) { + // Success + session.send(new PacketSceneKickPlayerRsp(req.getTargetUid())); + } else { + // Fail + session.send(new PacketSceneKickPlayerRsp()); + } + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEquipLockStateReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEquipLockStateReq.java index 2467650f9..01b75ee81 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEquipLockStateReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEquipLockStateReq.java @@ -8,12 +8,12 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.SetEquipLockStateReq) public class HandlerSetEquipLockStateReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - SetEquipLockStateReq req = SetEquipLockStateReq.parseFrom(payload); - - session.getServer().getInventorySystem().lockEquip(session.getPlayer(), req.getTargetEquipGuid(), req.getIsLocked()); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + SetEquipLockStateReq req = SetEquipLockStateReq.parseFrom(payload); + + session.getServer().getInventorySystem().lockEquip(session.getPlayer(), req.getTargetEquipGuid(), req.getIsLocked()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetOpenStateReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetOpenStateReq.java index f7f259a32..b1268cc50 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetOpenStateReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetOpenStateReq.java @@ -23,4 +23,4 @@ public class HandlerSetOpenStateReq extends PacketHandler { session.send(new PacketSetOpenStateRsp(openState,value)); } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java index 48ac9151f..1b406ad00 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java @@ -23,62 +23,62 @@ import java.util.Arrays; @Opcodes(PacketOpcodes.SetPlayerBornDataReq) public class HandlerSetPlayerBornDataReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - SetPlayerBornDataReq req = SetPlayerBornDataReq.parseFrom(payload); - - // Sanity checks - int avatarId = req.getAvatarId(); - int startingSkillDepot; - if (avatarId == GameConstants.MAIN_CHARACTER_MALE) { - startingSkillDepot = 504; - } else if (avatarId == GameConstants.MAIN_CHARACTER_FEMALE) { - startingSkillDepot = 704; - } else { - return; - } - - // Make sure resources folder is set - if (!GameData.getAvatarDataMap().containsKey(avatarId)) { - Grasscutter.getLogger().error("No avatar data found! Please check your ExcelBinOutput folder."); - session.close(); - return; - } - - // Get player object - Player player = session.getPlayer(); - player.setNickname(req.getNickName()); - // Create avatar - if (player.getAvatars().getAvatarCount() == 0) { - Avatar mainCharacter = new Avatar(avatarId); - mainCharacter.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(startingSkillDepot)); - // Manually handle adding to team - player.addAvatar(mainCharacter, false); - player.setMainCharacterId(avatarId); - player.setHeadImage(avatarId); - player.getTeamManager().getCurrentSinglePlayerTeamInfo().getAvatars().add(mainCharacter.getAvatarId()); - player.save(); // TODO save player team in different object - } else { - return; - } - - // Login done - session.getPlayer().onLogin(); - - // Born resp packet - session.send(new BasePacket(PacketOpcodes.SetPlayerBornDataRsp)); + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + SetPlayerBornDataReq req = SetPlayerBornDataReq.parseFrom(payload); - // Default mail - var welcomeMail = GAME_INFO.joinOptions.welcomeMail; - MailBuilder mailBuilder = new MailBuilder(player.getUid(), new Mail()); - mailBuilder.mail.mailContent.title = welcomeMail.title; - mailBuilder.mail.mailContent.sender = welcomeMail.sender; - // Please credit Grasscutter if changing something here. We don't condone commercial use of the project. - mailBuilder.mail.mailContent.content = welcomeMail.content + "\n"; - mailBuilder.mail.itemList.addAll(Arrays.asList(welcomeMail.items)); - mailBuilder.mail.importance = 1; - player.sendMail(mailBuilder.mail); - } + // Sanity checks + int avatarId = req.getAvatarId(); + int startingSkillDepot; + if (avatarId == GameConstants.MAIN_CHARACTER_MALE) { + startingSkillDepot = 504; + } else if (avatarId == GameConstants.MAIN_CHARACTER_FEMALE) { + startingSkillDepot = 704; + } else { + return; + } + + // Make sure resources folder is set + if (!GameData.getAvatarDataMap().containsKey(avatarId)) { + Grasscutter.getLogger().error("No avatar data found! Please check your ExcelBinOutput folder."); + session.close(); + return; + } + + // Get player object + Player player = session.getPlayer(); + player.setNickname(req.getNickName()); + + // Create avatar + if (player.getAvatars().getAvatarCount() == 0) { + Avatar mainCharacter = new Avatar(avatarId); + mainCharacter.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(startingSkillDepot)); + // Manually handle adding to team + player.addAvatar(mainCharacter, false); + player.setMainCharacterId(avatarId); + player.setHeadImage(avatarId); + player.getTeamManager().getCurrentSinglePlayerTeamInfo().getAvatars().add(mainCharacter.getAvatarId()); + player.save(); // TODO save player team in different object + } else { + return; + } + + // Login done + session.getPlayer().onLogin(); + + // Born resp packet + session.send(new BasePacket(PacketOpcodes.SetPlayerBornDataRsp)); + + // Default mail + var welcomeMail = GAME_INFO.joinOptions.welcomeMail; + MailBuilder mailBuilder = new MailBuilder(player.getUid(), new Mail()); + mailBuilder.mail.mailContent.title = welcomeMail.title; + mailBuilder.mail.mailContent.sender = welcomeMail.sender; + // Please credit Grasscutter if changing something here. We don't condone commercial use of the project. + mailBuilder.mail.mailContent.content = welcomeMail.content + "\n"; + mailBuilder.mail.itemList.addAll(Arrays.asList(welcomeMail.items)); + mailBuilder.mail.importance = 1; + player.sendMail(mailBuilder.mail); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTowerAllDataReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTowerAllDataReq.java index c9fa5b112..697a7f85c 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTowerAllDataReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTowerAllDataReq.java @@ -8,13 +8,13 @@ import emu.grasscutter.server.packet.send.PacketTowerAllDataRsp; @Opcodes(PacketOpcodes.TowerAllDataReq) public class HandlerTowerAllDataReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - session.send(new PacketTowerAllDataRsp( - session.getServer().getTowerSystem(), - session.getPlayer().getTowerManager() - )); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + session.send(new PacketTowerAllDataRsp( + session.getServer().getTowerSystem(), + session.getPlayer().getTowerManager() + )); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnionCmdNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnionCmdNotify.java index 9323ec5b6..daa1b1388 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnionCmdNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnionCmdNotify.java @@ -13,28 +13,28 @@ import emu.grasscutter.Grasscutter.ServerDebugMode; @Opcodes(PacketOpcodes.UnionCmdNotify) public class HandlerUnionCmdNotify extends PacketHandler { - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - UnionCmdNotify req = UnionCmdNotify.parseFrom(payload); - for (UnionCmd cmd : req.getCmdListList()) { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + UnionCmdNotify req = UnionCmdNotify.parseFrom(payload); + for (UnionCmd cmd : req.getCmdListList()) { int cmdOpcode = cmd.getMessageId(); byte[] cmdPayload = cmd.getBody().toByteArray(); - if(GAME_INFO.logPackets == ServerDebugMode.WHITELIST && SERVER.debugWhitelist.contains(cmd.getMessageId())) { + if (GAME_INFO.logPackets == ServerDebugMode.WHITELIST && SERVER.debugWhitelist.contains(cmd.getMessageId())) { session.logPacket("RECV in Union", cmdOpcode, cmdPayload); } else if (GAME_INFO.logPackets == ServerDebugMode.BLACKLIST && !SERVER.debugBlacklist.contains(cmd.getMessageId())) { session.logPacket("RECV in Union", cmdOpcode, cmdPayload); } //debugLevel ALL ignores UnionCmdNotify, so we will also ignore the contained opcodes session.getServer().getPacketHandler().handle(session, cmd.getMessageId(), EMPTY_BYTE_ARRAY, cmd.getBody().toByteArray()); - } + } - // Update - session.getPlayer().getCombatInvokeHandler().update(session.getPlayer()); - session.getPlayer().getAbilityInvokeHandler().update(session.getPlayer()); + // Update + session.getPlayer().getCombatInvokeHandler().update(session.getPlayer()); + session.getPlayer().getAbilityInvokeHandler().update(session.getPlayer()); // Handle attack results last - while (!session.getPlayer().getAttackResults().isEmpty()) { - session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll()); - } - } + while (!session.getPlayer().getAttackResults().isEmpty()) { + session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll()); + } + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnlockAvatarTalentReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnlockAvatarTalentReq.java index d37df8618..5016017f6 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnlockAvatarTalentReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnlockAvatarTalentReq.java @@ -8,13 +8,13 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.UnlockAvatarTalentReq) public class HandlerUnlockAvatarTalentReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - UnlockAvatarTalentReq req = UnlockAvatarTalentReq.parseFrom(payload); - - // Unlock avatar const - session.getServer().getInventorySystem().unlockAvatarConstellation(session.getPlayer(), req.getAvatarGuid()); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + UnlockAvatarTalentReq req = UnlockAvatarTalentReq.parseFrom(payload); + + // Unlock avatar const + session.getServer().getInventorySystem().unlockAvatarConstellation(session.getPlayer(), req.getAvatarGuid()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerUseItemReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerUseItemReq.java index 0941dafed..54314959d 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerUseItemReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerUseItemReq.java @@ -10,17 +10,17 @@ import emu.grasscutter.server.packet.send.PacketUseItemRsp; @Opcodes(PacketOpcodes.UseItemReq) public class HandlerUseItemReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - UseItemReq req = UseItemReq.parseFrom(payload); - - GameItem useItem = session.getServer().getInventorySystem().useItem(session.getPlayer(), req.getTargetGuid(), req.getGuid(), req.getCount(), req.getOptionIdx()); - if (useItem != null) { - session.send(new PacketUseItemRsp(req.getTargetGuid(), useItem)); - } else { - session.send(new PacketUseItemRsp()); - } - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + UseItemReq req = UseItemReq.parseFrom(payload); + + GameItem useItem = session.getServer().getInventorySystem().useItem(session.getPlayer(), req.getTargetGuid(), req.getGuid(), req.getCount(), req.getOptionIdx()); + if (useItem != null) { + session.send(new PacketUseItemRsp(req.getTargetGuid(), useItem)); + } else { + session.send(new PacketUseItemRsp()); + } + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerWeaponAwakenReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerWeaponAwakenReq.java index 0c9fcd68c..79326554d 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerWeaponAwakenReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerWeaponAwakenReq.java @@ -8,13 +8,13 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.WeaponAwakenReq) public class HandlerWeaponAwakenReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - WeaponAwakenReq req = WeaponAwakenReq.parseFrom(payload); - - // Weapon refinement - session.getServer().getInventorySystem().refineWeapon(session.getPlayer(), req.getTargetWeaponGuid(), req.getItemGuid()); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + WeaponAwakenReq req = WeaponAwakenReq.parseFrom(payload); + + // Weapon refinement + session.getServer().getInventorySystem().refineWeapon(session.getPlayer(), req.getTargetWeaponGuid(), req.getItemGuid()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerWeaponPromoteReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerWeaponPromoteReq.java index bbb50bd5d..19fa03fd6 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerWeaponPromoteReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerWeaponPromoteReq.java @@ -8,13 +8,13 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.WeaponPromoteReq) public class HandlerWeaponPromoteReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - WeaponPromoteReq req = WeaponPromoteReq.parseFrom(payload); - - // Ascend weapon - session.getServer().getInventorySystem().promoteWeapon(session.getPlayer(), req.getTargetWeaponGuid()); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + WeaponPromoteReq req = WeaponPromoteReq.parseFrom(payload); + + // Ascend weapon + session.getServer().getInventorySystem().promoteWeapon(session.getPlayer(), req.getTargetWeaponGuid()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerWeaponUpgradeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerWeaponUpgradeReq.java index 3e531afa6..4744a2f11 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerWeaponUpgradeReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerWeaponUpgradeReq.java @@ -8,18 +8,18 @@ import emu.grasscutter.server.game.GameSession; @Opcodes(PacketOpcodes.WeaponUpgradeReq) public class HandlerWeaponUpgradeReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - WeaponUpgradeReq req = WeaponUpgradeReq.parseFrom(payload); - - // Level up weapon - session.getServer().getInventorySystem().upgradeWeapon( - session.getPlayer(), - req.getTargetWeaponGuid(), - req.getFoodWeaponGuidListList(), - req.getItemParamListList() - ); - } + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + WeaponUpgradeReq req = WeaponUpgradeReq.parseFrom(payload); + + // Level up weapon + session.getServer().getInventorySystem().upgradeWeapon( + session.getPlayer(), + req.getTargetWeaponGuid(), + req.getFoodWeaponGuidListList(), + req.getItemParamListList() + ); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetAllMailRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetAllMailRsp.java index c6f2b7194..780ca4163 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetAllMailRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetAllMailRsp.java @@ -32,9 +32,9 @@ public class PacketGetAllMailRsp extends BasePacket { List mailDataList = new ArrayList(); for (Mail message : player.getAllMail()) { - if(message.stateValue == 1) { // Make sure it isn't a gift + if (message.stateValue == 1) { // Make sure it isn't a gift if (message.expireTime > (int) Instant.now().getEpochSecond()) { // Make sure the message isn't expired (The game won't show expired mail, but I don't want to send unnecessary information). - if(mailDataList.size() <= 1000) { // Make sure that there isn't over 1000 messages in the mailbox. (idk what will happen if there is but the game probably won't like it.) + if (mailDataList.size() <= 1000) { // Make sure that there isn't over 1000 messages in the mailbox. (idk what will happen if there is but the game probably won't like it.) MailTextContent.Builder mailTextContent = MailTextContent.newBuilder(); mailTextContent.setTitle(message.mailContent.title); mailTextContent.setContent(message.mailContent.content); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetGachaInfoRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetGachaInfoRsp.java index bc1e3f1e3..7e6eb3edd 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetGachaInfoRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetGachaInfoRsp.java @@ -6,11 +6,11 @@ import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; public class PacketGetGachaInfoRsp extends BasePacket { - - public PacketGetGachaInfoRsp(GachaSystem manager, Player player) { - super(PacketOpcodes.GetGachaInfoRsp); - - this.setData(manager.toProto(player)); - } + + public PacketGetGachaInfoRsp(GachaSystem manager, Player player) { + super(PacketOpcodes.GetGachaInfoRsp); + + this.setData(manager.toProto(player)); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetInvestigationMonsterRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetInvestigationMonsterRsp.java index 306cbca57..47a26e261 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetInvestigationMonsterRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetInvestigationMonsterRsp.java @@ -11,15 +11,15 @@ import java.util.List; public class PacketGetInvestigationMonsterRsp extends BasePacket { - public PacketGetInvestigationMonsterRsp(Player player, WorldDataSystem worldDataManager, List cityIdListList) { + public PacketGetInvestigationMonsterRsp(Player player, WorldDataSystem worldDataManager, List cityIdListList) { - super(PacketOpcodes.GetInvestigationMonsterRsp); + super(PacketOpcodes.GetInvestigationMonsterRsp); - var resp = GetInvestigationMonsterRspOuterClass.GetInvestigationMonsterRsp.newBuilder(); + var resp = GetInvestigationMonsterRspOuterClass.GetInvestigationMonsterRsp.newBuilder(); - cityIdListList.forEach(id -> resp.addAllMonsterList(worldDataManager.getInvestigationMonstersByCityId(player, id))); + cityIdListList.forEach(id -> resp.addAllMonsterList(worldDataManager.getInvestigationMonstersByCityId(player, id))); - this.setData(resp.build()); - } + this.setData(resp.build()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java index 97a7d4a97..82110ef78 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java @@ -14,32 +14,32 @@ import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture; import emu.grasscutter.net.proto.PlatformTypeOuterClass; public class PacketGetPlayerFriendListRsp extends BasePacket { - - public PacketGetPlayerFriendListRsp(Player player) { - super(PacketOpcodes.GetPlayerFriendListRsp); - - var serverAccount = GAME_INFO.serverAccount; - FriendBrief serverFriend = FriendBrief.newBuilder() - .setUid(GameConstants.SERVER_CONSOLE_UID) - .setNickname(serverAccount.nickName) - .setLevel(serverAccount.adventureRank) - .setProfilePicture(ProfilePicture.newBuilder().setAvatarId(serverAccount.avatarId)) - .setWorldLevel(serverAccount.worldLevel) - .setSignature(serverAccount.signature) - .setLastActiveTime((int) (System.currentTimeMillis() / 1000f)) - .setNameCardId(serverAccount.nameCardId) - .setOnlineState(FriendOnlineState.FRIEND_ONLINE_STATE_ONLINE) - .setParam(1) - .setIsGameSource(true) - .setPlatformType(PlatformTypeOuterClass.PlatformType.PLATFORM_TYPE_PC) - .build(); - - GetPlayerFriendListRsp.Builder proto = GetPlayerFriendListRsp.newBuilder().addFriendList(serverFriend); - - for (Friendship friendship : player.getFriendsList().getFriends().values()) { - proto.addFriendList(friendship.toProto()); - } - this.setData(proto); - } + public PacketGetPlayerFriendListRsp(Player player) { + super(PacketOpcodes.GetPlayerFriendListRsp); + + var serverAccount = GAME_INFO.serverAccount; + FriendBrief serverFriend = FriendBrief.newBuilder() + .setUid(GameConstants.SERVER_CONSOLE_UID) + .setNickname(serverAccount.nickName) + .setLevel(serverAccount.adventureRank) + .setProfilePicture(ProfilePicture.newBuilder().setAvatarId(serverAccount.avatarId)) + .setWorldLevel(serverAccount.worldLevel) + .setSignature(serverAccount.signature) + .setLastActiveTime((int) (System.currentTimeMillis() / 1000f)) + .setNameCardId(serverAccount.nameCardId) + .setOnlineState(FriendOnlineState.FRIEND_ONLINE_STATE_ONLINE) + .setParam(1) + .setIsGameSource(true) + .setPlatformType(PlatformTypeOuterClass.PlatformType.PLATFORM_TYPE_PC) + .build(); + + GetPlayerFriendListRsp.Builder proto = GetPlayerFriendListRsp.newBuilder().addFriendList(serverFriend); + + for (Friendship friendship : player.getFriendsList().getFriends().values()) { + proto.addFriendList(friendship.toProto()); + } + + this.setData(proto); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetSceneAreaRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetSceneAreaRsp.java index f81efb07a..b4d74741d 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetSceneAreaRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetSceneAreaRsp.java @@ -9,20 +9,20 @@ import emu.grasscutter.net.proto.CityInfoOuterClass.CityInfo; import emu.grasscutter.net.proto.GetSceneAreaRspOuterClass.GetSceneAreaRsp; public class PacketGetSceneAreaRsp extends BasePacket { - - public PacketGetSceneAreaRsp(int sceneId) { - super(PacketOpcodes.GetSceneAreaRsp); - - this.buildHeader(0); - - GetSceneAreaRsp p = GetSceneAreaRsp.newBuilder() - .setSceneId(sceneId) - .addAllAreaIdList(Arrays.stream(new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,18,19,100,101,102,103,200,210,300,400,401,402,403}).boxed().collect(Collectors.toList())) - .addCityInfoList(CityInfo.newBuilder().setCityId(1).setLevel(1).build()) - .addCityInfoList(CityInfo.newBuilder().setCityId(2).setLevel(1).build()) - .addCityInfoList(CityInfo.newBuilder().setCityId(3).setLevel(1).build()) - .build(); - - this.setData(p); - } + + public PacketGetSceneAreaRsp(int sceneId) { + super(PacketOpcodes.GetSceneAreaRsp); + + this.buildHeader(0); + + GetSceneAreaRsp p = GetSceneAreaRsp.newBuilder() + .setSceneId(sceneId) + .addAllAreaIdList(Arrays.stream(new int[] {1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,18,19,100,101,102,103,200,210,300,400,401,402,403}).boxed().collect(Collectors.toList())) + .addCityInfoList(CityInfo.newBuilder().setCityId(1).setLevel(1).build()) + .addCityInfoList(CityInfo.newBuilder().setCityId(2).setLevel(1).build()) + .addCityInfoList(CityInfo.newBuilder().setCityId(3).setLevel(1).build()) + .build(); + + this.setData(p); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetShopRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetShopRsp.java index 0bee63451..b52d58004 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetShopRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetShopRsp.java @@ -18,61 +18,61 @@ import java.util.List; import java.util.stream.Collectors; public class PacketGetShopRsp extends BasePacket { - public PacketGetShopRsp(Player inv, int shopType) { - super(PacketOpcodes.GetShopRsp); + public PacketGetShopRsp(Player inv, int shopType) { + super(PacketOpcodes.GetShopRsp); - // TODO: CityReputationLevel - Shop.Builder shop = Shop.newBuilder() - .setShopType(shopType) - .setCityId(1) //mock - .setCityReputationLevel(10); //mock + // TODO: CityReputationLevel + Shop.Builder shop = Shop.newBuilder() + .setShopType(shopType) + .setCityId(1) //mock + .setCityReputationLevel(10); //mock - ShopSystem manager = Grasscutter.getGameServer().getShopSystem(); - if (manager.getShopData().get(shopType) != null) { - List list = manager.getShopData().get(shopType); - List goodsList = new ArrayList<>(); - for (ShopInfo info : list) { - ShopGoods.Builder goods = ShopGoods.newBuilder() - .setGoodsId(info.getGoodsId()) - .setGoodsItem(ItemParamOuterClass.ItemParam.newBuilder().setItemId(info.getGoodsItem().getId()).setCount(info.getGoodsItem().getCount()).build()) - .setScoin(info.getScoin()) - .setHcoin(info.getHcoin()) - .setBuyLimit(info.getBuyLimit()) - .setBeginTime(info.getBeginTime()) - .setEndTime(info.getEndTime()) - .setMinLevel(info.getMinLevel()) - .setMaxLevel(info.getMaxLevel()) - .setMcoin(info.getMcoin()) - .setDisableType(info.getDisableType()) - .setSecondarySheetId(info.getSecondarySheetId()); - if (info.getCostItemList() != null) { - goods.addAllCostItemList(info.getCostItemList().stream().map(x -> ItemParamOuterClass.ItemParam.newBuilder().setItemId(x.getId()).setCount(x.getCount()).build()).collect(Collectors.toList())); - } - if (info.getPreGoodsIdList() != null) { - goods.addAllPreGoodsIdList(info.getPreGoodsIdList()); - } + ShopSystem manager = Grasscutter.getGameServer().getShopSystem(); + if (manager.getShopData().get(shopType) != null) { + List list = manager.getShopData().get(shopType); + List goodsList = new ArrayList<>(); + for (ShopInfo info : list) { + ShopGoods.Builder goods = ShopGoods.newBuilder() + .setGoodsId(info.getGoodsId()) + .setGoodsItem(ItemParamOuterClass.ItemParam.newBuilder().setItemId(info.getGoodsItem().getId()).setCount(info.getGoodsItem().getCount()).build()) + .setScoin(info.getScoin()) + .setHcoin(info.getHcoin()) + .setBuyLimit(info.getBuyLimit()) + .setBeginTime(info.getBeginTime()) + .setEndTime(info.getEndTime()) + .setMinLevel(info.getMinLevel()) + .setMaxLevel(info.getMaxLevel()) + .setMcoin(info.getMcoin()) + .setDisableType(info.getDisableType()) + .setSecondarySheetId(info.getSecondarySheetId()); + if (info.getCostItemList() != null) { + goods.addAllCostItemList(info.getCostItemList().stream().map(x -> ItemParamOuterClass.ItemParam.newBuilder().setItemId(x.getId()).setCount(x.getCount()).build()).collect(Collectors.toList())); + } + if (info.getPreGoodsIdList() != null) { + goods.addAllPreGoodsIdList(info.getPreGoodsIdList()); + } - int currentTs = Utils.getCurrentSeconds(); - ShopLimit currentShopLimit = inv.getGoodsLimit(info.getGoodsId()); - int nextRefreshTime = ShopSystem.getShopNextRefreshTime(info); - if (currentShopLimit != null) { - if (currentShopLimit.getNextRefreshTime() < currentTs) { // second game day - currentShopLimit.setHasBoughtInPeriod(0); - currentShopLimit.setNextRefreshTime(nextRefreshTime); - } - goods.setBoughtNum(currentShopLimit.getHasBoughtInPeriod()); - goods.setNextRefreshTime(currentShopLimit.getNextRefreshTime()); - } else { - inv.addShopLimit(goods.getGoodsId(), 0, nextRefreshTime); // save generated refresh time - goods.setNextRefreshTime(nextRefreshTime); - } + int currentTs = Utils.getCurrentSeconds(); + ShopLimit currentShopLimit = inv.getGoodsLimit(info.getGoodsId()); + int nextRefreshTime = ShopSystem.getShopNextRefreshTime(info); + if (currentShopLimit != null) { + if (currentShopLimit.getNextRefreshTime() < currentTs) { // second game day + currentShopLimit.setHasBoughtInPeriod(0); + currentShopLimit.setNextRefreshTime(nextRefreshTime); + } + goods.setBoughtNum(currentShopLimit.getHasBoughtInPeriod()); + goods.setNextRefreshTime(currentShopLimit.getNextRefreshTime()); + } else { + inv.addShopLimit(goods.getGoodsId(), 0, nextRefreshTime); // save generated refresh time + goods.setNextRefreshTime(nextRefreshTime); + } - goodsList.add(goods.build()); - } - shop.addAllGoodsList(goodsList); - } + goodsList.add(goods.build()); + } + shop.addAllGoodsList(goodsList); + } - inv.save(); - this.setData(GetShopRspOuterClass.GetShopRsp.newBuilder().setShop(shop).build()); - } + inv.save(); + this.setData(GetShopRspOuterClass.GetShopRsp.newBuilder().setShop(shop).build()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeBasicInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeBasicInfoNotify.java index a55091f32..6216cf2ea 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeBasicInfoNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeBasicInfoNotify.java @@ -10,28 +10,28 @@ import emu.grasscutter.net.proto.VectorOuterClass; public class PacketHomeBasicInfoNotify extends BasePacket { - public PacketHomeBasicInfoNotify(Player player, boolean editMode) { - super(PacketOpcodes.HomeBasicInfoNotify); + public PacketHomeBasicInfoNotify(Player player, boolean editMode) { + super(PacketOpcodes.HomeBasicInfoNotify); - if (player.getCurrentRealmId() <= 0) { - return; - } + if (player.getCurrentRealmId() <= 0) { + return; + } - var proto = HomeBasicInfoNotifyOuterClass.HomeBasicInfoNotify.newBuilder(); + var proto = HomeBasicInfoNotifyOuterClass.HomeBasicInfoNotify.newBuilder(); - var sceneId = player.getCurrentRealmId() + 2000; - var homeScene = player.getHome().getHomeSceneItem(sceneId); + var sceneId = player.getCurrentRealmId() + 2000; + var homeScene = player.getHome().getHomeSceneItem(sceneId); - proto.setBasicInfo(HomeBasicInfoOuterClass.HomeBasicInfo.newBuilder() - .setCurModuleId(player.getCurrentRealmId()) - .setCurRoomSceneId(homeScene.getRoomSceneId()) - .setIsInEditMode(editMode) - .setHomeOwnerUid(player.getUid()) - .setLevel(player.getHome().getLevel()) - .setOwnerNickName(player.getNickname()) - // TODO limit shop - .build()); + proto.setBasicInfo(HomeBasicInfoOuterClass.HomeBasicInfo.newBuilder() + .setCurModuleId(player.getCurrentRealmId()) + .setCurRoomSceneId(homeScene.getRoomSceneId()) + .setIsInEditMode(editMode) + .setHomeOwnerUid(player.getUid()) + .setLevel(player.getHome().getLevel()) + .setOwnerNickName(player.getNickname()) + // TODO limit shop + .build()); - this.setData(proto); - } + this.setData(proto); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketMailChangeNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketMailChangeNotify.java index d0ea5d96b..f8e196265 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketMailChangeNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketMailChangeNotify.java @@ -14,7 +14,7 @@ import java.util.List; public class PacketMailChangeNotify extends BasePacket { public PacketMailChangeNotify(Player player, Mail message) { - this (player, new ArrayList(){{add(message);}}); + this (player, new ArrayList() {{add(message);}}); } public PacketMailChangeNotify(Player player, List mailList) { @@ -60,10 +60,10 @@ public class PacketMailChangeNotify extends BasePacket { } } - if(delMailIdList != null) { + if (delMailIdList != null) { proto.addAllDelMailIdList(delMailIdList); } this.setData(proto.build()); } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketMarkMapRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketMarkMapRsp.java index f904e3892..1492ee914 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketMarkMapRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketMarkMapRsp.java @@ -40,4 +40,4 @@ public class PacketMarkMapRsp extends BasePacket { MarkMapRspOuterClass.MarkMapRsp data = proto.build(); this.setData(data); } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketOpenStateChangeNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketOpenStateChangeNotify.java index fafc5dc8f..902caebcb 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketOpenStateChangeNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketOpenStateChangeNotify.java @@ -16,4 +16,4 @@ public class PacketOpenStateChangeNotify extends BasePacket { this.setData(proto); } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketOpenStateUpdateNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketOpenStateUpdateNotify.java index dc18ba836..cb233ecc6 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketOpenStateUpdateNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketOpenStateUpdateNotify.java @@ -13,20 +13,20 @@ import emu.grasscutter.net.proto.OpenStateUpdateNotifyOuterClass.OpenStateUpdate public class PacketOpenStateUpdateNotify extends BasePacket { public PacketOpenStateUpdateNotify(PlayerOpenStateManager manager) { - super(PacketOpcodes.OpenStateUpdateNotify); + super(PacketOpcodes.OpenStateUpdateNotify); - OpenStateUpdateNotify.Builder proto = OpenStateUpdateNotify.newBuilder(); + OpenStateUpdateNotify.Builder proto = OpenStateUpdateNotify.newBuilder(); - for (OpenState state : OpenState.values()) { - // If the player has an open state stored in their map, then it would always override any default value - if (manager.getOpenStateMap().containsKey(state.getValue())) { - proto.putOpenStateMap(state.getValue(), manager.getOpenState(state)); - } else if (PlayerOpenStateManager.DEV_OPEN_STATES.contains(state)) { - // Add default value here. TODO properly put default values somewhere - proto.putOpenStateMap(state.getValue(), 1); - } - } + for (OpenState state : OpenState.values()) { + // If the player has an open state stored in their map, then it would always override any default value + if (manager.getOpenStateMap().containsKey(state.getValue())) { + proto.putOpenStateMap(state.getValue(), manager.getOpenState(state)); + } else if (PlayerOpenStateManager.DEV_OPEN_STATES.contains(state)) { + // Add default value here. TODO properly put default values somewhere + proto.putOpenStateMap(state.getValue(), 1); + } + } - this.setData(proto); - } + this.setData(proto); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java index a74ac4145..461b0bdce 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java @@ -11,60 +11,60 @@ import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Utils; public class PacketPlayerEnterSceneNotify extends BasePacket { - - // Login - public PacketPlayerEnterSceneNotify(Player player) { - super(PacketOpcodes.PlayerEnterSceneNotify); - - player.setSceneLoadState(SceneLoadState.LOADING); - player.setEnterSceneToken(Utils.randomRange(1000, 99999)); - PlayerEnterSceneNotify proto = PlayerEnterSceneNotify.newBuilder() - .setSceneId(player.getSceneId()) - .setPos(player.getPosition().toProto()) - .setSceneBeginTime(System.currentTimeMillis()) - .setType(EnterType.ENTER_TYPE_SELF) - .setTargetUid(player.getUid()) - .setEnterSceneToken(player.getEnterSceneToken()) - .setWorldLevel(player.getWorldLevel()) - .setEnterReason(EnterReason.Login.getValue()) - .setIsFirstLoginEnterScene(player.isFirstLoginEnterScene()) - .setWorldType(1) - .setSceneTransaction("3-" + player.getUid() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402) - .build(); - - this.setData(proto); - } - - public PacketPlayerEnterSceneNotify(Player player, EnterType type, EnterReason reason, int newScene, Position newPos) { - this(player, player, type, reason, newScene, newPos); - } - - // Teleport or go somewhere - public PacketPlayerEnterSceneNotify(Player player, Player target, EnterType type, EnterReason reason, int newScene, Position newPos) { - super(PacketOpcodes.PlayerEnterSceneNotify); - - player.setSceneLoadState(SceneLoadState.LOADING); - player.setEnterSceneToken(Utils.randomRange(1000, 99999)); + // Login + public PacketPlayerEnterSceneNotify(Player player) { + super(PacketOpcodes.PlayerEnterSceneNotify); - PlayerEnterSceneNotify.Builder proto = PlayerEnterSceneNotify.newBuilder() - .setPrevSceneId(player.getSceneId()) - .setPrevPos(player.getPosition().toProto()) - .setSceneId(newScene) - .setPos(newPos.toProto()) - .setSceneBeginTime(System.currentTimeMillis()) - .setType(type) - .setTargetUid(target.getUid()) - .setEnterSceneToken(player.getEnterSceneToken()) - .setWorldLevel(target.getWorld().getWorldLevel()) - .setEnterReason(reason.getValue()) - .setWorldType(1) - .setSceneTransaction(newScene + "-" + target.getUid() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402); + player.setSceneLoadState(SceneLoadState.LOADING); + player.setEnterSceneToken(Utils.randomRange(1000, 99999)); - for(int i = 0; i < 3000; i++) { - proto.addSceneTagIdList(i); - } + PlayerEnterSceneNotify proto = PlayerEnterSceneNotify.newBuilder() + .setSceneId(player.getSceneId()) + .setPos(player.getPosition().toProto()) + .setSceneBeginTime(System.currentTimeMillis()) + .setType(EnterType.ENTER_TYPE_SELF) + .setTargetUid(player.getUid()) + .setEnterSceneToken(player.getEnterSceneToken()) + .setWorldLevel(player.getWorldLevel()) + .setEnterReason(EnterReason.Login.getValue()) + .setIsFirstLoginEnterScene(player.isFirstLoginEnterScene()) + .setWorldType(1) + .setSceneTransaction("3-" + player.getUid() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402) + .build(); - this.setData(proto.build()); - } + this.setData(proto); + } + + public PacketPlayerEnterSceneNotify(Player player, EnterType type, EnterReason reason, int newScene, Position newPos) { + this(player, player, type, reason, newScene, newPos); + } + + // Teleport or go somewhere + public PacketPlayerEnterSceneNotify(Player player, Player target, EnterType type, EnterReason reason, int newScene, Position newPos) { + super(PacketOpcodes.PlayerEnterSceneNotify); + + player.setSceneLoadState(SceneLoadState.LOADING); + player.setEnterSceneToken(Utils.randomRange(1000, 99999)); + + PlayerEnterSceneNotify.Builder proto = PlayerEnterSceneNotify.newBuilder() + .setPrevSceneId(player.getSceneId()) + .setPrevPos(player.getPosition().toProto()) + .setSceneId(newScene) + .setPos(newPos.toProto()) + .setSceneBeginTime(System.currentTimeMillis()) + .setType(type) + .setTargetUid(target.getUid()) + .setEnterSceneToken(player.getEnterSceneToken()) + .setWorldLevel(target.getWorld().getWorldLevel()) + .setEnterReason(reason.getValue()) + .setWorldType(1) + .setSceneTransaction(newScene + "-" + target.getUid() + "-" + (int) (System.currentTimeMillis() / 1000) + "-" + 18402); + + for (int i = 0; i < 3000; i++) { + proto.addSceneTagIdList(i); + } + + this.setData(proto.build()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java index a05075460..696e8cd2a 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java @@ -21,53 +21,53 @@ import java.util.Objects; public class PacketPlayerLoginRsp extends BasePacket { - private static QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp regionCache; + private static QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp regionCache; - public PacketPlayerLoginRsp(GameSession session) { - super(PacketOpcodes.PlayerLoginRsp, 1); - - this.setUseDispatchKey(true); + public PacketPlayerLoginRsp(GameSession session) { + super(PacketOpcodes.PlayerLoginRsp, 1); - RegionInfo info; + this.setUseDispatchKey(true); - if (SERVER.runMode == ServerRunMode.GAME_ONLY) { - if (regionCache == null) { - try { - // todo: we might want to push custom config to client - RegionInfo serverRegion = RegionInfo.newBuilder() - .setGateserverIp(lr(GAME_INFO.accessAddress, GAME_INFO.bindAddress)) - .setGateserverPort(lr(GAME_INFO.accessPort, GAME_INFO.bindPort)) - .setSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED)) - .build(); + RegionInfo info; - regionCache = QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp.newBuilder().setRegionInfo(serverRegion).build(); - } catch (Exception e) { - Grasscutter.getLogger().error("Error while initializing region cache!", e); - } - } + if (SERVER.runMode == ServerRunMode.GAME_ONLY) { + if (regionCache == null) { + try { + // todo: we might want to push custom config to client + RegionInfo serverRegion = RegionInfo.newBuilder() + .setGateserverIp(lr(GAME_INFO.accessAddress, GAME_INFO.bindAddress)) + .setGateserverPort(lr(GAME_INFO.accessPort, GAME_INFO.bindPort)) + .setSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED)) + .build(); - info = regionCache.getRegionInfo(); - } else { - info = Objects.requireNonNull(RegionHandler.getCurrentRegion()).getRegionInfo(); - } + regionCache = QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp.newBuilder().setRegionInfo(serverRegion).build(); + } catch (Exception e) { + Grasscutter.getLogger().error("Error while initializing region cache!", e); + } + } - PlayerLoginRsp p = PlayerLoginRsp.newBuilder() - .setIsUseAbilityHash(true) // true - .setAbilityHashCode(1844674) // 1844674 - .setGameBiz("hk4e_global") - .setClientDataVersion(info.getClientDataVersion()) - .setClientSilenceDataVersion(info.getClientSilenceDataVersion()) - .setClientMd5(info.getClientDataMd5()) - .setClientSilenceMd5(info.getClientSilenceDataMd5()) - .setResVersionConfig(info.getResVersionConfig()) - .setClientVersionSuffix(info.getClientVersionSuffix()) - .setClientSilenceVersionSuffix(info.getClientSilenceVersionSuffix()) - .setIsScOpen(false) - //.setScInfo(ByteString.copyFrom(new byte[] {})) - .setRegisterCps("mihoyo") - .setCountryCode("US") - .build(); - - this.setData(p.toByteArray()); - } + info = regionCache.getRegionInfo(); + } else { + info = Objects.requireNonNull(RegionHandler.getCurrentRegion()).getRegionInfo(); + } + + PlayerLoginRsp p = PlayerLoginRsp.newBuilder() + .setIsUseAbilityHash(true) // true + .setAbilityHashCode(1844674) // 1844674 + .setGameBiz("hk4e_global") + .setClientDataVersion(info.getClientDataVersion()) + .setClientSilenceDataVersion(info.getClientSilenceDataVersion()) + .setClientMd5(info.getClientDataMd5()) + .setClientSilenceMd5(info.getClientSilenceDataMd5()) + .setResVersionConfig(info.getResVersionConfig()) + .setClientVersionSuffix(info.getClientVersionSuffix()) + .setClientSilenceVersionSuffix(info.getClientSilenceVersionSuffix()) + .setIsScOpen(false) + //.setScInfo(ByteString.copyFrom(new byte[] {})) + .setRegisterCps("mihoyo") + .setCountryCode("US") + .build(); + + this.setData(p.toByteArray()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java index 01c89a4ae..0445d068a 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java @@ -11,21 +11,21 @@ import emu.grasscutter.net.proto.PlayerStoreNotifyOuterClass.PlayerStoreNotify; import emu.grasscutter.net.proto.StoreTypeOuterClass.StoreType; public class PacketPlayerStoreNotify extends BasePacket { - - public PacketPlayerStoreNotify(Player player) { - super(PacketOpcodes.PlayerStoreNotify); - - this.buildHeader(2); - - PlayerStoreNotify.Builder p = PlayerStoreNotify.newBuilder() - .setStoreType(StoreType.STORE_TYPE_PACK) - .setWeightLimit(GAME_OPTIONS.inventoryLimits.all); - - for (GameItem item : player.getInventory()) { - Item itemProto = item.toProto(); - p.addItemList(itemProto); - } - - this.setData(p.build()); - } + + public PacketPlayerStoreNotify(Player player) { + super(PacketOpcodes.PlayerStoreNotify); + + this.buildHeader(2); + + PlayerStoreNotify.Builder p = PlayerStoreNotify.newBuilder() + .setStoreType(StoreType.STORE_TYPE_PACK) + .setWeightLimit(GAME_OPTIONS.inventoryLimits.all); + + for (GameItem item : player.getInventory()) { + Item itemProto = item.toProto(); + p.addItemList(itemProto); + } + + this.setData(p.build()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerWorldSceneInfoListNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerWorldSceneInfoListNotify.java index 51b6fef10..458a09288 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerWorldSceneInfoListNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerWorldSceneInfoListNotify.java @@ -9,64 +9,64 @@ import emu.grasscutter.net.proto.SceneUnlockInfoOuterClass.SceneUnlockInfo; import static emu.grasscutter.net.proto.PlayerWorldSceneInfoOuterClass.*; public class PacketPlayerWorldSceneInfoListNotify extends BasePacket { - - public PacketPlayerWorldSceneInfoListNotify() { - super(PacketOpcodes.PlayerWorldSceneInfoListNotify); // Rename opcode later - PlayerWorldSceneInfoListNotify.Builder proto = PlayerWorldSceneInfoListNotify.newBuilder() - .addInfoList( - PlayerWorldSceneInfo.newBuilder() - .setSceneId(1) - .setIsLocked(false) - .build() - ) - .addInfoList( - PlayerWorldSceneInfo.newBuilder() - .setSceneId(3) - .setIsLocked(false) - .addSceneTagIdList(102) - .addSceneTagIdList(113) - .addSceneTagIdList(117) - .build() - ) - .addInfoList( - PlayerWorldSceneInfo.newBuilder() - .setSceneId(4) - .setIsLocked(false) - .addSceneTagIdList(106) - .addSceneTagIdList(109) - .addSceneTagIdList(117) - .build() - ) - .addInfoList( - PlayerWorldSceneInfo.newBuilder() - .setSceneId(5) - .setIsLocked(false) - .build() - ) - .addInfoList( - PlayerWorldSceneInfo.newBuilder() - .setSceneId(6) - .setIsLocked(false) - .build() - ) - .addInfoList( - PlayerWorldSceneInfo.newBuilder() - .setSceneId(7) - .setIsLocked(false) - .build() - ); + public PacketPlayerWorldSceneInfoListNotify() { + super(PacketOpcodes.PlayerWorldSceneInfoListNotify); // Rename opcode later - var gaa = PlayerWorldSceneInfo.newBuilder() - .setSceneId(9) - .setIsLocked(false); + PlayerWorldSceneInfoListNotify.Builder proto = PlayerWorldSceneInfoListNotify.newBuilder() + .addInfoList( + PlayerWorldSceneInfo.newBuilder() + .setSceneId(1) + .setIsLocked(false) + .build() + ) + .addInfoList( + PlayerWorldSceneInfo.newBuilder() + .setSceneId(3) + .setIsLocked(false) + .addSceneTagIdList(102) + .addSceneTagIdList(113) + .addSceneTagIdList(117) + .build() + ) + .addInfoList( + PlayerWorldSceneInfo.newBuilder() + .setSceneId(4) + .setIsLocked(false) + .addSceneTagIdList(106) + .addSceneTagIdList(109) + .addSceneTagIdList(117) + .build() + ) + .addInfoList( + PlayerWorldSceneInfo.newBuilder() + .setSceneId(5) + .setIsLocked(false) + .build() + ) + .addInfoList( + PlayerWorldSceneInfo.newBuilder() + .setSceneId(6) + .setIsLocked(false) + .build() + ) + .addInfoList( + PlayerWorldSceneInfo.newBuilder() + .setSceneId(7) + .setIsLocked(false) + .build() + ); - for(int i = 0; i < 3000; i++) { - gaa.addSceneTagIdList(i); - } + var gaa = PlayerWorldSceneInfo.newBuilder() + .setSceneId(9) + .setIsLocked(false); - proto.addInfoList(gaa); + for (int i = 0; i < 3000; i++) { + gaa.addSceneTagIdList(i); + } - this.setData(proto); - } + proto.addInfoList(gaa); + + this.setData(proto); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPrivateChatNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPrivateChatNotify.java index 6454b497c..56516f6cc 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPrivateChatNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPrivateChatNotify.java @@ -6,45 +6,45 @@ import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo; import emu.grasscutter.net.proto.PrivateChatNotifyOuterClass.PrivateChatNotify; public class PacketPrivateChatNotify extends BasePacket { - private ChatInfo info; + private ChatInfo info; - public PacketPrivateChatNotify(int senderId, int recvId, String message) { - super(PacketOpcodes.PrivateChatNotify); - - ChatInfo info = ChatInfo.newBuilder() - .setTime((int) (System.currentTimeMillis() / 1000)) - .setUid(senderId) - .setToUid(recvId) - .setText(message) - .build(); - this.info = info; + public PacketPrivateChatNotify(int senderId, int recvId, String message) { + super(PacketOpcodes.PrivateChatNotify); - PrivateChatNotify proto = PrivateChatNotify.newBuilder() - .setChatInfo(info) - .build(); - - this.setData(proto); - } - - public PacketPrivateChatNotify(int senderId, int recvId, int emote) { - super(PacketOpcodes.PrivateChatNotify); - - ChatInfo info = ChatInfo.newBuilder() - .setTime((int) (System.currentTimeMillis() / 1000)) - .setUid(senderId) - .setToUid(recvId) - .setIcon(emote) - .build(); - this.info = info; - - PrivateChatNotify proto = PrivateChatNotify.newBuilder() - .setChatInfo(info) - .build(); - - this.setData(proto); - } + ChatInfo info = ChatInfo.newBuilder() + .setTime((int) (System.currentTimeMillis() / 1000)) + .setUid(senderId) + .setToUid(recvId) + .setText(message) + .build(); + this.info = info; - public ChatInfo getChatInfo() { - return this.info; - } + PrivateChatNotify proto = PrivateChatNotify.newBuilder() + .setChatInfo(info) + .build(); + + this.setData(proto); + } + + public PacketPrivateChatNotify(int senderId, int recvId, int emote) { + super(PacketOpcodes.PrivateChatNotify); + + ChatInfo info = ChatInfo.newBuilder() + .setTime((int) (System.currentTimeMillis() / 1000)) + .setUid(senderId) + .setToUid(recvId) + .setIcon(emote) + .build(); + this.info = info; + + PrivateChatNotify proto = PrivateChatNotify.newBuilder() + .setChatInfo(info) + .build(); + + this.setData(proto); + } + + public ChatInfo getChatInfo() { + return this.info; + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPullPrivateChatRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPullPrivateChatRsp.java index e7f52a5af..3bdf90f6e 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPullPrivateChatRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPullPrivateChatRsp.java @@ -9,21 +9,21 @@ import emu.grasscutter.net.proto.PullPrivateChatRspOuterClass.PullPrivateChatRsp import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; public class PacketPullPrivateChatRsp extends BasePacket { - - public PacketPullPrivateChatRsp(List history) { - super(PacketOpcodes.PullPrivateChatRsp); - PullPrivateChatRsp.Builder builder = PullPrivateChatRsp.newBuilder(); + public PacketPullPrivateChatRsp(List history) { + super(PacketOpcodes.PullPrivateChatRsp); - if (history == null) { - builder.setRetcode(Retcode.RET_FAIL_VALUE); - } - else { - for (var info : history) { - builder.addChatInfo(info); - } - } - - this.setData(builder.build()); - } + PullPrivateChatRsp.Builder builder = PullPrivateChatRsp.newBuilder(); + + if (history == null) { + builder.setRetcode(Retcode.RET_FAIL_VALUE); + } + else { + for (var info : history) { + builder.addChatInfo(info); + } + } + + this.setData(builder.build()); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java index ac846f8a9..c24f20f5a 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java @@ -13,12 +13,12 @@ import static emu.grasscutter.config.Configuration.*; import java.util.List; public class PacketPullRecentChatRsp extends BasePacket { - public PacketPullRecentChatRsp(List messages) { - super(PacketOpcodes.PullRecentChatRsp); - - PullRecentChatRsp.Builder proto = PullRecentChatRsp.newBuilder() - .addAllChatInfo(messages); + public PacketPullRecentChatRsp(List messages) { + super(PacketOpcodes.PullRecentChatRsp); - this.setData(proto); - } + PullRecentChatRsp.Builder proto = PullRecentChatRsp.newBuilder() + .addAllChatInfo(messages); + + this.setData(proto); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSetOpenStateRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSetOpenStateRsp.java index de94fdc90..939b473c1 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSetOpenStateRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSetOpenStateRsp.java @@ -15,4 +15,4 @@ public class PacketSetOpenStateRsp extends BasePacket { this.setData(proto); } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java index 17ccf8865..98cdde1b7 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java @@ -8,19 +8,19 @@ import emu.grasscutter.net.proto.StoreTypeOuterClass.StoreType; import emu.grasscutter.net.proto.StoreWeightLimitNotifyOuterClass.StoreWeightLimitNotify; public class PacketStoreWeightLimitNotify extends BasePacket { - - public PacketStoreWeightLimitNotify() { - super(PacketOpcodes.StoreWeightLimitNotify); - StoreWeightLimitNotify p = StoreWeightLimitNotify.newBuilder() - .setStoreType(StoreType.STORE_TYPE_PACK) - .setWeightLimit(INVENTORY_LIMITS.all) - .setWeaponCountLimit(INVENTORY_LIMITS.weapons) - .setReliquaryCountLimit(INVENTORY_LIMITS.relics) - .setMaterialCountLimit(INVENTORY_LIMITS.materials) - .setFurnitureCountLimit(INVENTORY_LIMITS.furniture) - .build(); - - this.setData(p); - } + public PacketStoreWeightLimitNotify() { + super(PacketOpcodes.StoreWeightLimitNotify); + + StoreWeightLimitNotify p = StoreWeightLimitNotify.newBuilder() + .setStoreType(StoreType.STORE_TYPE_PACK) + .setWeightLimit(INVENTORY_LIMITS.all) + .setWeaponCountLimit(INVENTORY_LIMITS.weapons) + .setReliquaryCountLimit(INVENTORY_LIMITS.relics) + .setMaterialCountLimit(INVENTORY_LIMITS.materials) + .setFurnitureCountLimit(INVENTORY_LIMITS.furniture) + .build(); + + this.setData(p); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerAllDataRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerAllDataRsp.java index e7adf1833..18424f95e 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerAllDataRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerAllDataRsp.java @@ -16,49 +16,49 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; public class PacketTowerAllDataRsp extends BasePacket { - - public PacketTowerAllDataRsp(TowerSystem towerScheduleManager, TowerManager towerManager) { - super(PacketOpcodes.TowerAllDataRsp); - var recordList = towerManager.getRecordMap().values().stream() - .map(rec -> TowerFloorRecord.newBuilder() - .setFloorId(rec.getFloorId()) - .setFloorStarRewardProgress(rec.getFloorStarRewardProgress()) - .putAllPassedLevelMap(rec.getPassedLevelMap()) - .addAllPassedLevelRecordList(buildFromPassedLevelMap(rec.getPassedLevelMap())) - .build() - ) - .toList(); + public PacketTowerAllDataRsp(TowerSystem towerScheduleManager, TowerManager towerManager) { + super(PacketOpcodes.TowerAllDataRsp); - var openTimeMap = towerScheduleManager.getScheduleFloors().stream() - .collect(Collectors.toMap(x -> x, - y -> DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig() - .getScheduleStartTime())) - ); + var recordList = towerManager.getRecordMap().values().stream() + .map(rec -> TowerFloorRecord.newBuilder() + .setFloorId(rec.getFloorId()) + .setFloorStarRewardProgress(rec.getFloorStarRewardProgress()) + .putAllPassedLevelMap(rec.getPassedLevelMap()) + .addAllPassedLevelRecordList(buildFromPassedLevelMap(rec.getPassedLevelMap())) + .build() + ) + .toList(); - TowerAllDataRsp proto = TowerAllDataRsp.newBuilder() - .setTowerScheduleId(towerScheduleManager.getCurrentTowerScheduleData().getScheduleId()) - .addAllTowerFloorRecordList(recordList) - .setCurLevelRecord(TowerCurLevelRecord.newBuilder().setIsEmpty(true)) - .setScheduleStartTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig() - .getScheduleStartTime())) - .setNextScheduleChangeTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig() - .getNextScheduleChangeTime())) - .putAllFloorOpenTimeMap(openTimeMap) - .setIsFinishedEntranceFloor(towerManager.canEnterScheduleFloor()) - .build(); - - this.setData(proto); - } + var openTimeMap = towerScheduleManager.getScheduleFloors().stream() + .collect(Collectors.toMap(x -> x, + y -> DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig() + .getScheduleStartTime())) + ); - private List buildFromPassedLevelMap(Map map){ - return map.entrySet().stream() - .map(item -> TowerLevelRecordOuterClass.TowerLevelRecord.newBuilder() - .setLevelId(item.getKey()) - .addAllSatisfiedCondList(IntStream.range(1, item.getValue() + 1).boxed().toList()) - .build()) - .toList(); + TowerAllDataRsp proto = TowerAllDataRsp.newBuilder() + .setTowerScheduleId(towerScheduleManager.getCurrentTowerScheduleData().getScheduleId()) + .addAllTowerFloorRecordList(recordList) + .setCurLevelRecord(TowerCurLevelRecord.newBuilder().setIsEmpty(true)) + .setScheduleStartTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig() + .getScheduleStartTime())) + .setNextScheduleChangeTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig() + .getNextScheduleChangeTime())) + .putAllFloorOpenTimeMap(openTimeMap) + .setIsFinishedEntranceFloor(towerManager.canEnterScheduleFloor()) + .build(); - } + this.setData(proto); + } + + private List buildFromPassedLevelMap(Map map) { + return map.entrySet().stream() + .map(item -> TowerLevelRecordOuterClass.TowerLevelRecord.newBuilder() + .setLevelId(item.getKey()) + .addAllSatisfiedCondList(IntStream.range(1, item.getValue() + 1).boxed().toList()) + .build()) + .toList(); + + } } diff --git a/src/main/java/emu/grasscutter/tools/Tools.java b/src/main/java/emu/grasscutter/tools/Tools.java index c473a145a..e34a68f29 100644 --- a/src/main/java/emu/grasscutter/tools/Tools.java +++ b/src/main/java/emu/grasscutter/tools/Tools.java @@ -31,236 +31,236 @@ import static emu.grasscutter.config.Configuration.*; import static emu.grasscutter.utils.Language.translate; public final class Tools { - public static void createGmHandbook() throws Exception { - ToolsWithLanguageOption.createGmHandbook(getLanguageOption()); - } + public static void createGmHandbook() throws Exception { + ToolsWithLanguageOption.createGmHandbook(getLanguageOption()); + } - public static void createGachaMapping(String location) throws Exception { - ToolsWithLanguageOption.createGachaMapping(location, getLanguageOption()); - } + public static void createGachaMapping(String location) throws Exception { + ToolsWithLanguageOption.createGachaMapping(location, getLanguageOption()); + } - public static List getAvailableLanguage() { - File textMapFolder = new File(RESOURCE("TextMap")); - List availableLangList = new ArrayList<>(); - for (String textMapFileName : Objects.requireNonNull(textMapFolder.list((dir, name) -> name.startsWith("TextMap") && name.endsWith(".json")))) { - availableLangList.add(textMapFileName.replace("TextMap", "").replace(".json", "").toLowerCase()); - } return availableLangList; - } + public static List getAvailableLanguage() { + File textMapFolder = new File(RESOURCE("TextMap")); + List availableLangList = new ArrayList<>(); + for (String textMapFileName : Objects.requireNonNull(textMapFolder.list((dir, name) -> name.startsWith("TextMap") && name.endsWith(".json")))) { + availableLangList.add(textMapFileName.replace("TextMap", "").replace(".json", "").toLowerCase()); + } return availableLangList; + } - public static String getLanguageOption() { - List availableLangList = getAvailableLanguage(); - - // Use system out for better format - if (availableLangList.size() == 1) { - return availableLangList.get(0).toUpperCase(); - } - StringBuilder stagedMessage = new StringBuilder(); - stagedMessage.append("The following languages mappings are available, please select one: [default: EN] \n"); - - StringBuilder groupedLangList = new StringBuilder(">\t"); String input; - int groupedLangCount = 0; - - for (String availableLanguage: availableLangList){ - groupedLangCount++; - groupedLangList.append(availableLanguage).append("\t"); - - if (groupedLangCount == 6) { - stagedMessage.append(groupedLangList).append("\n"); - groupedLangCount = 0; - groupedLangList = new StringBuilder(">\t"); - } - } - - if (groupedLangCount > 0) { - stagedMessage.append(groupedLangList).append("\n"); - } - - stagedMessage.append("\nYour choice:[EN] "); - - input = Grasscutter.getConsole().readLine(stagedMessage.toString()); - if (availableLangList.contains(input.toLowerCase())) { - return input.toUpperCase(); - } - Grasscutter.getLogger().info("Invalid option. Will use EN(English) as fallback"); + public static String getLanguageOption() { + List availableLangList = getAvailableLanguage(); - return "EN"; - } + // Use system out for better format + if (availableLangList.size() == 1) { + return availableLangList.get(0).toUpperCase(); + } + StringBuilder stagedMessage = new StringBuilder(); + stagedMessage.append("The following languages mappings are available, please select one: [default: EN] \n"); + + StringBuilder groupedLangList = new StringBuilder(">\t"); String input; + int groupedLangCount = 0; + + for (String availableLanguage: availableLangList) { + groupedLangCount++; + groupedLangList.append(availableLanguage).append("\t"); + + if (groupedLangCount == 6) { + stagedMessage.append(groupedLangList).append("\n"); + groupedLangCount = 0; + groupedLangList = new StringBuilder(">\t"); + } + } + + if (groupedLangCount > 0) { + stagedMessage.append(groupedLangList).append("\n"); + } + + stagedMessage.append("\nYour choice:[EN] "); + + input = Grasscutter.getConsole().readLine(stagedMessage.toString()); + if (availableLangList.contains(input.toLowerCase())) { + return input.toUpperCase(); + } + Grasscutter.getLogger().info("Invalid option. Will use EN(English) as fallback"); + + return "EN"; + } } final class ToolsWithLanguageOption { - @SuppressWarnings("deprecation") - public static void createGmHandbook(String language) throws Exception { - ResourceLoader.loadAll(); - - Map map; - try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(RESOURCE("TextMap/TextMap"+language+".json"))), StandardCharsets.UTF_8)) { - map = Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken>() {}.getType()); - } - - List list; - String fileName = "./GM Handbook.txt"; - try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(fileName), StandardCharsets.UTF_8), false)) { - DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); - LocalDateTime now = LocalDateTime.now(); - - writer.println("// Grasscutter " + GameConstants.VERSION + " GM Handbook"); - writer.println("// Created " + dtf.format(now) + System.lineSeparator() + System.lineSeparator()); + @SuppressWarnings("deprecation") + public static void createGmHandbook(String language) throws Exception { + ResourceLoader.loadAll(); - CommandMap cmdMap = new CommandMap(true); - List cmdList = new ArrayList<>(cmdMap.getAnnotationsAsList()); + Map map; + try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(RESOURCE("TextMap/TextMap"+language+".json"))), StandardCharsets.UTF_8)) { + map = Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken>() {}.getType()); + } - writer.println("// Commands"); - for (Command cmd : cmdList) { - StringBuilder cmdName = new StringBuilder(cmd.label()); - while (cmdName.length() <= 15) { - cmdName.insert(0, " "); - } - writer.println(cmdName + " : " + translate(cmd.description())); - } - writer.println(); + List list; + String fileName = "./GM Handbook.txt"; + try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(fileName), StandardCharsets.UTF_8), false)) { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); + LocalDateTime now = LocalDateTime.now(); - list = new ArrayList<>(GameData.getAvatarDataMap().keySet()); - Collections.sort(list); - - writer.println("// Avatars"); - for (Integer id : list) { - AvatarData data = GameData.getAvatarDataMap().get(id); - writer.println(data.getId() + " : " + map.get(data.getNameTextMapHash())); - } - - writer.println(); - - list = new ArrayList<>(GameData.getItemDataMap().keySet()); - Collections.sort(list); - - writer.println("// Items"); - for (Integer id : list) { - ItemData data = GameData.getItemDataMap().get(id); - writer.println(data.getId() + " : " + map.get(data.getNameTextMapHash())); - } - - writer.println(); - - writer.println("// Scenes"); - list = new ArrayList<>(GameData.getSceneDataMap().keySet()); - Collections.sort(list); - - for (Integer id : list) { - SceneData data = GameData.getSceneDataMap().get(id); - writer.println(data.getId() + " : " + data.getScriptData()); - } - - writer.println(); - - writer.println("// Quests"); - list = new ArrayList<>(GameData.getQuestDataMap().keySet()); - Collections.sort(list); - - for (Integer id : list) { - QuestData data = GameData.getQuestDataMap().get(id); - MainQuestData mainQuest = GameData.getMainQuestDataMap().get(data.getMainId()); - if (mainQuest != null) { - writer.println(data.getId() + " : " + map.get(mainQuest.getTitleTextMapHash()) + " - " + map.get(data.getDescTextMapHash())); - } - } - - writer.println(); - - writer.println("// Monsters"); - list = new ArrayList<>(GameData.getMonsterDataMap().keySet()); - Collections.sort(list); - - for (Integer id : list) { - MonsterData data = GameData.getMonsterDataMap().get(id); - writer.println(data.getId() + " : " + map.get(data.getNameTextMapHash())); - } - } - - Grasscutter.getLogger().info("GM Handbook generated!"); - } + writer.println("// Grasscutter " + GameConstants.VERSION + " GM Handbook"); + writer.println("// Created " + dtf.format(now) + System.lineSeparator() + System.lineSeparator()); - @SuppressWarnings("deprecation") - public static void createGachaMapping(String location, String language) throws Exception { - ResourceLoader.loadResources(); - - Map map; - try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(RESOURCE("TextMap/TextMap" + language + ".json"))), StandardCharsets.UTF_8)) { - map = Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken>() {}.getType()); - } - - List list; + CommandMap cmdMap = new CommandMap(true); + List cmdList = new ArrayList<>(cmdMap.getAnnotationsAsList()); - try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(location), StandardCharsets.UTF_8), false)) { - list = new ArrayList<>(GameData.getAvatarDataMap().keySet()); - Collections.sort(list); - - // if the user made choices for language, I assume it's okay to assign his/her selected language to "en-us" - // since it's the fallback language and there will be no difference in the gacha record page. - // The enduser can still modify the `gacha_mappings.js` directly to enable multilingual for the gacha record system. - writer.println("mappings = {\"en-us\": {"); + writer.println("// Commands"); + for (Command cmd : cmdList) { + StringBuilder cmdName = new StringBuilder(cmd.label()); + while (cmdName.length() <= 15) { + cmdName.insert(0, " "); + } + writer.println(cmdName + " : " + translate(cmd.description())); + } + writer.println(); - // Avatars - boolean first = true; - for (Integer id : list) { - AvatarData data = GameData.getAvatarDataMap().get(id); - int avatarID = data.getId(); - if (avatarID >= 11000000) { // skip test avatar - continue; - } - if (first) { // skip adding comma for the first element - first = false; - } else { - writer.print(","); - } - String color = switch (data.getQualityType()) { - case "QUALITY_PURPLE" -> "purple"; - case "QUALITY_ORANGE" -> "yellow"; - default -> "blue"; - }; - // Got the magic number 4233146695 from manually search in the json file - writer.println( - "\"" + (avatarID % 1000 + 1000) + "\" : [\"" - + map.get(data.getNameTextMapHash()) + "(" + map.get(4233146695L)+ ")\", \"" - + color + "\"]"); - } - - writer.println(); - - list = new ArrayList<>(GameData.getItemDataMap().keySet()); - Collections.sort(list); - - // Weapons - for (Integer id : list) { - ItemData data = GameData.getItemDataMap().get(id); - if (data.getId() <= 11101 || data.getId() >= 20000) { - continue; //skip non weapon items - } - String color; + list = new ArrayList<>(GameData.getAvatarDataMap().keySet()); + Collections.sort(list); - switch (data.getRankLevel()){ - case 3: - color = "blue"; - break; - case 4: - color = "purple"; - break; - case 5: - color = "yellow"; - break; - default: - continue; // skip unnecessary entries - } - - // Got the magic number 4231343903 from manually search in the json file + writer.println("// Avatars"); + for (Integer id : list) { + AvatarData data = GameData.getAvatarDataMap().get(id); + writer.println(data.getId() + " : " + map.get(data.getNameTextMapHash())); + } - writer.println(",\"" + data.getId() + - "\" : [\"" + map.get(data.getNameTextMapHash()).replaceAll("\"", "") - + "("+ map.get(4231343903L)+")\",\""+ color + "\"]"); - } - writer.println(",\"200\": \""+map.get(332935371L)+"\", \"301\": \""+ map.get(2272170627L) + "\", \"302\": \""+map.get(2864268523L)+"\""); - writer.println("}\n}"); - } - - Grasscutter.getLogger().info("Mappings generated to " + location + " !"); - } + writer.println(); + + list = new ArrayList<>(GameData.getItemDataMap().keySet()); + Collections.sort(list); + + writer.println("// Items"); + for (Integer id : list) { + ItemData data = GameData.getItemDataMap().get(id); + writer.println(data.getId() + " : " + map.get(data.getNameTextMapHash())); + } + + writer.println(); + + writer.println("// Scenes"); + list = new ArrayList<>(GameData.getSceneDataMap().keySet()); + Collections.sort(list); + + for (Integer id : list) { + SceneData data = GameData.getSceneDataMap().get(id); + writer.println(data.getId() + " : " + data.getScriptData()); + } + + writer.println(); + + writer.println("// Quests"); + list = new ArrayList<>(GameData.getQuestDataMap().keySet()); + Collections.sort(list); + + for (Integer id : list) { + QuestData data = GameData.getQuestDataMap().get(id); + MainQuestData mainQuest = GameData.getMainQuestDataMap().get(data.getMainId()); + if (mainQuest != null) { + writer.println(data.getId() + " : " + map.get(mainQuest.getTitleTextMapHash()) + " - " + map.get(data.getDescTextMapHash())); + } + } + + writer.println(); + + writer.println("// Monsters"); + list = new ArrayList<>(GameData.getMonsterDataMap().keySet()); + Collections.sort(list); + + for (Integer id : list) { + MonsterData data = GameData.getMonsterDataMap().get(id); + writer.println(data.getId() + " : " + map.get(data.getNameTextMapHash())); + } + } + + Grasscutter.getLogger().info("GM Handbook generated!"); + } + + @SuppressWarnings("deprecation") + public static void createGachaMapping(String location, String language) throws Exception { + ResourceLoader.loadResources(); + + Map map; + try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(RESOURCE("TextMap/TextMap" + language + ".json"))), StandardCharsets.UTF_8)) { + map = Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken>() {}.getType()); + } + + List list; + + try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(location), StandardCharsets.UTF_8), false)) { + list = new ArrayList<>(GameData.getAvatarDataMap().keySet()); + Collections.sort(list); + + // if the user made choices for language, I assume it's okay to assign his/her selected language to "en-us" + // since it's the fallback language and there will be no difference in the gacha record page. + // The enduser can still modify the `gacha_mappings.js` directly to enable multilingual for the gacha record system. + writer.println("mappings = {\"en-us\": {"); + + // Avatars + boolean first = true; + for (Integer id : list) { + AvatarData data = GameData.getAvatarDataMap().get(id); + int avatarID = data.getId(); + if (avatarID >= 11000000) { // skip test avatar + continue; + } + if (first) { // skip adding comma for the first element + first = false; + } else { + writer.print(","); + } + String color = switch (data.getQualityType()) { + case "QUALITY_PURPLE" -> "purple"; + case "QUALITY_ORANGE" -> "yellow"; + default -> "blue"; + }; + // Got the magic number 4233146695 from manually search in the json file + writer.println( + "\"" + (avatarID % 1000 + 1000) + "\" : [\"" + + map.get(data.getNameTextMapHash()) + "(" + map.get(4233146695L)+ ")\", \"" + + color + "\"]"); + } + + writer.println(); + + list = new ArrayList<>(GameData.getItemDataMap().keySet()); + Collections.sort(list); + + // Weapons + for (Integer id : list) { + ItemData data = GameData.getItemDataMap().get(id); + if (data.getId() <= 11101 || data.getId() >= 20000) { + continue; //skip non weapon items + } + String color; + + switch (data.getRankLevel()) { + case 3: + color = "blue"; + break; + case 4: + color = "purple"; + break; + case 5: + color = "yellow"; + break; + default: + continue; // skip unnecessary entries + } + + // Got the magic number 4231343903 from manually search in the json file + + writer.println(",\"" + data.getId() + + "\" : [\"" + map.get(data.getNameTextMapHash()).replaceAll("\"", "") + + "("+ map.get(4231343903L)+")\",\""+ color + "\"]"); + } + writer.println(",\"200\": \""+map.get(332935371L)+"\", \"301\": \""+ map.get(2272170627L) + "\", \"302\": \""+map.get(2864268523L)+"\""); + writer.println("}\n}"); + } + + Grasscutter.getLogger().info("Mappings generated to " + location + " !"); + } } diff --git a/src/main/java/emu/grasscutter/utils/ByteHelper.java b/src/main/java/emu/grasscutter/utils/ByteHelper.java index 1f9e408b1..9cd12bafe 100644 --- a/src/main/java/emu/grasscutter/utils/ByteHelper.java +++ b/src/main/java/emu/grasscutter/utils/ByteHelper.java @@ -1,24 +1,24 @@ -package emu.grasscutter.utils; - -public class ByteHelper { - public static byte[] changeBytes(byte[] a) { - byte[] b = new byte[a.length]; - for (int i = 0; i < a.length; i++) { - b[i] = a[a.length - i - 1]; - } - return b; - } - - public static byte[] longToBytes(long x) { - byte[] bytes = new byte[8]; - bytes[0] = (byte) (x >> 56); - bytes[1] = (byte) (x >> 48); - bytes[2] = (byte) (x >> 40); - bytes[3] = (byte) (x >> 32); - bytes[4] = (byte) (x >> 24); - bytes[5] = (byte) (x >> 16); - bytes[6] = (byte) (x >> 8); - bytes[7] = (byte) (x); - return bytes; - } -} \ No newline at end of file +package emu.grasscutter.utils; + +public class ByteHelper { + public static byte[] changeBytes(byte[] a) { + byte[] b = new byte[a.length]; + for (int i = 0; i < a.length; i++) { + b[i] = a[a.length - i - 1]; + } + return b; + } + + public static byte[] longToBytes(long x) { + byte[] bytes = new byte[8]; + bytes[0] = (byte) (x >> 56); + bytes[1] = (byte) (x >> 48); + bytes[2] = (byte) (x >> 40); + bytes[3] = (byte) (x >> 32); + bytes[4] = (byte) (x >> 24); + bytes[5] = (byte) (x >> 16); + bytes[6] = (byte) (x >> 8); + bytes[7] = (byte) (x); + return bytes; + } +} diff --git a/src/main/java/emu/grasscutter/utils/Crypto.java b/src/main/java/emu/grasscutter/utils/Crypto.java index c8a264f48..1ed4fd1bf 100644 --- a/src/main/java/emu/grasscutter/utils/Crypto.java +++ b/src/main/java/emu/grasscutter/utils/Crypto.java @@ -10,25 +10,25 @@ import java.security.spec.X509EncodedKeySpec; import emu.grasscutter.Grasscutter; public final class Crypto { - private static final SecureRandom secureRandom = new SecureRandom(); + private static final SecureRandom secureRandom = new SecureRandom(); - public static byte[] DISPATCH_KEY; - public static byte[] DISPATCH_SEED; + public static byte[] DISPATCH_KEY; + public static byte[] DISPATCH_SEED; - public static byte[] ENCRYPT_KEY; - public static long ENCRYPT_SEED = Long.parseUnsignedLong("11468049314633205968"); - public static byte[] ENCRYPT_SEED_BUFFER = new byte[0]; + public static byte[] ENCRYPT_KEY; + public static long ENCRYPT_SEED = Long.parseUnsignedLong("11468049314633205968"); + public static byte[] ENCRYPT_SEED_BUFFER = new byte[0]; public static PublicKey CUR_OS_ENCRYPT_KEY; public static PublicKey CUR_CN_ENCRYPT_KEY; public static PrivateKey CUR_SIGNING_KEY; - public static void loadKeys() { - DISPATCH_KEY = FileUtils.readResource("/keys/dispatchKey.bin"); - DISPATCH_SEED = FileUtils.readResource("/keys/dispatchSeed.bin"); + public static void loadKeys() { + DISPATCH_KEY = FileUtils.readResource("/keys/dispatchKey.bin"); + DISPATCH_SEED = FileUtils.readResource("/keys/dispatchSeed.bin"); - ENCRYPT_KEY = FileUtils.readResource("/keys/secretKey.bin"); - ENCRYPT_SEED_BUFFER = FileUtils.readResource("/keys/secretKeyBuffer.bin"); + ENCRYPT_KEY = FileUtils.readResource("/keys/secretKey.bin"); + ENCRYPT_SEED_BUFFER = FileUtils.readResource("/keys/secretKeyBuffer.bin"); try { //These should be loaded from ChannelConfig_whatever.json @@ -44,21 +44,21 @@ public final class Crypto { catch (Exception e) { Grasscutter.getLogger().error("An error occurred while loading keys.", e); } - } + } - public static void xor(byte[] packet, byte[] key) { - try { - for (int i = 0; i < packet.length; i++) { - packet[i] ^= key[i % key.length]; - } - } catch (Exception e) { - Grasscutter.getLogger().error("Crypto error.", e); - } - } + public static void xor(byte[] packet, byte[] key) { + try { + for (int i = 0; i < packet.length; i++) { + packet[i] ^= key[i % key.length]; + } + } catch (Exception e) { + Grasscutter.getLogger().error("Crypto error.", e); + } + } - public static byte[] createSessionKey(int length) { - byte[] bytes = new byte[length]; - secureRandom.nextBytes(bytes); - return bytes; - } + public static byte[] createSessionKey(int length) { + byte[] bytes = new byte[length]; + secureRandom.nextBytes(bytes); + return bytes; + } } diff --git a/src/main/java/emu/grasscutter/utils/Language.java b/src/main/java/emu/grasscutter/utils/Language.java index da022a25e..627c0f310 100644 --- a/src/main/java/emu/grasscutter/utils/Language.java +++ b/src/main/java/emu/grasscutter/utils/Language.java @@ -15,7 +15,7 @@ import java.util.Map; public final class Language { private static final Map cachedLanguages = new ConcurrentHashMap<>(); - + private final JsonObject languageData; private final String languageCode; private final Map cachedTranslations = new ConcurrentHashMap<>(); @@ -54,7 +54,7 @@ public final class Language { */ public static String translate(String key, Object... args) { String translated = Grasscutter.getLanguage().get(key); - + try { return translated.formatted(args); } catch (Exception exception) { @@ -77,7 +77,7 @@ public final class Language { var langCode = Utils.getLanguageCode(player.getAccount().getLocale()); String translated = Grasscutter.getLanguage(langCode).get(key); - + try { return translated.formatted(args); } catch (Exception exception) { @@ -99,13 +99,13 @@ public final class Language { private Language(LanguageStreamDescription description) { @Nullable JsonObject languageData = null; languageCode = description.getLanguageCode(); - + try { languageData = Grasscutter.getGsonFactory().fromJson(Utils.readFromInputStream(description.getLanguageFile()), JsonObject.class); } catch (Exception exception) { Grasscutter.getLogger().warn("Failed to load language file: " + description.getLanguageCode(), exception); } - + this.languageData = languageData; } @@ -117,7 +117,7 @@ public final class Language { private static LanguageStreamDescription getLanguageFileDescription(String languageCode, String fallbackLanguageCode) { var fileName = languageCode + ".json"; var fallback = fallbackLanguageCode + ".json"; - + String actualLanguageCode = languageCode; InputStream file = Grasscutter.class.getResourceAsStream("/languages/" + fileName); @@ -127,21 +127,21 @@ public final class Language { if (cachedLanguages.containsKey(actualLanguageCode)) { return new LanguageStreamDescription(actualLanguageCode, null); } - + file = Grasscutter.class.getResourceAsStream("/languages/" + fallback); } - if(file == null) { // Fallback the fallback language. + if (file == null) { // Fallback the fallback language. Grasscutter.getLogger().warn("Failed to load language file: " + fallback + ", falling back to: en-US.json"); actualLanguageCode = "en-US"; if (cachedLanguages.containsKey(actualLanguageCode)) { return new LanguageStreamDescription(actualLanguageCode, null); } - + file = Grasscutter.class.getResourceAsStream("/languages/en-US.json"); } - if(file == null) + if (file == null) throw new RuntimeException("Unable to load the primary, fallback, and 'en-US' language files."); return new LanguageStreamDescription(actualLanguageCode, file); @@ -153,10 +153,10 @@ public final class Language { * @return The value (as a string) from a nested key. */ public String get(String key) { - if(this.cachedTranslations.containsKey(key)) { + if (this.cachedTranslations.containsKey(key)) { return this.cachedTranslations.get(key); } - + String[] keys = key.split("\\."); JsonObject object = this.languageData; @@ -166,12 +166,12 @@ public final class Language { boolean isValueFound = false; while (true) { - if(index == keys.length) break; - + if (index == keys.length) break; + String currentKey = keys[index++]; - if(object.has(currentKey)) { + if (object.has(currentKey)) { JsonElement element = object.get(currentKey); - if(element.isJsonObject()) + if (element.isJsonObject()) object = element.getAsJsonObject(); else { isValueFound = true; @@ -186,7 +186,7 @@ public final class Language { result += "\nhere is english version:\n" + englishValue; } } - + this.cachedTranslations.put(key, result); return result; } diff --git a/src/main/java/emu/grasscutter/utils/Utils.java b/src/main/java/emu/grasscutter/utils/Utils.java index c7bdad311..45f6b563a 100644 --- a/src/main/java/emu/grasscutter/utils/Utils.java +++ b/src/main/java/emu/grasscutter/utils/Utils.java @@ -26,387 +26,387 @@ import static emu.grasscutter.utils.Language.translate; @SuppressWarnings({"UnusedReturnValue", "BooleanMethodIsAlwaysInverted"}) public final class Utils { - public static final Random random = new Random(); + public static final Random random = new Random(); - public static int randomRange(int min, int max) { - return random.nextInt(max - min + 1) + min; - } + public static int randomRange(int min, int max) { + return random.nextInt(max - min + 1) + min; + } - public static float randomFloatRange(float min, float max) { - return random.nextFloat() * (max - min) + min; - } + public static float randomFloatRange(float min, float max) { + return random.nextFloat() * (max - min) + min; + } - public static double getDist(Position pos1, Position pos2) { - double xs = pos1.getX() - pos2.getX(); - xs = xs * xs; + public static double getDist(Position pos1, Position pos2) { + double xs = pos1.getX() - pos2.getX(); + xs = xs * xs; - double ys = pos1.getY() - pos2.getY(); - ys = ys * ys; + double ys = pos1.getY() - pos2.getY(); + ys = ys * ys; - double zs = pos1.getZ() - pos2.getZ(); - zs = zs * zs; + double zs = pos1.getZ() - pos2.getZ(); + zs = zs * zs; - return Math.sqrt(xs + zs + ys); - } + return Math.sqrt(xs + zs + ys); + } - public static int getCurrentSeconds() { - return (int) (System.currentTimeMillis() / 1000.0); - } + public static int getCurrentSeconds() { + return (int) (System.currentTimeMillis() / 1000.0); + } - public static String lowerCaseFirstChar(String s) { - StringBuilder sb = new StringBuilder(s); - sb.setCharAt(0, Character.toLowerCase(sb.charAt(0))); - return sb.toString(); - } + public static String lowerCaseFirstChar(String s) { + StringBuilder sb = new StringBuilder(s); + sb.setCharAt(0, Character.toLowerCase(sb.charAt(0))); + return sb.toString(); + } - public static String toString(InputStream inputStream) throws IOException { - BufferedInputStream bis = new BufferedInputStream(inputStream); - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - for (int result = bis.read(); result != -1; result = bis.read()) { - buf.write((byte) result); - } - return buf.toString(); - } + public static String toString(InputStream inputStream) throws IOException { + BufferedInputStream bis = new BufferedInputStream(inputStream); + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + for (int result = bis.read(); result != -1; result = bis.read()) { + buf.write((byte) result); + } + return buf.toString(); + } - public static void logByteArray(byte[] array) { - ByteBuf b = Unpooled.wrappedBuffer(array); - Grasscutter.getLogger().info("\n" + ByteBufUtil.prettyHexDump(b)); - b.release(); - } + public static void logByteArray(byte[] array) { + ByteBuf b = Unpooled.wrappedBuffer(array); + Grasscutter.getLogger().info("\n" + ByteBufUtil.prettyHexDump(b)); + b.release(); + } - private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); - public static String bytesToHex(byte[] bytes) { - if (bytes == null) return ""; - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = HEX_ARRAY[v >>> 4]; - hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; - } - return new String(hexChars); - } + private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); + public static String bytesToHex(byte[] bytes) { + if (bytes == null) return ""; + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } - public static String bytesToHex(ByteBuf buf) { - return bytesToHex(byteBufToArray(buf)); - } + public static String bytesToHex(ByteBuf buf) { + return bytesToHex(byteBufToArray(buf)); + } - public static byte[] byteBufToArray(ByteBuf buf) { - byte[] bytes = new byte[buf.capacity()]; - buf.getBytes(0, bytes); - return bytes; - } + public static byte[] byteBufToArray(ByteBuf buf) { + byte[] bytes = new byte[buf.capacity()]; + buf.getBytes(0, bytes); + return bytes; + } - public static int abilityHash(String str) { - int v7 = 0; - int v8 = 0; - while (v8 < str.length()) { - v7 = str.charAt(v8++) + 131 * v7; - } - return v7; - } + public static int abilityHash(String str) { + int v7 = 0; + int v8 = 0; + while (v8 < str.length()) { + v7 = str.charAt(v8++) + 131 * v7; + } + return v7; + } - /** - * Creates a string with the path to a file. - * @param path The path to the file. - * @return A path using the operating system's file separator. - */ - public static String toFilePath(String path) { - return path.replace("/", File.separator); - } + /** + * Creates a string with the path to a file. + * @param path The path to the file. + * @return A path using the operating system's file separator. + */ + public static String toFilePath(String path) { + return path.replace("/", File.separator); + } - /** - * Checks if a file exists on the file system. - * @param path The path to the file. - * @return True if the file exists, false otherwise. - */ - public static boolean fileExists(String path) { - return new File(path).exists(); - } + /** + * Checks if a file exists on the file system. + * @param path The path to the file. + * @return True if the file exists, false otherwise. + */ + public static boolean fileExists(String path) { + return new File(path).exists(); + } - /** - * Creates a folder on the file system. - * @param path The path to the folder. - * @return True if the folder was created, false otherwise. - */ - public static boolean createFolder(String path) { - return new File(path).mkdirs(); - } + /** + * Creates a folder on the file system. + * @param path The path to the folder. + * @return True if the folder was created, false otherwise. + */ + public static boolean createFolder(String path) { + return new File(path).mkdirs(); + } - /** - * Copies a file from the archive's resources to the file system. - * @param resource The path to the resource. - * @param destination The path to copy the resource to. - * @return True if the file was copied, false otherwise. - */ - public static boolean copyFromResources(String resource, String destination) { - try (InputStream stream = Grasscutter.class.getResourceAsStream(resource)) { - if(stream == null) { - Grasscutter.getLogger().warn("Could not find resource: " + resource); - return false; - } + /** + * Copies a file from the archive's resources to the file system. + * @param resource The path to the resource. + * @param destination The path to copy the resource to. + * @return True if the file was copied, false otherwise. + */ + public static boolean copyFromResources(String resource, String destination) { + try (InputStream stream = Grasscutter.class.getResourceAsStream(resource)) { + if (stream == null) { + Grasscutter.getLogger().warn("Could not find resource: " + resource); + return false; + } - Files.copy(stream, new File(destination).toPath(), StandardCopyOption.REPLACE_EXISTING); - return true; - } catch (Exception exception) { - Grasscutter.getLogger().warn("Unable to copy resource " + resource + " to " + destination, exception); - return false; - } - } + Files.copy(stream, new File(destination).toPath(), StandardCopyOption.REPLACE_EXISTING); + return true; + } catch (Exception exception) { + Grasscutter.getLogger().warn("Unable to copy resource " + resource + " to " + destination, exception); + return false; + } + } - /** - * Logs an object to the console. - * @param object The object to log. - */ - public static void logObject(Object object) { - String asJson = Grasscutter.getGsonFactory().toJson(object); - Grasscutter.getLogger().info(asJson); - } + /** + * Logs an object to the console. + * @param object The object to log. + */ + public static void logObject(Object object) { + String asJson = Grasscutter.getGsonFactory().toJson(object); + Grasscutter.getLogger().info(asJson); + } - /** - * Checks for required files and folders before startup. - */ - public static void startupCheck() { - ConfigContainer config = Grasscutter.getConfig(); - Logger logger = Grasscutter.getLogger(); - boolean exit = false; + /** + * Checks for required files and folders before startup. + */ + public static void startupCheck() { + ConfigContainer config = Grasscutter.getConfig(); + Logger logger = Grasscutter.getLogger(); + boolean exit = false; - String resourcesFolder = config.folderStructure.resources; - String dataFolder = config.folderStructure.data; + String resourcesFolder = config.folderStructure.resources; + String dataFolder = config.folderStructure.data; - // Check for resources folder. - if(!fileExists(resourcesFolder)) { - logger.info(translate("messages.status.create_resources")); - logger.info(translate("messages.status.resources_error")); - createFolder(resourcesFolder); exit = true; - } + // Check for resources folder. + if (!fileExists(resourcesFolder)) { + logger.info(translate("messages.status.create_resources")); + logger.info(translate("messages.status.resources_error")); + createFolder(resourcesFolder); exit = true; + } - // Check for BinOutput + ExcelBinOutput. - if(!fileExists(resourcesFolder + "BinOutput") || - !fileExists(resourcesFolder + "ExcelBinOutput")) { - logger.info(translate("messages.status.resources_error")); - exit = true; - } + // Check for BinOutput + ExcelBinOutput. + if (!fileExists(resourcesFolder + "BinOutput") || + !fileExists(resourcesFolder + "ExcelBinOutput")) { + logger.info(translate("messages.status.resources_error")); + exit = true; + } - // Check for game data. - if(!fileExists(dataFolder)) - createFolder(dataFolder); + // Check for game data. + if (!fileExists(dataFolder)) + createFolder(dataFolder); - // Make sure the data folder is populated, if there are any missing files copy them from resources - DataLoader.checkAllFiles(); + // Make sure the data folder is populated, if there are any missing files copy them from resources + DataLoader.checkAllFiles(); - if(exit) System.exit(1); - } + if (exit) System.exit(1); + } - /** - * Gets the timestamp of the next hour. - * @return The timestamp in UNIX seconds. - */ - public static int getNextTimestampOfThisHour(int hour, String timeZone, int param) { - ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of(timeZone)); - for (int i = 0; i < param; i ++){ - if (zonedDateTime.getHour() < hour) { - zonedDateTime = zonedDateTime.withHour(hour).withMinute(0).withSecond(0); - } else { - zonedDateTime = zonedDateTime.plusDays(1).withHour(hour).withMinute(0).withSecond(0); - } - } - return (int) zonedDateTime.toInstant().atZone(ZoneOffset.UTC).toEpochSecond(); - } + /** + * Gets the timestamp of the next hour. + * @return The timestamp in UNIX seconds. + */ + public static int getNextTimestampOfThisHour(int hour, String timeZone, int param) { + ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of(timeZone)); + for (int i = 0; i < param; i ++) { + if (zonedDateTime.getHour() < hour) { + zonedDateTime = zonedDateTime.withHour(hour).withMinute(0).withSecond(0); + } else { + zonedDateTime = zonedDateTime.plusDays(1).withHour(hour).withMinute(0).withSecond(0); + } + } + return (int) zonedDateTime.toInstant().atZone(ZoneOffset.UTC).toEpochSecond(); + } - /** - * Gets the timestamp of the next hour in a week. - * @return The timestamp in UNIX seconds. - */ - public static int getNextTimestampOfThisHourInNextWeek(int hour, String timeZone, int param) { - ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of(timeZone)); - for (int i = 0; i < param; i++) { - if (zonedDateTime.getDayOfWeek() == DayOfWeek.MONDAY && zonedDateTime.getHour() < hour) { - zonedDateTime = ZonedDateTime.now(ZoneId.of(timeZone)).withHour(hour).withMinute(0).withSecond(0); - } else { - zonedDateTime = zonedDateTime.with(TemporalAdjusters.next(DayOfWeek.MONDAY)).withHour(hour).withMinute(0).withSecond(0); - } - } - return (int) zonedDateTime.toInstant().atZone(ZoneOffset.UTC).toEpochSecond(); - } + /** + * Gets the timestamp of the next hour in a week. + * @return The timestamp in UNIX seconds. + */ + public static int getNextTimestampOfThisHourInNextWeek(int hour, String timeZone, int param) { + ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of(timeZone)); + for (int i = 0; i < param; i++) { + if (zonedDateTime.getDayOfWeek() == DayOfWeek.MONDAY && zonedDateTime.getHour() < hour) { + zonedDateTime = ZonedDateTime.now(ZoneId.of(timeZone)).withHour(hour).withMinute(0).withSecond(0); + } else { + zonedDateTime = zonedDateTime.with(TemporalAdjusters.next(DayOfWeek.MONDAY)).withHour(hour).withMinute(0).withSecond(0); + } + } + return (int) zonedDateTime.toInstant().atZone(ZoneOffset.UTC).toEpochSecond(); + } - /** - * Gets the timestamp of the next hour in a month. - * @return The timestamp in UNIX seconds. - */ - public static int getNextTimestampOfThisHourInNextMonth(int hour, String timeZone, int param) { - ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of(timeZone)); - for (int i = 0; i < param; i++) { - if (zonedDateTime.getDayOfMonth() == 1 && zonedDateTime.getHour() < hour) { - zonedDateTime = ZonedDateTime.now(ZoneId.of(timeZone)).withHour(hour).withMinute(0).withSecond(0); - } else { - zonedDateTime = zonedDateTime.with(TemporalAdjusters.firstDayOfNextMonth()).withHour(hour).withMinute(0).withSecond(0); - } - } - return (int) zonedDateTime.toInstant().atZone(ZoneOffset.UTC).toEpochSecond(); - } + /** + * Gets the timestamp of the next hour in a month. + * @return The timestamp in UNIX seconds. + */ + public static int getNextTimestampOfThisHourInNextMonth(int hour, String timeZone, int param) { + ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of(timeZone)); + for (int i = 0; i < param; i++) { + if (zonedDateTime.getDayOfMonth() == 1 && zonedDateTime.getHour() < hour) { + zonedDateTime = ZonedDateTime.now(ZoneId.of(timeZone)).withHour(hour).withMinute(0).withSecond(0); + } else { + zonedDateTime = zonedDateTime.with(TemporalAdjusters.firstDayOfNextMonth()).withHour(hour).withMinute(0).withSecond(0); + } + } + return (int) zonedDateTime.toInstant().atZone(ZoneOffset.UTC).toEpochSecond(); + } - /** - * Retrieves a string from an input stream. - * @param stream The input stream. - * @return The string. - */ - public static String readFromInputStream(@Nullable InputStream stream) { - if(stream == null) return "empty"; + /** + * Retrieves a string from an input stream. + * @param stream The input stream. + * @return The string. + */ + public static String readFromInputStream(@Nullable InputStream stream) { + if (stream == null) return "empty"; - StringBuilder stringBuilder = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { - String line; while ((line = reader.readLine()) != null) { - stringBuilder.append(line); - } stream.close(); - } catch (IOException e) { - Grasscutter.getLogger().warn("Failed to read from input stream."); - } catch (NullPointerException ignored) { - return "empty"; - } return stringBuilder.toString(); - } + StringBuilder stringBuilder = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { + String line; while ((line = reader.readLine()) != null) { + stringBuilder.append(line); + } stream.close(); + } catch (IOException e) { + Grasscutter.getLogger().warn("Failed to read from input stream."); + } catch (NullPointerException ignored) { + return "empty"; + } return stringBuilder.toString(); + } - /** - * Performs a linear interpolation using a table of fixed points to create an effective piecewise f(x) = y function. - * @param x The x value. - * @param xyArray Array of points in [[x0,y0], ... [xN, yN]] format - * @return f(x) = y - */ - public static int lerp(int x, int[][] xyArray) { - try { - if (x <= xyArray[0][0]){ // Clamp to first point - return xyArray[0][1]; - } else if (x >= xyArray[xyArray.length-1][0]) { // Clamp to last point - return xyArray[xyArray.length-1][1]; - } - // At this point we're guaranteed to have two lerp points, and pity be somewhere between them. - for (int i=0; i < xyArray.length-1; i++) { - if (x == xyArray[i+1][0]) { - return xyArray[i+1][1]; - } - if (x < xyArray[i+1][0]) { - // We are between [i] and [i+1], interpolation time! - // Using floats would be slightly cleaner but we can just as easily use ints if we're careful with order of operations. - int position = x - xyArray[i][0]; - int fullDist = xyArray[i+1][0] - xyArray[i][0]; - int prevValue = xyArray[i][1]; - int fullDelta = xyArray[i+1][1] - prevValue; - return prevValue + ( (position * fullDelta) / fullDist ); - } - } - } catch (IndexOutOfBoundsException e) { - Grasscutter.getLogger().error("Malformed lerp point array. Must be of form [[x0, y0], ..., [xN, yN]]."); - } - return 0; - } + /** + * Performs a linear interpolation using a table of fixed points to create an effective piecewise f(x) = y function. + * @param x The x value. + * @param xyArray Array of points in [[x0,y0], ... [xN, yN]] format + * @return f(x) = y + */ + public static int lerp(int x, int[][] xyArray) { + try { + if (x <= xyArray[0][0]) { // Clamp to first point + return xyArray[0][1]; + } else if (x >= xyArray[xyArray.length-1][0]) { // Clamp to last point + return xyArray[xyArray.length-1][1]; + } + // At this point we're guaranteed to have two lerp points, and pity be somewhere between them. + for (int i=0; i < xyArray.length-1; i++) { + if (x == xyArray[i+1][0]) { + return xyArray[i+1][1]; + } + if (x < xyArray[i+1][0]) { + // We are between [i] and [i+1], interpolation time! + // Using floats would be slightly cleaner but we can just as easily use ints if we're careful with order of operations. + int position = x - xyArray[i][0]; + int fullDist = xyArray[i+1][0] - xyArray[i][0]; + int prevValue = xyArray[i][1]; + int fullDelta = xyArray[i+1][1] - prevValue; + return prevValue + ( (position * fullDelta) / fullDist ); + } + } + } catch (IndexOutOfBoundsException e) { + Grasscutter.getLogger().error("Malformed lerp point array. Must be of form [[x0, y0], ..., [xN, yN]]."); + } + return 0; + } - /** - * Checks if an int is in an int[] - * @param key int to look for - * @param array int[] to look in - * @return key in array - */ - public static boolean intInArray(int key, int[] array) { - for (int i : array) { - if (i == key) { - return true; - } - } - return false; - } + /** + * Checks if an int is in an int[] + * @param key int to look for + * @param array int[] to look in + * @return key in array + */ + public static boolean intInArray(int key, int[] array) { + for (int i : array) { + if (i == key) { + return true; + } + } + return false; + } - /** - * Return a copy of minuend without any elements found in subtrahend. - * @param minuend The array we want elements from - * @param subtrahend The array whose elements we don't want - * @return The array with only the elements we want, in the order that minuend had them - */ - public static int[] setSubtract(int[] minuend, int[] subtrahend) { - IntList temp = new IntArrayList(); - for (int i : minuend) { - if (!intInArray(i, subtrahend)) { - temp.add(i); - } - } - return temp.toIntArray(); - } + /** + * Return a copy of minuend without any elements found in subtrahend. + * @param minuend The array we want elements from + * @param subtrahend The array whose elements we don't want + * @return The array with only the elements we want, in the order that minuend had them + */ + public static int[] setSubtract(int[] minuend, int[] subtrahend) { + IntList temp = new IntArrayList(); + for (int i : minuend) { + if (!intInArray(i, subtrahend)) { + temp.add(i); + } + } + return temp.toIntArray(); + } - /** - * Gets the language code from a given locale. - * @param locale A locale. - * @return A string in the format of 'XX-XX'. - */ - public static String getLanguageCode(Locale locale) { - return String.format("%s-%s", locale.getLanguage(), locale.getCountry()); - } + /** + * Gets the language code from a given locale. + * @param locale A locale. + * @return A string in the format of 'XX-XX'. + */ + public static String getLanguageCode(Locale locale) { + return String.format("%s-%s", locale.getLanguage(), locale.getCountry()); + } - /** - * Base64 encodes a given byte array. - * @param toEncode An array of bytes. - * @return A base64 encoded string. - */ - public static String base64Encode(byte[] toEncode) { - return Base64.getEncoder().encodeToString(toEncode); - } + /** + * Base64 encodes a given byte array. + * @param toEncode An array of bytes. + * @return A base64 encoded string. + */ + public static String base64Encode(byte[] toEncode) { + return Base64.getEncoder().encodeToString(toEncode); + } - /** - * Base64 decodes a given string. - * @param toDecode A base64 encoded string. - * @return An array of bytes. - */ - public static byte[] base64Decode(String toDecode) { - return Base64.getDecoder().decode(toDecode); - } + /** + * Base64 decodes a given string. + * @param toDecode A base64 encoded string. + * @return An array of bytes. + */ + public static byte[] base64Decode(String toDecode) { + return Base64.getDecoder().decode(toDecode); + } - /** - * Safely JSON decodes a given string. - * @param jsonData The JSON-encoded data. - * @return JSON decoded data, or null if an exception occurred. - */ - public static T jsonDecode(String jsonData, Class classType) { - try { - return Grasscutter.getGsonFactory().fromJson(jsonData, classType); - } catch (Exception ignored) { - return null; - } - } + /** + * Safely JSON decodes a given string. + * @param jsonData The JSON-encoded data. + * @return JSON decoded data, or null if an exception occurred. + */ + public static T jsonDecode(String jsonData, Class classType) { + try { + return Grasscutter.getGsonFactory().fromJson(jsonData, classType); + } catch (Exception ignored) { + return null; + } + } - /*** - * Draws a random element from the given list, following the given probability distribution, if given. - * @param list The list from which to draw the element. - * @param probabilities The probability distribution. This is given as a list of probabilities of the same length it `list`. - * @return A randomly drawn element from the given list. - */ - public static T drawRandomListElement(List list, List probabilities) { - // If we don't have a probability distribution, or the size of the distribution does not match - // the size of the list, we assume uniform distribution. - if (probabilities == null || probabilities.size() <= 1 || probabilities.size() != list.size()) { - int index = ThreadLocalRandom.current().nextInt(0, list.size()); - return list.get(index); - } + /*** + * Draws a random element from the given list, following the given probability distribution, if given. + * @param list The list from which to draw the element. + * @param probabilities The probability distribution. This is given as a list of probabilities of the same length it `list`. + * @return A randomly drawn element from the given list. + */ + public static T drawRandomListElement(List list, List probabilities) { + // If we don't have a probability distribution, or the size of the distribution does not match + // the size of the list, we assume uniform distribution. + if (probabilities == null || probabilities.size() <= 1 || probabilities.size() != list.size()) { + int index = ThreadLocalRandom.current().nextInt(0, list.size()); + return list.get(index); + } - // Otherwise, we roll with the given distribution. - int totalProbabilityMass = probabilities.stream().reduce(Integer::sum).get(); - int roll = ThreadLocalRandom.current().nextInt(1, totalProbabilityMass + 1); + // Otherwise, we roll with the given distribution. + int totalProbabilityMass = probabilities.stream().reduce(Integer::sum).get(); + int roll = ThreadLocalRandom.current().nextInt(1, totalProbabilityMass + 1); - int currentTotalChance = 0; - for (int i = 0; i < list.size(); i++) { - currentTotalChance += probabilities.get(i); + int currentTotalChance = 0; + for (int i = 0; i < list.size(); i++) { + currentTotalChance += probabilities.get(i); - if (roll <= currentTotalChance) { - return list.get(i); - } - } + if (roll <= currentTotalChance) { + return list.get(i); + } + } - // Should never happen. - return list.get(0); - } + // Should never happen. + return list.get(0); + } - /*** - * Draws a random element from the given list, following a uniform probability distribution. - * @param list The list from which to draw the element. - * @return A randomly drawn element from the given list. - */ - public static T drawRandomListElement(List list) { - return drawRandomListElement(list, null); - } + /*** + * Draws a random element from the given list, following a uniform probability distribution. + * @param list The list from which to draw the element. + * @return A randomly drawn element from the given list. + */ + public static T drawRandomListElement(List list) { + return drawRandomListElement(list, null); + } }