From 334c8dc12e22fdf1f4dc2ad70fa240d7a1a8cbc6 Mon Sep 17 00:00:00 2001 From: Melledy <121644117+Melledy@users.noreply.github.com> Date: Mon, 17 Nov 2025 07:25:18 -0800 Subject: [PATCH] Add `!character` command --- .../java/emu/nebula/command/CommandArgs.java | 85 +++++++++++++++--- .../command/commands/CharacterCommand.java | 87 +++++++++++++++++++ .../game/character/CharacterContact.java | 6 +- .../game/character/CharacterGemPreset.java | 2 +- .../game/character/CharacterStorage.java | 14 +-- .../{Character.java => GameCharacter.java} | 16 +++- .../emu/nebula/game/player/PlayerModule.java | 4 +- .../game/scoreboss/ScoreBossRankEntry.java | 4 +- .../handlers/HandlerPlayerSignatureEdit.java | 17 ++++ src/main/java/emu/nebula/util/Utils.java | 4 +- 10 files changed, 206 insertions(+), 33 deletions(-) create mode 100644 src/main/java/emu/nebula/command/commands/CharacterCommand.java rename src/main/java/emu/nebula/game/character/{Character.java => GameCharacter.java} (98%) diff --git a/src/main/java/emu/nebula/command/CommandArgs.java b/src/main/java/emu/nebula/command/CommandArgs.java index f44f280..a2a05e9 100644 --- a/src/main/java/emu/nebula/command/CommandArgs.java +++ b/src/main/java/emu/nebula/command/CommandArgs.java @@ -3,6 +3,7 @@ package emu.nebula.command; import java.util.List; import emu.nebula.Nebula; +import emu.nebula.game.character.GameCharacter; import emu.nebula.game.player.Player; import emu.nebula.util.Utils; import it.unimi.dsi.fastutil.ints.Int2IntMap; @@ -21,9 +22,9 @@ public class CommandArgs { private int targetUid; private int amount; private int level = -1; - private int rank = -1; - private int promotion = -1; - private int stage = -1; + private int advance = -1; + private int talent = -1; + private int skill = -1; private Int2IntMap map; private ObjectSet flags; @@ -50,17 +51,14 @@ public class CommandArgs { } else if (arg.startsWith("lv")) { // Level this.level = Utils.parseSafeInt(arg.substring(2)); it.remove(); - } else if (arg.startsWith("r")) { // Rank - this.rank = Utils.parseSafeInt(arg.substring(1)); + } else if (arg.startsWith("a")) { // Advance + this.advance = Utils.parseSafeInt(arg.substring(1)); it.remove(); - } else if (arg.startsWith("e")) { // Eidolons - this.rank = Utils.parseSafeInt(arg.substring(1)); + } else if (arg.startsWith("t")) { // Talents + this.talent = Utils.parseSafeInt(arg.substring(1)); it.remove(); - } else if (arg.startsWith("p")) { // Promotion - this.promotion = Utils.parseSafeInt(arg.substring(1)); - it.remove(); - } else if (arg.startsWith("s")) { // Stage or Superimposition - this.stage = Utils.parseSafeInt(arg.substring(1)); + } else if (arg.startsWith("s")) { // Skill + this.skill = Utils.parseSafeInt(arg.substring(1)); it.remove(); } } else if (arg.startsWith("-")) { // Flag @@ -127,4 +125,67 @@ public class CommandArgs { return this.flags.contains(flag); } + // Utility commands + + /** + * Changes the properties of an character based on the arguments provided + * @param character The targeted character to change + * @return A boolean of whether or not any changes were made to the character + */ + public boolean setProperties(GameCharacter character) { + boolean hasChanged = false; + + // Try to set level + if (this.getLevel() > 0 && character.getLevel() != this.getLevel()) { + character.setLevel(Math.min(this.getLevel(), 90)); + character.setAdvance(Utils.getMinAdvanceForLevel(character.getLevel())); + hasChanged = true; + } + + // Try to set advance (ascension level) + if (this.getAdvance() >= 0 && character.getAdvance() != this.getAdvance()) { + character.setAdvance(Math.min(this.getAdvance(), 8)); + hasChanged = true; + } + + // Try to set skill trees + if (this.getSkill() > 0) { + int skill = Math.min(this.getSkill(), 10); + + for (int i = 0; i < 4; i++) { + int s = character.getSkills()[i]; + + if (s != skill) { + character.getSkills()[i] = skill; + hasChanged = true; + } + } + } + + // Try to set talents + if (this.getTalent() >= 0) { + // Clear talents first + character.getTalents().clear(); + + // Calculate how many talent stars we want to set + int talent = Math.min(this.getTalent(), 5); + + for (int i = 0; i < talent; i++) { + // Get bitset offset + int offset = i * 16; + + // First 10 sub nodes of a talent star + for (int x = 1; x <= 10; x++) { + character.getTalents().setBit(offset + x); + } + + // Final sub node of a talent star + character.getTalents().setBit(offset + 16); + } + + hasChanged = true; + } + + return hasChanged; + } } diff --git a/src/main/java/emu/nebula/command/commands/CharacterCommand.java b/src/main/java/emu/nebula/command/commands/CharacterCommand.java new file mode 100644 index 0000000..075502a --- /dev/null +++ b/src/main/java/emu/nebula/command/commands/CharacterCommand.java @@ -0,0 +1,87 @@ +package emu.nebula.command.commands; + +import emu.nebula.util.Utils; +import emu.nebula.game.character.GameCharacter; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.PubilcGm.Chars; + +import java.util.ArrayList; +import java.util.HashSet; + +import emu.nebula.command.Command; +import emu.nebula.command.CommandArgs; +import emu.nebula.command.CommandHandler; + +@Command( + label = "character", + aliases = {"c", "char"}, + permission = "player.character", + requireTarget = true, + desc = "!c [all | {characterId}] lv(level) a(ascension) s(skill level) t(talent level)" +) +public class CharacterCommand implements CommandHandler { + + @Override + public void execute(CommandArgs args) { + // Init + var player = args.getTarget(); + var characters = new HashSet(); + + // Parse args + for (String arg : args.getList()) { + // Lowercase + arg = arg.toLowerCase(); + + // Handle all characters + if (arg.equals("all")) { + characters.addAll(player.getCharacters().getCharacterCollection()); + continue; + } + + // Parse char id + int charId = Utils.parseSafeInt(arg); + + var character = player.getCharacters().getCharacterById(charId); + if (character == null) { + continue; + } + + characters.add(character); + } + + // Sanity check + if (characters.isEmpty()) { + return; + } + + // List of modified characters that we send to the client for updates + var modified = new ArrayList(); + + // Modify characters + for (var character : characters) { + // Apply changes + boolean changed = args.setProperties(character); + + if (changed) { + // Save to database + character.save(); + + // Add to modified list + modified.add(character); + } + } + + if (modified.isEmpty()) { + return; + } + + // Encode and send + var proto = Chars.newInstance(); + + for (var character : modified) { + proto.addList(character.toProto()); + } + + player.addNextPackage(NetMsgId.chars_final_notify, proto); + } +} diff --git a/src/main/java/emu/nebula/game/character/CharacterContact.java b/src/main/java/emu/nebula/game/character/CharacterContact.java index d09b771..b6551a2 100644 --- a/src/main/java/emu/nebula/game/character/CharacterContact.java +++ b/src/main/java/emu/nebula/game/character/CharacterContact.java @@ -16,7 +16,7 @@ import us.hebi.quickbuf.RepeatedInt; @Getter @Entity(useDiscriminator = false) public class CharacterContact { - private transient Character character; + private transient GameCharacter character; private boolean top; private long triggerTime; @@ -27,7 +27,7 @@ public class CharacterContact { } - public CharacterContact(Character character) { + public CharacterContact(GameCharacter character) { this.character = character; this.chats = new HashMap<>(); this.triggerTime = character.getCreateTime(); @@ -45,7 +45,7 @@ public class CharacterContact { } } - public void setCharacter(Character character) { + public void setCharacter(GameCharacter character) { this.character = character; } diff --git a/src/main/java/emu/nebula/game/character/CharacterGemPreset.java b/src/main/java/emu/nebula/game/character/CharacterGemPreset.java index 1c2e123..f0e68e9 100644 --- a/src/main/java/emu/nebula/game/character/CharacterGemPreset.java +++ b/src/main/java/emu/nebula/game/character/CharacterGemPreset.java @@ -15,7 +15,7 @@ public class CharacterGemPreset { } - public CharacterGemPreset(Character character) { + public CharacterGemPreset(GameCharacter character) { this.gems = new int[] {-1, -1, -1}; } diff --git a/src/main/java/emu/nebula/game/character/CharacterStorage.java b/src/main/java/emu/nebula/game/character/CharacterStorage.java index d8de760..1439d89 100644 --- a/src/main/java/emu/nebula/game/character/CharacterStorage.java +++ b/src/main/java/emu/nebula/game/character/CharacterStorage.java @@ -16,7 +16,7 @@ import lombok.Getter; @Getter public class CharacterStorage extends PlayerManager { - private final Int2ObjectMap characters; + private final Int2ObjectMap characters; private final Int2ObjectMap discs; public CharacterStorage(Player player) { @@ -28,7 +28,7 @@ public class CharacterStorage extends PlayerManager { // Characters - public Character getCharacterById(int id) { + public GameCharacter getCharacterById(int id) { if (id <= 0) { return null; } @@ -40,7 +40,7 @@ public class CharacterStorage extends PlayerManager { return this.characters.containsKey(id); } - public Character addCharacter(int charId) { + public GameCharacter addCharacter(int charId) { // Sanity check to make sure we dont have this character already if (this.hasCharacter(charId)) { return null; @@ -49,14 +49,14 @@ public class CharacterStorage extends PlayerManager { return this.addCharacter(GameData.getCharacterDataTable().get(charId)); } - private Character addCharacter(CharacterDef data) { + private GameCharacter addCharacter(CharacterDef data) { // Sanity check to make sure we dont have this character already if (this.hasCharacter(data.getId())) { return null; } // Create character - var character = new Character(this.getPlayer(), data); + var character = new GameCharacter(this.getPlayer(), data); // Save to database character.save(); @@ -66,7 +66,7 @@ public class CharacterStorage extends PlayerManager { return character; } - public Collection getCharacterCollection() { + public Collection getCharacterCollection() { return this.getCharacters().values(); } @@ -169,7 +169,7 @@ public class CharacterStorage extends PlayerManager { public void loadFromDatabase() { var db = Nebula.getGameDatabase(); - db.getObjects(Character.class, "playerUid", getPlayerUid()).forEach(character -> { + db.getObjects(GameCharacter.class, "playerUid", getPlayerUid()).forEach(character -> { // Get data var data = GameData.getCharacterDataTable().get(character.getCharId()); diff --git a/src/main/java/emu/nebula/game/character/Character.java b/src/main/java/emu/nebula/game/character/GameCharacter.java similarity index 98% rename from src/main/java/emu/nebula/game/character/Character.java rename to src/main/java/emu/nebula/game/character/GameCharacter.java index 6fd522d..9288f43 100644 --- a/src/main/java/emu/nebula/game/character/Character.java +++ b/src/main/java/emu/nebula/game/character/GameCharacter.java @@ -40,7 +40,7 @@ import us.hebi.quickbuf.RepeatedInt; @Getter @Entity(value = "characters", useDiscriminator = false) -public class Character implements GameDatabaseObject { +public class GameCharacter implements GameDatabaseObject { @Id private ObjectId uid; @Indexed @@ -65,15 +65,15 @@ public class Character implements GameDatabaseObject { private CharacterContact contact; @Deprecated // Morphia only! - public Character() { + public GameCharacter() { } - public Character(Player player, int charId) { + public GameCharacter(Player player, int charId) { this(player, GameData.getCharacterDataTable().get(charId)); } - public Character(Player player, CharacterDef data) { + public GameCharacter(Player player, CharacterDef data) { this.player = player; this.playerUid = player.getUid(); this.charId = data.getId(); @@ -110,6 +110,14 @@ public class Character implements GameDatabaseObject { } } + public void setLevel(int level) { + this.level = level; + } + + public void setAdvance(int advance) { + this.advance = advance; + } + public int getMaxGainableExp() { if (this.getLevel() >= this.getMaxLevel()) { return 0; diff --git a/src/main/java/emu/nebula/game/player/PlayerModule.java b/src/main/java/emu/nebula/game/player/PlayerModule.java index 6a6087c..e37f5d3 100644 --- a/src/main/java/emu/nebula/game/player/PlayerModule.java +++ b/src/main/java/emu/nebula/game/player/PlayerModule.java @@ -12,7 +12,7 @@ import emu.nebula.game.GameContextModule; import emu.nebula.game.account.Account; import emu.nebula.game.agent.AgentManager; import emu.nebula.game.battlepass.BattlePass; -import emu.nebula.game.character.Character; +import emu.nebula.game.character.GameCharacter; import emu.nebula.game.character.GameDisc; import emu.nebula.game.formation.FormationManager; import emu.nebula.game.friends.Friendship; @@ -167,7 +167,7 @@ public class PlayerModule extends GameContextModule { var datastore = Nebula.getGameDatabase().getDatastore(); // Delete data from collections - datastore.getCollection(Character.class).deleteMany(multiFilter); + datastore.getCollection(GameCharacter.class).deleteMany(multiFilter); datastore.getCollection(GameDisc.class).deleteMany(multiFilter); datastore.getCollection(GameItem.class).deleteMany(multiFilter); datastore.getCollection(GameResource.class).deleteMany(multiFilter); diff --git a/src/main/java/emu/nebula/game/scoreboss/ScoreBossRankEntry.java b/src/main/java/emu/nebula/game/scoreboss/ScoreBossRankEntry.java index a071165..6a625d4 100644 --- a/src/main/java/emu/nebula/game/scoreboss/ScoreBossRankEntry.java +++ b/src/main/java/emu/nebula/game/scoreboss/ScoreBossRankEntry.java @@ -11,7 +11,7 @@ import dev.morphia.annotations.Id; import emu.nebula.database.GameDatabaseObject; import emu.nebula.game.player.Player; import emu.nebula.game.tower.StarTowerBuild; -import emu.nebula.game.character.Character; +import emu.nebula.game.character.GameCharacter; import emu.nebula.proto.Public.HonorInfo; import emu.nebula.proto.ScoreBossRank.ScoreBossRankChar; import emu.nebula.proto.ScoreBossRank.ScoreBossRankData; @@ -155,7 +155,7 @@ public class ScoreBossRankEntry implements GameDatabaseObject { } - public ScoreBossCharEntry(Character character) { + public ScoreBossCharEntry(GameCharacter character) { this.id = character.getCharId(); this.level = character.getLevel(); } diff --git a/src/main/java/emu/nebula/server/handlers/HandlerPlayerSignatureEdit.java b/src/main/java/emu/nebula/server/handlers/HandlerPlayerSignatureEdit.java index cf7afb7..e9a77a8 100644 --- a/src/main/java/emu/nebula/server/handlers/HandlerPlayerSignatureEdit.java +++ b/src/main/java/emu/nebula/server/handlers/HandlerPlayerSignatureEdit.java @@ -3,7 +3,9 @@ package emu.nebula.server.handlers; import emu.nebula.net.NetHandler; import emu.nebula.net.NetMsgId; import emu.nebula.proto.PlayerSignatureEdit.PlayerSignatureEditReq; +import emu.nebula.proto.Public.Error; import emu.nebula.net.HandlerId; +import emu.nebula.Nebula; import emu.nebula.net.GameSession; @HandlerId(NetMsgId.player_signature_edit_req) @@ -13,7 +15,22 @@ public class HandlerPlayerSignatureEdit extends NetHandler { public byte[] handle(GameSession session, byte[] message) throws Exception { // Parse request var req = PlayerSignatureEditReq.parseFrom(message); + var signature = req.getSignature(); + if (signature == null) { + return session.encodeMsg(NetMsgId.player_signature_edit_failed_ack); + } + + // Check if we need to handle a command + if (signature.charAt(0) == '!' || signature.charAt(0) == '/') { + Nebula.getCommandManager().invoke(session.getPlayer(), signature); + return session.encodeMsg( + NetMsgId.player_signature_edit_failed_ack, + Error.newInstance().setCode(119902).addArguments("\nCommand Success") + ); + } + + // Edit signature session.getPlayer().editSignature(req.getSignature()); // Send response diff --git a/src/main/java/emu/nebula/util/Utils.java b/src/main/java/emu/nebula/util/Utils.java index 8b06e59..f8ae5e2 100644 --- a/src/main/java/emu/nebula/util/Utils.java +++ b/src/main/java/emu/nebula/util/Utils.java @@ -86,8 +86,8 @@ public class Utils { return Math.floorDiv(System.currentTimeMillis(), 1000); } - public static int getMinPromotionForLevel(int level) { - return Math.max(Math.min((int) ((level - 11) / 10D), 6), 0); + public static int getMinAdvanceForLevel(int level) { + return Math.max(Math.min((int) ((level - 1) / 10D), 8), 0); } /**