From 5ed1941795ec3986049f1ec51dbf76794b44c51c Mon Sep 17 00:00:00 2001 From: Melledy <121644117+Melledy@users.noreply.github.com> Date: Thu, 21 Dec 2023 17:07:07 -0800 Subject: [PATCH] Refactor how head icons/chat bubbles/phone themes are handled They now have to be added like on official servers instead of being always unlocked (use `/giveall usables`) --- .../UnlockChatBubbleScNotifyOuterClass.java | 260 ++++++++++++++++++ .../UnlockPhoneThemeScNotifyOuterClass.java | 260 ++++++++++++++++++ .../command/commands/GiveAllCommand.java | 25 +- .../lunarcore/data/excel/ChatBubbleExcel.java | 4 +- .../lunarcore/data/excel/PhoneThemeExcel.java | 3 +- .../lunarcore/game/avatar/AvatarStorage.java | 5 +- .../emu/lunarcore/game/avatar/GameAvatar.java | 29 +- .../game/enums/PersonalizeShowType.java | 18 ++ .../lunarcore/game/inventory/Inventory.java | 21 +- .../game/inventory/InventoryService.java | 4 +- .../emu/lunarcore/game/player/Player.java | 120 ++++---- .../game/player/PlayerUnlockData.java | 116 ++++++++ .../recv/HandlerSelectChatBubbleCsReq.java | 11 +- .../recv/HandlerSelectPhoneThemeCsReq.java | 12 +- .../packet/recv/HandlerSetHeadIconCsReq.java | 2 +- .../packet/send/PacketGetPhoneDataScRsp.java | 9 +- .../send/PacketGetPlayerBoardDataScRsp.java | 2 +- .../send/PacketSelectChatBubbleScRsp.java | 14 +- .../send/PacketSelectPhoneThemeScRsp.java | 14 +- .../send/PacketUnlockChatBubbleScNotify.java | 17 ++ .../send/PacketUnlockPhoneThemeScNotify.java | 17 ++ 21 files changed, 842 insertions(+), 121 deletions(-) create mode 100644 src/generated/main/emu/lunarcore/proto/UnlockChatBubbleScNotifyOuterClass.java create mode 100644 src/generated/main/emu/lunarcore/proto/UnlockPhoneThemeScNotifyOuterClass.java create mode 100644 src/main/java/emu/lunarcore/game/enums/PersonalizeShowType.java create mode 100644 src/main/java/emu/lunarcore/game/player/PlayerUnlockData.java create mode 100644 src/main/java/emu/lunarcore/server/packet/send/PacketUnlockChatBubbleScNotify.java create mode 100644 src/main/java/emu/lunarcore/server/packet/send/PacketUnlockPhoneThemeScNotify.java diff --git a/src/generated/main/emu/lunarcore/proto/UnlockChatBubbleScNotifyOuterClass.java b/src/generated/main/emu/lunarcore/proto/UnlockChatBubbleScNotifyOuterClass.java new file mode 100644 index 0000000..c6cab6a --- /dev/null +++ b/src/generated/main/emu/lunarcore/proto/UnlockChatBubbleScNotifyOuterClass.java @@ -0,0 +1,260 @@ +// Code generated by protocol buffer compiler. Do not edit! +package emu.lunarcore.proto; + +import java.io.IOException; +import us.hebi.quickbuf.FieldName; +import us.hebi.quickbuf.InvalidProtocolBufferException; +import us.hebi.quickbuf.JsonSink; +import us.hebi.quickbuf.JsonSource; +import us.hebi.quickbuf.MessageFactory; +import us.hebi.quickbuf.ProtoMessage; +import us.hebi.quickbuf.ProtoSink; +import us.hebi.quickbuf.ProtoSource; + +public final class UnlockChatBubbleScNotifyOuterClass { + /** + * Protobuf type {@code UnlockChatBubbleScNotify} + */ + public static final class UnlockChatBubbleScNotify extends ProtoMessage implements Cloneable { + private static final long serialVersionUID = 0L; + + /** + * optional uint32 bubble_id = 7; + */ + private int bubbleId; + + private UnlockChatBubbleScNotify() { + } + + /** + * @return a new empty instance of {@code UnlockChatBubbleScNotify} + */ + public static UnlockChatBubbleScNotify newInstance() { + return new UnlockChatBubbleScNotify(); + } + + /** + * optional uint32 bubble_id = 7; + * @return whether the bubbleId field is set + */ + public boolean hasBubbleId() { + return (bitField0_ & 0x00000001) != 0; + } + + /** + * optional uint32 bubble_id = 7; + * @return this + */ + public UnlockChatBubbleScNotify clearBubbleId() { + bitField0_ &= ~0x00000001; + bubbleId = 0; + return this; + } + + /** + * optional uint32 bubble_id = 7; + * @return the bubbleId + */ + public int getBubbleId() { + return bubbleId; + } + + /** + * optional uint32 bubble_id = 7; + * @param value the bubbleId to set + * @return this + */ + public UnlockChatBubbleScNotify setBubbleId(final int value) { + bitField0_ |= 0x00000001; + bubbleId = value; + return this; + } + + @Override + public UnlockChatBubbleScNotify copyFrom(final UnlockChatBubbleScNotify other) { + cachedSize = other.cachedSize; + if ((bitField0_ | other.bitField0_) != 0) { + bitField0_ = other.bitField0_; + bubbleId = other.bubbleId; + } + return this; + } + + @Override + public UnlockChatBubbleScNotify mergeFrom(final UnlockChatBubbleScNotify other) { + if (other.isEmpty()) { + return this; + } + cachedSize = -1; + if (other.hasBubbleId()) { + setBubbleId(other.bubbleId); + } + return this; + } + + @Override + public UnlockChatBubbleScNotify clear() { + if (isEmpty()) { + return this; + } + cachedSize = -1; + bitField0_ = 0; + bubbleId = 0; + return this; + } + + @Override + public UnlockChatBubbleScNotify clearQuick() { + if (isEmpty()) { + return this; + } + cachedSize = -1; + bitField0_ = 0; + return this; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof UnlockChatBubbleScNotify)) { + return false; + } + UnlockChatBubbleScNotify other = (UnlockChatBubbleScNotify) o; + return bitField0_ == other.bitField0_ + && (!hasBubbleId() || bubbleId == other.bubbleId); + } + + @Override + public void writeTo(final ProtoSink output) throws IOException { + if ((bitField0_ & 0x00000001) != 0) { + output.writeRawByte((byte) 56); + output.writeUInt32NoTag(bubbleId); + } + } + + @Override + protected int computeSerializedSize() { + int size = 0; + if ((bitField0_ & 0x00000001) != 0) { + size += 1 + ProtoSink.computeUInt32SizeNoTag(bubbleId); + } + return size; + } + + @Override + @SuppressWarnings("fallthrough") + public UnlockChatBubbleScNotify mergeFrom(final ProtoSource input) throws IOException { + // Enabled Fall-Through Optimization (QuickBuffers) + int tag = input.readTag(); + while (true) { + switch (tag) { + case 56: { + // bubbleId + bubbleId = input.readUInt32(); + bitField0_ |= 0x00000001; + tag = input.readTag(); + if (tag != 0) { + break; + } + } + case 0: { + return this; + } + default: { + if (!input.skipField(tag)) { + return this; + } + tag = input.readTag(); + break; + } + } + } + } + + @Override + public void writeTo(final JsonSink output) throws IOException { + output.beginObject(); + if ((bitField0_ & 0x00000001) != 0) { + output.writeUInt32(FieldNames.bubbleId, bubbleId); + } + output.endObject(); + } + + @Override + public UnlockChatBubbleScNotify mergeFrom(final JsonSource input) throws IOException { + if (!input.beginObject()) { + return this; + } + while (!input.isAtEnd()) { + switch (input.readFieldHash()) { + case -1640052025: + case 698016174: { + if (input.isAtField(FieldNames.bubbleId)) { + if (!input.trySkipNullValue()) { + bubbleId = input.readUInt32(); + bitField0_ |= 0x00000001; + } + } else { + input.skipUnknownField(); + } + break; + } + default: { + input.skipUnknownField(); + break; + } + } + } + input.endObject(); + return this; + } + + @Override + public UnlockChatBubbleScNotify clone() { + return new UnlockChatBubbleScNotify().copyFrom(this); + } + + @Override + public boolean isEmpty() { + return ((bitField0_) == 0); + } + + public static UnlockChatBubbleScNotify parseFrom(final byte[] data) throws + InvalidProtocolBufferException { + return ProtoMessage.mergeFrom(new UnlockChatBubbleScNotify(), data).checkInitialized(); + } + + public static UnlockChatBubbleScNotify parseFrom(final ProtoSource input) throws IOException { + return ProtoMessage.mergeFrom(new UnlockChatBubbleScNotify(), input).checkInitialized(); + } + + public static UnlockChatBubbleScNotify parseFrom(final JsonSource input) throws IOException { + return ProtoMessage.mergeFrom(new UnlockChatBubbleScNotify(), input).checkInitialized(); + } + + /** + * @return factory for creating UnlockChatBubbleScNotify messages + */ + public static MessageFactory getFactory() { + return UnlockChatBubbleScNotifyFactory.INSTANCE; + } + + private enum UnlockChatBubbleScNotifyFactory implements MessageFactory { + INSTANCE; + + @Override + public UnlockChatBubbleScNotify create() { + return UnlockChatBubbleScNotify.newInstance(); + } + } + + /** + * Contains name constants used for serializing JSON + */ + static class FieldNames { + static final FieldName bubbleId = FieldName.forField("bubbleId", "bubble_id"); + } + } +} diff --git a/src/generated/main/emu/lunarcore/proto/UnlockPhoneThemeScNotifyOuterClass.java b/src/generated/main/emu/lunarcore/proto/UnlockPhoneThemeScNotifyOuterClass.java new file mode 100644 index 0000000..f1cf79e --- /dev/null +++ b/src/generated/main/emu/lunarcore/proto/UnlockPhoneThemeScNotifyOuterClass.java @@ -0,0 +1,260 @@ +// Code generated by protocol buffer compiler. Do not edit! +package emu.lunarcore.proto; + +import java.io.IOException; +import us.hebi.quickbuf.FieldName; +import us.hebi.quickbuf.InvalidProtocolBufferException; +import us.hebi.quickbuf.JsonSink; +import us.hebi.quickbuf.JsonSource; +import us.hebi.quickbuf.MessageFactory; +import us.hebi.quickbuf.ProtoMessage; +import us.hebi.quickbuf.ProtoSink; +import us.hebi.quickbuf.ProtoSource; + +public final class UnlockPhoneThemeScNotifyOuterClass { + /** + * Protobuf type {@code UnlockPhoneThemeScNotify} + */ + public static final class UnlockPhoneThemeScNotify extends ProtoMessage implements Cloneable { + private static final long serialVersionUID = 0L; + + /** + * optional uint32 theme_id = 1; + */ + private int themeId; + + private UnlockPhoneThemeScNotify() { + } + + /** + * @return a new empty instance of {@code UnlockPhoneThemeScNotify} + */ + public static UnlockPhoneThemeScNotify newInstance() { + return new UnlockPhoneThemeScNotify(); + } + + /** + * optional uint32 theme_id = 1; + * @return whether the themeId field is set + */ + public boolean hasThemeId() { + return (bitField0_ & 0x00000001) != 0; + } + + /** + * optional uint32 theme_id = 1; + * @return this + */ + public UnlockPhoneThemeScNotify clearThemeId() { + bitField0_ &= ~0x00000001; + themeId = 0; + return this; + } + + /** + * optional uint32 theme_id = 1; + * @return the themeId + */ + public int getThemeId() { + return themeId; + } + + /** + * optional uint32 theme_id = 1; + * @param value the themeId to set + * @return this + */ + public UnlockPhoneThemeScNotify setThemeId(final int value) { + bitField0_ |= 0x00000001; + themeId = value; + return this; + } + + @Override + public UnlockPhoneThemeScNotify copyFrom(final UnlockPhoneThemeScNotify other) { + cachedSize = other.cachedSize; + if ((bitField0_ | other.bitField0_) != 0) { + bitField0_ = other.bitField0_; + themeId = other.themeId; + } + return this; + } + + @Override + public UnlockPhoneThemeScNotify mergeFrom(final UnlockPhoneThemeScNotify other) { + if (other.isEmpty()) { + return this; + } + cachedSize = -1; + if (other.hasThemeId()) { + setThemeId(other.themeId); + } + return this; + } + + @Override + public UnlockPhoneThemeScNotify clear() { + if (isEmpty()) { + return this; + } + cachedSize = -1; + bitField0_ = 0; + themeId = 0; + return this; + } + + @Override + public UnlockPhoneThemeScNotify clearQuick() { + if (isEmpty()) { + return this; + } + cachedSize = -1; + bitField0_ = 0; + return this; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof UnlockPhoneThemeScNotify)) { + return false; + } + UnlockPhoneThemeScNotify other = (UnlockPhoneThemeScNotify) o; + return bitField0_ == other.bitField0_ + && (!hasThemeId() || themeId == other.themeId); + } + + @Override + public void writeTo(final ProtoSink output) throws IOException { + if ((bitField0_ & 0x00000001) != 0) { + output.writeRawByte((byte) 8); + output.writeUInt32NoTag(themeId); + } + } + + @Override + protected int computeSerializedSize() { + int size = 0; + if ((bitField0_ & 0x00000001) != 0) { + size += 1 + ProtoSink.computeUInt32SizeNoTag(themeId); + } + return size; + } + + @Override + @SuppressWarnings("fallthrough") + public UnlockPhoneThemeScNotify mergeFrom(final ProtoSource input) throws IOException { + // Enabled Fall-Through Optimization (QuickBuffers) + int tag = input.readTag(); + while (true) { + switch (tag) { + case 8: { + // themeId + themeId = input.readUInt32(); + bitField0_ |= 0x00000001; + tag = input.readTag(); + if (tag != 0) { + break; + } + } + case 0: { + return this; + } + default: { + if (!input.skipField(tag)) { + return this; + } + tag = input.readTag(); + break; + } + } + } + } + + @Override + public void writeTo(final JsonSink output) throws IOException { + output.beginObject(); + if ((bitField0_ & 0x00000001) != 0) { + output.writeUInt32(FieldNames.themeId, themeId); + } + output.endObject(); + } + + @Override + public UnlockPhoneThemeScNotify mergeFrom(final JsonSource input) throws IOException { + if (!input.beginObject()) { + return this; + } + while (!input.isAtEnd()) { + switch (input.readFieldHash()) { + case -1349701436: + case 1108949841: { + if (input.isAtField(FieldNames.themeId)) { + if (!input.trySkipNullValue()) { + themeId = input.readUInt32(); + bitField0_ |= 0x00000001; + } + } else { + input.skipUnknownField(); + } + break; + } + default: { + input.skipUnknownField(); + break; + } + } + } + input.endObject(); + return this; + } + + @Override + public UnlockPhoneThemeScNotify clone() { + return new UnlockPhoneThemeScNotify().copyFrom(this); + } + + @Override + public boolean isEmpty() { + return ((bitField0_) == 0); + } + + public static UnlockPhoneThemeScNotify parseFrom(final byte[] data) throws + InvalidProtocolBufferException { + return ProtoMessage.mergeFrom(new UnlockPhoneThemeScNotify(), data).checkInitialized(); + } + + public static UnlockPhoneThemeScNotify parseFrom(final ProtoSource input) throws IOException { + return ProtoMessage.mergeFrom(new UnlockPhoneThemeScNotify(), input).checkInitialized(); + } + + public static UnlockPhoneThemeScNotify parseFrom(final JsonSource input) throws IOException { + return ProtoMessage.mergeFrom(new UnlockPhoneThemeScNotify(), input).checkInitialized(); + } + + /** + * @return factory for creating UnlockPhoneThemeScNotify messages + */ + public static MessageFactory getFactory() { + return UnlockPhoneThemeScNotifyFactory.INSTANCE; + } + + private enum UnlockPhoneThemeScNotifyFactory implements MessageFactory { + INSTANCE; + + @Override + public UnlockPhoneThemeScNotify create() { + return UnlockPhoneThemeScNotify.newInstance(); + } + } + + /** + * Contains name constants used for serializing JSON + */ + static class FieldNames { + static final FieldName themeId = FieldName.forField("themeId", "theme_id"); + } + } +} diff --git a/src/main/java/emu/lunarcore/command/commands/GiveAllCommand.java b/src/main/java/emu/lunarcore/command/commands/GiveAllCommand.java index 548b493..e27a069 100644 --- a/src/main/java/emu/lunarcore/command/commands/GiveAllCommand.java +++ b/src/main/java/emu/lunarcore/command/commands/GiveAllCommand.java @@ -20,7 +20,7 @@ import emu.lunarcore.game.player.Player; aliases = {"ga"}, permission = "player.give", requireTarget = true, - desc = "/giveall {materials | avatars | lightcones | relics} lv(level). Gives the targeted player items." + desc = "/giveall {materials | avatars | lightcones | relics | usables} lv(level). Gives the targeted player items." ) public class GiveAllCommand implements CommandHandler { @@ -147,15 +147,24 @@ public class GiveAllCommand implements CommandHandler { // Send message args.sendMessage("Giving " + target.getName() + " all avatars"); } - case "ic", "icons" -> { - // Get UnlockedHeads - for (var iconhead : GameData.getPlayerIconExcelMap().values()) { - // This function will handle any duplicate head icons - target.addHeadIcon(iconhead.getId()); + case "unlocks", "usables", "icons" -> { + // Add head icons - Duplicates are handled automatically + for (var excel : GameData.getPlayerIconExcelMap().values()) { + target.getUnlocks().addHeadIcon(excel.getId()); } - + + // Add chat bubbles - Duplicates are handled automatically + for (var excel : GameData.getChatBubbleExcelMap().values()) { + target.getUnlocks().addChatBubble(excel.getId()); + } + + // Add phone themes - Duplicates are handled automatically + for (var excel : GameData.getPhoneThemeExcelMap().values()) { + target.getUnlocks().addPhoneTheme(excel.getId()); + } + // Send message - args.sendMessage("Added all icons to " + target.getName()); + args.sendMessage("Added all icons/chat bubbles/phone themes to " + target.getName()); } case "consumables", "food" -> { // Get consumables diff --git a/src/main/java/emu/lunarcore/data/excel/ChatBubbleExcel.java b/src/main/java/emu/lunarcore/data/excel/ChatBubbleExcel.java index 436932f..1e1d587 100644 --- a/src/main/java/emu/lunarcore/data/excel/ChatBubbleExcel.java +++ b/src/main/java/emu/lunarcore/data/excel/ChatBubbleExcel.java @@ -3,14 +3,14 @@ package emu.lunarcore.data.excel; import emu.lunarcore.data.GameResource; import emu.lunarcore.data.ResourceType; import emu.lunarcore.data.ResourceType.LoadPriority; +import emu.lunarcore.game.enums.PersonalizeShowType; import lombok.Getter; @Getter @ResourceType(name = {"ChatBubbleConfig.json"}, loadPriority = LoadPriority.LOW) public class ChatBubbleExcel extends GameResource { private int ID; - private String ShowType; - private int ShowParam; + private PersonalizeShowType ShowType; @Override public int getId() { diff --git a/src/main/java/emu/lunarcore/data/excel/PhoneThemeExcel.java b/src/main/java/emu/lunarcore/data/excel/PhoneThemeExcel.java index 9f00281..1f45bfc 100644 --- a/src/main/java/emu/lunarcore/data/excel/PhoneThemeExcel.java +++ b/src/main/java/emu/lunarcore/data/excel/PhoneThemeExcel.java @@ -3,13 +3,14 @@ package emu.lunarcore.data.excel; import emu.lunarcore.data.GameResource; import emu.lunarcore.data.ResourceType; import emu.lunarcore.data.ResourceType.LoadPriority; +import emu.lunarcore.game.enums.PersonalizeShowType; import lombok.Getter; @Getter @ResourceType(name = {"PhoneThemeConfig.json"}, loadPriority = LoadPriority.LOW) public class PhoneThemeExcel extends GameResource { private int ID; - private String ShowType; + private PersonalizeShowType ShowType; @Override public int getId() { diff --git a/src/main/java/emu/lunarcore/game/avatar/AvatarStorage.java b/src/main/java/emu/lunarcore/game/avatar/AvatarStorage.java index cdb4fbe..1fc9bc1 100644 --- a/src/main/java/emu/lunarcore/game/avatar/AvatarStorage.java +++ b/src/main/java/emu/lunarcore/game/avatar/AvatarStorage.java @@ -61,10 +61,7 @@ public class AvatarStorage extends BasePlayerManager implements Iterable getTakenRewards() { - if (this.takenRewards == null) { - this.takenRewards = new IntOpenHashSet(); - } - return this.takenRewards; - } public void setOwner(Player player) { this.owner = player; @@ -134,6 +125,10 @@ public class GameAvatar implements GameEntity { return this.getOwner().getRot(); } + public int getHeadIconId() { + return 200000 + this.getAvatarId(); + } + public boolean isHero() { return GameData.getHeroExcelMap().containsKey(this.getAvatarId()); } @@ -200,6 +195,14 @@ public class GameAvatar implements GameEntity { this.heroPath.setAvatar(this); } + public boolean hasTakenReward(int promotion) { + return (this.rewards & (1 << promotion)) != 0; + } + + public void takeReward(int promotion) { + this.rewards |= 1 << promotion; + } + // Buffs public void addBuff(int buffId, int duration) { @@ -290,9 +293,9 @@ public class GameAvatar implements GameEntity { proto.addSkilltreeList(AvatarSkillTree.newInstance().setPointId(skill.getKey()).setLevel(skill.getValue())); } - if (this.takenRewards != null) { - for (int i : this.takenRewards) { - proto.addAllTakenRewards(i); + for (int i = 0; i < this.getPromotion(); i++) { + if (this.hasTakenReward(i)) { + proto.addTakenRewards(i); } } diff --git a/src/main/java/emu/lunarcore/game/enums/PersonalizeShowType.java b/src/main/java/emu/lunarcore/game/enums/PersonalizeShowType.java new file mode 100644 index 0000000..50b59ec --- /dev/null +++ b/src/main/java/emu/lunarcore/game/enums/PersonalizeShowType.java @@ -0,0 +1,18 @@ +package emu.lunarcore.game.enums; + +import lombok.Getter; + +@Getter +public enum PersonalizeShowType { + None (0), + Always (1), + AfterStart (2), + InSchedule (3), + UnlockedOnly (4); + + private int val; + + private PersonalizeShowType(int value) { + this.val = value; + } +} diff --git a/src/main/java/emu/lunarcore/game/inventory/Inventory.java b/src/main/java/emu/lunarcore/game/inventory/Inventory.java index 9b1454b..9b3e2b2 100644 --- a/src/main/java/emu/lunarcore/game/inventory/Inventory.java +++ b/src/main/java/emu/lunarcore/game/inventory/Inventory.java @@ -206,10 +206,23 @@ public class Inventory extends BasePlayerManager { } return null; case Usable: - // Add head icon - if (subType == ItemSubType.HeadIcon) { - getPlayer().addHeadIcon(item.getItemId()); - return item; + // Add usable + switch (subType) { + case HeadIcon -> { + getPlayer().getUnlocks().addHeadIcon(item.getItemId()); + return item; + } + case ChatBubble -> { + getPlayer().getUnlocks().addChatBubble(item.getItemId()); + return item; + } + case PhoneTheme -> { + getPlayer().getUnlocks().addPhoneTheme(item.getItemId()); + return item; + } + default -> { + // Skip + } } // Skip if not food item diff --git a/src/main/java/emu/lunarcore/game/inventory/InventoryService.java b/src/main/java/emu/lunarcore/game/inventory/InventoryService.java index 898635f..aa3581c 100644 --- a/src/main/java/emu/lunarcore/game/inventory/InventoryService.java +++ b/src/main/java/emu/lunarcore/game/inventory/InventoryService.java @@ -225,12 +225,12 @@ public class InventoryService extends BaseGameService { } // Make sure promotion level is odd + Make sure promotion reward isnt already taken - if (promotion % 2 == 0 || avatar.getTakenRewards().contains(promotion)) { + if (promotion % 2 == 0 || avatar.hasTakenReward(promotion)) { return null; } // Set reward as taken - avatar.getTakenRewards().add(promotion); + avatar.takeReward(promotion); avatar.save(); // Setup rewards diff --git a/src/main/java/emu/lunarcore/game/player/Player.java b/src/main/java/emu/lunarcore/game/player/Player.java index b42e4ba..d70dbc3 100644 --- a/src/main/java/emu/lunarcore/game/player/Player.java +++ b/src/main/java/emu/lunarcore/game/player/Player.java @@ -2,9 +2,6 @@ package emu.lunarcore.game.player; import java.nio.charset.StandardCharsets; import java.util.Base64; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; import com.mongodb.client.model.Filters; @@ -69,7 +66,8 @@ import emu.lunarcore.server.packet.CmdId; import emu.lunarcore.server.packet.send.*; import emu.lunarcore.util.Position; import emu.lunarcore.util.Utils; -import it.unimi.dsi.fastutil.ints.*; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import lombok.Getter; import lombok.Setter; @@ -80,13 +78,14 @@ public class Player implements Tickable { @Indexed private String accountUid; private String name; private String signature; + private int birthday; + private int curBasicType; private int headIcon; private int phoneTheme; private int chatBubble; - private int birthday; - private int curBasicType; + private int currentBgm; @Setter private PlayerGender gender; - + private int level; private int exp; // Total exp private int worldLevel; @@ -107,9 +106,6 @@ public class Player implements Tickable { private int floorId; private int entryId; - private int currentBgm; - - private IntSet unlockedHeadIcons; private long lastActiveTime; // Player managers @@ -135,7 +131,8 @@ public class Player implements Tickable { private transient boolean loggedIn; private transient boolean inAnchorRange; private transient int nextBattleId; - private transient Map foodBuffs; + private transient PlayerUnlockData unlocks; + private transient Int2ObjectMap foodBuffs; @Setter private transient boolean paused; @@ -143,7 +140,7 @@ public class Player implements Tickable { public Player() { this.curBasicType = GameConstants.TRAILBLAZER_AVATAR_ID; this.gender = PlayerGender.GENDER_MAN; - this.foodBuffs = new HashMap<>(); + this.foodBuffs = new Int2ObjectOpenHashMap<>(); this.avatars = new AvatarStorage(this); this.inventory = new Inventory(this); @@ -175,7 +172,6 @@ public class Player implements Tickable { this.currentBgm = 210000; - this.unlockedHeadIcons = new IntOpenHashSet(); this.lineupManager = new LineupManager(this); this.gachaInfo = new PlayerGachaInfo(); @@ -288,30 +284,6 @@ public class Player implements Tickable { } } - public int getWorldLevel() { - return this.worldLevel; - } - - public void setPhoneTheme(int themeId) { - this.phoneTheme = themeId; - this.save(); - this.sendPacket(new PacketPlayerSyncScNotify(this)); - } - - public int getPhoneTheme() { - return this.phoneTheme; - } - - public void setChatBubble(int bubbleId) { - this.chatBubble = bubbleId; - this.save(); - this.sendPacket(new PacketPlayerSyncScNotify(this)); - } - - public int getChatBubble() { - return this.chatBubble; - } - public int getCurrentBgm() { if (this.currentBgm == 0) { this.currentBgm = 210000; @@ -325,29 +297,6 @@ public class Player implements Tickable { this.save(); } - public Set getUnlockedHeadIcons() { - if (this.unlockedHeadIcons == null) { - this.unlockedHeadIcons = new IntOpenHashSet(); - } - return this.unlockedHeadIcons; - } - - public void addHeadIcon(int headIconId) { - boolean success = this.getUnlockedHeadIcons().add(headIconId); - if (success) { - this.sendPacket(new PacketPlayerSyncScNotify(this.toBoardData())); - } - } - - public boolean setHeadIcon(int id) { - if (this.getUnlockedHeadIcons().contains(id)) { - this.headIcon = id; - this.save(); - return true; - } - return false; - } - public void resetPosition() { if (this.isOnline()) { return; @@ -373,6 +322,35 @@ public class Player implements Tickable { return getAvatars().getAvatarById(avatarId); } + public boolean setHeadIcon(int id) { + if (this.getUnlocks().getHeadIcons().contains(id)) { + this.headIcon = id; + this.save(); + return true; + } + return false; + } + + public boolean setChatBubble(int id) { + if (this.getUnlocks().getChatBubbles().contains(id)) { + this.chatBubble = id; + this.save(); + this.sendPacket(new PacketPlayerSyncScNotify(this)); + return true; + } + return false; + } + + public boolean setPhoneTheme(int id) { + if (this.getUnlocks().getPhoneThemes().contains(id)) { + this.phoneTheme = id; + this.save(); + this.sendPacket(new PacketPlayerSyncScNotify(this)); + return true; + } + return false; + } + public PlayerLineup getCurrentLineup() { return this.getLineupManager().getCurrentLineup(); } @@ -581,7 +559,7 @@ public class Player implements Tickable { int avatarEntityId = getCurrentLeaderAvatar().getEntityId(); // Remove and send packet for each buff removed - for (var it = getFoodBuffs().entrySet().iterator(); it.hasNext();) { + for (var it = getFoodBuffs().int2ObjectEntrySet().iterator(); it.hasNext();) { var entry = it.next(); var buff = entry.getValue(); @@ -778,7 +756,7 @@ public class Player implements Tickable { @SuppressWarnings("deprecation") public void onLogin() { - // Validate + // Set up lineup manager this.getLineupManager().setPlayer(this); // Load avatars and inventory first @@ -790,6 +768,9 @@ public class Player implements Tickable { this.getChallengeManager().loadFromDatabase(); this.getRogueManager().loadFromDatabase(); + // Load unlockables + this.loadUnlocksFromDatabase(); + // Update stamina this.updateStamina(System.currentTimeMillis()); @@ -824,7 +805,7 @@ public class Player implements Tickable { } } } - + public void onLogout() { this.loggedIn = false; this.lastActiveTime = System.currentTimeMillis() / 1000; @@ -861,6 +842,7 @@ public class Player implements Tickable { datastore.getCollection(PlayerExtraLineup.class).deleteMany(filter); datastore.getCollection(Mail.class).deleteMany(filter); datastore.getCollection(RogueTalentData.class).deleteMany(filter); + datastore.getCollection(PlayerUnlockData.class).deleteOne(filter); // Delete friendships datastore.getCollection(Friendship.class).deleteMany(Filters.or(Filters.eq("ownerUid", uid), Filters.eq("friendUid", uid))); @@ -869,6 +851,16 @@ public class Player implements Tickable { LunarCore.getGameDatabase().delete(this); } + private void loadUnlocksFromDatabase() { + this.unlocks = LunarCore.getGameDatabase().getObjectByField(PlayerUnlockData.class, "ownerUid", this.getUid()); + + if (this.unlocks == null) { + this.unlocks = new PlayerUnlockData(this); + } else { + this.unlocks.setOwner(this); + } + } + // Protobuf serialization public PlayerBasicInfo toProto() { @@ -919,7 +911,7 @@ public class Player implements Tickable { var proto = BoardDataSync.newInstance() .setSignature(this.getSignature()); - for (int id : this.getUnlockedHeadIcons()) { + for (int id : this.getUnlocks().getHeadIcons()) { proto.addUnlockedHeadIconList(HeadIcon.newInstance().setId(id)); } diff --git a/src/main/java/emu/lunarcore/game/player/PlayerUnlockData.java b/src/main/java/emu/lunarcore/game/player/PlayerUnlockData.java new file mode 100644 index 0000000..e582e54 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/player/PlayerUnlockData.java @@ -0,0 +1,116 @@ +package emu.lunarcore.game.player; + +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; +import emu.lunarcore.LunarCore; +import emu.lunarcore.data.GameData; +import emu.lunarcore.game.avatar.GameAvatar; +import emu.lunarcore.game.enums.PersonalizeShowType; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.send.PacketPlayerSyncScNotify; +import emu.lunarcore.server.packet.send.PacketUnlockChatBubbleScNotify; +import emu.lunarcore.server.packet.send.PacketUnlockPhoneThemeScNotify; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import lombok.Getter; + +@Getter +@Entity(value = "unlocks", useDiscriminator = false) +public class PlayerUnlockData { + private transient Player owner; + + @Id private int ownerUid; + + private IntSet headIcons; + private IntSet chatBubbles; + private IntSet phoneThemes; + + @Deprecated // Morphia only + public PlayerUnlockData() {} + + public PlayerUnlockData(Player player) { + this.owner = player; + this.ownerUid = player.getUid(); + + // Add head icons from avatars we already have + for (GameAvatar avatar : owner.getAvatars()) { + this.addHeadIcon(avatar.getHeadIconId()); + } + + // Add default chat bubble(s) + for (var excel : GameData.getChatBubbleExcelMap().values()) { + if (excel.getShowType() == PersonalizeShowType.Always) { + this.addChatBubble(excel.getId()); + } + } + + // Add default phone theme(s) + for (var excel : GameData.getPhoneThemeExcelMap().values()) { + if (excel.getShowType() == PersonalizeShowType.Always) { + this.addPhoneTheme(excel.getId()); + } + } + + this.save(); + } + + protected void setOwner(Player player) { + this.owner = player; + } + + public IntSet getHeadIcons() { + if (this.headIcons == null) { + this.headIcons = new IntOpenHashSet(); + } + return this.headIcons; + } + + public IntSet getChatBubbles() { + if (this.chatBubbles == null) { + this.chatBubbles = new IntOpenHashSet(); + } + return this.chatBubbles; + } + + public IntSet getPhoneThemes() { + if (this.phoneThemes == null) { + this.phoneThemes = new IntOpenHashSet(); + } + return this.phoneThemes; + } + + public void addHeadIcon(int headIconId) { + boolean success = this.getHeadIcons().add(headIconId); + + if (success && this.getOwner().isLoggedIn()) { + this.sendPacket(new PacketPlayerSyncScNotify(getOwner().toBoardData())); + this.save(); + } + } + + public void addChatBubble(int chatBubbleId) { + boolean success = this.getChatBubbles().add(chatBubbleId); + + if (success && this.getOwner().isLoggedIn()) { + this.sendPacket(new PacketUnlockChatBubbleScNotify(chatBubbleId)); + this.save(); + } + } + + public void addPhoneTheme(int phoneThemeId) { + boolean success = this.getPhoneThemes().add(phoneThemeId); + + if (success && this.getOwner().isLoggedIn()) { + this.sendPacket(new PacketUnlockPhoneThemeScNotify(phoneThemeId)); + this.save(); + } + } + + private void sendPacket(BasePacket packet) { + this.getOwner().sendPacket(packet); + } + + public void save() { + LunarCore.getGameDatabase().save(this); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSelectChatBubbleCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSelectChatBubbleCsReq.java index bd76f7d..fad9615 100644 --- a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSelectChatBubbleCsReq.java +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSelectChatBubbleCsReq.java @@ -1,7 +1,6 @@ package emu.lunarcore.server.packet.recv; import emu.lunarcore.server.game.GameSession; -import emu.lunarcore.game.player.Player; import emu.lunarcore.server.packet.CmdId; import emu.lunarcore.server.packet.Opcodes; import emu.lunarcore.server.packet.PacketHandler; @@ -13,11 +12,15 @@ public class HandlerSelectChatBubbleCsReq extends PacketHandler { @Override public void handle(GameSession session, byte[] data) throws Exception { - var req = SelectChatBubbleCsReq.parseFrom(data); - Player player = session.getPlayer(); - session.send(new PacketSelectChatBubbleScRsp(player, req.getBubbleId())); + if (session.getPlayer().setChatBubble(req.getBubbleId())) { + // Success + session.send(new PacketSelectChatBubbleScRsp(req.getBubbleId())); + } else { + // Failure (player didnt have the chat bubble) + session.send(new PacketSelectChatBubbleScRsp()); + } } } diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSelectPhoneThemeCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSelectPhoneThemeCsReq.java index 3b54762..7822419 100644 --- a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSelectPhoneThemeCsReq.java +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSelectPhoneThemeCsReq.java @@ -1,23 +1,27 @@ package emu.lunarcore.server.packet.recv; import emu.lunarcore.server.game.GameSession; -import emu.lunarcore.game.player.Player; import emu.lunarcore.server.packet.CmdId; import emu.lunarcore.server.packet.Opcodes; import emu.lunarcore.server.packet.PacketHandler; import emu.lunarcore.proto.SelectPhoneThemeCsReqOuterClass.SelectPhoneThemeCsReq; import emu.lunarcore.server.packet.send.PacketSelectPhoneThemeScRsp; +import emu.lunarcore.server.packet.send.PacketSetHeadIconScRsp; @Opcodes(CmdId.SelectPhoneThemeCsReq) public class HandlerSelectPhoneThemeCsReq extends PacketHandler { @Override public void handle(GameSession session, byte[] data) throws Exception { - var req = SelectPhoneThemeCsReq.parseFrom(data); - Player player = session.getPlayer(); - session.send(new PacketSelectPhoneThemeScRsp(player, req.getThemeId())); + if (session.getPlayer().setPhoneTheme(req.getThemeId())) { + // Success + session.send(new PacketSelectPhoneThemeScRsp(req.getThemeId())); + } else { + // Failure (player didnt have the phone theme) + session.send(new PacketSetHeadIconScRsp()); + } } } diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetHeadIconCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetHeadIconCsReq.java index c1cbe7d..5a6a01d 100644 --- a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetHeadIconCsReq.java +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSetHeadIconCsReq.java @@ -18,7 +18,7 @@ public class HandlerSetHeadIconCsReq extends PacketHandler { // Success session.send(new PacketSetHeadIconScRsp(req.getId())); } else { - // Failure (player probably didnt have the head icon) + // Failure (player didnt have the head icon) session.send(new PacketSetHeadIconScRsp()); } } diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetPhoneDataScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetPhoneDataScRsp.java index f33f43c..416eb20 100644 --- a/src/main/java/emu/lunarcore/server/packet/send/PacketGetPhoneDataScRsp.java +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetPhoneDataScRsp.java @@ -2,7 +2,6 @@ package emu.lunarcore.server.packet.send; import emu.lunarcore.proto.GetPhoneDataScRspOuterClass.GetPhoneDataScRsp; import emu.lunarcore.server.packet.BasePacket; -import emu.lunarcore.data.GameData; import emu.lunarcore.server.packet.CmdId; import emu.lunarcore.game.player.Player; @@ -15,12 +14,12 @@ public class PacketGetPhoneDataScRsp extends BasePacket { .setCurChatBubble(player.getChatBubble()) .setCurPhoneTheme(player.getPhoneTheme()); - for (var chatBubble : GameData.getChatBubbleExcelMap().values()) { - data.addOwnedChatBubbles(chatBubble.getId()); + for (int chatBubbleId : player.getUnlocks().getChatBubbles()) { + data.addOwnedChatBubbles(chatBubbleId); } - for (var phoneTheme : GameData.getPhoneThemeExcelMap().values()) { - data.addOwnedPhoneThemes(phoneTheme.getId()); + for (int phoneThemeId : player.getUnlocks().getPhoneThemes()) { + data.addOwnedPhoneThemes(phoneThemeId); } this.setData(data); diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketGetPlayerBoardDataScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketGetPlayerBoardDataScRsp.java index 184dcaa..8cebde7 100644 --- a/src/main/java/emu/lunarcore/server/packet/send/PacketGetPlayerBoardDataScRsp.java +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketGetPlayerBoardDataScRsp.java @@ -16,7 +16,7 @@ public class PacketGetPlayerBoardDataScRsp extends BasePacket { .setCurrentHeadIconId(player.getHeadIcon()) .setSignature(player.getSignature()); - for (int id : player.getUnlockedHeadIcons()) { + for (int id : player.getUnlocks().getHeadIcons()) { data.addUnlockedHeadIconList(HeadIcon.newInstance().setId(id)); } diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketSelectChatBubbleScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketSelectChatBubbleScRsp.java index fc79551..a10efde 100644 --- a/src/main/java/emu/lunarcore/server/packet/send/PacketSelectChatBubbleScRsp.java +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketSelectChatBubbleScRsp.java @@ -1,16 +1,22 @@ package emu.lunarcore.server.packet.send; -import emu.lunarcore.game.player.Player; import emu.lunarcore.proto.SelectChatBubbleScRspOuterClass.SelectChatBubbleScRsp; import emu.lunarcore.server.packet.BasePacket; import emu.lunarcore.server.packet.CmdId; public class PacketSelectChatBubbleScRsp extends BasePacket { - - public PacketSelectChatBubbleScRsp(Player player, int bubbleId) { + + public PacketSelectChatBubbleScRsp() { super(CmdId.SelectChatBubbleScRsp); + + var data = SelectChatBubbleScRsp.newInstance() + .setRetcode(1); - player.setChatBubble(bubbleId); + this.setData(data); + } + + public PacketSelectChatBubbleScRsp(int bubbleId) { + super(CmdId.SelectChatBubbleScRsp); var data = SelectChatBubbleScRsp.newInstance() .setCurChatBubble(bubbleId); diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketSelectPhoneThemeScRsp.java b/src/main/java/emu/lunarcore/server/packet/send/PacketSelectPhoneThemeScRsp.java index 6f5e453..0c93ae7 100644 --- a/src/main/java/emu/lunarcore/server/packet/send/PacketSelectPhoneThemeScRsp.java +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketSelectPhoneThemeScRsp.java @@ -3,14 +3,20 @@ package emu.lunarcore.server.packet.send; import emu.lunarcore.proto.SelectPhoneThemeScRspOuterClass.SelectPhoneThemeScRsp; import emu.lunarcore.server.packet.BasePacket; import emu.lunarcore.server.packet.CmdId; -import emu.lunarcore.game.player.Player; public class PacketSelectPhoneThemeScRsp extends BasePacket { - - public PacketSelectPhoneThemeScRsp(Player player, int themeId) { + + public PacketSelectPhoneThemeScRsp() { super(CmdId.SelectPhoneThemeScRsp); + + var data = SelectPhoneThemeScRsp.newInstance() + .setRetcode(1); - player.setPhoneTheme(themeId); + this.setData(data); + } + + public PacketSelectPhoneThemeScRsp(int themeId) { + super(CmdId.SelectPhoneThemeScRsp); var data = SelectPhoneThemeScRsp.newInstance() .setCurPhoneTheme(themeId); diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketUnlockChatBubbleScNotify.java b/src/main/java/emu/lunarcore/server/packet/send/PacketUnlockChatBubbleScNotify.java new file mode 100644 index 0000000..b182849 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketUnlockChatBubbleScNotify.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.UnlockChatBubbleScNotifyOuterClass.UnlockChatBubbleScNotify; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketUnlockChatBubbleScNotify extends BasePacket { + + public PacketUnlockChatBubbleScNotify(int id) { + super(CmdId.UnlockChatBubbleScNotify); + + var data = UnlockChatBubbleScNotify.newInstance() + .setBubbleId(id); + + this.setData(data); + } +} diff --git a/src/main/java/emu/lunarcore/server/packet/send/PacketUnlockPhoneThemeScNotify.java b/src/main/java/emu/lunarcore/server/packet/send/PacketUnlockPhoneThemeScNotify.java new file mode 100644 index 0000000..ed4d550 --- /dev/null +++ b/src/main/java/emu/lunarcore/server/packet/send/PacketUnlockPhoneThemeScNotify.java @@ -0,0 +1,17 @@ +package emu.lunarcore.server.packet.send; + +import emu.lunarcore.proto.UnlockPhoneThemeScNotifyOuterClass.UnlockPhoneThemeScNotify; +import emu.lunarcore.server.packet.BasePacket; +import emu.lunarcore.server.packet.CmdId; + +public class PacketUnlockPhoneThemeScNotify extends BasePacket { + + public PacketUnlockPhoneThemeScNotify(int id) { + super(CmdId.UnlockPhoneThemeScNotify); + + var data = UnlockPhoneThemeScNotify.newInstance() + .setThemeId(id); + + this.setData(data); + } +}