Skip to content

Commit

Permalink
Partially Implement Papel Shop
Browse files Browse the repository at this point in the history
  • Loading branch information
JMC47 committed Jul 2, 2023
1 parent 8efc797 commit 4d174c3
Show file tree
Hide file tree
Showing 9 changed files with 311 additions and 3 deletions.
23 changes: 23 additions & 0 deletions game/packet/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ var ClientMessageTable = common.NewMessageTable(map[uint16]ClientMessage{
0x0082: &ClientMultiplayerLeave{},
0x0088: &Client0088{},
0x008B: &ClientRequestMessengerList{},
0x0098: &ClientRareShopOpen{},
0x009C: &ClientRequestPlayerHistory{},
0x00AE: &ClientTutorialClear{},
0x00B5: &ClientEnterMyRoom{},
Expand All @@ -81,10 +82,13 @@ var ClientMessageTable = common.NewMessageTable(map[uint16]ClientMessage{
0x0140: &ClientShopJoin{},
0x0143: &ClientRequestInboxList{},
0x0144: &ClientRequestInboxMessage{},
0x014B: &ClientBlackPapelPlay{},
0x0157: &ClientAchievementStatusRequest{},
0x016E: &ClientRequestDailyReward{},
0x0176: &ClientEventLobbyJoin{},
0x0177: &ClientEventLobbyLeave{},
0x0184: &ClientAssistModeToggle{},
0x0186: &ClientBigPapelPlay{},
})

// ClientAuth is a message sent to authenticate a session.
Expand Down Expand Up @@ -112,6 +116,11 @@ type ClientGetUserOnlineStatus struct {
Username common.PString
}

// ClientRareShopOpen notifies the server if a user opens the rare shop menu.
type ClientRareShopOpen struct {
ClientMessage_
}

// ClientRoomEdit is sent when the client changes room settings.
type ClientRoomEdit struct {
ClientMessage_
Expand Down Expand Up @@ -383,6 +392,12 @@ type ClientRequestMessengerList struct {
ClientMessage_
}

// ClientAchievementStatusRequest requests Achievement Status for a user.
type ClientAchievementStatusRequest struct {
ClientMessage_
UserID uint32
}

// ClientGetUserData is a message sent by the client to request
// the client state.
type ClientGetUserData struct {
Expand Down Expand Up @@ -517,3 +532,11 @@ type ClientLockerInventoryRequest struct {
type Client00FE struct {
ClientMessage_
}

type ClientBlackPapelPlay struct {
ClientMessage_
}

type ClientBigPapelPlay struct {
ClientMessage_
}
75 changes: 74 additions & 1 deletion game/packet/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ var ServerMessageTable = common.NewMessageTable(map[uint16]ServerMessage{
0x00F1: &ServerMessageConnect{},
0x00F5: &ServerMultiplayerJoined{},
0x00F6: &ServerMultiplayerLeft{},
0x00FB: &ServerBlackPapelResponse{},
0x010B: &ServerRareShopOpen{},
0x010E: &ServerPlayerHistory{},
0x011F: &ServerTutorialStatus{},
0x012B: &ServerMyRoomEntered{},
Expand All @@ -89,13 +91,17 @@ var ServerMessageTable = common.NewMessageTable(map[uint16]ServerMessage{
0x0211: &ServerInboxList{},
0x0212: &ServerMailMessage{},
0x0216: &ServerUserStatusUpdate{},
0x021B: &ServerBlackPapelWinnings{},
0x021D: &ServerAchievementProgress{},
0x022C: &ServerAchievementUnknownResponse{},
0x022D: &ServerAchievementStatusResponse{},
0x0230: &Server0230{},
0x0231: &Server0231{},
0x0248: &ServerLoginBonusStatus{},
0x0250: &ServerEventLobbyJoined{},
0x0251: &ServerEventLobbyLeft{},
0x026A: &ServerAssistModeToggled{},
0x026C: &ServerBigPapelWinnings{},
})

// ConnectMessage is the message sent upon connecting.
Expand Down Expand Up @@ -250,6 +256,39 @@ type GamePlayer struct {
NumCards uint8
}

type ServerRareShopOpen struct {
ServerMessage_
UnknownA int32
UnknownB int32
UnknownC uint32
}

type Achievement struct {
ID uint32
Value uint32
Timestamp uint32
}

type AchievementGroup struct {
GroupID uint32
ID uint32
Count uint32 `struct:"sizeof=Achievements"`
Achievements []Achievement
}

type ServerAchievementStatusResponse struct {
ServerMessage_
Status uint32
Remaining uint32
Count uint32 `struct:"sizeof=Groups"`
Groups []AchievementGroup
}

type ServerAchievementUnknownResponse struct {
ServerMessage_
UnknownA [4]byte
}

type GameInitFull struct {
NumPlayers byte `struct:"sizeof=Players"`
Players []GamePlayer
Expand Down Expand Up @@ -299,7 +338,7 @@ type ServerRoomAction struct {
gamemodel.RoomAction
}

// ServerPangBalanceData is sent after a pang purchase succeeds.
// ServerPangBalanceData is sent after a pang purchase succeeds. Sometimes Pang Spent is not set like on Black Papel Transactions.
type ServerPangBalanceData struct {
ServerMessage_
PangsRemaining uint64
Expand Down Expand Up @@ -878,3 +917,37 @@ type ServerAssistModeToggled struct {
ServerMessage_
Unknown uint32
}

type ServerBlackPapelWinnings struct {
ServerMessage_
Status uint32
BlackPapelInvTicketSlot uint32
UniqueItemsWon uint32 `struct:"sizeof=Items"`
Items []ServerBlackPapelItems
PangsRemaining uint64
CookiesRemaining uint64 //not filled in but seems correct
}

type ServerBlackPapelResponse struct {
ServerMessage_
RemainingTurns int32
UnknownA int32
}

type ServerBlackPapelItems struct {
DolfiniBallColor uint32
ItemTypeID uint32
InventorySlot uint32
Quantity uint32
Rarity uint32
}

type ServerBigPapelWinnings struct {
ServerMessage_
Status uint32
BlackPapelInvTicketSlot uint32
UniqueItemsWon uint32 `struct:"sizeof=Items"`
Items []ServerBlackPapelItems
PangsRemaining uint64
CookiesRemaining uint64
}
116 changes: 116 additions & 0 deletions game/server/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"context"
"errors"
"fmt"
"math/rand"
"time"

"github.com/pangbox/server/common"
"github.com/pangbox/server/database/accounts"
Expand Down Expand Up @@ -416,6 +418,120 @@ func (c *Conn) Handle(ctx context.Context) error {
})
case *gamepacket.Client0088:
// Unknown tutorial-related message.
case *gamepacket.ClientRareShopOpen:
c.SendMessage(ctx, &gamepacket.ServerRareShopOpen{
UnknownA: 0,
UnknownB: 0,
UnknownC: 0,
})
case *gamepacket.ClientAchievementStatusRequest:
c.SendMessage(ctx, &gamepacket.ServerAchievementStatusResponse{
Remaining: 1,
Count: 1,
Groups: []gamepacket.AchievementGroup{
{
GroupID: 0x4c80002d,
ID: 248388034,
Count: 5,
Achievements: []gamepacket.Achievement{
{
ID: 0x7480087c,
Value: 1,
Timestamp: uint32(time.Date(2018, 11, 3, 0, 25, 10, 0, time.UTC).Unix()),
},
{
ID: 0x7480087d,
Value: 3,
Timestamp: uint32(time.Date(2018, 11, 7, 2, 5, 26, 0, time.UTC).Unix()),
},
{
ID: 0x7480087e,
Value: 5,
Timestamp: uint32(time.Date(2018, 11, 7, 3, 3, 45, 0, time.UTC).Unix()),
},
{
ID: 0x7480087f,
Value: 7,
Timestamp: uint32(time.Date(2018, 11, 8, 23, 27, 19, 0, time.UTC).Unix()),
},
{
ID: 0x74800880,
Value: 10,
Timestamp: uint32(time.Date(2019, 8, 25, 19, 39, 45, 0, time.UTC).Unix()),
},
},
},
},
})
c.SendMessage(ctx, &gamepacket.ServerAchievementUnknownResponse{
UnknownA: [4]byte{0x00, 0x00, 0x00, 0x00},
})
case *gamepacket.ClientBigPapelPlay:
c.SendMessage(ctx, &gamepacket.ServerBlackPapelResponse{
RemainingTurns: 50, //Displays as remaining turns in the box.
UnknownA: -1,
})
// TODO make Pang interaction transactional
// TODO make items show up in inventory
// TODO make sure not to subtract pang if a ticket is used.
// TODO Fix pang able to go negative.
c.player.Pang, err = c.s.accountsService.AddPang(ctx, c.player.PlayerID, -10000)
if err != nil {
return err
}
packet := &gamepacket.ServerBigPapelWinnings{}
const minBigPapelItems = 10
const maxBigPapelItems = 20
itemQuantities := make(map[uint32]int)
numItems := rand.Intn(maxBigPapelItems-minBigPapelItems) + minBigPapelItems
for i := 0; i < numItems; i++ {
typeID := c.s.papelShop.Choose()
itemQuantities[typeID]++
}
for typeID, quantity := range itemQuantities {
item := gamepacket.ServerBlackPapelItems{}
item.ItemTypeID = typeID
item.Quantity = uint32(quantity)
packet.Items = append(packet.Items, item)
}
packet.PangsRemaining = uint64(c.player.Pang) //This should be calculated by player data
packet.CookiesRemaining = uint64(c.player.Points) //Cookies remaining even if the action doesn't cost cookies
c.SendMessage(ctx, packet)
case *gamepacket.ClientBlackPapelPlay:
// TODO make Pang interaction transactional.
// TODO make items show up in inventory.
// TODO make sure not to subtract pang if a ticket is used.
c.player.Pang, err = c.s.accountsService.AddPang(ctx, c.player.PlayerID, -500)
if err != nil {
return err
}
packet := &gamepacket.ServerBlackPapelWinnings{}
drawnItemsSet := make(map[uint32]struct{})
for i := rand.Intn(4) + 1; i > 0; {
item := gamepacket.ServerBlackPapelItems{}
var typeID uint32
for {
typeID = c.s.papelShop.Choose()
if _, ok := drawnItemsSet[typeID]; !ok {
drawnItemsSet[typeID] = struct{}{}
break
}
}
item.ItemTypeID = typeID
item.DolfiniBallColor = uint32(rand.Intn(3))
item.Rarity = c.s.papelRarity[typeID]
if item.Rarity == 2 {
item.Quantity = 1
} else {
item.Quantity = uint32(rand.Intn(3) + 1)
}
packet.UniqueItemsWon += 1
packet.Items = append(packet.Items, item)
i--
}
packet.PangsRemaining = uint64(c.player.Pang)
packet.CookiesRemaining = uint64(c.player.Points)
c.SendMessage(ctx, packet)
case *gamepacket.ClientRoomUserEquipmentChange:
// TODO
case *gamepacket.ClientTutorialStart:
Expand Down
1 change: 1 addition & 0 deletions game/server/playerdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func (c *Conn) getPlayerInfo() pangya.PlayerInfo {
func (c *Conn) getPlayerStats() pangya.PlayerStats {
return pangya.PlayerStats{
Pang: uint64(c.player.Pang),
Rank: byte(c.player.Rank),
// TODO
}
}
Expand Down
10 changes: 10 additions & 0 deletions game/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,19 @@ type Server struct {
configProvider gameconfig.Provider
lobby *room.Lobby
logger *log.Entry
papelShop *WeightedRand
papelRarity map[uint32]uint32
}

// New creates a new instance of the game server.
func New(opts Options) *Server {
logger := log.WithField("server", "GameServer")
papelShop := NewWeightedRand()
papelRarity := make(map[uint32]uint32)
for _, item := range opts.ConfigProvider.GetPapelShopOdds() {
papelShop.Add(item.TypeID, item.Weight)
papelRarity[item.TypeID] = uint32(item.Rarity)
}
return &Server{
baseServer: &common.BaseServer{},
topologyClient: opts.TopologyClient,
Expand All @@ -66,6 +74,8 @@ func New(opts Options) *Server {
channelName: opts.ChannelName,
configProvider: opts.ConfigProvider,
logger: logger,
papelShop: papelShop,
papelRarity: papelRarity,
}
}

Expand Down
52 changes: 52 additions & 0 deletions game/server/weightedrandom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package gameserver

import (
"errors"
"math/rand"
"sort"
)

type WeightedRand struct {
cumulativeWeights []int64
values []uint32
totalWeight int64
}

func NewWeightedRand() *WeightedRand {
return &WeightedRand{}
}

func (w *WeightedRand) Add(value uint32, weight int64) error {
// Detect if the total weight is going to overflow
if w.totalWeight+weight < w.totalWeight {
return errors.New("total weight will overflow")
}

// Update total weight
w.totalWeight += weight

// Append the value
w.values = append(w.values, value)

// Compute cumulative weight
var cumulativeWeight int64
if len(w.cumulativeWeights) > 0 {
cumulativeWeight = w.cumulativeWeights[len(w.cumulativeWeights)-1] + weight
} else {
cumulativeWeight = weight
}
w.cumulativeWeights = append(w.cumulativeWeights, cumulativeWeight)

return nil
}

func (w *WeightedRand) Choose() uint32 {
// Generate a random number in the range [0, totalWeight)
r := rand.Int63n(w.totalWeight)

// Use binary search to find the index where our random number fits in
index := sort.Search(len(w.cumulativeWeights), func(i int) bool { return w.cumulativeWeights[i] > r })

// Return the corresponding value
return w.values[index]
}
Loading

0 comments on commit 4d174c3

Please sign in to comment.