diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java index 271a6f014..3c05e6ef5 100644 --- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java +++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java @@ -1,7 +1,5 @@ package emu.grasscutter.game; -import java.util.*; - import dev.morphia.annotations.*; import emu.grasscutter.GenshinConstants; import emu.grasscutter.Grasscutter; @@ -23,7 +21,6 @@ import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; -import emu.grasscutter.net.proto.BirthdayOuterClass.Birthday; import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry; import emu.grasscutter.net.proto.HeadImageOuterClass.HeadImage; import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType; @@ -35,39 +32,18 @@ import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; import emu.grasscutter.net.proto.WorldPlayerLocationInfoOuterClass.WorldPlayerLocationInfo; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameSession; -import emu.grasscutter.server.packet.send.PacketAbilityInvocationsNotify; -import emu.grasscutter.server.packet.send.PacketAvatarAddNotify; -import emu.grasscutter.server.packet.send.PacketAvatarDataNotify; -import emu.grasscutter.server.packet.send.PacketAvatarGainCostumeNotify; -import emu.grasscutter.server.packet.send.PacketAvatarGainFlycloakNotify; -import emu.grasscutter.server.packet.send.PacketClientAbilityInitFinishNotify; -import emu.grasscutter.server.packet.send.PacketCombatInvocationsNotify; -import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp; -import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; -import emu.grasscutter.server.packet.send.PacketOpenStateUpdateNotify; -import emu.grasscutter.server.packet.send.PacketPlayerApplyEnterMpResultNotify; -import emu.grasscutter.server.packet.send.PacketPlayerDataNotify; -import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify; -import emu.grasscutter.server.packet.send.PacketPlayerPropNotify; -import emu.grasscutter.server.packet.send.PacketPlayerStoreNotify; -import emu.grasscutter.server.packet.send.PacketPrivateChatNotify; -import emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify; -import emu.grasscutter.server.packet.send.PacketPlayerLevelRewardUpdateNotify; -import emu.grasscutter.server.packet.send.PacketSetNameCardRsp; -import emu.grasscutter.server.packet.send.PacketStoreWeightLimitNotify; -import emu.grasscutter.server.packet.send.PacketUnlockNameCardNotify; -import emu.grasscutter.server.packet.send.PacketWorldPlayerLocationNotify; -import emu.grasscutter.server.packet.send.PacketWorldPlayerRTTNotify; +import emu.grasscutter.server.packet.send.*; import emu.grasscutter.utils.Position; - import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import java.util.*; + @Entity(value = "players", useDiscriminator = false) public class GenshinPlayer { @Id private int id; @Indexed(options = @IndexOptions(unique = true)) private String accountId; - + @Transient private Account account; private String nickname; private String signature; @@ -76,12 +52,12 @@ public class GenshinPlayer { private Position pos; private Position rotation; private PlayerBirthday birthday; - + private Map properties; private Set nameCardList; private Set flyCloakList; private Set costumeList; - + @Transient private long nextGuid = 0; @Transient private int peerId; @Transient private World world; @@ -90,7 +66,7 @@ public class GenshinPlayer { @Transient private AvatarStorage avatars; @Transient private Inventory inventory; @Transient private FriendsList friendsList; - + private TeamManager teamManager; private PlayerGachaInfo gachaInfo; private PlayerProfile playerProfile; @@ -98,25 +74,26 @@ public class GenshinPlayer { private boolean showAvatar; private ArrayList shownAvatars; private Set rewardedLevels; - + private int sceneId; private int regionId; private int mainCharacterId; private boolean godmode; - + @Transient private boolean paused; @Transient private int enterSceneToken; @Transient private SceneLoadState sceneState; @Transient private boolean hasSentAvatarDataNotify; @Transient private long nextSendPlayerLocTime = 0; - + @Transient private final Int2ObjectMap coopRequests; @Transient private final InvokeHandler combatInvokeHandler; @Transient private final InvokeHandler abilityInvokeHandler; @Transient private final InvokeHandler clientAbilityInitFinishHandler; - - @Deprecated @SuppressWarnings({ "rawtypes", "unchecked" }) // Morphia only! - public GenshinPlayer() { + + @Deprecated + @SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only! + public GenshinPlayer() { this.inventory = new Inventory(this); this.avatars = new AvatarStorage(this); this.friendsList = new FriendsList(this); @@ -129,16 +106,16 @@ public class GenshinPlayer { } this.properties.put(prop.getId(), 0); } - + this.gachaInfo = new PlayerGachaInfo(); this.nameCardList = new HashSet<>(); this.flyCloakList = new HashSet<>(); this.costumeList = new HashSet<>(); - + this.setSceneId(3); this.setRegionId(1); this.sceneState = SceneLoadState.NONE; - + this.coopRequests = new Int2ObjectOpenHashMap<>(); this.combatInvokeHandler = new InvokeHandler(PacketCombatInvocationsNotify.class); this.abilityInvokeHandler = new InvokeHandler(PacketAbilityInvocationsNotify.class); @@ -147,7 +124,7 @@ public class GenshinPlayer { this.birthday = new PlayerBirthday(); this.rewardedLevels = new HashSet<>(); } - + // On player creation public GenshinPlayer(GameSession session) { this(); @@ -179,7 +156,7 @@ public class GenshinPlayer { public void setUid(int id) { this.id = id; } - + public long getNextGenshinGuid() { long nextId = ++this.nextGuid; return ((long) this.getUid() << 32) + nextId; @@ -201,23 +178,23 @@ public class GenshinPlayer { public void setSession(GameSession session) { this.session = session; } - + public boolean isOnline() { return this.getSession() != null && this.getSession().isActive(); } - + public GameServer getServer() { return this.getSession().getServer(); } - + public synchronized World getWorld() { return this.world; } - + public synchronized void setWorld(World world) { this.world = world; } - + public GenshinScene getScene() { return scene; } @@ -238,7 +215,7 @@ public class GenshinPlayer { this.nickname = nickName; this.updateProfile(); } - + public int getHeadImage() { return headImage; } @@ -260,71 +237,71 @@ public class GenshinPlayer { public Position getPos() { return pos; } - + public Position getRotation() { return rotation; } - + public int getLevel() { return this.getProperty(PlayerProperty.PROP_PLAYER_LEVEL); } - + public int getExp() { return this.getProperty(PlayerProperty.PROP_PLAYER_EXP); } - + public int getWorldLevel() { return this.getProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL); } - + public int getPrimogems() { return this.getProperty(PlayerProperty.PROP_PLAYER_HCOIN); } - + public void setPrimogems(int primogem) { this.setProperty(PlayerProperty.PROP_PLAYER_HCOIN, primogem); this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_HCOIN)); } - + public int getMora() { return this.getProperty(PlayerProperty.PROP_PLAYER_SCOIN); } - + public void setMora(int mora) { this.setProperty(PlayerProperty.PROP_PLAYER_SCOIN, mora); this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_SCOIN)); } - + private int getExpRequired(int level) { PlayerLevelData levelData = GenshinData.getPlayerLevelDataMap().get(level); - return levelData != null ? levelData.getExp() : 0; + return levelData != null ? levelData.getExp() : 0; } - + private float getExpModifier() { return Grasscutter.getConfig().getGameServerOptions().getGameRates().ADVENTURE_EXP_RATE; } - + // Affected by exp rate public void earnExp(int exp) { addExpDirectly((int) (exp * getExpModifier())); } - + // Directly give player exp public void addExpDirectly(int gain) { boolean hasLeveledUp = false; int level = getLevel(); int exp = getExp(); int reqExp = getExpRequired(level); - + exp += gain; - + while (exp >= reqExp && reqExp > 0) { exp -= reqExp; level += 1; reqExp = getExpRequired(level); hasLeveledUp = true; } - + if (hasLeveledUp) { // Set level property this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, level); @@ -333,18 +310,18 @@ public class GenshinPlayer { // Update player with packet this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_LEVEL)); } - + // Set exp this.setProperty(PlayerProperty.PROP_PLAYER_EXP, exp); - + // Update player with packet this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_EXP)); } - + private void updateProfile() { getProfile().syncWithCharacter(this); } - + public boolean isFirstLoginEnterScene() { return !this.hasSentAvatarDataNotify; } @@ -367,11 +344,11 @@ public class GenshinPlayer { public Map getProperties() { return properties; } - + public void setProperty(PlayerProperty prop, int value) { getProperties().put(prop.getId(), value); } - + public int getProperty(PlayerProperty prop) { return getProperties().get(prop.getId()); } @@ -379,11 +356,11 @@ public class GenshinPlayer { public Set getFlyCloakList() { return flyCloakList; } - + public Set getCostumeList() { return costumeList; } - + public Set getNameCardList() { return this.nameCardList; } @@ -391,7 +368,7 @@ public class GenshinPlayer { public MpSettingType getMpSetting() { return mpSetting; } - + public synchronized Int2ObjectMap getCoopRequests() { return coopRequests; } @@ -399,7 +376,7 @@ public class GenshinPlayer { public InvokeHandler getCombatInvokeHandler() { return this.combatInvokeHandler; } - + public InvokeHandler getAbilityInvokeHandler() { return this.abilityInvokeHandler; } @@ -415,11 +392,11 @@ public class GenshinPlayer { public AvatarStorage getAvatars() { return avatars; } - + public Inventory getInventory() { return inventory; } - + public FriendsList getFriendsList() { return this.friendsList; } @@ -472,7 +449,7 @@ public class GenshinPlayer { public void setPaused(boolean newPauseState) { boolean oldPauseState = this.paused; this.paused = newPauseState; - + if (newPauseState && !oldPauseState) { this.onPause(); } else if (oldPauseState && !newPauseState) { @@ -487,7 +464,7 @@ public class GenshinPlayer { public void setSceneLoadState(SceneLoadState sceneState) { this.sceneState = sceneState; } - + public boolean isInMultiplayer() { return this.getWorld() != null && this.getWorld().isMultiplayer(); } @@ -507,7 +484,7 @@ public class GenshinPlayer { public void setRegionId(int regionId) { this.regionId = regionId; } - + public boolean inGodmode() { return godmode; } @@ -526,11 +503,11 @@ public class GenshinPlayer { public void addAvatar(GenshinAvatar avatar) { boolean result = getAvatars().addAvatar(avatar); - + if (result) { // Add starting weapon getAvatars().addStartingWeapon(avatar); - + // Try adding to team if possible //List currentTeam = this.getTeamManager().getCurrentTeam(); boolean addedToTeam = false; @@ -540,7 +517,7 @@ public class GenshinPlayer { addedToTeam = currentTeam } */ - + // Done if (hasSentAvatarDataNotify()) { // Recalc stats @@ -552,55 +529,56 @@ public class GenshinPlayer { // Failed adding avatar } } - + public void addFlycloak(int flycloakId) { this.getFlyCloakList().add(flycloakId); this.sendPacket(new PacketAvatarGainFlycloakNotify(flycloakId)); } - + public void addCostume(int costumeId) { this.getCostumeList().add(costumeId); this.sendPacket(new PacketAvatarGainCostumeNotify(costumeId)); } - + public void addNameCard(int nameCardId) { this.getNameCardList().add(nameCardId); this.sendPacket(new PacketUnlockNameCardNotify(nameCardId)); } - + public void setNameCard(int nameCardId) { if (!this.getNameCardList().contains(nameCardId)) { return; } - + this.setNameCardId(nameCardId); - + this.sendPacket(new PacketSetNameCardRsp(nameCardId)); } - + public void dropMessage(Object message) { this.sendPacket(new PacketPrivateChatNotify(GenshinConstants.SERVER_CONSOLE_UID, getUid(), message.toString())); } /** * Sends a message to another player. - * @param sender The sender of the message. + * + * @param sender The sender of the message. * @param message The message to send. */ public void sendMessage(GenshinPlayer sender, Object message) { this.sendPacket(new PacketPrivateChatNotify(sender.getUid(), this.getUid(), message.toString())); } - + public void interactWith(int gadgetEntityId) { GenshinEntity entity = getScene().getEntityById(gadgetEntityId); - + if (entity == null) { return; } - + // Delete entity.getScene().removeEntity(entity); - + // Handle if (entity instanceof EntityItem) { // Pick item @@ -613,44 +591,44 @@ public class GenshinPlayer { this.sendPacket(new PacketItemAddHintNotify(item, ActionReason.SubfieldDrop)); } } - + return; } - + public void onPause() { - + } - + public void onUnpause() { - + } - + public void sendPacket(GenshinPacket packet) { if (this.hasSentAvatarDataNotify) { this.getSession().send(packet); } } - + public OnlinePlayerInfo getOnlinePlayerInfo() { OnlinePlayerInfo.Builder onlineInfo = OnlinePlayerInfo.newBuilder() - .setUid(this.getUid()) - .setNickname(this.getNickname()) - .setPlayerLevel(this.getLevel()) - .setMpSettingType(this.getMpSetting()) - .setNameCardId(this.getNameCardId()) - .setSignature(this.getSignature()) - .setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())); - + .setUid(this.getUid()) + .setNickname(this.getNickname()) + .setPlayerLevel(this.getLevel()) + .setMpSettingType(this.getMpSetting()) + .setNameCardId(this.getNameCardId()) + .setSignature(this.getSignature()) + .setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())); + if (this.getWorld() != null) { onlineInfo.setCurPlayerNumInWorld(this.getWorld().getPlayers().indexOf(this) + 1); } else { onlineInfo.setCurPlayerNumInWorld(1); } - + return onlineInfo.build(); } - public PlayerBirthday getBirthday(){ + public PlayerBirthday getBirthday() { return this.birthday; } @@ -659,6 +637,10 @@ public class GenshinPlayer { this.updateProfile(); } + public boolean hasBirthday() { + return this.birthday.getDay() > 0; + } + public Set getRewardedLevels() { return rewardedLevels; } @@ -668,36 +650,18 @@ public class GenshinPlayer { } public SocialDetail.Builder getSocialDetail() { - SocialDetail.Builder social = SocialDetail.newBuilder() - .setUid(this.getUid()) - .setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())) - .setNickname(this.getNickname()) - .setSignature(this.getSignature()) - .setLevel(this.getLevel()) - .setBirthday(this.getBirthday().getFilledProtoWhenNotEmpty()) - .setWorldLevel(this.getWorldLevel()) - .setUnk1(1) - .setUnk3(1) - .setNameCardId(this.getNameCardId()) - .setFinishAchievementNum(0); + SocialDetail.Builder social = SocialDetail.newBuilder().setUid(this.getUid()).setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage())).setNickname(this.getNickname()).setSignature(this.getSignature()).setLevel(this.getLevel()).setBirthday(this.getBirthday().getFilledProtoWhenNotEmpty()).setWorldLevel(this.getWorldLevel()).setUnk1(1).setUnk3(1).setNameCardId(this.getNameCardId()).setFinishAchievementNum(0); return social; } - + public WorldPlayerLocationInfo getWorldPlayerLocationInfo() { - return WorldPlayerLocationInfo.newBuilder() - .setSceneId(this.getSceneId()) - .setPlayerLoc(this.getPlayerLocationInfo()) - .build(); + return WorldPlayerLocationInfo.newBuilder().setSceneId(this.getSceneId()).setPlayerLoc(this.getPlayerLocationInfo()).build(); } - + public PlayerLocationInfo getPlayerLocationInfo() { - return PlayerLocationInfo.newBuilder() - .setUid(this.getUid()) - .setPos(this.getPos().toProto()) - .setRot(this.getRotation().toProto()) - .build(); + return PlayerLocationInfo.newBuilder().setUid(this.getUid()).setPos(this.getPos().toProto()).setRot(this.getRotation().toProto()).build(); } - + public synchronized void onTick() { // Check ping if (this.getLastPingTime() > System.currentTimeMillis() + 60000) { @@ -717,7 +681,7 @@ public class GenshinPlayer { if (this.getWorld() != null) { // RTT notify - very important to send this often this.sendPacket(new PacketWorldPlayerRTTNotify(this.getWorld())); - + // Update player locations if in multiplayer every 5 seconds long time = System.currentTimeMillis(); if (this.getWorld().isMultiplayer() && this.getScene() != null && time > nextSendPlayerLocTime) { @@ -727,7 +691,7 @@ public class GenshinPlayer { } } } - + public void resetSendPlayerLocTime() { this.nextSendPlayerLocTime = System.currentTimeMillis() + 5000; } @@ -736,7 +700,7 @@ public class GenshinPlayer { private void onLoad() { this.getTeamManager().setPlayer(this); } - + public void save() { DatabaseHelper.savePlayer(this); } @@ -745,44 +709,45 @@ public class GenshinPlayer { // Make sure these exist if (this.getTeamManager() == null) { this.teamManager = new TeamManager(this); - } if (this.getProfile().getUid() == 0) { + } + if (this.getProfile().getUid() == 0) { this.getProfile().syncWithCharacter(this); } - + // Check if player object exists in server // TODO - optimize GenshinPlayer exists = this.getServer().getPlayerByUid(getUid()); if (exists != null) { exists.getSession().close(); } - + // Load from db this.getAvatars().loadFromDatabase(); this.getInventory().loadFromDatabase(); this.getAvatars().postLoad(); - + this.getFriendsList().loadFromDatabase(); - + // Create world World world = new World(this); world.addPlayer(this); - + // Add to gameserver if (getSession().isActive()) { getServer().registerPlayer(this); getProfile().setPlayer(this); // Set online } - - // Multiplayer setting + + // Multiplayer setting this.setProperty(PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE, this.getMpSetting().getNumber()); this.setProperty(PlayerProperty.PROP_IS_MP_MODE_AVAILABLE, 1); - + // Packets session.send(new PacketPlayerDataNotify(this)); // Player data session.send(new PacketStoreWeightLimitNotify()); session.send(new PacketPlayerStoreNotify(this)); session.send(new PacketAvatarDataNotify(this)); - + session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels)); session.send(new PacketOpenStateUpdateNotify()); @@ -790,34 +755,34 @@ public class GenshinPlayer { // First notify packets sent this.setHasSentAvatarDataNotify(true); } - + public void onLogout() { // Leave world if (this.getWorld() != null) { this.getWorld().removePlayer(this); } - + // Status stuff this.getProfile().syncWithCharacter(this); this.getProfile().setPlayer(null); // Set offline - + this.getCoopRequests().clear(); - + // Save to db this.save(); this.getTeamManager().saveAvatars(); this.getFriendsList().save(); } - + public enum SceneLoadState { - NONE (0), LOADING (1), INIT (2), LOADED (3); - + NONE(0), LOADING(1), INIT(2), LOADED(3); + private final int value; - + private SceneLoadState(int value) { this.value = value; } - + public int getValue() { return this.value; } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java index 5bfa9f8d5..31f86128a 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java @@ -11,23 +11,57 @@ import emu.grasscutter.server.packet.send.PacketSetPlayerBirthdayRsp; @Opcodes(PacketOpcodes.SetPlayerBirthdayReq) public class HandlerSetPlayerBirthdayReq extends PacketHandler { - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - SetPlayerBirthdayReq req = SetPlayerBirthdayReq.parseFrom(payload); + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + SetPlayerBirthdayReq req = SetPlayerBirthdayReq.parseFrom(payload); - if (req.getBirthday().getDay() > 0 && req.getBirthday().getMonth() > 0) { - int day = req.getBirthday().getDay(); - int month = req.getBirthday().getMonth(); + // RET_BIRTHDAY_CANNOT_BE_SET_TWICE = 7009 + if (session.getPlayer().hasBirthday()) { + session.send(new PacketSetPlayerBirthdayRsp(7009)); + return; + } - // Update birthday value - session.getPlayer().setBirthday(day, month); + int month = req.getBirthday().getMonth(); + int day = req.getBirthday().getDay(); - // Save birthday month and day - session.getPlayer().save(); - SocialDetail.Builder detail = session.getPlayer().getSocialDetail(); + // RET_BIRTHDAY_FORMAT_ERROR = 7022 + if (!isValidBirthday(month, day)) { + session.send(new PacketSetPlayerBirthdayRsp(7022)); + return; + } + + // Update birthday value + session.getPlayer().setBirthday(day, month); + + // Save birthday month and day + session.getPlayer().save(); + SocialDetail.Builder detail = session.getPlayer().getSocialDetail(); + + session.send(new PacketSetPlayerBirthdayRsp(session.getPlayer())); + session.send(new PacketGetPlayerSocialDetailRsp(detail)); + } + + private boolean isValidBirthday(int month, int day) { + + switch (month) { + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + return day > 0 & day <= 31; + case 4: + case 6: + case 9: + case 11: + return day > 0 && day <= 30; + case 2: + return day > 0 & day <= 29; + } + + return false; + } - session.send(new PacketSetPlayerBirthdayRsp(session.getPlayer())); - session.send(new PacketGetPlayerSocialDetailRsp(detail)); - } - } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java index 135d22f2f..8a343bf1d 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java @@ -6,13 +6,24 @@ import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp; public class PacketSetPlayerBirthdayRsp extends GenshinPacket { - public PacketSetPlayerBirthdayRsp(GenshinPlayer player) { - super(PacketOpcodes.SetPlayerBirthdayRsp); - SetPlayerBirthdayRsp proto = SetPlayerBirthdayRsp.newBuilder() - .setBirthday(player.getBirthday().toProto()) - .build(); + public PacketSetPlayerBirthdayRsp(int retCode) { + super(PacketOpcodes.SetPlayerBirthdayRsp); - this.setData(proto); - } + SetPlayerBirthdayRsp proto = SetPlayerBirthdayRsp.newBuilder() + .setRetcode(retCode) + .build(); + + this.setData(proto); + } + + public PacketSetPlayerBirthdayRsp(GenshinPlayer player) { + super(PacketOpcodes.SetPlayerBirthdayRsp); + + SetPlayerBirthdayRsp proto = SetPlayerBirthdayRsp.newBuilder() + .setBirthday(player.getBirthday().toProto()) + .build(); + + this.setData(proto); + } }