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`)
This commit is contained in:
Melledy
2023-12-21 17:07:07 -08:00
parent 96eae134dc
commit 5ed1941795
21 changed files with 842 additions and 121 deletions

View File

@@ -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<UnlockChatBubbleScNotify> implements Cloneable {
private static final long serialVersionUID = 0L;
/**
* <code>optional uint32 bubble_id = 7;</code>
*/
private int bubbleId;
private UnlockChatBubbleScNotify() {
}
/**
* @return a new empty instance of {@code UnlockChatBubbleScNotify}
*/
public static UnlockChatBubbleScNotify newInstance() {
return new UnlockChatBubbleScNotify();
}
/**
* <code>optional uint32 bubble_id = 7;</code>
* @return whether the bubbleId field is set
*/
public boolean hasBubbleId() {
return (bitField0_ & 0x00000001) != 0;
}
/**
* <code>optional uint32 bubble_id = 7;</code>
* @return this
*/
public UnlockChatBubbleScNotify clearBubbleId() {
bitField0_ &= ~0x00000001;
bubbleId = 0;
return this;
}
/**
* <code>optional uint32 bubble_id = 7;</code>
* @return the bubbleId
*/
public int getBubbleId() {
return bubbleId;
}
/**
* <code>optional uint32 bubble_id = 7;</code>
* @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<UnlockChatBubbleScNotify> getFactory() {
return UnlockChatBubbleScNotifyFactory.INSTANCE;
}
private enum UnlockChatBubbleScNotifyFactory implements MessageFactory<UnlockChatBubbleScNotify> {
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");
}
}
}

View File

@@ -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<UnlockPhoneThemeScNotify> implements Cloneable {
private static final long serialVersionUID = 0L;
/**
* <code>optional uint32 theme_id = 1;</code>
*/
private int themeId;
private UnlockPhoneThemeScNotify() {
}
/**
* @return a new empty instance of {@code UnlockPhoneThemeScNotify}
*/
public static UnlockPhoneThemeScNotify newInstance() {
return new UnlockPhoneThemeScNotify();
}
/**
* <code>optional uint32 theme_id = 1;</code>
* @return whether the themeId field is set
*/
public boolean hasThemeId() {
return (bitField0_ & 0x00000001) != 0;
}
/**
* <code>optional uint32 theme_id = 1;</code>
* @return this
*/
public UnlockPhoneThemeScNotify clearThemeId() {
bitField0_ &= ~0x00000001;
themeId = 0;
return this;
}
/**
* <code>optional uint32 theme_id = 1;</code>
* @return the themeId
*/
public int getThemeId() {
return themeId;
}
/**
* <code>optional uint32 theme_id = 1;</code>
* @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<UnlockPhoneThemeScNotify> getFactory() {
return UnlockPhoneThemeScNotifyFactory.INSTANCE;
}
private enum UnlockPhoneThemeScNotifyFactory implements MessageFactory<UnlockPhoneThemeScNotify> {
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");
}
}
}

View File

@@ -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

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -61,10 +61,7 @@ public class AvatarStorage extends BasePlayerManager implements Iterable<GameAva
getPlayer().sendPacket(new PacketPlayerSyncScNotify(avatar));
// Add head icon
int headIconId = 200000 + avatar.getAvatarId();
if (GameData.getItemExcelMap().containsKey(headIconId)) {
getPlayer().addHeadIcon(headIconId);
}
getPlayer().getUnlocks().addHeadIcon(avatar.getHeadIconId());
// Done
return true;

View File

@@ -1,13 +1,11 @@
package emu.lunarcore.game.avatar;
import java.util.Map;
import java.util.Set;
import org.bson.types.ObjectId;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed;
import emu.lunarcore.GameConstants;
import emu.lunarcore.LunarCore;
import emu.lunarcore.data.GameData;
@@ -53,7 +51,7 @@ public class GameAvatar implements GameEntity {
@Setter private int exp;
@Setter private int promotion;
private IntSet takenRewards;
private int rewards; // Previously known as "taken rewards"
private long timestamp;
@Getter(AccessLevel.NONE) private int currentHp;
@@ -107,13 +105,6 @@ public class GameAvatar implements GameEntity {
}
}
public Set<Integer> getTakenRewards() {
if (this.takenRewards == null) {
this.takenRewards = new IntOpenHashSet();
}
return this.takenRewards;
}
public void setOwner(Player player) {
this.owner = player;
this.ownerUid = player.getUid();
@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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,11 +78,12 @@ 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;
@@ -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<Integer, SceneBuff> foodBuffs;
private transient PlayerUnlockData unlocks;
private transient Int2ObjectMap<SceneBuff> 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<Integer> 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());
@@ -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));
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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());
}
}

View File

@@ -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);

View File

@@ -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));
}

View File

@@ -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);
player.setChatBubble(bubbleId);
var data = SelectChatBubbleScRsp.newInstance()
.setRetcode(1);
this.setData(data);
}
public PacketSelectChatBubbleScRsp(int bubbleId) {
super(CmdId.SelectChatBubbleScRsp);
var data = SelectChatBubbleScRsp.newInstance()
.setCurChatBubble(bubbleId);

View File

@@ -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);
player.setPhoneTheme(themeId);
var data = SelectPhoneThemeScRsp.newInstance()
.setRetcode(1);
this.setData(data);
}
public PacketSelectPhoneThemeScRsp(int themeId) {
super(CmdId.SelectPhoneThemeScRsp);
var data = SelectPhoneThemeScRsp.newInstance()
.setCurPhoneTheme(themeId);

View File

@@ -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);
}
}

View File

@@ -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);
}
}