From 2b42c727dccd93010ff881ff399c056891f38b59 Mon Sep 17 00:00:00 2001 From: Melledy <121644117+Melledy@users.noreply.github.com> Date: Thu, 30 Oct 2025 08:04:42 -0700 Subject: [PATCH] Implement gallery showcase --- src/main/java/emu/nebula/GameConstants.java | 2 +- src/main/java/emu/nebula/data/GameData.java | 1 + .../nebula/data/resources/HandbookDef.java | 24 +++++++++ .../game/character/CharacterStorage.java | 30 +++++++++++ .../java/emu/nebula/game/player/Player.java | 47 +++++++++++----- .../nebula/game/player/PlayerHandbook.java | 53 +++++++++++++++++++ .../handlers/HandlerPlayerBoardSetReq.java | 23 ++++++++ 7 files changed, 166 insertions(+), 14 deletions(-) create mode 100644 src/main/java/emu/nebula/data/resources/HandbookDef.java create mode 100644 src/main/java/emu/nebula/game/player/PlayerHandbook.java create mode 100644 src/main/java/emu/nebula/server/handlers/HandlerPlayerBoardSetReq.java diff --git a/src/main/java/emu/nebula/GameConstants.java b/src/main/java/emu/nebula/GameConstants.java index 98aa93f..7e66516 100644 --- a/src/main/java/emu/nebula/GameConstants.java +++ b/src/main/java/emu/nebula/GameConstants.java @@ -12,5 +12,5 @@ public class GameConstants { public static final int EXP_ITEM_ID = 21; public static final int MAX_FORMATIONS = 5; - + public static final int MAX_SHOWCASE_IDS = 5; } diff --git a/src/main/java/emu/nebula/data/GameData.java b/src/main/java/emu/nebula/data/GameData.java index 239bba4..4c4e24f 100644 --- a/src/main/java/emu/nebula/data/GameData.java +++ b/src/main/java/emu/nebula/data/GameData.java @@ -49,6 +49,7 @@ public class GameData { @Getter private static DataTable WorldClassDataTable = new DataTable<>(); @Getter private static DataTable GuideGroupDataTable = new DataTable<>(); + @Getter private static DataTable HandbookDataTable = new DataTable<>(); @Getter private static DataTable StoryDataTable = new DataTable<>(); @Getter private static DataTable StorySetSectionDataTable = new DataTable<>(); diff --git a/src/main/java/emu/nebula/data/resources/HandbookDef.java b/src/main/java/emu/nebula/data/resources/HandbookDef.java new file mode 100644 index 0000000..170de2a --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/HandbookDef.java @@ -0,0 +1,24 @@ +package emu.nebula.data.resources; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.ResourceType; +import emu.nebula.data.ResourceType.LoadPriority; +import lombok.Getter; + +@Getter +@ResourceType(name = "Handbook.json", loadPriority = LoadPriority.LOW) +public class HandbookDef extends BaseDef { + private int Id; + private int Index; + private int Type; + + @Override + public int getId() { + return Id; + } + + @Override + public void onLoad() { + + } +} diff --git a/src/main/java/emu/nebula/game/character/CharacterStorage.java b/src/main/java/emu/nebula/game/character/CharacterStorage.java index fb5488f..8e19f8d 100644 --- a/src/main/java/emu/nebula/game/character/CharacterStorage.java +++ b/src/main/java/emu/nebula/game/character/CharacterStorage.java @@ -8,6 +8,7 @@ import emu.nebula.data.resources.CharacterDef; import emu.nebula.data.resources.DiscDef; import emu.nebula.game.player.PlayerManager; import emu.nebula.game.player.Player; +import emu.nebula.game.player.PlayerHandbook; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import lombok.Getter; @@ -68,6 +69,21 @@ public class CharacterStorage extends PlayerManager { return this.getCharacters().values(); } + public PlayerHandbook getCharacterHandbook() { + var handbook = new PlayerHandbook(1); + + for (var character : this.getCharacterCollection()) { + // Get handbook + var data = GameData.getHandbookDataTable().get(400000 + character.getSkin()); + if (data == null) continue; + + // Set flag + handbook.setBit(data.getIndex()); + } + + return handbook; + } + // Discs public GameDisc getDiscById(int id) { @@ -112,6 +128,20 @@ public class CharacterStorage extends PlayerManager { return this.getDiscs().values(); } + public PlayerHandbook getDiscHandbook() { + var handbook = new PlayerHandbook(2); + + for (var disc : this.getDiscCollection()) { + // Get handbook + var data = GameData.getHandbookDataTable().get(disc.getDiscId()); + if (data == null) continue; + + // Set flag + handbook.setBit(data.getIndex()); + } + + return handbook; + } // Database diff --git a/src/main/java/emu/nebula/game/player/Player.java b/src/main/java/emu/nebula/game/player/Player.java index 2bea5dc..2fe13e0 100644 --- a/src/main/java/emu/nebula/game/player/Player.java +++ b/src/main/java/emu/nebula/game/player/Player.java @@ -33,6 +33,7 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; import lombok.Getter; +import us.hebi.quickbuf.RepeatedInt; @Getter @Entity(value = "players", useDiscriminator = false) @@ -56,7 +57,7 @@ public class Player implements GameDatabaseObject { private int energy; - private IntSet boards; + private int[] boards; private IntSet headIcons; private IntSet titles; @@ -101,7 +102,7 @@ public class Player implements GameDatabaseObject { this.titleSuffix = 2; this.level = 1; this.energy = 240; - this.boards = new IntOpenHashSet(); + this.boards = new int[] {410301}; this.headIcons = new IntOpenHashSet(); this.titles = new IntOpenHashSet(); this.createTime = Nebula.getCurrentTime(); @@ -120,9 +121,6 @@ public class Player implements GameDatabaseObject { // Add titles this.getTitles().add(this.getTitlePrefix()); this.getTitles().add(this.getTitleSuffix()); - - // Add board ids - this.getBoards().add(410301); } public Account getAccount() { @@ -225,6 +223,28 @@ public class Player implements GameDatabaseObject { // Success return true; } + + public boolean setBoard(RepeatedInt ids) { + // Length check + if (ids.length() <= 0 || ids.length() > GameConstants.MAX_SHOWCASE_IDS) { + return false; + } + + // Get max length + this.boards = new int[ids.length()]; + + // Copy ids to our boards array + for (int i = 0; i < ids.length(); i++) { + int id = ids.get(i); + this.boards[i] = id; + } + + // Save to database + Nebula.getGameDatabase().update(this, this.getUid(), "boards", this.getBoards()); + + // Success + return true; + } public void setNewbieInfo(int groupId, int stepId) { // TODO @@ -338,7 +358,9 @@ public class Player implements GameDatabaseObject { // Proto public PlayerInfo toProto() { - PlayerInfo proto = PlayerInfo.newInstance(); + PlayerInfo proto = PlayerInfo.newInstance() + .setServerTs(Nebula.getCurrentTime()) + .setAchievements(new byte[64]); var acc = proto.getMutableAcc() .setNickName(this.getName()) @@ -452,16 +474,15 @@ public class Player implements GameDatabaseObject { proto.addDictionaries(dictionaryProto); } - - // Server timestamp - proto.setServerTs(Nebula.getCurrentTime()); - // Extra - proto.setAchievements(new byte[64]); - - // Add instance + // Add instances this.getInstanceManager().toProto(proto); + // Handbook + proto.addHandbook(this.getCharacters().getCharacterHandbook().toProto()); + proto.addHandbook(this.getCharacters().getDiscHandbook().toProto()); + + // Extra proto.getMutableVampireSurvivorRecord() .getMutableSeason(); diff --git a/src/main/java/emu/nebula/game/player/PlayerHandbook.java b/src/main/java/emu/nebula/game/player/PlayerHandbook.java new file mode 100644 index 0000000..79ce241 --- /dev/null +++ b/src/main/java/emu/nebula/game/player/PlayerHandbook.java @@ -0,0 +1,53 @@ +package emu.nebula.game.player; + +import emu.nebula.proto.Public.HandbookInfo; +import lombok.Getter; + +@Getter +public class PlayerHandbook { + private int type; + private long[] data; + + public PlayerHandbook(int type) { + this.type = type; + this.data = new long[1]; + } + + public void setBit(int index) { + int longArrayOffset = (int) Math.floor((index - 1) / 64D); + int bytePosition = ((index - 1) % 64); + + if (longArrayOffset >= this.data.length) { + var oldData = this.data; + this.data = new long[longArrayOffset + 1]; + System.arraycopy(oldData, 0, this.data, 0, oldData.length); + } + + this.data[longArrayOffset] |= (1L << bytePosition); + } + + public byte[] toByteArray() { + byte[] array = new byte[this.getData().length * 8]; + + for (int i = 0; i < this.getData().length; i++) { + long value = this.getData()[i]; + + for (int x = 7; x >= 0; x--) { + array[(i * 8) + x] = (byte) (value & 0xFF); + value >>= Byte.SIZE; + } + } + + return array; + } + + // Proto + + public HandbookInfo toProto() { + var proto = HandbookInfo.newInstance() + .setType(this.getType()) + .setData(this.toByteArray()); + + return proto; + } +} diff --git a/src/main/java/emu/nebula/server/handlers/HandlerPlayerBoardSetReq.java b/src/main/java/emu/nebula/server/handlers/HandlerPlayerBoardSetReq.java new file mode 100644 index 0000000..28eb88f --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerPlayerBoardSetReq.java @@ -0,0 +1,23 @@ +package emu.nebula.server.handlers; + +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.PlayerBoard.PlayerBoardSetReq; +import emu.nebula.net.HandlerId; +import emu.nebula.net.GameSession; + +@HandlerId(NetMsgId.player_board_set_req) +public class HandlerPlayerBoardSetReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse request + var req = PlayerBoardSetReq.parseFrom(message); + + // Set board + boolean success = session.getPlayer().setBoard(req.getIds()); + + return this.encodeMsg(success ? NetMsgId.player_board_set_succeed_ack : NetMsgId.player_board_set_failed_ack); + } + +}