From 1f995b6bd237dbb4ab625bac6e71480aaef5acb7 Mon Sep 17 00:00:00 2001 From: John Chadwick Date: Tue, 4 Jul 2023 22:24:54 -0400 Subject: [PATCH] Stub a bunch of missing functionality. --- database/accounts/service.go | 2 - game/packet/client.go | 22 ++++- game/packet/server.go | 70 +++++++++++++-- game/server/auth.go | 12 +++ game/server/conn.go | 55 +++++++++++- game/server/playerdata.go | 170 ++++++++++++++++++++++++----------- pangya/guild.go | 33 +++++++ 7 files changed, 299 insertions(+), 65 deletions(-) create mode 100644 pangya/guild.go diff --git a/database/accounts/service.go b/database/accounts/service.go index 623c258..640b2c6 100755 --- a/database/accounts/service.go +++ b/database/accounts/service.go @@ -141,9 +141,7 @@ func (s *Service) GetCharacters(ctx context.Context, playerID int64) ([]pangya.P uint32(character.AuxPart4ID.Int64), }, CutInID: uint32(character.CutInID.Int64), - //Mastery: uint32(character.Mastery), } - result[i].ID = uint32(character.CharacterID) } return result, nil } diff --git a/game/packet/client.go b/game/packet/client.go index 7e873ed..e84ed39 100755 --- a/game/packet/client.go +++ b/game/packet/client.go @@ -54,7 +54,7 @@ var ClientMessageTable = common.NewMessageTable(map[uint16]ClientMessage{ 0x0022: &ClientShotActiveUserAcknowledge{}, 0x0026: &ClientRoomKick{}, 0x002D: &ClientRoomInfo{}, - 0x002F: &ClientGetUserData{}, + 0x002F: &ClientGetPlayerData{}, 0x0030: &ClientPauseGame{}, 0x0031: &ClientHoleEnd{}, 0x0032: &ClientSetIdleStatus{}, @@ -80,10 +80,13 @@ var ClientMessageTable = common.NewMessageTable(map[uint16]ClientMessage{ 0x00CC: &ClientLockerCombinationAttempt{}, 0x00D3: &ClientLockerInventoryRequest{}, 0x00FE: &Client00FE{}, + 0x0108: &ClientGuildListRequest{}, + 0x012A: &ClientScratchyMenuOpen{}, 0x0140: &ClientShopJoin{}, 0x0143: &ClientRequestInboxList{}, 0x0144: &ClientRequestInboxMessage{}, 0x014B: &ClientBlackPapelPlay{}, + 0x0151: &ClientQuestStatusRequest{}, 0x0157: &ClientAchievementStatusRequest{}, 0x016E: &ClientRequestDailyReward{}, 0x0176: &ClientEventLobbyJoin{}, @@ -400,9 +403,9 @@ type ClientAchievementStatusRequest struct { UserID uint32 } -// ClientGetUserData is a message sent by the client to request +// ClientGetPlayerData is a message sent by the client to request // the client state. -type ClientGetUserData struct { +type ClientGetPlayerData struct { ClientMessage_ UserID uint32 Request byte @@ -543,10 +546,23 @@ type Client00FE struct { ClientMessage_ } +type ClientGuildListRequest struct { + ClientMessage_ + Page uint32 +} + +type ClientScratchyMenuOpen struct { + ClientMessage_ +} + type ClientBlackPapelPlay struct { ClientMessage_ } +type ClientQuestStatusRequest struct { + ClientMessage_ +} + type ClientBigPapelPlay struct { ClientMessage_ } diff --git a/game/packet/server.go b/game/packet/server.go index 147ab06..5e23a23 100755 --- a/game/packet/server.go +++ b/game/packet/server.go @@ -60,6 +60,7 @@ var ServerMessageTable = common.NewMessageTable(map[uint16]ServerMessage{ 0x0077: &Server0077{}, 0x0078: &ServerPlayerReady{}, 0x0086: &ServerRoomInfoResponse{}, + 0x0089: &ServerPlayerDataResponse{}, 0x0090: &ServerPlayerFirstShotReady{}, 0x0092: &ServerOpponentQuit{}, 0x0095: &ServerMoneyUpdate{}, @@ -81,12 +82,17 @@ var ServerMessageTable = common.NewMessageTable(map[uint16]ServerMessage{ 0x012B: &ServerMyRoomEntered{}, 0x012D: &ServerMyRoomLayout{}, 0x0151: &Server0151{}, - 0x0158: &ServerPlayerStats{}, + 0x0156: &ServerPlayerEquipmentResponse{}, + 0x0157: &ServerPlayerInfoResponse{}, + 0x0158: &ServerPlayerStatisticsResponse{}, + 0x015E: &ServerPlayerCharacterResponse{}, 0x0168: &ServerPlayerInfo{}, 0x016A: &Server016A{}, 0x016C: &ServerLockerCombinationResponse{}, 0x0170: &ServerLockerInventoryResponse{}, 0x0199: &ServerRoomPlayerFinished{}, + 0x01BC: &ServerGuildListPage{}, + 0x01EB: &ServerScratchyMenuResponse{}, 0x01F6: &Server01F6{}, 0x020E: &Server020E{}, 0x0210: &ServerInboxNotify{}, @@ -95,6 +101,7 @@ var ServerMessageTable = common.NewMessageTable(map[uint16]ServerMessage{ 0x0216: &ServerUserStatusUpdate{}, 0x021B: &ServerBlackPapelWinnings{}, 0x021D: &ServerAchievementProgress{}, + 0x0225: &ServerQuestStatus{}, 0x022C: &ServerAchievementUnknownResponse{}, 0x022D: &ServerAchievementStatusResponse{}, 0x0230: &Server0230{}, @@ -645,6 +652,21 @@ type ServerRoomPlayerFinished struct { ServerMessage_ } +type ServerGuildListPage struct { + ServerMessage_ + Status uint32 + Page uint32 + NumPages uint32 + GuildCount uint16 + Guilds []pangya.GuildData +} + +type ServerScratchyMenuResponse struct { + ServerMessage_ + Status uint32 + Unknown uint8 +} + // ServerRoomJoin is sent when a room is joined. type ServerRoomJoin struct { ServerMessage_ @@ -668,6 +690,13 @@ type ServerRoomInfoResponse struct { RoomInfo gamemodel.RoomInfo } +type ServerPlayerDataResponse struct { + ServerMessage_ + Status uint32 + Request uint8 + UserID uint32 +} + type ServerPlayerFirstShotReady struct { ServerMessage_ } @@ -769,12 +798,32 @@ type Server0151 struct { Unknown []byte } -type ServerPlayerStats struct { +type ServerPlayerEquipmentResponse struct { ServerMessage_ + Request uint8 + UserID uint32 + Equipment pangya.PlayerEquipment +} - SessionID uint32 - Unknown byte - Stats pangya.PlayerStats +type ServerPlayerInfoResponse struct { + ServerMessage_ + Request uint8 + UserID uint32 + Info pangya.PlayerInfo + Unknown uint32 +} + +type ServerPlayerStatisticsResponse struct { + ServerMessage_ + Request uint8 + UserID uint32 + Stats pangya.PlayerStats +} + +type ServerPlayerCharacterResponse struct { + ServerMessage_ + UserID uint32 + Character pangya.PlayerCharacterData } type ServerPlayerInfo struct { @@ -903,6 +952,17 @@ type ServerAchievementProgress struct { Achievements []AchievementProgress } +type ServerQuestStatus struct { + ServerMessage_ + Status uint32 + QuestExpiryUnixTime uint32 + QuestStartedUnixTime uint32 + QuestCount uint32 `struct:"sizeof=QuestTypeID"` + QuestTypeID []uint32 + QuestSlotCount uint32 `struct:"sizeof=QuestStatusSlot"` + QuestStatusSlot []uint32 +} + type Server0230 struct { ServerMessage_ } diff --git a/game/server/auth.go b/game/server/auth.go index 024f978..6124278 100644 --- a/game/server/auth.go +++ b/game/server/auth.go @@ -69,6 +69,10 @@ func (c *Conn) handleAuth(ctx context.Context) error { return fmt.Errorf("sending server list: %w", err) } + if err := c.sendPlayerStatistics(ctx); err != nil { + return fmt.Errorf("sending player statistics to client: %w", err) + } + return nil } @@ -239,3 +243,11 @@ func (c *Conn) sendServerList(ctx context.Context) error { message.Count = uint8(len(response.Msg.Server)) return c.SendMessage(ctx, message) } + +func (c *Conn) sendPlayerStatistics(ctx context.Context) error { + return c.SendMessage(ctx, &gamepacket.ServerPlayerStatisticsResponse{ + Request: 0, + UserID: uint32(c.player.PlayerID), + Stats: c.getPlayerStats(), + }) +} diff --git a/game/server/conn.go b/game/server/conn.go index 7af9062..5f1ae3f 100755 --- a/game/server/conn.go +++ b/game/server/conn.go @@ -183,9 +183,43 @@ func (c *Conn) Handle(ctx context.Context) error { case *gamepacket.ClientGetUserOnlineStatus: // TODO log.Debug("TODO: online status") - case *gamepacket.ClientGetUserData: - // TODO - log.Debug("TODO: user data") + case *gamepacket.ClientGetPlayerData: + player, err := c.s.accountsService.GetPlayer(ctx, int64(t.UserID)) + if err != nil { + c.SendMessage(ctx, &gamepacket.ServerPlayerDataResponse{ + Status: 2, + Request: t.Request, + UserID: t.UserID, + }) + break + } + if t.Request == 5 { + c.SendMessage(ctx, &gamepacket.ServerPlayerInfoResponse{ + Request: t.Request, + UserID: t.UserID, + Info: playerInfoFromDB(&player, 0), // TODO: set conn id somehow + }) + c.SendMessage(ctx, &gamepacket.ServerPlayerCharacterResponse{ + UserID: t.UserID, + Character: playerEquippedCharacterFromDB(&player), + }) + c.SendMessage(ctx, &gamepacket.ServerPlayerEquipmentResponse{ + Request: t.Request, + UserID: t.UserID, + Equipment: playerEquippedItemsFromDB(&player), + }) + } + c.SendMessage(ctx, &gamepacket.ServerPlayerStatisticsResponse{ + Request: t.Request, + UserID: t.UserID, + Stats: playerStatsFromDB(&player), + }) + // TODO: Missing a lot of responses. + c.SendMessage(ctx, &gamepacket.ServerPlayerDataResponse{ + Status: 1, + Request: t.Request, + UserID: t.UserID, + }) case *gamepacket.ClientRoomLoungeAction: if c.currentRoom == nil { break @@ -826,6 +860,21 @@ func (c *Conn) Handle(ctx context.Context) error { c.sendCharacterData(ctx) case *gamepacket.Client004F: // ignore + case *gamepacket.ClientQuestStatusRequest: + // TODO + c.SendMessage(ctx, &gamepacket.ServerQuestStatus{ + Status: 1, + }) + case *gamepacket.ClientGuildListRequest: + // TODO + c.SendMessage(ctx, &gamepacket.ServerGuildListPage{ + Status: 1, + }) + case *gamepacket.ClientScratchyMenuOpen: + // TODO + c.SendMessage(ctx, &gamepacket.ServerScratchyMenuResponse{ + Status: 1, + }) default: return fmt.Errorf("unexpected message: %T", t) } diff --git a/game/server/playerdata.go b/game/server/playerdata.go index 8a222bc..5e9882c 100644 --- a/game/server/playerdata.go +++ b/game/server/playerdata.go @@ -17,90 +17,156 @@ package gameserver -import "github.com/pangbox/server/pangya" +import ( + "github.com/pangbox/server/gen/dbmodels" + "github.com/pangbox/server/pangya" +) -func (c *Conn) getPlayerInfo() pangya.PlayerInfo { +func playerInfoFromDB(player *dbmodels.GetPlayerRow, connID uint32) pangya.PlayerInfo { return pangya.PlayerInfo{ - Username: c.player.Username, - Nickname: c.player.Nickname.String, - PlayerID: uint32(c.player.PlayerID), - ConnID: c.connID, + Username: player.Username, + Nickname: player.Nickname.String, + PlayerID: uint32(player.PlayerID), + ConnID: connID, // TODO } } -func (c *Conn) getPlayerStats() pangya.PlayerStats { +func playerStatsFromDB(player *dbmodels.GetPlayerRow) pangya.PlayerStats { return pangya.PlayerStats{ - Pang: uint64(c.player.Pang), - Rank: byte(c.player.Rank), - TotalXP: uint32(c.player.Exp), + Pang: uint64(player.Pang), + Rank: byte(player.Rank), + TotalXP: uint32(player.Exp), // TODO } } -func (c *Conn) getPlayerEquippedConsumables() [10]uint32 { +func playerEquippedConsumablesFromDB(player *dbmodels.GetPlayerRow) [10]uint32 { return [10]uint32{ - uint32(c.player.Slot0TypeID), - uint32(c.player.Slot1TypeID), - uint32(c.player.Slot2TypeID), - uint32(c.player.Slot3TypeID), - uint32(c.player.Slot4TypeID), - uint32(c.player.Slot5TypeID), - uint32(c.player.Slot6TypeID), - uint32(c.player.Slot7TypeID), - uint32(c.player.Slot8TypeID), - uint32(c.player.Slot9TypeID), + uint32(player.Slot0TypeID), + uint32(player.Slot1TypeID), + uint32(player.Slot2TypeID), + uint32(player.Slot3TypeID), + uint32(player.Slot4TypeID), + uint32(player.Slot5TypeID), + uint32(player.Slot6TypeID), + uint32(player.Slot7TypeID), + uint32(player.Slot8TypeID), + uint32(player.Slot9TypeID), } } -func (c *Conn) getPlayerEquippedItems() pangya.PlayerEquipment { - comet := c.player.BallTypeID +func playerEquippedClubSetFromDB(player *dbmodels.GetPlayerRow) pangya.PlayerClubData { + return pangya.PlayerClubData{ + Item: pangya.PlayerItem{ + ID: uint32(player.ClubID.Int64), + TypeID: uint32(player.ClubTypeID.Int64), + }, + // TODO: stats/enchantments are not implemented. + Stats: pangya.ClubStats{ + UpgradeStats: [5]uint16{8, 9, 8, 3, 3}, + }, + } +} + +func playerEquippedItemsFromDB(player *dbmodels.GetPlayerRow) pangya.PlayerEquipment { + comet := player.BallTypeID if comet == 0 { comet = 0x14000000 } return pangya.PlayerEquipment{ - CaddieID: uint32(c.player.CaddieID.Int64), - CharacterID: uint32(c.player.CharacterID.Int64), - ClubSetID: uint32(c.player.ClubID.Int64), + CaddieID: uint32(player.CaddieID.Int64), + CharacterID: uint32(player.CharacterID.Int64), + ClubSetID: uint32(player.ClubID.Int64), CometTypeID: uint32(comet), Items: pangya.PlayerEquippedItems{ - ItemIDs: c.getPlayerEquippedConsumables(), + ItemIDs: playerEquippedConsumablesFromDB(player), }, - BackgroundID: uint32(c.player.BackgroundID.Int64), - FrameID: uint32(c.player.FrameID.Int64), - StickerID: uint32(c.player.StickerID.Int64), - SlotID: uint32(c.player.SlotID.Int64), - CutInID: uint32(c.player.CutInID.Int64), - TitleID: uint32(c.player.TitleID.Int64), - BackgroundTypeID: uint32(c.player.BackgroundTypeID.Int64), - FrameTypeID: uint32(c.player.FrameTypeID.Int64), - StickerTypeID: uint32(c.player.StickerTypeID.Int64), - SlotTypeID: uint32(c.player.SlotTypeID.Int64), - CutInTypeID: uint32(c.player.CutInTypeID.Int64), - TitleTypeID: uint32(c.player.TitleTypeID.Int64), - MascotID: uint32(c.player.MascotTypeID), + BackgroundID: uint32(player.BackgroundID.Int64), + FrameID: uint32(player.FrameID.Int64), + StickerID: uint32(player.StickerID.Int64), + SlotID: uint32(player.SlotID.Int64), + CutInID: uint32(player.CutInID.Int64), + TitleID: uint32(player.TitleID.Int64), + BackgroundTypeID: uint32(player.BackgroundTypeID.Int64), + FrameTypeID: uint32(player.FrameTypeID.Int64), + StickerTypeID: uint32(player.StickerTypeID.Int64), + SlotTypeID: uint32(player.SlotTypeID.Int64), + CutInTypeID: uint32(player.CutInTypeID.Int64), + TitleTypeID: uint32(player.TitleTypeID.Int64), + MascotID: uint32(player.MascotTypeID), PosterID: [2]uint32{ - uint32(c.player.Poster0TypeID.Int64), - uint32(c.player.Poster1TypeID.Int64), + uint32(player.Poster0TypeID.Int64), + uint32(player.Poster1TypeID.Int64), }, } } +func playerEquippedCharacterFromDB(player *dbmodels.GetPlayerRow) pangya.PlayerCharacterData { + return pangya.PlayerCharacterData{ + CharTypeID: uint32(player.CharacterTypeID.Int64), + ID: uint32(player.CharacterID.Int64), + HairColor: uint8(player.HairColor), + Shirt: uint8(player.Shirt), + PartTypeIDs: [24]uint32{ + uint32(player.Part00ItemTypeID), uint32(player.Part01ItemTypeID), uint32(player.Part02ItemTypeID), uint32(player.Part03ItemTypeID), + uint32(player.Part04ItemTypeID), uint32(player.Part05ItemTypeID), uint32(player.Part06ItemTypeID), uint32(player.Part07ItemTypeID), + uint32(player.Part08ItemTypeID), uint32(player.Part09ItemTypeID), uint32(player.Part10ItemTypeID), uint32(player.Part11ItemTypeID), + uint32(player.Part12ItemTypeID), uint32(player.Part13ItemTypeID), uint32(player.Part14ItemTypeID), uint32(player.Part15ItemTypeID), + uint32(player.Part16ItemTypeID), uint32(player.Part17ItemTypeID), uint32(player.Part18ItemTypeID), uint32(player.Part19ItemTypeID), + uint32(player.Part20ItemTypeID), uint32(player.Part21ItemTypeID), uint32(player.Part22ItemTypeID), uint32(player.Part23ItemTypeID), + }, + PartIDs: [24]uint32{ + uint32(player.Part00ItemID.Int64), uint32(player.Part01ItemID.Int64), uint32(player.Part02ItemID.Int64), uint32(player.Part03ItemID.Int64), + uint32(player.Part04ItemID.Int64), uint32(player.Part05ItemID.Int64), uint32(player.Part06ItemID.Int64), uint32(player.Part07ItemID.Int64), + uint32(player.Part08ItemID.Int64), uint32(player.Part09ItemID.Int64), uint32(player.Part10ItemID.Int64), uint32(player.Part11ItemID.Int64), + uint32(player.Part12ItemID.Int64), uint32(player.Part13ItemID.Int64), uint32(player.Part14ItemID.Int64), uint32(player.Part15ItemID.Int64), + uint32(player.Part16ItemID.Int64), uint32(player.Part17ItemID.Int64), uint32(player.Part18ItemID.Int64), uint32(player.Part19ItemID.Int64), + uint32(player.Part20ItemID.Int64), uint32(player.Part21ItemID.Int64), uint32(player.Part22ItemID.Int64), uint32(player.Part23ItemID.Int64), + }, + AuxParts: [5]uint32{ + uint32(player.AuxPart0ID.Int64), + uint32(player.AuxPart1ID.Int64), + uint32(player.AuxPart2ID.Int64), + uint32(player.AuxPart3ID.Int64), + uint32(player.AuxPart4ID.Int64), + }, + CutInID: uint32(player.CutInID.Int64), + } +} + +func playerDataFromDB(player *dbmodels.GetPlayerRow, connID uint32) pangya.PlayerData { + return pangya.PlayerData{ + UserInfo: playerInfoFromDB(player, connID), + PlayerStats: playerStatsFromDB(player), + EquippedItems: playerEquippedItemsFromDB(player), + EquippedCharacter: playerEquippedCharacterFromDB(player), + EquippedClub: playerEquippedClubSetFromDB(player), + } +} + +func (c *Conn) getPlayerInfo() pangya.PlayerInfo { + return playerInfoFromDB(&c.player, c.connID) +} + +func (c *Conn) getPlayerStats() pangya.PlayerStats { + return playerStatsFromDB(&c.player) +} + +func (c *Conn) getPlayerEquippedConsumables() [10]uint32 { + return playerEquippedConsumablesFromDB(&c.player) +} + +func (c *Conn) getPlayerEquippedItems() pangya.PlayerEquipment { + return playerEquippedItemsFromDB(&c.player) +} + func (c *Conn) getPlayerEquippedCharacter() pangya.PlayerCharacterData { return *c.currentCharacter } func (c *Conn) getPlayerEquippedClubSet() pangya.PlayerClubData { - return pangya.PlayerClubData{ - Item: pangya.PlayerItem{ - ID: uint32(c.player.ClubID.Int64), - TypeID: uint32(c.player.ClubTypeID.Int64), - }, - // TODO: stats/enchantments are not implemented. - Stats: pangya.ClubStats{ - UpgradeStats: [5]uint16{8, 9, 8, 3, 3}, - }, - } + return playerEquippedClubSetFromDB(&c.player) } func (c *Conn) getPlayerData() pangya.PlayerData { diff --git a/pangya/guild.go b/pangya/guild.go new file mode 100644 index 0000000..21c632b --- /dev/null +++ b/pangya/guild.go @@ -0,0 +1,33 @@ +// Copyright (C) 2018-2023, John Chadwick +// +// Permission to use, copy, modify, and/or distribute this software for any purpose +// with or without fee is hereby granted, provided that the above copyright notice +// and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +// OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +// THIS SOFTWARE. +// +// SPDX-FileCopyrightText: Copyright (c) 2018-2023 John Chadwick +// SPDX-License-Identifier: ISC + +package pangya + +type GuildData struct { + // TODO: This structure is 100% untested, modified based on Pangya TH. + // It is absolutely not going to be right. + Unknown uint32 + GuildName string `struct:"[17]byte"` + Pang uint32 + Point uint32 + NumMembers uint32 + Unknown2 [16]byte + Description [105]byte + LeaderUserID uint32 + LeaderNickname string `struct:"[22]byte"` + GuildEmblemImage string `struct:"[24]byte"` +}