Skip to content

Commit

Permalink
feat: rework for better multi-game support, add marathon support
Browse files Browse the repository at this point in the history
  • Loading branch information
ZakShearman committed Sep 20, 2024
1 parent 997581a commit e429357
Show file tree
Hide file tree
Showing 11 changed files with 235 additions and 188 deletions.
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ module game-player-data
go 1.23

require (
github.com/emortalmc/proto-specs/gen/go v0.0.0-20240905105617-8c6a55ee2bae
github.com/gogo/protobuf v1.3.2
github.com/emortalmc/proto-specs/gen/go v0.0.0-20240920201852-8d931ec7a9aa
github.com/google/uuid v1.3.0
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/segmentio/kafka-go v0.4.39
github.com/spf13/viper v1.16.0
go.mongodb.org/mongo-driver v1.11.6
go.uber.org/zap v1.24.0
google.golang.org/grpc v1.55.0
google.golang.org/protobuf v1.30.0
)

require (
Expand Down Expand Up @@ -43,7 +43,6 @@ require (
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
14 changes: 4 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,10 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20230602153610-3e1cd1adcd5a h1:8Koes2hRKavzHtFB6+mgjlZesdkCnZ4RQ7kWK+xv83M=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20230602153610-3e1cd1adcd5a/go.mod h1:se+tHcK9FWxeadkxLF5uj+SPauEye0X+Iq6cGczXGJY=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20230916193435-6abf652a5c2f h1:DvOyf+SLgDoqLwDlV3IqDetZO7DwANJZm9clQGHD67g=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20230916193435-6abf652a5c2f/go.mod h1:se+tHcK9FWxeadkxLF5uj+SPauEye0X+Iq6cGczXGJY=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20230916202652-8975e470ce34 h1:q095naqNtgsBaI8o1TgkIo3U1pkSbIEnRW1KqWWu8Ww=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20230916202652-8975e470ce34/go.mod h1:se+tHcK9FWxeadkxLF5uj+SPauEye0X+Iq6cGczXGJY=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20240905105043-b1abec6eb8cf h1:IdMv84dOIWFMLeIYifQKxc1EmQybknwtiyTbBDrUnvc=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20240905105043-b1abec6eb8cf/go.mod h1:se+tHcK9FWxeadkxLF5uj+SPauEye0X+Iq6cGczXGJY=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20240905105617-8c6a55ee2bae h1:Y9ofcDfHuJbXUjTP/5+jMKOPpIysHhDWibpG0nYhy98=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20240905105617-8c6a55ee2bae/go.mod h1:se+tHcK9FWxeadkxLF5uj+SPauEye0X+Iq6cGczXGJY=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20240909152322-b05808d73899 h1:iz2XOymnELhMCFCmRd3CyhkbOPaqhpK8vaLfn1dN2XI=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20240909152322-b05808d73899/go.mod h1:se+tHcK9FWxeadkxLF5uj+SPauEye0X+Iq6cGczXGJY=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20240920201852-8d931ec7a9aa h1:twr+Yjdt3DCPnawjyEzi9ij/SXHk0IUY8i/EkVxNoew=
github.com/emortalmc/proto-specs/gen/go v0.0.0-20240920201852-8d931ec7a9aa/go.mod h1:se+tHcK9FWxeadkxLF5uj+SPauEye0X+Iq6cGczXGJY=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
Expand Down
16 changes: 5 additions & 11 deletions internal/app/game_player_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,16 @@ func Run(cfg *config.Config, logger *zap.SugaredLogger) {
repoCtx, repoCancel := context.WithCancel(ctx)
repoWg := &sync.WaitGroup{}

repo, err := repository.NewMongoRepository(repoCtx, logger, repoWg, cfg.MongoDB)
mongoDB, err := repository.CreateDatabase(repoCtx, cfg.MongoDB, repoWg, logger)
if err != nil {
logger.Fatalw("failed to create repository", err)
logger.Fatalw("failed to create database", err)
}

//if err := repo.SaveBlockSumoPlayer(ctx, &model.BlockSumoData{
// PlayerId: uuid.MustParse("8d36737e-1c0a-4a71-87de-9906f577845e"),
// BlockSlot: 1,
// ShearsSlot: 2,
//}); err != nil {
// panic(err)
//}
repoColl := repository.NewGamePlayerDataRepoColl(mongoDB)

kafka.NewConsumer(ctx, wg, cfg.Kafka, logger, repo)
kafka.NewConsumer(ctx, wg, cfg.Kafka, logger, repoColl)

service.RunServices(ctx, logger, wg, cfg, repo)
service.RunServices(ctx, logger, wg, cfg, repoColl)

wg.Wait()
logger.Info("shutting down")
Expand Down
14 changes: 0 additions & 14 deletions internal/config/gamemode.go

This file was deleted.

51 changes: 37 additions & 14 deletions internal/kafka/consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ package kafka

import (
"context"
"errors"
"fmt"
"game-player-data/internal/config"
"game-player-data/internal/repository"
"game-player-data/internal/repository/model"
pbmsg "github.com/emortalmc/proto-specs/gen/go/message/gameplayerdata"
pbmodel "github.com/emortalmc/proto-specs/gen/go/model/gameplayerdata"
"github.com/emortalmc/proto-specs/gen/go/nongenerated/kafkautils"
"github.com/google/uuid"
"github.com/segmentio/kafka-go"
"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
Expand All @@ -22,13 +21,13 @@ const GamePlayerDataTopic = "game-player-data"

type consumer struct {
logger *zap.SugaredLogger
repo repository.Repository
repos *repository.GameDataRepoColl

reader *kafka.Reader
}

func NewConsumer(ctx context.Context, wg *sync.WaitGroup, cfg *config.KafkaConfig, logger *zap.SugaredLogger,
repo repository.Repository) {
repos *repository.GameDataRepoColl) {

reader := kafka.NewReader(kafka.ReaderConfig{
Brokers: []string{fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)},
Expand All @@ -45,7 +44,7 @@ func NewConsumer(ctx context.Context, wg *sync.WaitGroup, cfg *config.KafkaConfi

c := &consumer{
logger: logger,
repo: repo,
repos: repos,

reader: reader,
}
Expand Down Expand Up @@ -77,6 +76,11 @@ func (c *consumer) handleUpdateGamePlayerDataMessage(ctx context.Context, _ *kaf
switch msg.GameMode {
case pbmodel.GameDataGameMode_BLOCK_SUMO:
err = c.handleBlockSumoUpdate(ctx, pId, msg)
case pbmodel.GameDataGameMode_MARATHON:
err = c.handleMarathonUpdate(ctx, pId, msg)

default:
c.logger.Errorw("unsupported game mode", "gameMode", msg.GameMode)
}

if err != nil {
Expand All @@ -85,15 +89,34 @@ func (c *consumer) handleUpdateGamePlayerDataMessage(ctx context.Context, _ *kaf
}
}

func (c *consumer) handleBlockSumoUpdate(ctx context.Context, pId uuid.UUID, msg *pbmsg.UpdateGamePlayerDataMessage) error {
player, err := c.repo.GetBlockSumoData(ctx, pId)

func (c *consumer) handleMarathonUpdate(ctx context.Context, pId uuid.UUID, msg *pbmsg.UpdateGamePlayerDataMessage) error {
gameData, err := c.repos.Marathon.GetOrDefault(ctx, pId, &model.MarathonData{BaseGameData: model.BaseGameData{PlayerId: pId}})
if err != nil {
if !errors.Is(err, mongo.ErrNoDocuments) {
return fmt.Errorf("failed to get block sumo data: %w", err)
return fmt.Errorf("failed to get block sumo data: %w", err)

}

msgData := &pbmodel.V1MarathonData{}

if err := anypb.UnmarshalTo(msg.Data, msgData, proto.UnmarshalOptions{}); err != nil {
return fmt.Errorf("failed to unmarshal data: %w", err)
}

for _, path := range msg.DataMask.Paths {
switch path {
case "block_palette":
gameData.BlockPalette = msgData.BlockPalette
case "time":
gameData.Time = msgData.Time
}
}

player = config.CreateDefaultBlockSumoData(pId)
return c.repos.Marathon.Save(ctx, gameData)
}
func (c *consumer) handleBlockSumoUpdate(ctx context.Context, pId uuid.UUID, msg *pbmsg.UpdateGamePlayerDataMessage) error {
gameData, err := c.repos.BlockSumo.GetOrDefault(ctx, pId, &model.BlockSumoData{BaseGameData: model.BaseGameData{PlayerId: pId}})
if err != nil {
return fmt.Errorf("failed to get block sumo data: %w", err)
}

msgData := &pbmodel.V1BlockSumoPlayerData{}
Expand All @@ -105,11 +128,11 @@ func (c *consumer) handleBlockSumoUpdate(ctx context.Context, pId uuid.UUID, msg
for _, path := range msg.DataMask.Paths {
switch path {
case "block_slot":
player.BlockSlot = msgData.BlockSlot
gameData.BlockSlot = msgData.BlockSlot
case "shears_slot":
player.ShearsSlot = msgData.ShearsSlot
gameData.ShearsSlot = msgData.ShearsSlot
}
}

return c.repo.SaveBlockSumoPlayer(ctx, player)
return c.repos.BlockSumo.Save(ctx, gameData)
}
104 changes: 104 additions & 0 deletions internal/repository/game_data_mongo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package repository

import (
"context"
"errors"
"fmt"
"game-player-data/internal/repository/model"
"game-player-data/internal/utils"
"github.com/google/uuid"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"reflect"
"time"
)

const (
blockSumoCollection = "blockSumo"
towerDefenceCollection = "towerDefence"
minesweeperCollection = "minesweeper"
marathonCollection = "marathon"
)

var typeToCollection = map[reflect.Type]string{
reflect.TypeOf(&model.BlockSumoData{}): blockSumoCollection,
reflect.TypeOf(&model.TowerDefenceData{}): towerDefenceCollection,
reflect.TypeOf(&model.MinesweeperData{}): minesweeperCollection,
reflect.TypeOf(&model.MarathonData{}): marathonCollection,
}

type mongoGameDataRepository[T model.GameData] struct {
coll *mongo.Collection

example T
}

func NewMongoGameDataRepository[T model.GameData](db *mongo.Database, example T) GameDataRepository[T] {
collName, ok := typeToCollection[reflect.TypeOf(example)]
if !ok {
panic(fmt.Sprintf("no collection found for type %T", example))
}

return &mongoGameDataRepository[T]{
coll: db.Collection(collName),
}
}

func (m *mongoGameDataRepository[T]) Get(ctx context.Context, playerID uuid.UUID) (T, error) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

example := reflect.New(reflect.TypeOf(m.example).Elem()).Interface().(T)

res := m.coll.FindOne(ctx, bson.M{"_id": playerID})
if err := res.Decode(&example); err != nil {
return example, fmt.Errorf("failed to decode data: %w", err)
}

return example, nil
}

func (m *mongoGameDataRepository[T]) GetOrDefault(ctx context.Context, playerID uuid.UUID, defaultData T) (T, error) {
res, err := m.Get(ctx, playerID)
if err != nil {
if errors.Is(err, mongo.ErrNoDocuments) {
return defaultData, nil
}

return m.example, err
}

return res, nil
}

func (m *mongoGameDataRepository[T]) GetMultiple(ctx context.Context, playerIds []uuid.UUID) ([]T, error) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

cursor, err := m.coll.Find(ctx, bson.M{"_id": bson.M{"$in": playerIds}})
if err != nil {
return nil, err
}

exampleType := reflect.TypeOf(m.example)
sliceType := reflect.SliceOf(exampleType)
data := reflect.MakeSlice(sliceType, 0, 0).Interface().([]T)
if err := cursor.All(ctx, &data); err != nil {
return nil, err
}

return data, nil
}

func (m *mongoGameDataRepository[T]) Save(ctx context.Context, data T) error {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

_, err := m.coll.ReplaceOne(ctx, bson.M{"_id": data.PlayerID()}, data, &options.ReplaceOptions{Upsert: utils.PointerOf(true)})
if err != nil {
return fmt.Errorf("failed to insert data: %w", err)
}

return nil
}
27 changes: 25 additions & 2 deletions internal/repository/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,19 @@ import (

type GameData interface {
ToAnyProto() (*anypb.Any, error)
PlayerID() uuid.UUID
}

type BlockSumoData struct {
type BaseGameData struct {
PlayerId uuid.UUID `bson:"_id"`
}

func (d *BaseGameData) PlayerID() uuid.UUID {
return d.PlayerId
}

type BlockSumoData struct {
BaseGameData `bson:",inline"`

BlockSlot uint32 `bson:"blockSlot"`
ShearsSlot uint32 `bson:"shearsSlot"`
Expand All @@ -30,9 +39,23 @@ func (d *BlockSumoData) FromProto(pId uuid.UUID, data *gameplayerdata.V1BlockSum
d.ShearsSlot = data.ShearsSlot
}

type MarathonData struct {
BaseGameData `bson:",inline"`

Time string
BlockPalette string
}

func (d *MarathonData) ToAnyProto() (*anypb.Any, error) {
return anypb.New(&gameplayerdata.V1MarathonData{
Time: d.Time,
BlockPalette: d.BlockPalette,
})
}

// MinesweeperData TODO
type MinesweeperData struct {
PlayerId uuid.UUID `bson:"_id"`
BaseGameData `bson:",inline"`
}

// TODO
Expand Down
Loading

0 comments on commit e429357

Please sign in to comment.