From 09cfcfd0d78be4f8e46b16485b8846b3c6a7a514 Mon Sep 17 00:00:00 2001 From: chaewonkong Date: Sat, 7 Mar 2026 17:21:47 +0900 Subject: [PATCH 1/6] refactor: remove legacy project --- .env.example | 1 - .gitignore | 3 +- cmd/apiserver/main.go | 60 ---- cmd/main.go | 7 + cmd/simulator/main.go | 11 - docker-compose.yml | 6 +- docs/README.md | 5 - docs/index.html | 264 ------------------ docs/matchmaking.apib | 64 ----- docs/sample_requests/api.md | 12 - go.mod | 28 +- go.sum | 45 --- mise.toml | 2 +- schema/config.go | 56 ---- schema/entity.go | 33 --- schema/request.go | 25 -- services/apiserver/list/list.go | 31 -- services/apiserver/list/list_test.go | 32 --- services/apiserver/server.go | 100 ------- services/apiserver/usecase/match_service.go | 43 --- .../strategy/dualteam/dual_team_strategy.go | 70 ----- .../usecase/strategy/pve/pve_strategy.go | 67 ----- .../usecase/strategy/pve/pve_strategy_test.go | 83 ------ .../apiserver/usecase/strategy/strategy.go | 17 -- services/apiserver/usecase/ticket_service.go | 35 --- services/apiserver/validator.go | 26 -- services/queue/interface.go | 16 -- services/queue/matching_queue.go | 63 ----- services/queue/matching_queue_test.go | 88 ------ services/queue/queue.go | 53 ---- 30 files changed, 12 insertions(+), 1334 deletions(-) delete mode 100644 .env.example delete mode 100644 cmd/apiserver/main.go create mode 100644 cmd/main.go delete mode 100644 cmd/simulator/main.go delete mode 100644 docs/README.md delete mode 100644 docs/index.html delete mode 100644 docs/matchmaking.apib delete mode 100644 docs/sample_requests/api.md delete mode 100644 schema/config.go delete mode 100644 schema/entity.go delete mode 100644 schema/request.go delete mode 100644 services/apiserver/list/list.go delete mode 100644 services/apiserver/list/list_test.go delete mode 100644 services/apiserver/server.go delete mode 100644 services/apiserver/usecase/match_service.go delete mode 100644 services/apiserver/usecase/strategy/dualteam/dual_team_strategy.go delete mode 100644 services/apiserver/usecase/strategy/pve/pve_strategy.go delete mode 100644 services/apiserver/usecase/strategy/pve/pve_strategy_test.go delete mode 100644 services/apiserver/usecase/strategy/strategy.go delete mode 100644 services/apiserver/usecase/ticket_service.go delete mode 100644 services/apiserver/validator.go delete mode 100644 services/queue/interface.go delete mode 100644 services/queue/matching_queue.go delete mode 100644 services/queue/matching_queue_test.go delete mode 100644 services/queue/queue.go diff --git a/.env.example b/.env.example deleted file mode 100644 index 650a92b..0000000 --- a/.env.example +++ /dev/null @@ -1 +0,0 @@ -QUEUE_CONFIG_PATH=/.config/queue.yaml \ No newline at end of file diff --git a/.gitignore b/.gitignore index 001f0fa..600d2d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -.vscode -.config \ No newline at end of file +.vscode \ No newline at end of file diff --git a/cmd/apiserver/main.go b/cmd/apiserver/main.go deleted file mode 100644 index e019426..0000000 --- a/cmd/apiserver/main.go +++ /dev/null @@ -1,60 +0,0 @@ -package main - -import ( - "log/slog" - "os" - - "github.com/chaewonkong/matchmaker/schema" - "github.com/chaewonkong/matchmaker/services/apiserver" - "github.com/chaewonkong/matchmaker/services/apiserver/usecase" - "github.com/chaewonkong/matchmaker/services/queue" - "github.com/labstack/echo/v4" -) - -const ( - // ExitCodeSuccess success - ExitCodeSuccess = 0 - // ExitCodeFailure failure - ExitCodeFailure = 1 -) - -func main() { - code := run() - os.Exit(code) -} - -func run() int { - queueConfigPath := os.Getenv("QUEUE_CONFIG_PATH") - - queueConfig := schema.NewQueueConfig() - err := queueConfig.UnmarshalFromYAML(queueConfigPath) - if err != nil { - slog.Error("failed to load queue config", "error", err) - return ExitCodeFailure - } - - logger := slog.New(slog.NewJSONHandler(os.Stderr, nil)) - logger.Info("starting the application...") - - e := echo.New() - q := queue.New() - ts := usecase.NewTicketService(q) - ms, err := usecase.NewMatchService(queueConfig, q) - if err != nil { - logger.Error("failed to create match service", "error", err) - } - h := apiserver.NewHandler(ts, ms) - - // middlewares etc - e.Validator = apiserver.NewCustomValidator() - - apiserver.RegisterRoutes(e, h) - - err = e.Start(":8080") - if err != nil { - logger.Error("failed to start the server", "error", err) - return ExitCodeFailure - } - - return ExitCodeSuccess -} diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..635db7a --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("hello, world") +} diff --git a/cmd/simulator/main.go b/cmd/simulator/main.go deleted file mode 100644 index 00b8b2d..0000000 --- a/cmd/simulator/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import "log" - -func main() { - log.Fatal("not ready") - - // 1. create_ticket - // 2. delete_ticket on random ratio - // 3. get and ack events -} diff --git a/docker-compose.yml b/docker-compose.yml index 1944d72..c2410c9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,12 +12,10 @@ services: restart: always command: redis-server /usr/local/conf/redis.conf healthcheck: - test: ['CMD', 'redis-cli', 'ping'] + test: [ 'CMD', 'redis-cli', 'ping' ] interval: 10s timeout: 5s retries: 3 - api-server: - image: api-server:latest volumes: - redis-data: \ No newline at end of file + redis-data: diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 2fe6b40..0000000 --- a/docs/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Generate API Documentation html - -```bash -aglio -i matchmaking.apib --theme-template triple -o index.html -``` \ No newline at end of file diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 085250f..0000000 --- a/docs/index.html +++ /dev/null @@ -1,264 +0,0 @@ -Matchmaker API

Matchmaker API

API Endpoint

Matchmaker API (WIP)

-

Ticket API

Creates or Deletes matchmaking request.

-

Resource

POST /tickets
Requestsexample 1
Headers
Content-Type: application/json
Body
{
-  "ticket_id": "abc123",
-  "player_ids": [
-    "player1",
-    "player2"
-  ],
-  "time": "2025-07-23T15:04:05Z"
-}
Responses200
Headers
Content-Type: application/json
Body
{
-  "message": "Ticket created successfully",
-  "ticket_id": "abc123"
-}

POST/tickets

Creates a matchmaking request by sending a ticket.

-

Resource

DELETE /tickets/ticket_id
Responses200
Headers
Content-Type: application/json
Body
{
-  "message": "Ticket cancelled successfully"
-}

DELETE/tickets/{ticket_id}

Cancels a matchmaking request.

-
URI Parameters
HideShow
ticket_id
string (required) 

ID of the ticket

-

Match API

Finds and acknowledges match candidates

-

Resource

GET /matches/candidates
Responses200
Headers
Content-Type: application/json
Body
[
-  {
-    "id": "match-001",
-    "tickets": [
-      {
-        "id": "ticket-101",
-        "player_ids": [
-          "player-1",
-          "player-2"
-        ],
-        "timestamp": "2025-07-26T08:00:00Z"
-      },
-      {
-        "id": "ticket-102",
-        "player_ids": [
-          "player-3",
-          "player-4"
-        ],
-        "timestamp": "2025-07-26T08:05:00Z"
-      }
-    ]
-  }
-]

GET/matches/candidates


Generated by aglio on 28 Jul 2025

\ No newline at end of file diff --git a/docs/matchmaking.apib b/docs/matchmaking.apib deleted file mode 100644 index 6b17f93..0000000 --- a/docs/matchmaking.apib +++ /dev/null @@ -1,64 +0,0 @@ -FORMAT: 1A - -# Matchmaker API - -Matchmaker API (WIP) - -## Group Ticket API -Creates or Deletes matchmaking request. - -### POST /tickets -Creates a matchmaking request by sending a ticket. - -+ Request (application/json) - - { - "ticket_id": "abc123", - "player_ids": ["player1", "player2"], - "time": "2025-07-23T15:04:05Z" - } - -+ Response 200 (application/json) - - { - "message": "Ticket created successfully", - "ticket_id": "abc123" - } - -### DELETE /tickets/{ticket_id} -Cancels a matchmaking request. - -+ Parameters - + ticket_id (string) - ID of the ticket - -+ Response 200 (application/json) - - { - "message": "Ticket cancelled successfully" - } - -## Group Match API - -Finds and acknowledges match candidates - -### GET /matches/candidates - -+ Response 200 (application/json) - - [ - { - "id": "match-001", - "tickets": [ - { - "id": "ticket-101", - "player_ids": ["player-1", "player-2"], - "timestamp": "2025-07-26T08:00:00Z" - }, - { - "id": "ticket-102", - "player_ids": ["player-3", "player-4"], - "timestamp": "2025-07-26T08:05:00Z" - } - ] - } - ] \ No newline at end of file diff --git a/docs/sample_requests/api.md b/docs/sample_requests/api.md deleted file mode 100644 index 7e0e8e6..0000000 --- a/docs/sample_requests/api.md +++ /dev/null @@ -1,12 +0,0 @@ -# API - -### create ticket -```bash -curl -X POST http://localhost:8080/tickets \ - -H "Content-Type: application/json" \ - -d '{ - "ticket_id": "abc123", - "player_ids": ["player1", "player2"], - "time": "2025-07-23T15:04:05Z" - }' -``` \ No newline at end of file diff --git a/go.mod b/go.mod index 34805fc..e0b0db6 100644 --- a/go.mod +++ b/go.mod @@ -1,29 +1,3 @@ module github.com/chaewonkong/matchmaker -go 1.25 - -require ( - github.com/go-playground/validator/v10 v10.27.0 - github.com/google/uuid v1.6.0 - github.com/labstack/echo/v4 v4.13.4 - github.com/stretchr/testify v1.10.0 - gopkg.in/yaml.v3 v3.0.1 -) - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/gabriel-vasile/mimetype v1.4.8 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/labstack/gommon v0.4.2 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasttemplate v1.2.2 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect -) +go 1.26 diff --git a/go.sum b/go.sum index 9f0038d..e69de29 100644 --- a/go.sum +++ b/go.sum @@ -1,45 +0,0 @@ -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/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= -github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= -github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA= -github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ= -github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= -github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= -github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/mise.toml b/mise.toml index ae35574..5ab3c3a 100644 --- a/mise.toml +++ b/mise.toml @@ -1,2 +1,2 @@ [tools] -go = "1.25.4" \ No newline at end of file +go = "1.26.1" diff --git a/schema/config.go b/schema/config.go deleted file mode 100644 index d1f5092..0000000 --- a/schema/config.go +++ /dev/null @@ -1,56 +0,0 @@ -package schema - -import ( - "fmt" - "os" - - "gopkg.in/yaml.v3" -) - -type MatchingStrategy string - -const ( - // player vs Environment - PvE MatchingStrategy = "PvE" - - // Nop - Nop MatchingStrategy = "Nop" - - // DualTeam red team vs blue team - DualTeam MatchingStrategy = "DualTeam" -) - -// QueueConfig represents the configuration for the matchmaking queue. -type QueueConfig struct { - Name string `json:"name" yaml:"name"` - ID string `json:"id" yaml:"id"` - Version string `json:"version" yaml:"version"` - TeamLayout TeamLayout `json:"team_layout" yaml:"team_layout"` - Strategy MatchingStrategy `json:"matching_strategy" yaml:"matching_strategy"` -} - -// TeamLayout represents the layout of teams in the matchmaking system. -type TeamLayout struct { - NumberOfTeams int `json:"number_of_teams" yaml:"number_of_teams"` - TeamCapacity int `json:"team_capacity" yaml:"team_capacity"` -} - -// NewQueueConfig constructor -func NewQueueConfig() *QueueConfig { - return &QueueConfig{} -} - -// UnmarshalFromYAML reads the QueueConfig from a YAML file at the specified path. -func (c *QueueConfig) UnmarshalFromYAML(path string) error { - f, err := os.Open(path) - if err != nil { - return fmt.Errorf("failed to open queue config file: %w", err) - } - - err = yaml.NewDecoder(f).Decode(c) - if err != nil { - return fmt.Errorf("failed to decode queue config file: %w", err) - } - - return nil -} diff --git a/schema/entity.go b/schema/entity.go deleted file mode 100644 index f607cf4..0000000 --- a/schema/entity.go +++ /dev/null @@ -1,33 +0,0 @@ -package schema - -import "time" - -// Ticket represents a matchmaking ticket. -type Ticket struct { - ID string `json:"id" validate:"required"` - PlayerIDs []string `json:"player_ids" validate:"required"` - Timestamp time.Time `json:"timestamp" validate:"required"` -} - -// Player represents a player. -type Player struct { - ID string `json:"id"` -} - -// Match represents a match -type Match struct { - ID string `json:"id"` - Teams []Team `json:"teams"` -} - -// Team represents a team -type Team struct { - // offset, index, id, number, order, - Index int `json:"index"` - Tickets []Ticket -} - -// MatchResult represents the result of a match. -type MatchResult struct { - MatchID string `json:"match_id"` -} diff --git a/schema/request.go b/schema/request.go deleted file mode 100644 index bc35a38..0000000 --- a/schema/request.go +++ /dev/null @@ -1,25 +0,0 @@ -package schema - -import "time" - -// TicketRequest create ticket request -type TicketRequest struct { - ID string `json:"ticket_id" validate:"required"` - PlayerIDs []string `json:"player_ids" validate:"required"` - Time string `json:"time" validate:"required"` // ISO 8601 format -} - -// ToTicket converts TicketRequest to Ticket -func (t *TicketRequest) ToTicket() (tkt Ticket, err error) { - // parse ISO 8601 time format - timestamp, err := time.Parse(time.RFC3339, t.Time) - if err != nil { - return - } - - tkt.ID = t.ID - tkt.PlayerIDs = t.PlayerIDs - tkt.Timestamp = timestamp - - return -} diff --git a/services/apiserver/list/list.go b/services/apiserver/list/list.go deleted file mode 100644 index 47ea2ca..0000000 --- a/services/apiserver/list/list.go +++ /dev/null @@ -1,31 +0,0 @@ -package list - -import container "container/list" - -type List[T any] struct { - list *container.List -} - -func New[T any]() List[T] { - return List[T]{ - list: container.New(), - } -} - -func (l List[T]) Len() int { - return l.list.Len() -} - -func (l List[T]) Push(v T) { - l.list.PushBack(v) -} - -func (l List[T]) Pop() (T, bool) { - v := l.list.Back() - if v != nil { - val := l.list.Remove(v) - return val.(T), true - } - var zero T - return zero, false -} diff --git a/services/apiserver/list/list_test.go b/services/apiserver/list/list_test.go deleted file mode 100644 index b48edac..0000000 --- a/services/apiserver/list/list_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package list_test - -import ( - "testing" - - "github.com/chaewonkong/matchmaker/services/apiserver/list" - "github.com/stretchr/testify/assert" -) - -type mockStruct struct { - name string -} - -func TestList(t *testing.T) { - s := mockStruct{ - name: "Leon", - } - - l := list.New[mockStruct]() - - assert.Equal(t, 0, l.Len()) - l.Push(s) - assert.Equal(t, 1, l.Len()) - - v, ok := l.Pop() - assert.True(t, ok) - assert.Equal(t, 0, l.Len()) - assert.Equal(t, s, v) - - _, ok = l.Pop() - assert.False(t, ok) -} diff --git a/services/apiserver/server.go b/services/apiserver/server.go deleted file mode 100644 index eb416a1..0000000 --- a/services/apiserver/server.go +++ /dev/null @@ -1,100 +0,0 @@ -package apiserver - -import ( - "net/http" - - "github.com/chaewonkong/matchmaker/schema" - "github.com/chaewonkong/matchmaker/services/apiserver/usecase" - "github.com/labstack/echo/v4" -) - -// Handler api handler -type Handler struct { - ticketService *usecase.TicketService - matchService *usecase.MatchService -} - -// NewHandler creates a new API handler -func NewHandler(ts *usecase.TicketService, ms *usecase.MatchService) *Handler { - return &Handler{ticketService: ts, matchService: ms} -} - -// CreateTicket handles the creation of a matchmaking ticket -func (h *Handler) CreateTicket(c echo.Context) error { - // Implementation for creating a ticket - t := schema.TicketRequest{} - err := c.Bind(&t) - if err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request body"}) - } - - // Validate the ticket - err = c.Validate(&t) - if err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()}) - } - - tkt, err := t.ToTicket() - if err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid ticket time data"}) - } - - h.ticketService.Add(tkt) - - return c.JSON(http.StatusOK, map[string]string{"message": "Ticket created successfully", "ticket_id": t.ID}) -} - -// DeleteTicketByID handles the cancellation of a matchmaking ticket by ID -func (h *Handler) DeleteTicketByID(c echo.Context) error { - ticketID := c.Param("ticket_id") - - err := h.ticketService.RemoveByID(ticketID) - if err != nil { - return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) - } - - return c.JSON(http.StatusOK, map[string]string{"message": "Ticket cancelled successfully"}) -} - -// FindAllMatchCandidates retrieves all current match candidates -func (h *Handler) FindAllMatchCandidates(c echo.Context) error { - matches, err := h.matchService.FindAllMatchCandidates() - if err != nil { - return c.JSON(http.StatusInternalServerError, map[string]string{"error": "find match failed"}) - } - - return c.JSON(http.StatusOK, matches) -} - -// CreateOrUpdateMatchAck handles the creation or update of player acknowledgement for a match -func (h *Handler) CreateOrUpdateMatchAck(c echo.Context) error { - // Implementation for creating or updating match acknowledgement - return nil -} - -// CreateMatchResult handles the submission of game results (win/loss) -func (h *Handler) CreateMatchResult(c echo.Context) error { - // Implementation for creating match results - r := &schema.MatchResult{} - err := c.Bind(r) - if err != nil { - return c.JSON(400, map[string]string{"error": "Invalid request body"}) - } - - // Validate the match result - err = c.Validate(&r) - if err != nil { - return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()}) - } - - return nil -} - -// RegisterRoutes registers the API routes with the provided Echo instance -func RegisterRoutes(e *echo.Echo, h *Handler) { - e.POST("/tickets", h.CreateTicket) - e.DELETE("/tickets/:ticket_id", h.DeleteTicketByID) - e.GET("/matches/candidates", h.FindAllMatchCandidates) - e.PUT("/matches/:match_id/ack", h.CreateOrUpdateMatchAck) - e.POST("/match-results", h.CreateMatchResult) -} diff --git a/services/apiserver/usecase/match_service.go b/services/apiserver/usecase/match_service.go deleted file mode 100644 index 0ea06be..0000000 --- a/services/apiserver/usecase/match_service.go +++ /dev/null @@ -1,43 +0,0 @@ -package usecase - -import ( - "github.com/chaewonkong/matchmaker/schema" - "github.com/chaewonkong/matchmaker/services/apiserver/usecase/strategy" - "github.com/chaewonkong/matchmaker/services/apiserver/usecase/strategy/dualteam" - "github.com/chaewonkong/matchmaker/services/apiserver/usecase/strategy/pve" - "github.com/chaewonkong/matchmaker/services/queue" -) - -// MatchService match service -type MatchService struct { - strategy strategy.Strategy -} - -// NewMatchService constructor -func NewMatchService(cfg *schema.QueueConfig, q *queue.MatchingQueue) (*MatchService, error) { - switch cfg.Strategy { - case schema.PvE: - return &MatchService{ - pve.PvEStrategy{ - Queue: q, - QueueConfig: cfg, - }, - }, nil - case schema.DualTeam: - return &MatchService{ - dualteam.DualteamStrategy{ - Queue: q, - QueueConfig: cfg, - }, - }, nil - default: // nop - return &MatchService{ - strategy.NopStrategy{}, - }, nil - } -} - -// FindAllMatchCandidates searches all possible match candidates -func (ms *MatchService) FindAllMatchCandidates() ([]schema.Match, error) { - return ms.strategy.FindMatchCandidates() -} diff --git a/services/apiserver/usecase/strategy/dualteam/dual_team_strategy.go b/services/apiserver/usecase/strategy/dualteam/dual_team_strategy.go deleted file mode 100644 index e164e1a..0000000 --- a/services/apiserver/usecase/strategy/dualteam/dual_team_strategy.go +++ /dev/null @@ -1,70 +0,0 @@ -package dualteam - -import ( - "github.com/chaewonkong/matchmaker/schema" - "github.com/chaewonkong/matchmaker/services/apiserver/list" - "github.com/chaewonkong/matchmaker/services/apiserver/usecase/strategy" - "github.com/chaewonkong/matchmaker/services/queue" -) - -var _ strategy.Strategy = (*DualteamStrategy)(nil) - -// DualteamStrategy dual team layout strategy -type DualteamStrategy struct { - Queue *queue.MatchingQueue - QueueConfig *schema.QueueConfig -} - -// FindMatchCandidates finds match candidates in dual team layout -func (d DualteamStrategy) FindMatchCandidates() ([]schema.Match, error) { - numTeams := d.QueueConfig.TeamLayout.NumberOfTeams - teamCap := d.QueueConfig.TeamLayout.TeamCapacity - - // generate teams first - teams := list.New[schema.Team]() - - for d.Queue.Len() > 0 { - team := schema.Team{} - - for len(team.Tickets) < teamCap { - tkt, ok := d.Queue.Dequeue() - if !ok { - break // discard - } - - team.Tickets = append(team.Tickets, tkt) - } - teams.Push(team) - } - - // TODO: DI - return CandidateBuilder{}.Build(teams, numTeams) -} - -// CandidateBuilder composes match candidates from generated teams -type CandidateBuilder struct{} - -// Build composes match candidates from generated teams -func (c CandidateBuilder) Build(teams list.List[schema.Team], numTeams int) ([]schema.Match, error) { - candidates := []schema.Match{} - for teams.Len() > 0 { - m := schema.Match{} - - // TODO: shuffle teams, or make each team in match candidate fair and even. - // FIXME: this code discards team when match team layout is not satisfied. - for i := range numTeams { - if team, ok := teams.Pop(); ok { - team.Index = i - m.Teams = append(m.Teams, team) - } - } - - // if m has enough teams, append m to candidates - if len(m.Teams) == numTeams { - candidates = append(candidates, m) - } - // if len(m.Teams) < numTeams, discard m - } - - return candidates, nil -} diff --git a/services/apiserver/usecase/strategy/pve/pve_strategy.go b/services/apiserver/usecase/strategy/pve/pve_strategy.go deleted file mode 100644 index 9894fbc..0000000 --- a/services/apiserver/usecase/strategy/pve/pve_strategy.go +++ /dev/null @@ -1,67 +0,0 @@ -package pve - -import ( - "fmt" - - "github.com/chaewonkong/matchmaker/schema" - "github.com/chaewonkong/matchmaker/services/queue" - "github.com/google/uuid" -) - -// PvEStrategy PvE strategy -type PvEStrategy struct { - Queue *queue.MatchingQueue - QueueConfig *schema.QueueConfig -} - -// FindMatchCandidates finds match candidates according to PvE strategy -func (pe PvEStrategy) FindMatchCandidates() ([]schema.Match, error) { - matchCandidates := []schema.Match{} - cap := pe.QueueConfig.TeamLayout.TeamCapacity - - if cap < 1 { - return nil, fmt.Errorf("error team capacity must be greater than 0") - } - - for pe.Queue.Len() > 0 { - - candidate := []schema.Ticket{} - rejected := []schema.Ticket{} - slots := cap - for { - if slots == 0 { - // full - break - } - - tkt, ok := pe.Queue.Dequeue() - if !ok { - break - } - - n := len(tkt.PlayerIDs) - if n > slots { // too many players - rejected = append(rejected, tkt) - continue - } - candidate = append(candidate, tkt) - slots -= n - } - - // add candidate - if slots == 0 { - matchID := uuid.NewString() - teams := []schema.Team{ - {Index: 0, Tickets: candidate}, - } - matchCandidates = append(matchCandidates, schema.Match{ID: matchID, Teams: teams}) - } - - // add rejected tickets to queue again - for _, tkt := range rejected { - pe.Queue.Enqueue(tkt) - } - } - - return matchCandidates, nil -} diff --git a/services/apiserver/usecase/strategy/pve/pve_strategy_test.go b/services/apiserver/usecase/strategy/pve/pve_strategy_test.go deleted file mode 100644 index 8991454..0000000 --- a/services/apiserver/usecase/strategy/pve/pve_strategy_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package pve_test - -import ( - "testing" - "time" - - "github.com/chaewonkong/matchmaker/schema" - "github.com/chaewonkong/matchmaker/services/apiserver/usecase/strategy/pve" - "github.com/chaewonkong/matchmaker/services/queue" - "github.com/stretchr/testify/assert" -) - -func TestPvEStrategyFindMatchCandidates(t *testing.T) { - t.Run("Finds match candidates successfully", func(t *testing.T) { - // given - queue := queue.New() - cfg := &schema.QueueConfig{ - ID: "queue1", - Name: "player vs environment", - TeamLayout: schema.TeamLayout{ - NumberOfTeams: 1, - TeamCapacity: 4, - }, - Strategy: schema.PvE, - } - s := &pve.PvEStrategy{ - Queue: queue, - QueueConfig: cfg, - } - now := time.Now() - - t1 := schema.Ticket{ - ID: "1", - PlayerIDs: []string{"p1", "p2"}, - Timestamp: now, - } - t2 := schema.Ticket{ - ID: "2", - PlayerIDs: []string{"p3", "p4", "p5"}, - Timestamp: now.Add(1 * time.Second), - } - t3 := schema.Ticket{ - ID: "3", - PlayerIDs: []string{"p6"}, - Timestamp: now.Add(2 * time.Second), - } - t4 := schema.Ticket{ - ID: "4", - PlayerIDs: []string{"p7"}, - Timestamp: now.Add(3 * time.Second), - } - t5 := schema.Ticket{ - ID: "5", - PlayerIDs: []string{"p8"}, - Timestamp: now.Add(4 * time.Second), - } - - // when - queue.Enqueue(t1) - queue.Enqueue(t2) - queue.Enqueue(t3) - queue.Enqueue(t4) - queue.Enqueue(t5) - - assert.Equal(t, 5, queue.Len()) - - // then - results, err := s.FindMatchCandidates() - assert.NoError(t, err) - - for _, match := range results { - cnt := 0 - - for _, team := range match.Teams { - for _, tkt := range team.Tickets { - cnt += len(tkt.PlayerIDs) - } - } - assert.Equal(t, 4, cnt) - } - - }) -} diff --git a/services/apiserver/usecase/strategy/strategy.go b/services/apiserver/usecase/strategy/strategy.go deleted file mode 100644 index 90e6829..0000000 --- a/services/apiserver/usecase/strategy/strategy.go +++ /dev/null @@ -1,17 +0,0 @@ -package strategy - -import "github.com/chaewonkong/matchmaker/schema" - -// Strategy match candidate finder algorithm -type Strategy interface { - // FindMatchCandidates finds match candidates - FindMatchCandidates() ([]schema.Match, error) -} - -// NopStrategy nop -type NopStrategy struct{} - -// FindMatchCandidates nop -func (NopStrategy) FindMatchCandidates() ([]schema.Match, error) { - return nil, nil -} diff --git a/services/apiserver/usecase/ticket_service.go b/services/apiserver/usecase/ticket_service.go deleted file mode 100644 index d4a3d16..0000000 --- a/services/apiserver/usecase/ticket_service.go +++ /dev/null @@ -1,35 +0,0 @@ -package usecase - -import ( - "fmt" - - "github.com/chaewonkong/matchmaker/schema" - "github.com/chaewonkong/matchmaker/services/queue" -) - -// TicketService is responsible for creating and managing matchmaking tickets. -type TicketService struct { - queue *queue.MatchingQueue -} - -// NewTicketService creates a new TicketCreator instance. -func NewTicketService(q *queue.MatchingQueue) *TicketService { - return &TicketService{ - queue: q, - } -} - -// Add creates a new matchmaking adds it to the queue. -func (t *TicketService) Add(ticket schema.Ticket) { - t.queue.Enqueue(ticket) -} - -// RemoveByID removes a matchmaking ticket from the queue by its ID. -func (t *TicketService) RemoveByID(ticketID string) error { - _, ok := t.queue.RemoveTicketByID(ticketID) - if !ok { - return fmt.Errorf("ticket with ID %s not found", ticketID) - } - - return nil -} diff --git a/services/apiserver/validator.go b/services/apiserver/validator.go deleted file mode 100644 index 77e989b..0000000 --- a/services/apiserver/validator.go +++ /dev/null @@ -1,26 +0,0 @@ -package apiserver - -import ( - "net/http" - - "github.com/go-playground/validator/v10" - "github.com/labstack/echo/v4" -) - -// CustomValidator implements the echo.Validator interface -type CustomValidator struct { - validator *validator.Validate -} - -// NewCustomValidator constructor -func NewCustomValidator() *CustomValidator { - return &CustomValidator{validator: validator.New()} -} - -// Validate validates the request body against the struct tags -func (cv *CustomValidator) Validate(i interface{}) error { - if err := cv.validator.Struct(i); err != nil { - return echo.NewHTTPError(http.StatusBadRequest, err.Error()) - } - return nil -} diff --git a/services/queue/interface.go b/services/queue/interface.go deleted file mode 100644 index f13569a..0000000 --- a/services/queue/interface.go +++ /dev/null @@ -1,16 +0,0 @@ -package queue - -import ( - "context" - - "github.com/chaewonkong/matchmaker/schema" -) - -// Queue where tickets wait to be matched -type Queue interface { - // Add add requested ticket to the queue - Add(ctx context.Context, ticket schema.Ticket) error - - // Fetch fetches top n number of tickets from queue. - Fetch(ctx, n int) ([]schema.Ticket, error) -} diff --git a/services/queue/matching_queue.go b/services/queue/matching_queue.go deleted file mode 100644 index cfae279..0000000 --- a/services/queue/matching_queue.go +++ /dev/null @@ -1,63 +0,0 @@ -package queue - -import ( - "container/heap" - - "github.com/chaewonkong/matchmaker/schema" -) - -// MatchingQueue is a priority queue for matchmaking tickets. -type MatchingQueue struct { - queue queue - index map[string]*ticketEntry -} - -// New creates a new MatchingQueue instance. -func New() *MatchingQueue { - return &MatchingQueue{ - queue: queue{}, - index: make(map[string]*ticketEntry), - } -} - -// Len returns the number of tickets in the queue. -func (q *MatchingQueue) Len() int { - return q.queue.Len() -} - -// Enqueue adds a ticket to the queue. -func (q *MatchingQueue) Enqueue(ticket schema.Ticket) { - if _, exists := q.index[ticket.ID]; exists { - return // Ticket already exists in the queue - } - entry := &ticketEntry{ - Ticket: ticket, - } - heap.Push(&q.queue, entry) - q.index[ticket.ID] = entry -} - -// Dequeue removes and returns the oldest ticket from the queue. -func (q *MatchingQueue) Dequeue() (schema.Ticket, bool) { - if q.Len() == 0 { - return schema.Ticket{}, false // Return an empty ticket if the queue is empty - } - - entry, ok := heap.Pop(&q.queue).(*ticketEntry) - delete(q.index, entry.Ticket.ID) - return entry.Ticket, ok -} - -// RemoveTicketByID removes a ticket from the queue by its ID. -func (q *MatchingQueue) RemoveTicketByID(ticketID string) (schema.Ticket, bool) { - entry, exists := q.index[ticketID] - if !exists { - return schema.Ticket{}, false // Ticket not found - } - - v := heap.Remove(&q.queue, entry.index) - tkt := v.(*ticketEntry).Ticket - delete(q.index, tkt.ID) - - return tkt, true -} diff --git a/services/queue/matching_queue_test.go b/services/queue/matching_queue_test.go deleted file mode 100644 index f4ad51a..0000000 --- a/services/queue/matching_queue_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package queue_test - -import ( - "testing" - "time" - - "github.com/chaewonkong/matchmaker/schema" - "github.com/chaewonkong/matchmaker/services/queue" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestMatchingQueue(t *testing.T) { - t.Run("Len 0", func(t *testing.T) { - q := queue.New() - assert.NotPanics(t, func() { - assert.Equal(t, 0, q.Len(), "Expected queue length to be 0") - }) - }) - - t.Run("Enqueue", func(t *testing.T) { - q := queue.New() - assert.NotPanics(t, func() { - q.Enqueue(schema.Ticket{}) - assert.Equal(t, 1, q.Len(), "Expected queue length to be 1 after enqueue") - }) - }) - - t.Run("Dequeue 1 item", func(t *testing.T) { - q := queue.New() - ticketID := "ticket1" - assert.NotPanics(t, func() { - q.Enqueue(schema.Ticket{ID: ticketID}) - assert.Equal(t, 1, q.Len(), "Expected queue length to be 1 before dequeue") - ticket, ok := q.Dequeue() - assert.True(t, ok, "Expected dequeue to succeed") - assert.Equal(t, ticketID, ticket.ID, "Expected dequeued ticket ID to match") - assert.Equal(t, 0, q.Len(), "Expected queue length to be 0 after dequeue") - }) - }) - - t.Run("enqueue, dequeue order check", func(t *testing.T) { - q := queue.New() - now := time.Now() - tickets := []schema.Ticket{ - {ID: "1", Timestamp: now.Add(3 * time.Second)}, - {ID: "2", Timestamp: now.Add(2 * time.Second)}, - {ID: "3", Timestamp: now.Add(1 * time.Second)}, - } - - for _, tkt := range tickets { - q.Enqueue(tkt) - } - - assert.Equal(t, 3, q.Len(), "Expected queue length to be 3 after enqueueing 3 tickets") - - for i := 2; i >= 0; i-- { - ticket, ok := q.Dequeue() - assert.True(t, ok, "Expected dequeue to succeed") - assert.Equal(t, tickets[i].ID, ticket.ID, "Expected dequeued ticket ID to match") - } - }) - - t.Run("RemoveTicketByID", func(t *testing.T) { - q := queue.New() - now := time.Now() - tickets := []schema.Ticket{ - {ID: "1", Timestamp: now.Add(3 * time.Second)}, - {ID: "2", Timestamp: now.Add(2 * time.Second)}, - {ID: "3", Timestamp: now.Add(1 * time.Second)}, - } - - for _, tkt := range tickets { - q.Enqueue(tkt) - } - - assert.Equal(t, 3, q.Len(), "Expected queue length to be 3 after enqueueing 3 tickets") - - require.NotPanics(t, func() { - tkt, ok := q.RemoveTicketByID("2") - assert.True(t, ok, "Expected ticket with ID '2' to be removed successfully") - assert.Equal(t, "2", tkt.ID, "Expected removed ticket ID to match '2'") - - assert.Equal(t, 2, q.Len(), "Expected queue length to be 2 after removing one ticket") - - }) - }) -} diff --git a/services/queue/queue.go b/services/queue/queue.go deleted file mode 100644 index 8a7cdf4..0000000 --- a/services/queue/queue.go +++ /dev/null @@ -1,53 +0,0 @@ -package queue - -import ( - "container/heap" - - "github.com/chaewonkong/matchmaker/schema" -) - -type ticketEntry struct { - schema.Ticket - index int -} - -type queue []*ticketEntry - -// Less implements heap.Interface. -func (q queue) Less(i int, j int) bool { - return q[i].Timestamp.Before(q[j].Timestamp) -} - -// Pop implements heap.Interface. -func (q *queue) Pop() any { - old := *q - n := len(old) - ticket := old[n-1] - *q = old[0 : n-1] - - return ticket -} - -// Push implements heap.Interface. -func (q *queue) Push(x any) { - entry, ok := x.(*ticketEntry) - if !ok { - return - } - *q = append(*q, entry) -} - -// Swap implements heap.Interface. -func (q *queue) Swap(i int, j int) { - qs := *q - qs[i], qs[j] = qs[j], qs[i] - qs[i].index = i - qs[j].index = j -} - -// Len implements heap.Interface. -func (q queue) Len() int { - return len(q) -} - -var _ heap.Interface = (*queue)(nil) From 4bffc1775a6d55ba6e1e37c272913a99115994bd Mon Sep 17 00:00:00 2001 From: chaewonkong Date: Sat, 7 Mar 2026 18:12:09 +0900 Subject: [PATCH 2/6] chore: add grpc schema --- README.md | 24 +++++----- architecture.md | 2 +- buf.gen.yaml | 9 ++++ buf.yaml | 3 ++ cmd/{ => gateway}/main.go | 2 +- mise.toml | 1 + proto/gateway/v1/gateway.proto | 84 ++++++++++++++++++++++++++++++++++ 7 files changed, 111 insertions(+), 14 deletions(-) create mode 100644 buf.gen.yaml create mode 100644 buf.yaml rename cmd/{ => gateway}/main.go (58%) create mode 100644 proto/gateway/v1/gateway.proto diff --git a/README.md b/README.md index 87469cd..f665efd 100644 --- a/README.md +++ b/README.md @@ -3,18 +3,18 @@ A lightweight and high-performance matchmaker service written in Go. Designed for real-time game matchmaking with concurrent player support. -# Components -## API Server -The API server provides a RESTful interface for managing matchmaking tickets, match candidates, player acknowledgements, and game results. -### REST API Endpoints +# gRPC +```shell +$ mise install +$ mise use buf@1.66.0 +``` + +Install Go plugins +```shell +$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest +$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest +``` -Below is a summary of the REST API endpoints and their purposes: +# Components -| Description | HTTP Method | Endpoint | -|--------------------------------------------|-------------|-----------------------------------------| -| Create matchmaking ticket | POST | `/tickets` | -| Cancel matchmaking ticket | DELETE | `/tickets/{ticket_id}` | -| List current match candidates | GET | `/matches/candidates` | -| Create or update player acknowledgement | PUT | `/matches/{match_id}/acknowledgement` | -| Submit game result (win/loss) | POST | `/match-results` | diff --git a/architecture.md b/architecture.md index fcb534c..3b530aa 100644 --- a/architecture.md +++ b/architecture.md @@ -2,7 +2,7 @@ ```mermaid flowchart LR - A[API Server] + A[Gateway] B@{shape: das, label: "ticket"} C@{shape: das, label: "match"} D@{shape: cyl, label: "Feature Sture"} diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..6b09b0a --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,9 @@ +# buf.gen.yaml +version: v2 +plugins: + - remote: buf.build/protocolbuffers/go + out: gen/go + opt: paths=source_relative + - remote: buf.build/grpc/go + out: gen/go + opt: paths=source_relative \ No newline at end of file diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000..448778b --- /dev/null +++ b/buf.yaml @@ -0,0 +1,3 @@ +version: v2 +modules: + - path: proto \ No newline at end of file diff --git a/cmd/main.go b/cmd/gateway/main.go similarity index 58% rename from cmd/main.go rename to cmd/gateway/main.go index 635db7a..8000942 100644 --- a/cmd/main.go +++ b/cmd/gateway/main.go @@ -3,5 +3,5 @@ package main import "fmt" func main() { - fmt.Println("hello, world") + fmt.Println("hello, gateway") } diff --git a/mise.toml b/mise.toml index 5ab3c3a..ea92f40 100644 --- a/mise.toml +++ b/mise.toml @@ -1,2 +1,3 @@ [tools] go = "1.26.1" +buf = "1.66.0" diff --git a/proto/gateway/v1/gateway.proto b/proto/gateway/v1/gateway.proto new file mode 100644 index 0000000..7a10bf0 --- /dev/null +++ b/proto/gateway/v1/gateway.proto @@ -0,0 +1,84 @@ +syntax = "proto3"; +package gateway.v1; +option go_package = "github.com/chaewonkong/matchmaker/gen/go/gateway/v1"; + +service GatewayService { + // Unary RPCs + rpc CreateTicket(CreateTicketRequest) returns (CreateTicketResponse); + rpc CancelTicket(CancelTicketRequest) returns (CancelTicketResponse); + rpc AcknowledgeMatch(AcknowledgeMatchRequest) returns (AcknowledgeMatchResponse); + rpc SubmitMatchResult(SubmitMatchResultRequest) returns (SubmitMatchResultResponse); + + // Server-side streaming + rpc WatchMatchCandidates(WatchMatchCandidatesRequest) + returns (stream WatchMatchCandidatesResponse); +} + +message CreateTicketRequest { + repeated Player players = 1; +} + +message CreateTicketResponse { + string ticket_id = 1; + double estimated_wait_time_sec = 2; +} + +message CancelTicketRequest { + string ticket_id = 1; +} + +message CancelTicketResponse { + string ticket_id = 1; + // success / fail +} + +message AcknowledgeMatchRequest { + string match_id = 1; +} + +message AcknowledgeMatchResponse { + string match_id = 1; +} + +message SubmitMatchResultRequest { + Match match = 1; +} + +message SubmitMatchResultResponse { + string match_id = 1; +} + +message WatchMatchCandidatesRequest { + string ticket_id = 1; +} + +message WatchMatchCandidatesResponse { + oneof event { + Match match = 1; // 매치 후보 발견 + MatchExpired expired = 2; // 타임아웃/만료 + } +} + +message Match { + string match_id = 1; + repeated Team teams = 2; + // 게임서버 접속 정보 등 +} + +message MatchExpired { + string reason = 1; +} + +message Team { + string team_id = 1; + repeated Ticket tickets = 2; +} + +message Ticket { + string ticket_id = 1; + repeated Player players = 2; +} + +message Player { + string player_id = 1; +} \ No newline at end of file From cd8f197718b98d531547fc51407c4e9adaea6de0 Mon Sep 17 00:00:00 2001 From: chaewonkong Date: Sat, 7 Mar 2026 18:14:37 +0900 Subject: [PATCH 3/6] chore: update gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 600d2d3..67341d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.vscode \ No newline at end of file +.vscode +handoff.md \ No newline at end of file From 42d414f52e2905c0a215986e5a05227b24ccabb1 Mon Sep 17 00:00:00 2001 From: chaewonkong Date: Sun, 8 Mar 2026 17:17:52 +0900 Subject: [PATCH 4/6] feat: implement custom attrs --- proto/common/v1/attribute.proto | 20 ++++++++ proto/common/v1/entity.proto | 28 +++++++++++ proto/gateway/v1/gateway.proto | 84 --------------------------------- proto/gateway/v1/messages.proto | 54 +++++++++++++++++++++ proto/gateway/v1/service.proto | 15 ++++++ 5 files changed, 117 insertions(+), 84 deletions(-) create mode 100644 proto/common/v1/attribute.proto create mode 100644 proto/common/v1/entity.proto delete mode 100644 proto/gateway/v1/gateway.proto create mode 100644 proto/gateway/v1/messages.proto create mode 100644 proto/gateway/v1/service.proto diff --git a/proto/common/v1/attribute.proto b/proto/common/v1/attribute.proto new file mode 100644 index 0000000..c127536 --- /dev/null +++ b/proto/common/v1/attribute.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package common.v1; +option go_package = "github.com/chaewonkong/matchmaker/gen/go/common/v1"; + +message Attribute { + oneof value { + double double_value = 1; + string string_value = 2; + bool bool_value = 3; + StringList string_list_value = 4; + DoubleList double_list_value = 5; + StringMap string_map_value = 6; + DoubleMap double_map_value = 7; + } +} + +message StringList { repeated string values = 1; } +message DoubleList { repeated double values = 1; } +message StringMap { map values = 1; } +message DoubleMap { map values = 1; } diff --git a/proto/common/v1/entity.proto b/proto/common/v1/entity.proto new file mode 100644 index 0000000..a1dacd6 --- /dev/null +++ b/proto/common/v1/entity.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; +package common.v1; +option go_package = "github.com/chaewonkong/matchmaker/gen/go/common/v1"; + +import "common/v1/attribute.proto"; + +message Player { + string player_id = 1; + map attributes = 2; +} + +message Ticket { + string ticket_id = 1; + repeated Player players = 2; + map attributes = 3; +} + +message Team { + string team_id = 1; + repeated Ticket tickets = 2; + map attributes = 3; +} + +message Match { + string match_id = 1; + repeated Team teams = 2; + map attributes = 3; +} diff --git a/proto/gateway/v1/gateway.proto b/proto/gateway/v1/gateway.proto deleted file mode 100644 index 7a10bf0..0000000 --- a/proto/gateway/v1/gateway.proto +++ /dev/null @@ -1,84 +0,0 @@ -syntax = "proto3"; -package gateway.v1; -option go_package = "github.com/chaewonkong/matchmaker/gen/go/gateway/v1"; - -service GatewayService { - // Unary RPCs - rpc CreateTicket(CreateTicketRequest) returns (CreateTicketResponse); - rpc CancelTicket(CancelTicketRequest) returns (CancelTicketResponse); - rpc AcknowledgeMatch(AcknowledgeMatchRequest) returns (AcknowledgeMatchResponse); - rpc SubmitMatchResult(SubmitMatchResultRequest) returns (SubmitMatchResultResponse); - - // Server-side streaming - rpc WatchMatchCandidates(WatchMatchCandidatesRequest) - returns (stream WatchMatchCandidatesResponse); -} - -message CreateTicketRequest { - repeated Player players = 1; -} - -message CreateTicketResponse { - string ticket_id = 1; - double estimated_wait_time_sec = 2; -} - -message CancelTicketRequest { - string ticket_id = 1; -} - -message CancelTicketResponse { - string ticket_id = 1; - // success / fail -} - -message AcknowledgeMatchRequest { - string match_id = 1; -} - -message AcknowledgeMatchResponse { - string match_id = 1; -} - -message SubmitMatchResultRequest { - Match match = 1; -} - -message SubmitMatchResultResponse { - string match_id = 1; -} - -message WatchMatchCandidatesRequest { - string ticket_id = 1; -} - -message WatchMatchCandidatesResponse { - oneof event { - Match match = 1; // 매치 후보 발견 - MatchExpired expired = 2; // 타임아웃/만료 - } -} - -message Match { - string match_id = 1; - repeated Team teams = 2; - // 게임서버 접속 정보 등 -} - -message MatchExpired { - string reason = 1; -} - -message Team { - string team_id = 1; - repeated Ticket tickets = 2; -} - -message Ticket { - string ticket_id = 1; - repeated Player players = 2; -} - -message Player { - string player_id = 1; -} \ No newline at end of file diff --git a/proto/gateway/v1/messages.proto b/proto/gateway/v1/messages.proto new file mode 100644 index 0000000..991299d --- /dev/null +++ b/proto/gateway/v1/messages.proto @@ -0,0 +1,54 @@ +syntax = "proto3"; +package gateway.v1; +option go_package = "github.com/chaewonkong/matchmaker/gen/go/gateway/v1"; + +import "common/v1/entity.proto"; + +message CreateTicketRequest { + repeated common.v1.Ticket tickets = 1; +} + +message CreateTicketResponse { + string ticket_id = 1; + double estimated_wait_time_sec = 2; +} + +message CancelTicketRequest { + string ticket_id = 1; +} + +message CancelTicketResponse { + string ticket_id = 1; +} + +message AcknowledgeMatchRequest { + string match_id = 1; +} + +message AcknowledgeMatchResponse { + string match_id = 1; +} + +message SubmitMatchResultRequest { + common.v1.Match match = 1; +} + +message SubmitMatchResultResponse { + string match_id = 1; +} + +message WatchMatchCandidatesRequest { + string ticket_id = 1; +} + +message WatchMatchCandidatesResponse { + oneof event { + common.v1.Match match = 1; + MatchExpired expired = 2; + } +} + + +message MatchExpired { + string reason = 1; +} \ No newline at end of file diff --git a/proto/gateway/v1/service.proto b/proto/gateway/v1/service.proto new file mode 100644 index 0000000..4e3acd6 --- /dev/null +++ b/proto/gateway/v1/service.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package gateway.v1; +option go_package = "github.com/chaewonkong/matchmaker/gen/go/gateway/v1"; + +import "gateway/v1/messages.proto"; + +service GatewayService { + rpc CreateTicket(CreateTicketRequest) returns (CreateTicketResponse); + rpc CancelTicket(CancelTicketRequest) returns (CancelTicketResponse); + rpc AcknowledgeMatch(AcknowledgeMatchRequest) returns (AcknowledgeMatchResponse); + rpc SubmitMatchResult(SubmitMatchResultRequest) returns (SubmitMatchResultResponse); + + rpc WatchMatchCandidates(WatchMatchCandidatesRequest) + returns (stream WatchMatchCandidatesResponse); +} \ No newline at end of file From 445c8431724af3a00b44d1952327b4b5513a3136 Mon Sep 17 00:00:00 2001 From: chaewonkong Date: Sun, 8 Mar 2026 17:19:10 +0900 Subject: [PATCH 5/6] chore: gen proto --- gen/go/common/v1/attribute.pb.go | 461 +++++++++++++++++++ gen/go/common/v1/entity.pb.go | 363 +++++++++++++++ gen/go/gateway/v1/messages.pb.go | 653 +++++++++++++++++++++++++++ gen/go/gateway/v1/service.pb.go | 88 ++++ gen/go/gateway/v1/service_grpc.pb.go | 277 ++++++++++++ go.mod | 12 + go.sum | 38 ++ 7 files changed, 1892 insertions(+) create mode 100644 gen/go/common/v1/attribute.pb.go create mode 100644 gen/go/common/v1/entity.pb.go create mode 100644 gen/go/gateway/v1/messages.pb.go create mode 100644 gen/go/gateway/v1/service.pb.go create mode 100644 gen/go/gateway/v1/service_grpc.pb.go diff --git a/gen/go/common/v1/attribute.pb.go b/gen/go/common/v1/attribute.pb.go new file mode 100644 index 0000000..a486443 --- /dev/null +++ b/gen/go/common/v1/attribute.pb.go @@ -0,0 +1,461 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: common/v1/attribute.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Attribute struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Value: + // + // *Attribute_DoubleValue + // *Attribute_StringValue + // *Attribute_BoolValue + // *Attribute_StringListValue + // *Attribute_DoubleListValue + // *Attribute_StringMapValue + // *Attribute_DoubleMapValue + Value isAttribute_Value `protobuf_oneof:"value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Attribute) Reset() { + *x = Attribute{} + mi := &file_common_v1_attribute_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Attribute) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Attribute) ProtoMessage() {} + +func (x *Attribute) ProtoReflect() protoreflect.Message { + mi := &file_common_v1_attribute_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Attribute.ProtoReflect.Descriptor instead. +func (*Attribute) Descriptor() ([]byte, []int) { + return file_common_v1_attribute_proto_rawDescGZIP(), []int{0} +} + +func (x *Attribute) GetValue() isAttribute_Value { + if x != nil { + return x.Value + } + return nil +} + +func (x *Attribute) GetDoubleValue() float64 { + if x != nil { + if x, ok := x.Value.(*Attribute_DoubleValue); ok { + return x.DoubleValue + } + } + return 0 +} + +func (x *Attribute) GetStringValue() string { + if x != nil { + if x, ok := x.Value.(*Attribute_StringValue); ok { + return x.StringValue + } + } + return "" +} + +func (x *Attribute) GetBoolValue() bool { + if x != nil { + if x, ok := x.Value.(*Attribute_BoolValue); ok { + return x.BoolValue + } + } + return false +} + +func (x *Attribute) GetStringListValue() *StringList { + if x != nil { + if x, ok := x.Value.(*Attribute_StringListValue); ok { + return x.StringListValue + } + } + return nil +} + +func (x *Attribute) GetDoubleListValue() *DoubleList { + if x != nil { + if x, ok := x.Value.(*Attribute_DoubleListValue); ok { + return x.DoubleListValue + } + } + return nil +} + +func (x *Attribute) GetStringMapValue() *StringMap { + if x != nil { + if x, ok := x.Value.(*Attribute_StringMapValue); ok { + return x.StringMapValue + } + } + return nil +} + +func (x *Attribute) GetDoubleMapValue() *DoubleMap { + if x != nil { + if x, ok := x.Value.(*Attribute_DoubleMapValue); ok { + return x.DoubleMapValue + } + } + return nil +} + +type isAttribute_Value interface { + isAttribute_Value() +} + +type Attribute_DoubleValue struct { + DoubleValue float64 `protobuf:"fixed64,1,opt,name=double_value,json=doubleValue,proto3,oneof"` +} + +type Attribute_StringValue struct { + StringValue string `protobuf:"bytes,2,opt,name=string_value,json=stringValue,proto3,oneof"` +} + +type Attribute_BoolValue struct { + BoolValue bool `protobuf:"varint,3,opt,name=bool_value,json=boolValue,proto3,oneof"` +} + +type Attribute_StringListValue struct { + StringListValue *StringList `protobuf:"bytes,4,opt,name=string_list_value,json=stringListValue,proto3,oneof"` +} + +type Attribute_DoubleListValue struct { + DoubleListValue *DoubleList `protobuf:"bytes,5,opt,name=double_list_value,json=doubleListValue,proto3,oneof"` +} + +type Attribute_StringMapValue struct { + StringMapValue *StringMap `protobuf:"bytes,6,opt,name=string_map_value,json=stringMapValue,proto3,oneof"` +} + +type Attribute_DoubleMapValue struct { + DoubleMapValue *DoubleMap `protobuf:"bytes,7,opt,name=double_map_value,json=doubleMapValue,proto3,oneof"` +} + +func (*Attribute_DoubleValue) isAttribute_Value() {} + +func (*Attribute_StringValue) isAttribute_Value() {} + +func (*Attribute_BoolValue) isAttribute_Value() {} + +func (*Attribute_StringListValue) isAttribute_Value() {} + +func (*Attribute_DoubleListValue) isAttribute_Value() {} + +func (*Attribute_StringMapValue) isAttribute_Value() {} + +func (*Attribute_DoubleMapValue) isAttribute_Value() {} + +type StringList struct { + state protoimpl.MessageState `protogen:"open.v1"` + Values []string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StringList) Reset() { + *x = StringList{} + mi := &file_common_v1_attribute_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StringList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringList) ProtoMessage() {} + +func (x *StringList) ProtoReflect() protoreflect.Message { + mi := &file_common_v1_attribute_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringList.ProtoReflect.Descriptor instead. +func (*StringList) Descriptor() ([]byte, []int) { + return file_common_v1_attribute_proto_rawDescGZIP(), []int{1} +} + +func (x *StringList) GetValues() []string { + if x != nil { + return x.Values + } + return nil +} + +type DoubleList struct { + state protoimpl.MessageState `protogen:"open.v1"` + Values []float64 `protobuf:"fixed64,1,rep,packed,name=values,proto3" json:"values,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DoubleList) Reset() { + *x = DoubleList{} + mi := &file_common_v1_attribute_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DoubleList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DoubleList) ProtoMessage() {} + +func (x *DoubleList) ProtoReflect() protoreflect.Message { + mi := &file_common_v1_attribute_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DoubleList.ProtoReflect.Descriptor instead. +func (*DoubleList) Descriptor() ([]byte, []int) { + return file_common_v1_attribute_proto_rawDescGZIP(), []int{2} +} + +func (x *DoubleList) GetValues() []float64 { + if x != nil { + return x.Values + } + return nil +} + +type StringMap struct { + state protoimpl.MessageState `protogen:"open.v1"` + Values map[string]string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StringMap) Reset() { + *x = StringMap{} + mi := &file_common_v1_attribute_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StringMap) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringMap) ProtoMessage() {} + +func (x *StringMap) ProtoReflect() protoreflect.Message { + mi := &file_common_v1_attribute_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringMap.ProtoReflect.Descriptor instead. +func (*StringMap) Descriptor() ([]byte, []int) { + return file_common_v1_attribute_proto_rawDescGZIP(), []int{3} +} + +func (x *StringMap) GetValues() map[string]string { + if x != nil { + return x.Values + } + return nil +} + +type DoubleMap struct { + state protoimpl.MessageState `protogen:"open.v1"` + Values map[string]float64 `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"fixed64,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DoubleMap) Reset() { + *x = DoubleMap{} + mi := &file_common_v1_attribute_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DoubleMap) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DoubleMap) ProtoMessage() {} + +func (x *DoubleMap) ProtoReflect() protoreflect.Message { + mi := &file_common_v1_attribute_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DoubleMap.ProtoReflect.Descriptor instead. +func (*DoubleMap) Descriptor() ([]byte, []int) { + return file_common_v1_attribute_proto_rawDescGZIP(), []int{4} +} + +func (x *DoubleMap) GetValues() map[string]float64 { + if x != nil { + return x.Values + } + return nil +} + +var File_common_v1_attribute_proto protoreflect.FileDescriptor + +const file_common_v1_attribute_proto_rawDesc = "" + + "\n" + + "\x19common/v1/attribute.proto\x12\tcommon.v1\"\x8d\x03\n" + + "\tAttribute\x12#\n" + + "\fdouble_value\x18\x01 \x01(\x01H\x00R\vdoubleValue\x12#\n" + + "\fstring_value\x18\x02 \x01(\tH\x00R\vstringValue\x12\x1f\n" + + "\n" + + "bool_value\x18\x03 \x01(\bH\x00R\tboolValue\x12C\n" + + "\x11string_list_value\x18\x04 \x01(\v2\x15.common.v1.StringListH\x00R\x0fstringListValue\x12C\n" + + "\x11double_list_value\x18\x05 \x01(\v2\x15.common.v1.DoubleListH\x00R\x0fdoubleListValue\x12@\n" + + "\x10string_map_value\x18\x06 \x01(\v2\x14.common.v1.StringMapH\x00R\x0estringMapValue\x12@\n" + + "\x10double_map_value\x18\a \x01(\v2\x14.common.v1.DoubleMapH\x00R\x0edoubleMapValueB\a\n" + + "\x05value\"$\n" + + "\n" + + "StringList\x12\x16\n" + + "\x06values\x18\x01 \x03(\tR\x06values\"$\n" + + "\n" + + "DoubleList\x12\x16\n" + + "\x06values\x18\x01 \x03(\x01R\x06values\"\x80\x01\n" + + "\tStringMap\x128\n" + + "\x06values\x18\x01 \x03(\v2 .common.v1.StringMap.ValuesEntryR\x06values\x1a9\n" + + "\vValuesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x80\x01\n" + + "\tDoubleMap\x128\n" + + "\x06values\x18\x01 \x03(\v2 .common.v1.DoubleMap.ValuesEntryR\x06values\x1a9\n" + + "\vValuesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\x01R\x05value:\x028\x01B4Z2github.com/chaewonkong/matchmaker/gen/go/common/v1b\x06proto3" + +var ( + file_common_v1_attribute_proto_rawDescOnce sync.Once + file_common_v1_attribute_proto_rawDescData []byte +) + +func file_common_v1_attribute_proto_rawDescGZIP() []byte { + file_common_v1_attribute_proto_rawDescOnce.Do(func() { + file_common_v1_attribute_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_v1_attribute_proto_rawDesc), len(file_common_v1_attribute_proto_rawDesc))) + }) + return file_common_v1_attribute_proto_rawDescData +} + +var file_common_v1_attribute_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_common_v1_attribute_proto_goTypes = []any{ + (*Attribute)(nil), // 0: common.v1.Attribute + (*StringList)(nil), // 1: common.v1.StringList + (*DoubleList)(nil), // 2: common.v1.DoubleList + (*StringMap)(nil), // 3: common.v1.StringMap + (*DoubleMap)(nil), // 4: common.v1.DoubleMap + nil, // 5: common.v1.StringMap.ValuesEntry + nil, // 6: common.v1.DoubleMap.ValuesEntry +} +var file_common_v1_attribute_proto_depIdxs = []int32{ + 1, // 0: common.v1.Attribute.string_list_value:type_name -> common.v1.StringList + 2, // 1: common.v1.Attribute.double_list_value:type_name -> common.v1.DoubleList + 3, // 2: common.v1.Attribute.string_map_value:type_name -> common.v1.StringMap + 4, // 3: common.v1.Attribute.double_map_value:type_name -> common.v1.DoubleMap + 5, // 4: common.v1.StringMap.values:type_name -> common.v1.StringMap.ValuesEntry + 6, // 5: common.v1.DoubleMap.values:type_name -> common.v1.DoubleMap.ValuesEntry + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_common_v1_attribute_proto_init() } +func file_common_v1_attribute_proto_init() { + if File_common_v1_attribute_proto != nil { + return + } + file_common_v1_attribute_proto_msgTypes[0].OneofWrappers = []any{ + (*Attribute_DoubleValue)(nil), + (*Attribute_StringValue)(nil), + (*Attribute_BoolValue)(nil), + (*Attribute_StringListValue)(nil), + (*Attribute_DoubleListValue)(nil), + (*Attribute_StringMapValue)(nil), + (*Attribute_DoubleMapValue)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_v1_attribute_proto_rawDesc), len(file_common_v1_attribute_proto_rawDesc)), + NumEnums: 0, + NumMessages: 7, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_common_v1_attribute_proto_goTypes, + DependencyIndexes: file_common_v1_attribute_proto_depIdxs, + MessageInfos: file_common_v1_attribute_proto_msgTypes, + }.Build() + File_common_v1_attribute_proto = out.File + file_common_v1_attribute_proto_goTypes = nil + file_common_v1_attribute_proto_depIdxs = nil +} diff --git a/gen/go/common/v1/entity.pb.go b/gen/go/common/v1/entity.pb.go new file mode 100644 index 0000000..41ebe5d --- /dev/null +++ b/gen/go/common/v1/entity.pb.go @@ -0,0 +1,363 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: common/v1/entity.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Player struct { + state protoimpl.MessageState `protogen:"open.v1"` + PlayerId string `protobuf:"bytes,1,opt,name=player_id,json=playerId,proto3" json:"player_id,omitempty"` + Attributes map[string]*Attribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Player) Reset() { + *x = Player{} + mi := &file_common_v1_entity_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Player) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Player) ProtoMessage() {} + +func (x *Player) ProtoReflect() protoreflect.Message { + mi := &file_common_v1_entity_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Player.ProtoReflect.Descriptor instead. +func (*Player) Descriptor() ([]byte, []int) { + return file_common_v1_entity_proto_rawDescGZIP(), []int{0} +} + +func (x *Player) GetPlayerId() string { + if x != nil { + return x.PlayerId + } + return "" +} + +func (x *Player) GetAttributes() map[string]*Attribute { + if x != nil { + return x.Attributes + } + return nil +} + +type Ticket struct { + state protoimpl.MessageState `protogen:"open.v1"` + TicketId string `protobuf:"bytes,1,opt,name=ticket_id,json=ticketId,proto3" json:"ticket_id,omitempty"` + Players []*Player `protobuf:"bytes,2,rep,name=players,proto3" json:"players,omitempty"` + Attributes map[string]*Attribute `protobuf:"bytes,3,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Ticket) Reset() { + *x = Ticket{} + mi := &file_common_v1_entity_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Ticket) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ticket) ProtoMessage() {} + +func (x *Ticket) ProtoReflect() protoreflect.Message { + mi := &file_common_v1_entity_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ticket.ProtoReflect.Descriptor instead. +func (*Ticket) Descriptor() ([]byte, []int) { + return file_common_v1_entity_proto_rawDescGZIP(), []int{1} +} + +func (x *Ticket) GetTicketId() string { + if x != nil { + return x.TicketId + } + return "" +} + +func (x *Ticket) GetPlayers() []*Player { + if x != nil { + return x.Players + } + return nil +} + +func (x *Ticket) GetAttributes() map[string]*Attribute { + if x != nil { + return x.Attributes + } + return nil +} + +type Team struct { + state protoimpl.MessageState `protogen:"open.v1"` + TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` + Tickets []*Ticket `protobuf:"bytes,2,rep,name=tickets,proto3" json:"tickets,omitempty"` + Attributes map[string]*Attribute `protobuf:"bytes,3,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Team) Reset() { + *x = Team{} + mi := &file_common_v1_entity_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Team) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Team) ProtoMessage() {} + +func (x *Team) ProtoReflect() protoreflect.Message { + mi := &file_common_v1_entity_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Team.ProtoReflect.Descriptor instead. +func (*Team) Descriptor() ([]byte, []int) { + return file_common_v1_entity_proto_rawDescGZIP(), []int{2} +} + +func (x *Team) GetTeamId() string { + if x != nil { + return x.TeamId + } + return "" +} + +func (x *Team) GetTickets() []*Ticket { + if x != nil { + return x.Tickets + } + return nil +} + +func (x *Team) GetAttributes() map[string]*Attribute { + if x != nil { + return x.Attributes + } + return nil +} + +type Match struct { + state protoimpl.MessageState `protogen:"open.v1"` + MatchId string `protobuf:"bytes,1,opt,name=match_id,json=matchId,proto3" json:"match_id,omitempty"` + Teams []*Team `protobuf:"bytes,2,rep,name=teams,proto3" json:"teams,omitempty"` + Attributes map[string]*Attribute `protobuf:"bytes,3,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Match) Reset() { + *x = Match{} + mi := &file_common_v1_entity_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Match) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Match) ProtoMessage() {} + +func (x *Match) ProtoReflect() protoreflect.Message { + mi := &file_common_v1_entity_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Match.ProtoReflect.Descriptor instead. +func (*Match) Descriptor() ([]byte, []int) { + return file_common_v1_entity_proto_rawDescGZIP(), []int{3} +} + +func (x *Match) GetMatchId() string { + if x != nil { + return x.MatchId + } + return "" +} + +func (x *Match) GetTeams() []*Team { + if x != nil { + return x.Teams + } + return nil +} + +func (x *Match) GetAttributes() map[string]*Attribute { + if x != nil { + return x.Attributes + } + return nil +} + +var File_common_v1_entity_proto protoreflect.FileDescriptor + +const file_common_v1_entity_proto_rawDesc = "" + + "\n" + + "\x16common/v1/entity.proto\x12\tcommon.v1\x1a\x19common/v1/attribute.proto\"\xbd\x01\n" + + "\x06Player\x12\x1b\n" + + "\tplayer_id\x18\x01 \x01(\tR\bplayerId\x12A\n" + + "\n" + + "attributes\x18\x02 \x03(\v2!.common.v1.Player.AttributesEntryR\n" + + "attributes\x1aS\n" + + "\x0fAttributesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12*\n" + + "\x05value\x18\x02 \x01(\v2\x14.common.v1.AttributeR\x05value:\x028\x01\"\xea\x01\n" + + "\x06Ticket\x12\x1b\n" + + "\tticket_id\x18\x01 \x01(\tR\bticketId\x12+\n" + + "\aplayers\x18\x02 \x03(\v2\x11.common.v1.PlayerR\aplayers\x12A\n" + + "\n" + + "attributes\x18\x03 \x03(\v2!.common.v1.Ticket.AttributesEntryR\n" + + "attributes\x1aS\n" + + "\x0fAttributesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12*\n" + + "\x05value\x18\x02 \x01(\v2\x14.common.v1.AttributeR\x05value:\x028\x01\"\xe2\x01\n" + + "\x04Team\x12\x17\n" + + "\ateam_id\x18\x01 \x01(\tR\x06teamId\x12+\n" + + "\atickets\x18\x02 \x03(\v2\x11.common.v1.TicketR\atickets\x12?\n" + + "\n" + + "attributes\x18\x03 \x03(\v2\x1f.common.v1.Team.AttributesEntryR\n" + + "attributes\x1aS\n" + + "\x0fAttributesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12*\n" + + "\x05value\x18\x02 \x01(\v2\x14.common.v1.AttributeR\x05value:\x028\x01\"\xe0\x01\n" + + "\x05Match\x12\x19\n" + + "\bmatch_id\x18\x01 \x01(\tR\amatchId\x12%\n" + + "\x05teams\x18\x02 \x03(\v2\x0f.common.v1.TeamR\x05teams\x12@\n" + + "\n" + + "attributes\x18\x03 \x03(\v2 .common.v1.Match.AttributesEntryR\n" + + "attributes\x1aS\n" + + "\x0fAttributesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12*\n" + + "\x05value\x18\x02 \x01(\v2\x14.common.v1.AttributeR\x05value:\x028\x01B4Z2github.com/chaewonkong/matchmaker/gen/go/common/v1b\x06proto3" + +var ( + file_common_v1_entity_proto_rawDescOnce sync.Once + file_common_v1_entity_proto_rawDescData []byte +) + +func file_common_v1_entity_proto_rawDescGZIP() []byte { + file_common_v1_entity_proto_rawDescOnce.Do(func() { + file_common_v1_entity_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_v1_entity_proto_rawDesc), len(file_common_v1_entity_proto_rawDesc))) + }) + return file_common_v1_entity_proto_rawDescData +} + +var file_common_v1_entity_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_common_v1_entity_proto_goTypes = []any{ + (*Player)(nil), // 0: common.v1.Player + (*Ticket)(nil), // 1: common.v1.Ticket + (*Team)(nil), // 2: common.v1.Team + (*Match)(nil), // 3: common.v1.Match + nil, // 4: common.v1.Player.AttributesEntry + nil, // 5: common.v1.Ticket.AttributesEntry + nil, // 6: common.v1.Team.AttributesEntry + nil, // 7: common.v1.Match.AttributesEntry + (*Attribute)(nil), // 8: common.v1.Attribute +} +var file_common_v1_entity_proto_depIdxs = []int32{ + 4, // 0: common.v1.Player.attributes:type_name -> common.v1.Player.AttributesEntry + 0, // 1: common.v1.Ticket.players:type_name -> common.v1.Player + 5, // 2: common.v1.Ticket.attributes:type_name -> common.v1.Ticket.AttributesEntry + 1, // 3: common.v1.Team.tickets:type_name -> common.v1.Ticket + 6, // 4: common.v1.Team.attributes:type_name -> common.v1.Team.AttributesEntry + 2, // 5: common.v1.Match.teams:type_name -> common.v1.Team + 7, // 6: common.v1.Match.attributes:type_name -> common.v1.Match.AttributesEntry + 8, // 7: common.v1.Player.AttributesEntry.value:type_name -> common.v1.Attribute + 8, // 8: common.v1.Ticket.AttributesEntry.value:type_name -> common.v1.Attribute + 8, // 9: common.v1.Team.AttributesEntry.value:type_name -> common.v1.Attribute + 8, // 10: common.v1.Match.AttributesEntry.value:type_name -> common.v1.Attribute + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name +} + +func init() { file_common_v1_entity_proto_init() } +func file_common_v1_entity_proto_init() { + if File_common_v1_entity_proto != nil { + return + } + file_common_v1_attribute_proto_init() + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_v1_entity_proto_rawDesc), len(file_common_v1_entity_proto_rawDesc)), + NumEnums: 0, + NumMessages: 8, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_common_v1_entity_proto_goTypes, + DependencyIndexes: file_common_v1_entity_proto_depIdxs, + MessageInfos: file_common_v1_entity_proto_msgTypes, + }.Build() + File_common_v1_entity_proto = out.File + file_common_v1_entity_proto_goTypes = nil + file_common_v1_entity_proto_depIdxs = nil +} diff --git a/gen/go/gateway/v1/messages.pb.go b/gen/go/gateway/v1/messages.pb.go new file mode 100644 index 0000000..2a34e50 --- /dev/null +++ b/gen/go/gateway/v1/messages.pb.go @@ -0,0 +1,653 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: gateway/v1/messages.proto + +package v1 + +import ( + v1 "github.com/chaewonkong/matchmaker/gen/go/common/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type CreateTicketRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Tickets []*v1.Ticket `protobuf:"bytes,1,rep,name=tickets,proto3" json:"tickets,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateTicketRequest) Reset() { + *x = CreateTicketRequest{} + mi := &file_gateway_v1_messages_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateTicketRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateTicketRequest) ProtoMessage() {} + +func (x *CreateTicketRequest) ProtoReflect() protoreflect.Message { + mi := &file_gateway_v1_messages_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateTicketRequest.ProtoReflect.Descriptor instead. +func (*CreateTicketRequest) Descriptor() ([]byte, []int) { + return file_gateway_v1_messages_proto_rawDescGZIP(), []int{0} +} + +func (x *CreateTicketRequest) GetTickets() []*v1.Ticket { + if x != nil { + return x.Tickets + } + return nil +} + +type CreateTicketResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + TicketId string `protobuf:"bytes,1,opt,name=ticket_id,json=ticketId,proto3" json:"ticket_id,omitempty"` + EstimatedWaitTimeSec float64 `protobuf:"fixed64,2,opt,name=estimated_wait_time_sec,json=estimatedWaitTimeSec,proto3" json:"estimated_wait_time_sec,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateTicketResponse) Reset() { + *x = CreateTicketResponse{} + mi := &file_gateway_v1_messages_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateTicketResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateTicketResponse) ProtoMessage() {} + +func (x *CreateTicketResponse) ProtoReflect() protoreflect.Message { + mi := &file_gateway_v1_messages_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateTicketResponse.ProtoReflect.Descriptor instead. +func (*CreateTicketResponse) Descriptor() ([]byte, []int) { + return file_gateway_v1_messages_proto_rawDescGZIP(), []int{1} +} + +func (x *CreateTicketResponse) GetTicketId() string { + if x != nil { + return x.TicketId + } + return "" +} + +func (x *CreateTicketResponse) GetEstimatedWaitTimeSec() float64 { + if x != nil { + return x.EstimatedWaitTimeSec + } + return 0 +} + +type CancelTicketRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + TicketId string `protobuf:"bytes,1,opt,name=ticket_id,json=ticketId,proto3" json:"ticket_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CancelTicketRequest) Reset() { + *x = CancelTicketRequest{} + mi := &file_gateway_v1_messages_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CancelTicketRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CancelTicketRequest) ProtoMessage() {} + +func (x *CancelTicketRequest) ProtoReflect() protoreflect.Message { + mi := &file_gateway_v1_messages_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CancelTicketRequest.ProtoReflect.Descriptor instead. +func (*CancelTicketRequest) Descriptor() ([]byte, []int) { + return file_gateway_v1_messages_proto_rawDescGZIP(), []int{2} +} + +func (x *CancelTicketRequest) GetTicketId() string { + if x != nil { + return x.TicketId + } + return "" +} + +type CancelTicketResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + TicketId string `protobuf:"bytes,1,opt,name=ticket_id,json=ticketId,proto3" json:"ticket_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CancelTicketResponse) Reset() { + *x = CancelTicketResponse{} + mi := &file_gateway_v1_messages_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CancelTicketResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CancelTicketResponse) ProtoMessage() {} + +func (x *CancelTicketResponse) ProtoReflect() protoreflect.Message { + mi := &file_gateway_v1_messages_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CancelTicketResponse.ProtoReflect.Descriptor instead. +func (*CancelTicketResponse) Descriptor() ([]byte, []int) { + return file_gateway_v1_messages_proto_rawDescGZIP(), []int{3} +} + +func (x *CancelTicketResponse) GetTicketId() string { + if x != nil { + return x.TicketId + } + return "" +} + +type AcknowledgeMatchRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + MatchId string `protobuf:"bytes,1,opt,name=match_id,json=matchId,proto3" json:"match_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AcknowledgeMatchRequest) Reset() { + *x = AcknowledgeMatchRequest{} + mi := &file_gateway_v1_messages_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AcknowledgeMatchRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AcknowledgeMatchRequest) ProtoMessage() {} + +func (x *AcknowledgeMatchRequest) ProtoReflect() protoreflect.Message { + mi := &file_gateway_v1_messages_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AcknowledgeMatchRequest.ProtoReflect.Descriptor instead. +func (*AcknowledgeMatchRequest) Descriptor() ([]byte, []int) { + return file_gateway_v1_messages_proto_rawDescGZIP(), []int{4} +} + +func (x *AcknowledgeMatchRequest) GetMatchId() string { + if x != nil { + return x.MatchId + } + return "" +} + +type AcknowledgeMatchResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + MatchId string `protobuf:"bytes,1,opt,name=match_id,json=matchId,proto3" json:"match_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AcknowledgeMatchResponse) Reset() { + *x = AcknowledgeMatchResponse{} + mi := &file_gateway_v1_messages_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AcknowledgeMatchResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AcknowledgeMatchResponse) ProtoMessage() {} + +func (x *AcknowledgeMatchResponse) ProtoReflect() protoreflect.Message { + mi := &file_gateway_v1_messages_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AcknowledgeMatchResponse.ProtoReflect.Descriptor instead. +func (*AcknowledgeMatchResponse) Descriptor() ([]byte, []int) { + return file_gateway_v1_messages_proto_rawDescGZIP(), []int{5} +} + +func (x *AcknowledgeMatchResponse) GetMatchId() string { + if x != nil { + return x.MatchId + } + return "" +} + +type SubmitMatchResultRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Match *v1.Match `protobuf:"bytes,1,opt,name=match,proto3" json:"match,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitMatchResultRequest) Reset() { + *x = SubmitMatchResultRequest{} + mi := &file_gateway_v1_messages_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitMatchResultRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitMatchResultRequest) ProtoMessage() {} + +func (x *SubmitMatchResultRequest) ProtoReflect() protoreflect.Message { + mi := &file_gateway_v1_messages_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitMatchResultRequest.ProtoReflect.Descriptor instead. +func (*SubmitMatchResultRequest) Descriptor() ([]byte, []int) { + return file_gateway_v1_messages_proto_rawDescGZIP(), []int{6} +} + +func (x *SubmitMatchResultRequest) GetMatch() *v1.Match { + if x != nil { + return x.Match + } + return nil +} + +type SubmitMatchResultResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + MatchId string `protobuf:"bytes,1,opt,name=match_id,json=matchId,proto3" json:"match_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitMatchResultResponse) Reset() { + *x = SubmitMatchResultResponse{} + mi := &file_gateway_v1_messages_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitMatchResultResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitMatchResultResponse) ProtoMessage() {} + +func (x *SubmitMatchResultResponse) ProtoReflect() protoreflect.Message { + mi := &file_gateway_v1_messages_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitMatchResultResponse.ProtoReflect.Descriptor instead. +func (*SubmitMatchResultResponse) Descriptor() ([]byte, []int) { + return file_gateway_v1_messages_proto_rawDescGZIP(), []int{7} +} + +func (x *SubmitMatchResultResponse) GetMatchId() string { + if x != nil { + return x.MatchId + } + return "" +} + +type WatchMatchCandidatesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + TicketId string `protobuf:"bytes,1,opt,name=ticket_id,json=ticketId,proto3" json:"ticket_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchMatchCandidatesRequest) Reset() { + *x = WatchMatchCandidatesRequest{} + mi := &file_gateway_v1_messages_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchMatchCandidatesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchMatchCandidatesRequest) ProtoMessage() {} + +func (x *WatchMatchCandidatesRequest) ProtoReflect() protoreflect.Message { + mi := &file_gateway_v1_messages_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchMatchCandidatesRequest.ProtoReflect.Descriptor instead. +func (*WatchMatchCandidatesRequest) Descriptor() ([]byte, []int) { + return file_gateway_v1_messages_proto_rawDescGZIP(), []int{8} +} + +func (x *WatchMatchCandidatesRequest) GetTicketId() string { + if x != nil { + return x.TicketId + } + return "" +} + +type WatchMatchCandidatesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Event: + // + // *WatchMatchCandidatesResponse_Match + // *WatchMatchCandidatesResponse_Expired + Event isWatchMatchCandidatesResponse_Event `protobuf_oneof:"event"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchMatchCandidatesResponse) Reset() { + *x = WatchMatchCandidatesResponse{} + mi := &file_gateway_v1_messages_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchMatchCandidatesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchMatchCandidatesResponse) ProtoMessage() {} + +func (x *WatchMatchCandidatesResponse) ProtoReflect() protoreflect.Message { + mi := &file_gateway_v1_messages_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchMatchCandidatesResponse.ProtoReflect.Descriptor instead. +func (*WatchMatchCandidatesResponse) Descriptor() ([]byte, []int) { + return file_gateway_v1_messages_proto_rawDescGZIP(), []int{9} +} + +func (x *WatchMatchCandidatesResponse) GetEvent() isWatchMatchCandidatesResponse_Event { + if x != nil { + return x.Event + } + return nil +} + +func (x *WatchMatchCandidatesResponse) GetMatch() *v1.Match { + if x != nil { + if x, ok := x.Event.(*WatchMatchCandidatesResponse_Match); ok { + return x.Match + } + } + return nil +} + +func (x *WatchMatchCandidatesResponse) GetExpired() *MatchExpired { + if x != nil { + if x, ok := x.Event.(*WatchMatchCandidatesResponse_Expired); ok { + return x.Expired + } + } + return nil +} + +type isWatchMatchCandidatesResponse_Event interface { + isWatchMatchCandidatesResponse_Event() +} + +type WatchMatchCandidatesResponse_Match struct { + Match *v1.Match `protobuf:"bytes,1,opt,name=match,proto3,oneof"` +} + +type WatchMatchCandidatesResponse_Expired struct { + Expired *MatchExpired `protobuf:"bytes,2,opt,name=expired,proto3,oneof"` +} + +func (*WatchMatchCandidatesResponse_Match) isWatchMatchCandidatesResponse_Event() {} + +func (*WatchMatchCandidatesResponse_Expired) isWatchMatchCandidatesResponse_Event() {} + +type MatchExpired struct { + state protoimpl.MessageState `protogen:"open.v1"` + Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MatchExpired) Reset() { + *x = MatchExpired{} + mi := &file_gateway_v1_messages_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MatchExpired) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MatchExpired) ProtoMessage() {} + +func (x *MatchExpired) ProtoReflect() protoreflect.Message { + mi := &file_gateway_v1_messages_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MatchExpired.ProtoReflect.Descriptor instead. +func (*MatchExpired) Descriptor() ([]byte, []int) { + return file_gateway_v1_messages_proto_rawDescGZIP(), []int{10} +} + +func (x *MatchExpired) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +var File_gateway_v1_messages_proto protoreflect.FileDescriptor + +const file_gateway_v1_messages_proto_rawDesc = "" + + "\n" + + "\x19gateway/v1/messages.proto\x12\n" + + "gateway.v1\x1a\x16common/v1/entity.proto\"B\n" + + "\x13CreateTicketRequest\x12+\n" + + "\atickets\x18\x01 \x03(\v2\x11.common.v1.TicketR\atickets\"j\n" + + "\x14CreateTicketResponse\x12\x1b\n" + + "\tticket_id\x18\x01 \x01(\tR\bticketId\x125\n" + + "\x17estimated_wait_time_sec\x18\x02 \x01(\x01R\x14estimatedWaitTimeSec\"2\n" + + "\x13CancelTicketRequest\x12\x1b\n" + + "\tticket_id\x18\x01 \x01(\tR\bticketId\"3\n" + + "\x14CancelTicketResponse\x12\x1b\n" + + "\tticket_id\x18\x01 \x01(\tR\bticketId\"4\n" + + "\x17AcknowledgeMatchRequest\x12\x19\n" + + "\bmatch_id\x18\x01 \x01(\tR\amatchId\"5\n" + + "\x18AcknowledgeMatchResponse\x12\x19\n" + + "\bmatch_id\x18\x01 \x01(\tR\amatchId\"B\n" + + "\x18SubmitMatchResultRequest\x12&\n" + + "\x05match\x18\x01 \x01(\v2\x10.common.v1.MatchR\x05match\"6\n" + + "\x19SubmitMatchResultResponse\x12\x19\n" + + "\bmatch_id\x18\x01 \x01(\tR\amatchId\":\n" + + "\x1bWatchMatchCandidatesRequest\x12\x1b\n" + + "\tticket_id\x18\x01 \x01(\tR\bticketId\"\x87\x01\n" + + "\x1cWatchMatchCandidatesResponse\x12(\n" + + "\x05match\x18\x01 \x01(\v2\x10.common.v1.MatchH\x00R\x05match\x124\n" + + "\aexpired\x18\x02 \x01(\v2\x18.gateway.v1.MatchExpiredH\x00R\aexpiredB\a\n" + + "\x05event\"&\n" + + "\fMatchExpired\x12\x16\n" + + "\x06reason\x18\x01 \x01(\tR\x06reasonB5Z3github.com/chaewonkong/matchmaker/gen/go/gateway/v1b\x06proto3" + +var ( + file_gateway_v1_messages_proto_rawDescOnce sync.Once + file_gateway_v1_messages_proto_rawDescData []byte +) + +func file_gateway_v1_messages_proto_rawDescGZIP() []byte { + file_gateway_v1_messages_proto_rawDescOnce.Do(func() { + file_gateway_v1_messages_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_gateway_v1_messages_proto_rawDesc), len(file_gateway_v1_messages_proto_rawDesc))) + }) + return file_gateway_v1_messages_proto_rawDescData +} + +var file_gateway_v1_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_gateway_v1_messages_proto_goTypes = []any{ + (*CreateTicketRequest)(nil), // 0: gateway.v1.CreateTicketRequest + (*CreateTicketResponse)(nil), // 1: gateway.v1.CreateTicketResponse + (*CancelTicketRequest)(nil), // 2: gateway.v1.CancelTicketRequest + (*CancelTicketResponse)(nil), // 3: gateway.v1.CancelTicketResponse + (*AcknowledgeMatchRequest)(nil), // 4: gateway.v1.AcknowledgeMatchRequest + (*AcknowledgeMatchResponse)(nil), // 5: gateway.v1.AcknowledgeMatchResponse + (*SubmitMatchResultRequest)(nil), // 6: gateway.v1.SubmitMatchResultRequest + (*SubmitMatchResultResponse)(nil), // 7: gateway.v1.SubmitMatchResultResponse + (*WatchMatchCandidatesRequest)(nil), // 8: gateway.v1.WatchMatchCandidatesRequest + (*WatchMatchCandidatesResponse)(nil), // 9: gateway.v1.WatchMatchCandidatesResponse + (*MatchExpired)(nil), // 10: gateway.v1.MatchExpired + (*v1.Ticket)(nil), // 11: common.v1.Ticket + (*v1.Match)(nil), // 12: common.v1.Match +} +var file_gateway_v1_messages_proto_depIdxs = []int32{ + 11, // 0: gateway.v1.CreateTicketRequest.tickets:type_name -> common.v1.Ticket + 12, // 1: gateway.v1.SubmitMatchResultRequest.match:type_name -> common.v1.Match + 12, // 2: gateway.v1.WatchMatchCandidatesResponse.match:type_name -> common.v1.Match + 10, // 3: gateway.v1.WatchMatchCandidatesResponse.expired:type_name -> gateway.v1.MatchExpired + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_gateway_v1_messages_proto_init() } +func file_gateway_v1_messages_proto_init() { + if File_gateway_v1_messages_proto != nil { + return + } + file_gateway_v1_messages_proto_msgTypes[9].OneofWrappers = []any{ + (*WatchMatchCandidatesResponse_Match)(nil), + (*WatchMatchCandidatesResponse_Expired)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_gateway_v1_messages_proto_rawDesc), len(file_gateway_v1_messages_proto_rawDesc)), + NumEnums: 0, + NumMessages: 11, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_gateway_v1_messages_proto_goTypes, + DependencyIndexes: file_gateway_v1_messages_proto_depIdxs, + MessageInfos: file_gateway_v1_messages_proto_msgTypes, + }.Build() + File_gateway_v1_messages_proto = out.File + file_gateway_v1_messages_proto_goTypes = nil + file_gateway_v1_messages_proto_depIdxs = nil +} diff --git a/gen/go/gateway/v1/service.pb.go b/gen/go/gateway/v1/service.pb.go new file mode 100644 index 0000000..4e9ce21 --- /dev/null +++ b/gen/go/gateway/v1/service.pb.go @@ -0,0 +1,88 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: gateway/v1/service.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +var File_gateway_v1_service_proto protoreflect.FileDescriptor + +const file_gateway_v1_service_proto_rawDesc = "" + + "\n" + + "\x18gateway/v1/service.proto\x12\n" + + "gateway.v1\x1a\x19gateway/v1/messages.proto2\xe4\x03\n" + + "\x0eGatewayService\x12Q\n" + + "\fCreateTicket\x12\x1f.gateway.v1.CreateTicketRequest\x1a .gateway.v1.CreateTicketResponse\x12Q\n" + + "\fCancelTicket\x12\x1f.gateway.v1.CancelTicketRequest\x1a .gateway.v1.CancelTicketResponse\x12]\n" + + "\x10AcknowledgeMatch\x12#.gateway.v1.AcknowledgeMatchRequest\x1a$.gateway.v1.AcknowledgeMatchResponse\x12`\n" + + "\x11SubmitMatchResult\x12$.gateway.v1.SubmitMatchResultRequest\x1a%.gateway.v1.SubmitMatchResultResponse\x12k\n" + + "\x14WatchMatchCandidates\x12'.gateway.v1.WatchMatchCandidatesRequest\x1a(.gateway.v1.WatchMatchCandidatesResponse0\x01B5Z3github.com/chaewonkong/matchmaker/gen/go/gateway/v1b\x06proto3" + +var file_gateway_v1_service_proto_goTypes = []any{ + (*CreateTicketRequest)(nil), // 0: gateway.v1.CreateTicketRequest + (*CancelTicketRequest)(nil), // 1: gateway.v1.CancelTicketRequest + (*AcknowledgeMatchRequest)(nil), // 2: gateway.v1.AcknowledgeMatchRequest + (*SubmitMatchResultRequest)(nil), // 3: gateway.v1.SubmitMatchResultRequest + (*WatchMatchCandidatesRequest)(nil), // 4: gateway.v1.WatchMatchCandidatesRequest + (*CreateTicketResponse)(nil), // 5: gateway.v1.CreateTicketResponse + (*CancelTicketResponse)(nil), // 6: gateway.v1.CancelTicketResponse + (*AcknowledgeMatchResponse)(nil), // 7: gateway.v1.AcknowledgeMatchResponse + (*SubmitMatchResultResponse)(nil), // 8: gateway.v1.SubmitMatchResultResponse + (*WatchMatchCandidatesResponse)(nil), // 9: gateway.v1.WatchMatchCandidatesResponse +} +var file_gateway_v1_service_proto_depIdxs = []int32{ + 0, // 0: gateway.v1.GatewayService.CreateTicket:input_type -> gateway.v1.CreateTicketRequest + 1, // 1: gateway.v1.GatewayService.CancelTicket:input_type -> gateway.v1.CancelTicketRequest + 2, // 2: gateway.v1.GatewayService.AcknowledgeMatch:input_type -> gateway.v1.AcknowledgeMatchRequest + 3, // 3: gateway.v1.GatewayService.SubmitMatchResult:input_type -> gateway.v1.SubmitMatchResultRequest + 4, // 4: gateway.v1.GatewayService.WatchMatchCandidates:input_type -> gateway.v1.WatchMatchCandidatesRequest + 5, // 5: gateway.v1.GatewayService.CreateTicket:output_type -> gateway.v1.CreateTicketResponse + 6, // 6: gateway.v1.GatewayService.CancelTicket:output_type -> gateway.v1.CancelTicketResponse + 7, // 7: gateway.v1.GatewayService.AcknowledgeMatch:output_type -> gateway.v1.AcknowledgeMatchResponse + 8, // 8: gateway.v1.GatewayService.SubmitMatchResult:output_type -> gateway.v1.SubmitMatchResultResponse + 9, // 9: gateway.v1.GatewayService.WatchMatchCandidates:output_type -> gateway.v1.WatchMatchCandidatesResponse + 5, // [5:10] is the sub-list for method output_type + 0, // [0:5] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_gateway_v1_service_proto_init() } +func file_gateway_v1_service_proto_init() { + if File_gateway_v1_service_proto != nil { + return + } + file_gateway_v1_messages_proto_init() + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_gateway_v1_service_proto_rawDesc), len(file_gateway_v1_service_proto_rawDesc)), + NumEnums: 0, + NumMessages: 0, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_gateway_v1_service_proto_goTypes, + DependencyIndexes: file_gateway_v1_service_proto_depIdxs, + }.Build() + File_gateway_v1_service_proto = out.File + file_gateway_v1_service_proto_goTypes = nil + file_gateway_v1_service_proto_depIdxs = nil +} diff --git a/gen/go/gateway/v1/service_grpc.pb.go b/gen/go/gateway/v1/service_grpc.pb.go new file mode 100644 index 0000000..3bfa2fb --- /dev/null +++ b/gen/go/gateway/v1/service_grpc.pb.go @@ -0,0 +1,277 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc (unknown) +// source: gateway/v1/service.proto + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + GatewayService_CreateTicket_FullMethodName = "/gateway.v1.GatewayService/CreateTicket" + GatewayService_CancelTicket_FullMethodName = "/gateway.v1.GatewayService/CancelTicket" + GatewayService_AcknowledgeMatch_FullMethodName = "/gateway.v1.GatewayService/AcknowledgeMatch" + GatewayService_SubmitMatchResult_FullMethodName = "/gateway.v1.GatewayService/SubmitMatchResult" + GatewayService_WatchMatchCandidates_FullMethodName = "/gateway.v1.GatewayService/WatchMatchCandidates" +) + +// GatewayServiceClient is the client API for GatewayService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type GatewayServiceClient interface { + CreateTicket(ctx context.Context, in *CreateTicketRequest, opts ...grpc.CallOption) (*CreateTicketResponse, error) + CancelTicket(ctx context.Context, in *CancelTicketRequest, opts ...grpc.CallOption) (*CancelTicketResponse, error) + AcknowledgeMatch(ctx context.Context, in *AcknowledgeMatchRequest, opts ...grpc.CallOption) (*AcknowledgeMatchResponse, error) + SubmitMatchResult(ctx context.Context, in *SubmitMatchResultRequest, opts ...grpc.CallOption) (*SubmitMatchResultResponse, error) + WatchMatchCandidates(ctx context.Context, in *WatchMatchCandidatesRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[WatchMatchCandidatesResponse], error) +} + +type gatewayServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewGatewayServiceClient(cc grpc.ClientConnInterface) GatewayServiceClient { + return &gatewayServiceClient{cc} +} + +func (c *gatewayServiceClient) CreateTicket(ctx context.Context, in *CreateTicketRequest, opts ...grpc.CallOption) (*CreateTicketResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateTicketResponse) + err := c.cc.Invoke(ctx, GatewayService_CreateTicket_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gatewayServiceClient) CancelTicket(ctx context.Context, in *CancelTicketRequest, opts ...grpc.CallOption) (*CancelTicketResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CancelTicketResponse) + err := c.cc.Invoke(ctx, GatewayService_CancelTicket_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gatewayServiceClient) AcknowledgeMatch(ctx context.Context, in *AcknowledgeMatchRequest, opts ...grpc.CallOption) (*AcknowledgeMatchResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(AcknowledgeMatchResponse) + err := c.cc.Invoke(ctx, GatewayService_AcknowledgeMatch_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gatewayServiceClient) SubmitMatchResult(ctx context.Context, in *SubmitMatchResultRequest, opts ...grpc.CallOption) (*SubmitMatchResultResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SubmitMatchResultResponse) + err := c.cc.Invoke(ctx, GatewayService_SubmitMatchResult_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gatewayServiceClient) WatchMatchCandidates(ctx context.Context, in *WatchMatchCandidatesRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[WatchMatchCandidatesResponse], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &GatewayService_ServiceDesc.Streams[0], GatewayService_WatchMatchCandidates_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[WatchMatchCandidatesRequest, WatchMatchCandidatesResponse]{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GatewayService_WatchMatchCandidatesClient = grpc.ServerStreamingClient[WatchMatchCandidatesResponse] + +// GatewayServiceServer is the server API for GatewayService service. +// All implementations must embed UnimplementedGatewayServiceServer +// for forward compatibility. +type GatewayServiceServer interface { + CreateTicket(context.Context, *CreateTicketRequest) (*CreateTicketResponse, error) + CancelTicket(context.Context, *CancelTicketRequest) (*CancelTicketResponse, error) + AcknowledgeMatch(context.Context, *AcknowledgeMatchRequest) (*AcknowledgeMatchResponse, error) + SubmitMatchResult(context.Context, *SubmitMatchResultRequest) (*SubmitMatchResultResponse, error) + WatchMatchCandidates(*WatchMatchCandidatesRequest, grpc.ServerStreamingServer[WatchMatchCandidatesResponse]) error + mustEmbedUnimplementedGatewayServiceServer() +} + +// UnimplementedGatewayServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedGatewayServiceServer struct{} + +func (UnimplementedGatewayServiceServer) CreateTicket(context.Context, *CreateTicketRequest) (*CreateTicketResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateTicket not implemented") +} +func (UnimplementedGatewayServiceServer) CancelTicket(context.Context, *CancelTicketRequest) (*CancelTicketResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CancelTicket not implemented") +} +func (UnimplementedGatewayServiceServer) AcknowledgeMatch(context.Context, *AcknowledgeMatchRequest) (*AcknowledgeMatchResponse, error) { + return nil, status.Error(codes.Unimplemented, "method AcknowledgeMatch not implemented") +} +func (UnimplementedGatewayServiceServer) SubmitMatchResult(context.Context, *SubmitMatchResultRequest) (*SubmitMatchResultResponse, error) { + return nil, status.Error(codes.Unimplemented, "method SubmitMatchResult not implemented") +} +func (UnimplementedGatewayServiceServer) WatchMatchCandidates(*WatchMatchCandidatesRequest, grpc.ServerStreamingServer[WatchMatchCandidatesResponse]) error { + return status.Error(codes.Unimplemented, "method WatchMatchCandidates not implemented") +} +func (UnimplementedGatewayServiceServer) mustEmbedUnimplementedGatewayServiceServer() {} +func (UnimplementedGatewayServiceServer) testEmbeddedByValue() {} + +// UnsafeGatewayServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GatewayServiceServer will +// result in compilation errors. +type UnsafeGatewayServiceServer interface { + mustEmbedUnimplementedGatewayServiceServer() +} + +func RegisterGatewayServiceServer(s grpc.ServiceRegistrar, srv GatewayServiceServer) { + // If the following call panics, it indicates UnimplementedGatewayServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&GatewayService_ServiceDesc, srv) +} + +func _GatewayService_CreateTicket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateTicketRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GatewayServiceServer).CreateTicket(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GatewayService_CreateTicket_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GatewayServiceServer).CreateTicket(ctx, req.(*CreateTicketRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GatewayService_CancelTicket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CancelTicketRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GatewayServiceServer).CancelTicket(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GatewayService_CancelTicket_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GatewayServiceServer).CancelTicket(ctx, req.(*CancelTicketRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GatewayService_AcknowledgeMatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AcknowledgeMatchRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GatewayServiceServer).AcknowledgeMatch(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GatewayService_AcknowledgeMatch_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GatewayServiceServer).AcknowledgeMatch(ctx, req.(*AcknowledgeMatchRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GatewayService_SubmitMatchResult_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SubmitMatchResultRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GatewayServiceServer).SubmitMatchResult(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: GatewayService_SubmitMatchResult_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GatewayServiceServer).SubmitMatchResult(ctx, req.(*SubmitMatchResultRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GatewayService_WatchMatchCandidates_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(WatchMatchCandidatesRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GatewayServiceServer).WatchMatchCandidates(m, &grpc.GenericServerStream[WatchMatchCandidatesRequest, WatchMatchCandidatesResponse]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type GatewayService_WatchMatchCandidatesServer = grpc.ServerStreamingServer[WatchMatchCandidatesResponse] + +// GatewayService_ServiceDesc is the grpc.ServiceDesc for GatewayService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var GatewayService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "gateway.v1.GatewayService", + HandlerType: (*GatewayServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateTicket", + Handler: _GatewayService_CreateTicket_Handler, + }, + { + MethodName: "CancelTicket", + Handler: _GatewayService_CancelTicket_Handler, + }, + { + MethodName: "AcknowledgeMatch", + Handler: _GatewayService_AcknowledgeMatch_Handler, + }, + { + MethodName: "SubmitMatchResult", + Handler: _GatewayService_SubmitMatchResult_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "WatchMatchCandidates", + Handler: _GatewayService_WatchMatchCandidates_Handler, + ServerStreams: true, + }, + }, + Metadata: "gateway/v1/service.proto", +} diff --git a/go.mod b/go.mod index e0b0db6..9015982 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,15 @@ module github.com/chaewonkong/matchmaker go 1.26 + +require ( + google.golang.org/grpc v1.79.2 + google.golang.org/protobuf v1.36.11 +) + +require ( + golang.org/x/net v0.48.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect +) diff --git a/go.sum b/go.sum index e69de29..25c7c0f 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,38 @@ +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= +google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= From 4c425cbccdd4ce6eb4ede5dab26892013f022d11 Mon Sep 17 00:00:00 2001 From: chaewonkong Date: Sat, 23 May 2026 20:52:51 +0900 Subject: [PATCH 6/6] feat: add system design --- .gitignore | 2 +- CLAUDE.md | 98 +++ README.md | 25 +- architecture.md | 21 - buf.gen.yaml | 9 - buf.yaml | 3 - cmd/gateway/main.go | 7 - docker-compose.yml | 21 - docs/decisions/001-postgres-only.md | 79 +++ docs/decisions/002-polling-over-streaming.md | 87 +++ docs/decisions/003-gateway-config-agnostic.md | 104 +++ docs/decisions/004-director-push-model.md | 87 +++ docs/decisions/005-config-runtime-mutable.md | 85 +++ docs/decisions/006-db-separation.md | 79 +++ .../007-allocator-agnostic-pull-api.md | 170 +++++ docs/design-discussion.md | 564 +++++++++++++++ docs/design/matchmaker_0523rev3.png | Bin 0 -> 360301 bytes docs/design/overview.md | 284 ++++++++ gen/go/common/v1/attribute.pb.go | 461 ------------- gen/go/common/v1/entity.pb.go | 363 ---------- gen/go/gateway/v1/messages.pb.go | 653 ------------------ gen/go/gateway/v1/service.pb.go | 88 --- gen/go/gateway/v1/service_grpc.pb.go | 277 -------- go.mod | 15 - go.sum | 38 - mise.toml | 3 - proto/common/v1/attribute.proto | 20 - proto/common/v1/entity.proto | 28 - proto/gateway/v1/messages.proto | 54 -- proto/gateway/v1/service.proto | 15 - 30 files changed, 1646 insertions(+), 2094 deletions(-) create mode 100644 CLAUDE.md delete mode 100644 architecture.md delete mode 100644 buf.gen.yaml delete mode 100644 buf.yaml delete mode 100644 cmd/gateway/main.go delete mode 100644 docker-compose.yml create mode 100644 docs/decisions/001-postgres-only.md create mode 100644 docs/decisions/002-polling-over-streaming.md create mode 100644 docs/decisions/003-gateway-config-agnostic.md create mode 100644 docs/decisions/004-director-push-model.md create mode 100644 docs/decisions/005-config-runtime-mutable.md create mode 100644 docs/decisions/006-db-separation.md create mode 100644 docs/decisions/007-allocator-agnostic-pull-api.md create mode 100644 docs/design-discussion.md create mode 100644 docs/design/matchmaker_0523rev3.png create mode 100644 docs/design/overview.md delete mode 100644 gen/go/common/v1/attribute.pb.go delete mode 100644 gen/go/common/v1/entity.pb.go delete mode 100644 gen/go/gateway/v1/messages.pb.go delete mode 100644 gen/go/gateway/v1/service.pb.go delete mode 100644 gen/go/gateway/v1/service_grpc.pb.go delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 mise.toml delete mode 100644 proto/common/v1/attribute.proto delete mode 100644 proto/common/v1/entity.proto delete mode 100644 proto/gateway/v1/messages.proto delete mode 100644 proto/gateway/v1/service.proto diff --git a/.gitignore b/.gitignore index 67341d6..b992343 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ .vscode -handoff.md \ No newline at end of file +.omc \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d508a7f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,98 @@ +# Matchmaker + +Postgres 기반 minimalistic 매치메이커 OSS. Open Match 대안. + +## 핵심 원칙 + +- **의존성은 Postgres만** — Redis, Kafka, NATS 등 외부 큐/캐시 없음 +- **Polling 기반 통신** — streaming 회피, 운영 단순성 우선 +- **Config 런타임 변경** — 재배포 없이 라이브 튜닝 (API 기반) +- **Allocator-agnostic** — Agones/GameLift/PlayFab 등 모두 통합 가능 +- **설치형 배포** — Helm chart 단일 명령으로 설치 + +## 컴포넌트 + +- **API Gateway** — gRPC API, stateless, 큐/풀 설정 무지 (단순 transport) +- **Director** — 1초 cycle, Entity Store batch pull, pool 분류, ME로 dispatch (1 active + standby) +- **MatchingEngine (ME)** — pool당 1개, 후보 매치 생성, lazy config fetch +- **Evaluator** — 후보 aggregation, 충돌 해결, 최종 매치 확정 (1 active + standby) +- **Config Syncer** — Config Store polling 캐싱, 컴포넌트에 config API 제공 +- **Entity Store** — Postgres (tickets, matches) +- **Config Store** — Postgres (queues, pools, rules, history) — runtime DB와 분리 + +## 통신 경계 + +**외부 시스템(Game Backend, External DGS Allocator)은 오직 API Gateway를 통해서만 매치메이커와 통신한다.** Entity Store, Director, ME, Evaluator 등 내부 컴포넌트는 외부에 노출되지 않는다. + +## 데이터 흐름 + +``` +[외부 경계] +Client ──→ Game Backend ──┐ + ├──gRPC──→ API Gateway ──┐ +External DGS Allocator ───┘ │ + ↑ ClaimMatches/Report │ + ↓ +[내부 컴포넌트] Entity Store + ↑ polling + Director ← Config Syncer ← Config Store + ↓ dispatch (with config_version) + Matching Engine × N + ↓ candidates + Evaluator + ↓ matches insert + Entity Store + +[게임 서버 할당] +External DGS Allocator ──→ DGS Fleet (게임 서버 시작) + ──→ API Gateway (ReportAssignment) +``` + +외부 통신 경로 (모두 API Gateway 경유): + +- **Game Backend → Gateway**: CreateTicket, GetTicket (매칭 결과 polling) +- **DGS Allocator → Gateway**: ClaimMatches (polling), ReportAssignment, ReleaseMatch +- **Gateway → 외부**: 없음 (Pull-only) + +## 코드 컨벤션 + +- Go 1.22+ +- gRPC 인터페이스는 `proto/` 하위 정의 +- DB 접근은 pgx 사용 +- CEL 룰 평가는 cel-go +- 컴포넌트 간 통신은 gRPC unary RPC (streaming 지양) + +## 부하 기준 + +목표: 동접 100만, ~600 RPS (CreateTicket 기준) +- Director polling: 1 QPS +- Evaluator → matches insert: ~139 QPS +- DGS Allocator polling: 1 QPS +- 단일 Postgres가 압도적으로 여유 있게 처리 + +## 디렉토리 + +``` +matchmaker/ +├── proto/ # gRPC 정의 +├── services/ # 매치메이커 본체 +│ ├── gateway/ +│ ├── director/ +│ ├── engine/ +│ ├── evaluator/ +│ └── syncer/ +├── helm/ # Helm chart +├── examples/ +│ ├── agones-adapter/ +│ └── kubernetes-adapter/ +└── docs/ + ├── design/ # 컴포넌트별 상세 + ├── decisions/ # ADR + └── design-discussion.md # 초기 토론 전문 +``` + +## 상세 문서 + +설계 상세: `docs/design/` +의사결정 이력: `docs/decisions/` (ADR 형식) +초기 토론: `docs/design-discussion.md` diff --git a/README.md b/README.md index f665efd..6d5fb98 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,11 @@ -# 🎮 matchmaker +# Matchmaker +A minimalistic self hosted matchmaker for multiplayer games. -A lightweight and high-performance matchmaker service written in Go. -Designed for real-time game matchmaking with concurrent player support. +Supports 1M concurrent players. +## System Design +![matchmaker system design](docs/design/matchmaker_0523rev3.png) -# gRPC -```shell -$ mise install -$ mise use buf@1.66.0 -``` - -Install Go plugins -```shell -$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest -$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest -``` - -# Components - +## Features +- Backfill +- \ No newline at end of file diff --git a/architecture.md b/architecture.md deleted file mode 100644 index 3b530aa..0000000 --- a/architecture.md +++ /dev/null @@ -1,21 +0,0 @@ -## Architecture - -```mermaid -flowchart LR - A[Gateway] - B@{shape: das, label: "ticket"} - C@{shape: das, label: "match"} - D@{shape: cyl, label: "Feature Sture"} - E[Loader] - F[MatchFinder] - G[Redis] - - A --request ticket--> B - B -->E - D --features--> E - E --ticket with feature--> G - G --periodic fetch--> F - F --match candidates--> C - C --match response-->A - A --game result-->D -``` \ No newline at end of file diff --git a/buf.gen.yaml b/buf.gen.yaml deleted file mode 100644 index 6b09b0a..0000000 --- a/buf.gen.yaml +++ /dev/null @@ -1,9 +0,0 @@ -# buf.gen.yaml -version: v2 -plugins: - - remote: buf.build/protocolbuffers/go - out: gen/go - opt: paths=source_relative - - remote: buf.build/grpc/go - out: gen/go - opt: paths=source_relative \ No newline at end of file diff --git a/buf.yaml b/buf.yaml deleted file mode 100644 index 448778b..0000000 --- a/buf.yaml +++ /dev/null @@ -1,3 +0,0 @@ -version: v2 -modules: - - path: proto \ No newline at end of file diff --git a/cmd/gateway/main.go b/cmd/gateway/main.go deleted file mode 100644 index 8000942..0000000 --- a/cmd/gateway/main.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "fmt" - -func main() { - fmt.Println("hello, gateway") -} diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index c2410c9..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,21 +0,0 @@ -services: - redis: - image: redis:latest - ports: - - 6379:6379 - volumes: - - redis-data:/data - - redis-data:/usr/local/conf/redis.conf - labels: - - 'name=redis' - - 'mode=standalone' - restart: always - command: redis-server /usr/local/conf/redis.conf - healthcheck: - test: [ 'CMD', 'redis-cli', 'ping' ] - interval: 10s - timeout: 5s - retries: 3 - -volumes: - redis-data: diff --git a/docs/decisions/001-postgres-only.md b/docs/decisions/001-postgres-only.md new file mode 100644 index 0000000..c03ec40 --- /dev/null +++ b/docs/decisions/001-postgres-only.md @@ -0,0 +1,79 @@ +# ADR-001: Postgres-only 의존성 + +## Status + +Accepted (2026-05-23) + +## Context + +매치메이커는 다음과 같은 데이터 처리가 필요하다: + +- Ticket 영속화 및 lifecycle 관리 +- 매칭 워커의 분산 컨슈머 패턴 +- 매치 결과 저장 +- 설정 (큐/풀/룰) 저장 +- 컴포넌트 간 이벤트 통보 + +전통적으로 이런 시스템은 Redis (캐시/큐), Kafka/NATS (메시지 큐), Postgres (영속화)를 조합해서 구현한다. Open Match도 Redis를 핵심 의존성으로 사용한다. + +설치형 OSS 도구로 배포할 경우, 사용자(게임사)가 직접 운영해야 한다. 의존성이 많을수록 도입 장벽과 운영 부담이 커진다. + +## Decision + +**외부 큐/캐시 의존성 없이 Postgres만 사용한다.** + +- 분산 컨슈머 패턴: `SELECT ... FOR UPDATE SKIP LOCKED` +- 이벤트 통보: `LISTEN/NOTIFY` (선택적, polling이 기본) +- JSONB로 동적 attribute 저장 +- 트랜잭션으로 복잡한 상태 전이 일관성 보장 + +단, runtime DB와 config DB는 분리 (ADR-006 참조). + +## Consequences + +### 장점 + +- **운영 부담 최소**: 사용자가 Postgres 하나만 관리하면 됨 +- **Helm chart 단순**: 의존성 명세가 짧음 +- **트랜잭션 일관성 자연스러움**: 매치 lifecycle의 복잡한 상태 전이를 native transaction으로 처리 +- **JSONB로 동적 attribute 표현 가능**: 큐별 커스텀 schema 지원 +- **운영자 친화적 디버깅**: SQL로 모든 상태 조회 가능 + +### 단점 + +- **Throughput 상한 존재**: 단일 Postgres는 결국 수만 QPS가 한계 +- **Multi-region 시 복잡**: Logical replication 직접 설계 필요 +- **GIN 인덱스의 range query 한계**: JSONB attribute의 SQL 쿼리 성능 제한 + +### 부하 검증 + +목표 100만 동접 + 600 RPS 환경에서 측정한 부하: + +- CreateTicket: ~556 QPS +- Match insert: ~139 QPS +- 나머지 컴포넌트 polling: 1자리 QPS + +단일 Postgres가 압도적으로 여유 있게 처리하는 영역. + +## Alternatives Considered + +### Redis Streams (Open Match 방식) + +- 장점: 빠른 throughput, 분산 컨슈머 패턴 (XREADGROUP) +- 단점: 복잡한 상태 전이에 약함 (백필 등), 의존성 추가, Lua 스크립트 필요 + +### Kafka/NATS 등 메시지 큐 + +- 장점: 확실한 분리, 백프레셔 표현 +- 단점: 트랜잭션 일관성 어려움, 운영 부담 큼 + +### Postgres + Redis 하이브리드 + +- 장점: 각자 잘하는 곳에 사용 +- 단점: 두 시스템의 일관성 관리, 운영 부담 + +## Notes + +부하 측면에서는 Redis가 빠르지만, 매치메이커는 **상태 전이가 많은 도메인**(ticket lifecycle, match lifecycle, 백필, 취소, 타임아웃)이므로 transaction이 일급 시민인 Postgres가 적합하다. + +매치메이커의 본질적 어려움은 throughput이 아니라 매칭 품질, fairness, 다운스트림 통합에 있다 (ADR-003 참조). diff --git a/docs/decisions/002-polling-over-streaming.md b/docs/decisions/002-polling-over-streaming.md new file mode 100644 index 0000000..99f4e9c --- /dev/null +++ b/docs/decisions/002-polling-over-streaming.md @@ -0,0 +1,87 @@ +# ADR-002: Polling 기반 통신 채택 + +## Status + +Accepted (2026-05-23) + +## Context + +매치메이커 시스템의 통신 경로: + +- 외부 → 매치메이커: 게임 백엔드의 ticket 생성, DGS Allocator의 매치 조회 +- 매치메이커 내부: Director ↔ Entity Store, Config Syncer ↔ Config Store, Syncer ↔ 각 컴포넌트 +- 매치메이커 → 외부: 없음 (Pull-only API, ADR-007 참조) + +각 경로마다 gRPC streaming과 polling 사이 선택지가 있다. + +## Decision + +**외부 인터페이스 및 컴포넌트 간 통신의 기본은 polling으로 한다.** + +구체적으로: + +- 외부 → 매치메이커: gRPC unary RPC polling (1초 또는 그 이상) +- Director → Entity Store: SQL polling (1초 cycle) +- Config Syncer → Config Store: SQL polling (5~10초) +- 각 컴포넌트 → Config Syncer: polling 또는 Director가 dispatch에 config_version 명시 (ADR-005 참조) + +Streaming은 도입하지 않는다. + +## Consequences + +### 장점 + +- **로드밸런서 단순**: 표준 HTTP/2 unary RPC라 일반 L4/L7 LB로 충분 +- **Keep-alive 부담 없음**: long-lived connection 관리 불필요 +- **재시도 시맨틱 자명**: 각 요청 독립, 그냥 다시 요청하면 됨 +- **장애 복구 자동**: 일시 단절 후 다음 cycle에 자연스럽게 catch-up +- **디버깅 단순**: 각 요청을 독립적으로 추적 가능 +- **운영자 mental model 단순**: "N초마다 본다"는 한 줄로 설명 + +### 단점 + +- **Latency**: 평균 `interval/2`, 최악 `interval`의 지연 +- **불필요한 query**: 변경 없어도 매 cycle 조회 + +### Latency 검증 + +- Director cycle: 1초 → 새 ticket이 매칭 시작까지 평균 0.5초 +- Config polling: 5~10초 → 운영자 변경 반영까지 최대 ~10초 +- 실제 매치 대기 시간이 수십 초~분 단위이므로 무시 가능 + +## Alternatives Considered + +### gRPC streaming + +- 장점: 낮은 latency (~수십 ms), 변경 즉시 전파 +- 단점: + - L4 LB의 connection 균등 분배 깨짐 → L7 (Envoy, gRPC-LB) 필요 + - HTTP/2 ping, TCP keep-alive, 프록시 idle timeout 모두 정렬 필요 + - Connection 재분배 (Gateway 추가 시 rebalancing) + - 재시도 시맨틱 모호 (at-least-once vs at-most-once) + - 조용한 connection drop 디버깅 어려움 +- 설치형 OSS에서 사용자 운영 부담이 너무 큼 + +### LISTEN/NOTIFY + +- Postgres 변경을 즉시 알림 받음 +- 매치메이커 내부 (Syncer ↔ Config Store)에서는 보강으로 사용 가능 +- 단, drop 가능성 있어 polling fallback 필요 +- 컴포넌트 ↔ Syncer 사이는 polling이 더 단순 + +### Long polling + +- 중간 지점이지만 단순 polling으로 부하 충분히 처리되므로 도입 가치 낮음 + +## Notes + +게임 매칭 도메인의 시간 단위: + +- Director 매칭 cycle: 1초 +- 일반적인 매치 대기: 수 초 ~ 1~2분 +- 운영자 룰 변경 → 효과 관찰: 분 단위 +- MMR 분포 변화: 시간 ~ 일 단위 + +Polling 모델이 실시간성보다 우월하다고 판단할 근거가 없다. **시스템 전체의 자연 시간 단위가 polling과 잘 맞는다**. + +부하 측면에서도 Director/Allocator/Syncer 모두 1자리 QPS 수준이므로 polling 비용 무시 가능. diff --git a/docs/decisions/003-gateway-config-agnostic.md b/docs/decisions/003-gateway-config-agnostic.md new file mode 100644 index 0000000..e01e1d5 --- /dev/null +++ b/docs/decisions/003-gateway-config-agnostic.md @@ -0,0 +1,104 @@ +# ADR-003: API Gateway는 유일한 외부 진입점이자 큐/풀 설정 무지 + +## Status + +Accepted (2026-05-23) + +## Context + +매치메이커의 외부 진입점은 API Gateway다. 두 가지 결정이 얽혀 있다: + +**결정 1: 외부 진입점을 단일화할 것인가?** + +매치메이커의 외부 시스템(Game Backend, External DGS Allocator)이 어떤 컴포넌트와 통신할 수 있는지 정의해야 한다. 옵션: + +1. 외부가 Entity Store, Director 등 내부 컴포넌트를 직접 호출 +2. 외부는 API Gateway만 통하고, 내부 컴포넌트는 외부에 노출되지 않음 + +**결정 2: Gateway가 어떤 책임을 가지는가?** + +1. Gateway가 모든 비즈니스 로직 수행: 인증, 검증, attribute schema 체크, pool 분류, DB INSERT +2. Gateway는 transport만: 받아서 다음 컴포넌트로 넘기는 역할만 + +두 결정 모두 부하나 성능이 아닌 **bounded context 분리**의 문제다. + +## Decision + +**API Gateway는 매치메이커의 유일한 외부 진입점이며, 큐/풀 설정을 모르는 단순 transport 역할만 한다.** + +### 외부 진입점 단일화 + +- Game Backend, External DGS Allocator 등 외부 시스템은 **오직 API Gateway를 통해서만** 매치메이커와 통신한다 +- Entity Store, Director, MatchingEngine, Evaluator, Config Syncer 등 내부 컴포넌트는 **외부에 노출되지 않는다** +- Gateway가 외부에 노출하는 API: + - `CreateTicket` (Game Backend → Gateway) + - `GetTicket` (Game Backend → Gateway, 매칭 결과 polling) + - `ClaimMatches` (DGS Allocator → Gateway) + - `ReportAssignment` (DGS Allocator → Gateway) + - `ReleaseMatch` (DGS Allocator → Gateway) + +### Gateway의 큐/풀 무지 + +Gateway가 알아야 할 것 vs 몰라도 되는 것: + +| Gateway가 알아야 함 | Gateway가 몰라도 됨 | +|---|---| +| RPC schema (gRPC proto) | Pool 정의 | +| Queue ID (라우팅용) | Pool 분류 룰 | +| 인증 정보 | Attribute schema | +| Ticket의 raw payload | CEL 룰 | +| Entity Store INSERT/SELECT 방법 | Matching 알고리즘 | + +Gateway는 받은 ticket을 그대로 Entity Store에 INSERT만 한다. 분류는 Director가 책임진다. ClaimMatches 등 매치 dequeue도 Gateway가 Entity Store에 SQL 쿼리로 수행한다. + +## Consequences + +### 장점 + +**외부 진입점 단일화 효과**: + +- **보안 경계 명확** — 내부 컴포넌트가 외부에 노출되지 않음, attack surface 최소화 +- **인증/인가 단일 지점** — Gateway에서만 인증 처리, 내부 통신은 mTLS 등 단순화 +- **rate limit / quota 단일 지점** — Gateway에서 전사적 정책 적용 +- **관제 단일 지점** — 외부 트래픽 메트릭이 Gateway 하나에 집중 +- **내부 리팩토링 자유** — Entity Store 스키마 변경 등이 외부 API에 영향 없음 + +**Gateway 큐/풀 무지 효과**: + +- **큐/풀 설정 변경 시 Gateway 재배포 불필요** — Director만 hot-reload +- **Gateway가 진짜 stateless + dumb** — CEL 런타임, 룰 캐시 불필요, footprint 작음, 시작 빠름, 수평 확장 자유 +- **멀티 테넌시 자연스러움** — 여러 게임 모드/큐가 한 인스턴스에 공존해도 Gateway는 모드별 차이 무지 +- **비즈니스 로직 격리** — 외부 노출 면에 매칭 룰이 reverse engineering될 단서 없음 +- **설정 검증의 단일 소유자** — Director 또는 Config Service에 집중 + +### 단점 + +- **추가 컴포넌트 필요** — Pool 분류 책임이 별도 컴포넌트(Director)로 이동 +- **운영 시나리오에 따라 복잡도가 다른 곳으로 이동** — Gateway가 단순해진 대가로 Director가 무거워짐 + +## Alternatives Considered + +### Gateway가 직접 분류 후 INSERT + +- 장점: 컴포넌트 수 적음, write 한 번 +- 단점: + - 큐 설정 변경 시 Gateway 재배포 필요 + - Gateway가 stateful (CEL 룰 캐시) + - 외부 노출 면에 비즈니스 로직 노출 + - 멀티 테넌시 시 복잡 + +### Inbox 테이블을 두고 Pool Assigner가 별도 처리 + +검토했으나 단일 테이블 + 상태 머신으로 표현 가능하므로 over-engineering. + +## Notes + +이 결정의 핵심 가치는 **외부 노출 면을 단순하게 유지하고 내부 복잡도를 응집된 컴포넌트에 모으는 것**이다. + +Gateway가 단순해진 결과: +1. 신규 큐 추가 시 Gateway 무관 +2. 매칭 룰 튜닝 시 Gateway 무관 +3. 게임 추가 (멀티 테넌트) 시 Gateway는 game_id 라우팅만 +4. 긴급 큐 정지 시 Director가 drain, Gateway는 여전히 수신 + +운영 시나리오 대부분이 Gateway 무지의 이점을 누린다. diff --git a/docs/decisions/004-director-push-model.md b/docs/decisions/004-director-push-model.md new file mode 100644 index 0000000..0cea3c7 --- /dev/null +++ b/docs/decisions/004-director-push-model.md @@ -0,0 +1,87 @@ +# ADR-004: Director Push 모델 채택 (Pool 멤버십을 메모리로) + +## Status + +Accepted (2026-05-23) + +## Context + +한 큐에 여러 pool이 있고 (Pool 범위 overlap), 한 ticket이 여러 pool에 동시 속할 수 있다. MatchingEngine은 pool당 1개씩 동작한다. + +Pool 멤버십을 어디에 저장할지 두 가지 선택지: + +### Pull 모델 (DB-centric) + +- Pool 멤버십을 DB에 영속화 (`ticket_pool_membership` 테이블 또는 generated column) +- ME가 SQL로 자기 pool ticket 조회 (SKIP LOCKED) + +### Push 모델 (Director-centric) + +- Director가 batch pull → 메모리에서 pool 분류 → ME로 직접 push +- Pool 멤버십이 Director의 in-memory 상태 + +## Decision + +**Push 모델 채택. Director가 분류 권위자 역할.** + +흐름: + +1. Ticket은 Entity Store에 영속화 (state = `created`) +2. Director가 1초 cycle로 batch pull (SKIP LOCKED, state → `in_director`) +3. Director가 메모리에서 pool 분류 +4. Director가 ticket + pool_id + config_version을 ME로 push (gRPC) +5. ME가 매칭 후보 생성 +6. Evaluator가 후보 수집 → 충돌 해결 → 최종 매치 확정 + +## Consequences + +### 장점 + +- **JSONB 인덱스 문제 해소** — ME가 SQL로 attribute 쿼리할 필요 없음 +- **Config version 전파 자연스러움** — Director가 권위 발급자, 일관성 보장 (ADR-005 참조) +- **Pool 정의 변경 시 즉시 반영** — Director 메모리만 갱신 +- **ticket_pool_membership 테이블 불필요** — write 감소 +- **ME는 stateless에 가까움** — 받은 ticket batch만 처리 + +### 단점 + +- **Director가 SPOF에 가까워짐** — single active + standby 필요 +- **Push 모델의 coordination 복잡도** — ME discovery, health check, fan-out +- **ME 가용성 처리가 Director 책임** — 재dispatch 로직 필요 +- **분산 컨슈머 패턴의 자연스러움 상실** — SKIP LOCKED 패턴 일부만 활용 + +### Lease 패턴 + +Director가 batch pull 시 `state = 'in_director'`로 변경하고 `leased_at`을 기록한다. Director 죽으면 별도 reaper가 stale lease를 `created`로 복원한다 (SQS visibility timeout과 동일한 패턴). + +## Alternatives Considered + +### Pull 모델 + DB에 영속화된 pool 멤버십 + +- 장점: ME가 자기 페이스로 동작, 단순한 분산 컨슈머, 운영자 친화적 디버깅 +- 단점: + - JSONB 인덱스로 동적 attribute 쿼리하는 문제 + - Pool 정의 변경 시 멤버십 재계산 + - Config version 전파 메커니즘 별도 필요 + - 컴포넌트 간 config 버전 불일치 가능 + +### Hybrid (DB에 ticket lifecycle + 메모리에 pool 멤버십) + +채택한 모델이 이것에 해당. Director가 ticket lifecycle 상태는 DB에 기록하고, pool 분류만 메모리에서 처리. + +## Notes + +이 결정은 Open Match의 Director 패턴과 유사하지만 차이가 있다: + +- Open Match: Director가 외부 사용자 구현 (Backend API 호출) +- 본 설계: Director가 내장 컴포넌트 (사용자는 CEL 룰만 정의) + +내장하는 대신 외부 통합 부담을 매우 낮추는 trade-off. + +부하 검증 (100만 동접 기준): + +- 매치 생성률 ~139 match/s +- Director cycle 1초마다 batch pull (limit 500~1000) +- 단일 Director가 압도적으로 여유 있게 처리 + +향후 부하가 더 커지면 큐별로 Director 분리 가능 (multi-Director). diff --git a/docs/decisions/005-config-runtime-mutable.md b/docs/decisions/005-config-runtime-mutable.md new file mode 100644 index 0000000..d71ea0f --- /dev/null +++ b/docs/decisions/005-config-runtime-mutable.md @@ -0,0 +1,85 @@ +# ADR-005: Config 런타임 변경 가능 (DB 기반, GitOps 불채택) + +## Status + +Accepted (2026-05-23) + +## Context + +큐/풀/매칭 룰 설정의 저장 및 변경 메커니즘을 결정해야 한다. + +선택지: + +1. **GitOps 방식**: YAML 파일을 Git으로 관리, Helm/ConfigMap으로 배포, 변경 시 재배포 +2. **DB 기반 런타임 변경**: API로 설정 변경, 즉시 반영 + +일반적인 OSS 인프라 도구(Prometheus, Argo Workflows 등)는 GitOps를 선호한다. 그러나 라이브 운영 게임의 매치메이커는 다른 특성을 가진다. + +## Decision + +**DB 기반 런타임 변경 API를 채택한다. GitOps는 부적합.** + +- 설정은 별도 Postgres (Config Store, ADR-006)에 저장 +- 운영자가 Config API로 변경 → 즉시 반영 +- Config 변경 이력은 `config_history` 테이블에 보관 (audit, rollback) +- 변경은 atomic transaction + version 단조 증가 +- Syncer가 polling으로 변경 감지 (ADR-002) + +## Consequences + +### 장점 + +- **라이브 튜닝 가능** — MMR 범위, 매칭 룰 등을 분 단위로 조정 +- **롤백 자연스러움** — 이전 version으로 즉시 복원 +- **canary 적용 가능** — 한 큐에만 먼저 적용 후 확장 +- **재배포 사이클 회피** — 게임 서버 무중단 운영과 호환 + +### 단점 + +- **GUI/API 실수 위험** — 잘못된 룰이 즉시 운영에 영향 → 검증 단계 필요 +- **버전 관리가 Git에 비해 약함** — config_history 테이블로 보강 +- **외부 도구 통합 어려움** — Git 기반 워크플로우와 자연스럽지 않음 + +### 필수 검증 단계 + +운영자의 룰 변경 시 Config API는 다음 검증을 수행: + +1. **문법 검증** — CEL 파싱 +2. **타입 검증** — referenced attribute가 schema에 있는지 +3. **시맨틱 검증** — dry-run으로 sample ticket에 적용 +4. **canary 옵션** — 한 큐에만 먼저 적용 +5. **atomic write + version 증가** + +특히 dry-run과 canary는 라이브 운영의 안전성에 결정적. + +## Alternatives Considered + +### GitOps (YAML + Helm ConfigMap) + +검토했으나 다음 이유로 부적합: + +**이유 1: 큐 설정과 게임 서버 파라미터의 바인딩** + +큐 설정은 게임 서버가 보내야 할 파라미터에 바인딩되는 경우가 많다. 큐 설정 롤백 시 게임 서버도 롤백해야 하는데, 게임 서버는 무중단 롤백이 현실적으로 어렵다. 한 번 적용한 설정의 롤백이 *기술적으로 가능해야* 한다. + +**이유 2: 라이브 운영의 빠른 튜닝 사이클** + +신규 게임 출시 초기 MMR 분포가 정규분포로 수렴하기까지 몇 주~몇 달 걸린다. 그 동안 지표 보며 분 단위로 룰을 튜닝해야 하는데, 배포 프로세스(PR → review → merge → deploy)는 이 사이클과 안 맞는다. + +**이유 3: 운영자 권한 모델** + +게임 운영팀이 PR 권한 + CI/CD 권한을 갖는 모델은 보안/책임 측면에서 부적합. + +### 하이브리드 (DB + Git 백업) + +DB가 primary, Git은 audit/backup 용도. 검토 가치 있으나 복잡도 증가 대비 이득 명확하지 않음. + +## Notes + +이 결정이 다른 결정들에 미치는 영향: + +- **ADR-006 (DB 분리)**: Config가 런타임 critical이 되므로 runtime DB와 격리 필요 +- **ADR-002 (Polling)**: Config 변경 반영 latency 1~10초가 운영적으로 충분 +- **ADR-005-ME-version-propagation**: Config 변경 시 in-flight ticket 처리 정책 명확화 필요 + +Unity Matchmaker, AWS FlexMatch, PlayFab Matchmaking 등 상용 매치메이커가 모두 런타임 설정 변경을 지원하는 것으로 보인다. 라이브 게임 운영의 표준 요구사항. diff --git a/docs/decisions/006-db-separation.md b/docs/decisions/006-db-separation.md new file mode 100644 index 0000000..8834c14 --- /dev/null +++ b/docs/decisions/006-db-separation.md @@ -0,0 +1,79 @@ +# ADR-006: Runtime DB와 Config DB 분리 + +## Status + +Accepted (2026-05-23) + +## Context + +매치메이커가 다루는 데이터는 크게 두 종류: + +- **Runtime 데이터**: tickets, matches, ticket_pool_membership (운영 중 빈번한 read/write) +- **Config 데이터**: 큐/풀/룰 설정 + 변경 이력 (read-heavy, 가끔 write, 라이브 튜닝) + +ADR-005에서 Config가 런타임 critical path에 들어왔다 (즉시 변경 반영 필요). 이 상태에서 한 DB에 둘지, 분리할지 결정 필요. + +## Decision + +**두 개의 Postgres 인스턴스로 분리한다.** + +- `postgres-runtime`: inbox(없으면 생략), tickets, matches, ticket lifecycle +- `postgres-config`: queues, pools, rules, config_history, attribute_schemas + +둘 다 Postgres이므로 운영 노하우는 공유된다. + +## Consequences + +### 장점 + +- **운영 격리** — Config DB 작업이 매칭 시스템에 영향 없음 + - 큰 트랜잭션, 마이그레이션, 운영자 실수가 운영 DB와 격리 +- **다른 백업/보존 정책 가능** — Config는 장기 보존 + audit, Runtime은 짧게 +- **다른 권한 모델** — Config DB는 운영자/관리자 접근, Runtime은 서비스 계정만 +- **다른 scaling 전략** — Config는 read replica 적극 활용 가능 +- **장애 격리** — Config DB 장애 시 cached config로 매칭 계속 동작 (ADR-002 stale cache 정책) + +### 단점 + +- **운영 부담 증가** — Postgres 인스턴스 2개 +- **Helm chart 복잡도 증가** — 의존성 명세가 길어짐 +- **트랜잭션 분리** — Config 변경과 Runtime 상태 변경을 한 트랜잭션에 묶을 수 없음 (필요 없는 경우가 대부분이므로 큰 문제 아님) + +## Alternatives Considered + +### 단일 Postgres, schema로 분리 + +```sql +create schema runtime; +create schema config; +``` + +- 장점: 운영 단순, Helm chart 단순, 트랜잭션 자유 +- 단점: + - Config 변경 작업 (큰 트랜잭션, 마이그레이션)이 운영 DB에 영향 + - 백업 정책이 일률적 (transient runtime도 같이 백업) + - Config DB 장애 = 매칭 시스템 장애 + +ADR-005에서 *Config가 런타임 critical이 되었기 때문에* 단일 DB는 부적합. 만약 Config가 GitOps 기반이었다면 단일 DB가 맞았을 것. + +### 완전 분리 (3개 DB) + +- runtime / config / inbox 각각 분리 +- Over-engineering. 600 RPS 규모에서 정당화 어려움. + +## Notes + +이 결정의 핵심: **둘 다 Postgres라는 점**. + +운영팀이 새로운 기술을 배우거나 새로운 운영 노하우를 쌓을 필요 없다. 백업, 모니터링, 튜닝 모두 같은 패턴. 그래서 "Postgres 2대"는 여전히 **minimalistic 원칙과 호환**된다. + +만약 Config Store가 etcd, S3, Git 등 다른 시스템이었다면 운영 부담이 진짜 늘었을 것. Postgres-Postgres 분리는 부담 적음. + +Sentry, Mattermost 같은 설치형 OSS도 비슷한 패턴: 운영 DB와 별개의 작은 DB(또는 schema)를 사용한다. + +향후 multi-region 운영이 필요해지면: + +- Runtime DB는 region-local primary +- Config DB는 global write + region read replica + +자연스럽게 확장 가능한 구조. diff --git a/docs/decisions/007-allocator-agnostic-pull-api.md b/docs/decisions/007-allocator-agnostic-pull-api.md new file mode 100644 index 0000000..832dabb --- /dev/null +++ b/docs/decisions/007-allocator-agnostic-pull-api.md @@ -0,0 +1,170 @@ +# ADR-007: Allocator-agnostic Pull-only API + +## Status + +Accepted (2026-05-23) + +## Context + +매치메이커가 생성한 매치는 결국 DGS(Dedicated Game Server)에 할당되어야 한다. DGS fleet 관리 방식은 게임사마다 천차만별: + +- Agones (K8s-native) +- AWS GameLift +- PlayFab Multiplayer Servers +- 자체 Kubernetes (StatefulSet/Deployment) +- 베어메탈 풀 + 자체 스케줄러 + +매치메이커가 이 다양한 시스템을 어떻게 통합할지가 OSS 도구의 도입 장벽을 결정한다. + +## Decision + +**DGS Allocator는 외부 시스템으로 두고, 매치메이커는 API Gateway를 통한 Pull-only API만 노출한다.** + +- 매치메이커는 외부 Allocator API를 호출하지 않는다 +- Allocator는 **API Gateway를 통해** 매치메이커를 polling한다 (Entity Store 직접 접근 불가, ADR-003) +- 매치메이커는 Allocator의 존재 자체를 모른다 (추상화 비용 zero) +- 참조 구현(adapter)을 examples로 제공 + +### API 명세 + +```protobuf +service MatchmakerBackend { + rpc ClaimMatches(ClaimMatchesRequest) returns (ClaimMatchesResponse); + rpc ReportAssignment(ReportAssignmentRequest) returns (ReportAssignmentResponse); + rpc ReleaseMatch(ReleaseMatchRequest) returns (ReleaseMatchResponse); +} + +message ClaimMatchesRequest { + string allocator_id = 1; // 어느 allocator인지 + repeated string queue_ids = 2; // 관심 있는 큐 (필터링) + int32 max_matches = 3; // batch 크기 + map labels = 4; // region, capability 등 +} + +message ClaimMatchesResponse { + repeated Match matches = 1; + string lease_token = 2; + google.protobuf.Timestamp lease_expires_at = 3; +} + +message ReportAssignmentRequest { + string match_id = 1; + oneof result { + AssignmentSuccess success = 2; // dgs_address, dgs_port 등 + AssignmentFailure failure = 3; // 실패 사유 + } +} +``` + +### Adapter 패턴 + +``` +matchmaker/ +├── services/ # 매치메이커 본체 +└── examples/ + ├── agones-adapter/ # 참조 구현 + ├── kubernetes-adapter/ + └── README.md # 사용자 정의 adapter 가이드 +``` + +Adapter는 매치메이커 외부에서: +1. `ClaimMatches` polling +2. 자기 Allocator API 호출 (Agones Allocation API 등) +3. `ReportAssignment`로 결과 통보 + +## Consequences + +### 장점 + +- **매치메이커 자체가 깨끗하게 유지** — Allocator 통합 코드 없음 +- **모든 Allocator 지원 가능** — Adapter만 작성하면 됨 +- **운영 환경 무관** — K8s, AWS, 베어메탈 등 어디든 OK +- **테스트 단순** — 매치메이커 테스트에 mock Allocator 불필요 (인터페이스만 검증) + +### 단점 + +- **사용자 부담 약간 증가** — Adapter 작성 필요 (단, 참조 구현 제공) +- **Pull 모델의 latency** — Allocator polling interval만큼 지연 (보통 1초) + +### Lease 패턴 + +ClaimMatches로 가져간 매치는 임시 locked (`state = 'assigning'`). lease 시간 내 `ReportAssignment`가 오지 않으면 reaper가 자동으로 `ready`로 복원. + +```sql +-- Reaper job (주기 실행) +update matches set state = 'ready', assigned_to_allocator = null +where state = 'assigning' and claimed_at < now() - interval '1 minute'; +``` + +### 매치 dequeue 쿼리 + +Allocator의 `ClaimMatches` gRPC 호출을 받은 **API Gateway가 Entity Store에 다음 쿼리를 실행**한다: + +```sql +update matches set + state = 'assigning', + assigned_to_allocator = $allocator_id, + claimed_at = now() +where id in ( + select id from matches + where state = 'ready' + and ($2::text[] is null or queue_id = any($2)) -- queue 필터 + order by created_at + limit $3 + for update skip locked +) +returning *; +``` + +SKIP LOCKED로 여러 Allocator가 동시에 polling해도 서로 다른 매치를 받음. Allocator는 Entity Store를 직접 알지 못한다. + +## Alternatives Considered + +### Push 모델 (매치메이커가 Allocator API 호출) + +- 장점: latency 낮음 +- 단점: + - Allocator별 SDK/API 통합 필요 (Agones, GameLift, PlayFab 각각) + - 매치메이커가 Allocator의 가용성에 의존 + - 의존성 폭발 (각 SDK 버전 관리) +- **OSS 도입 장벽이 매우 높아짐** → 부적합 + +### Allocator 내장 (Agones 전용) + +- 장점: 단순, K8s 친화적 +- 단점: + - 특정 환경에 lock-in + - 베어메탈, AWS 등 사용자 배제 +- **시장 축소** → 부적합 + +### Webhook 콜백 + +- 매치메이커가 매치 생성 시 등록된 webhook URL 호출 +- 장점: latency 낮음 +- 단점: + - Webhook 등록 메커니즘 필요 + - 신뢰성 보장 어려움 (재시도, 순서) + - polling이 더 단순 + +## Notes + +이 결정이 Open Match와 차별화되는 지점이다: + +- Open Match: Director를 외부 사용자 구현 (매칭 알고리즘 + DGS 통합 모두 사용자 부담) +- 본 설계: Director, ME, Evaluator 내장, **외부 통합은 Allocator 한 곳만** + +사용자 입장에서: +- 매칭 룰: CEL config로 정의 +- DGS 통합: 작은 adapter 작성 (참조 구현 활용) + +이게 인디 게임사도 도입 가능한 수준의 진입 장벽이다. + +### 다중 Allocator + region routing + +여러 Allocator가 동시 운영되는 경우 (region별 등) **매치에 region 라벨이 명확히 박혀있는 모델**을 권장: + +- ticket의 region attribute → 매치의 region 라벨 +- region별 Allocator는 자기 region 매치만 ClaimMatches +- region 간 분산은 Director의 분류 단계에서 처리 (region별 pool) + +이게 fairness 문제와 routing 복잡도를 모두 단순화한다. diff --git a/docs/design-discussion.md b/docs/design-discussion.md new file mode 100644 index 0000000..5edb4d2 --- /dev/null +++ b/docs/design-discussion.md @@ -0,0 +1,564 @@ +# Matchmaker 설계 토론 정리 + +> Postgres 기반 minimalistic 매치메이커 OSS 설계에 대한 토론 정리 + +--- + +## 1. 부하 산정과 RPS 추정 + +### 1.1 초기 가정으로 시작한 부하 계산 + +처음에는 다음 가정으로 부하를 추정함: + +- 동접 20만 명 +- 매치 생성 요청 + 폴링 (1초 1회) +- 게임 30분 진행 +- 4인 매치, 1티켓 = 1명 + +**잘못된 모델**: 클라이언트 1인당 1Hz polling 가정 → 200,000 QPS라는 비현실적 수치. + +### 1.2 폴링 모델 정정 + +폴링은 클라이언트별 상태 조회가 아니라 **컨슈머가 큐에서 매치 이벤트를 끌어가는 워커 패턴**임을 정정. + +``` +새 매치 생성률 = 200,000 / 1,800초 ≈ 111 ticket/s +완성 매치 = 111 / 4 ≈ 28 match/s +``` + +폴링 QPS는 동접 수가 아닌 *컨슈머 수 × 폴링 주기*로 결정됨. 실질 부하는 무시 가능. + +### 1.3 결론: 부하는 문제가 아니다 + +| 동접 | 매치 생성률 | +|---|---| +| 20만 | ~28 match/s | +| 100만 | ~139 match/s | + +이 수준의 부하는 단일 Go 프로세스 + 단일 Postgres로 충분히 처리 가능. **매치메이커 설계의 본질적 어려움은 throughput이 아닌 매칭 품질, 백프레셔, 운영성에 있음**. + +--- + +## 2. 시스템 요구사항 + +### 2.1 목표 명세 + +- 동접 100만 가능, **500~600 RPS** +- 백필 지원 +- 동적인 커스텀 어트리뷰트 (큐 설정 기반 payload 관리) +- expr language 기반 동적 매칭 룰 (cel-go 등) +- 관제 기능 +- gRPC 기반 인터페이스 +- Helm chart 설치형 배포 +- **Minimalistic 원칙**: Redis 등 외부 의존성 없이 Postgres만으로 달성 + +### 2.2 Postgres-only 가능성 검토 + +가능. 600 RPS는 Postgres가 압도적으로 여유 있게 처리하는 수준. 다만 구현 패턴이 중요: + +- **`SELECT ... FOR UPDATE SKIP LOCKED`**: 분산 컨슈머 워커 패턴 +- **JSONB + GIN/generated column**: 동적 attribute 저장 +- **LISTEN/NOTIFY**: 이벤트 통보 (best-effort, fallback polling 필요) +- **트랜잭션**: 매치 상태 전이의 일관성 보장 + +### 2.3 매치메이커의 진짜 설계 난점 + +부하가 아닌 다음 항목이 본질적 어려움: + +1. **매칭 품질** — MMR, 핑, 파티, 역할 구성 등 다차원 최적화 +2. **큐 체류 시간의 tail latency** — 평균보다 p99가 더 중요 +3. **백프레셔와 부분 장애** — 다운스트림(DGS) 장애 대응 +4. **파티 처리** — 솔로 vs 파티 우선순위 +5. **운영성** — A/B 테스트, 룰 튜닝, 옵저버빌리티 + +--- + +## 3. 백프레셔와 Circuit Breaker + +### 3.1 단순 차단 모델의 함정 + +"매치 큐 적체 → 티켓 차단"은 너무 단순. 적체 원인에 따라 대응이 달라야 함: + +- **Case A**: 매칭 알고리즘이 느림 → 티켓 차단은 매칭 자체를 멈춤 +- **Case B**: DGS 프로비저닝 막힘 → 티켓 차단이 정답 +- **Case C**: 폴링 컨슈머 죽음 → 컨슈머 살리면 됨 + +증상은 같아도 원인과 대응이 다름. + +### 3.2 매치메이킹 circuit breaker의 특수성 + +일반적인 HTTP Hystrix 패턴과 다름: + +- **실패율이 아닌 큐 깊이 + age가 신호**: `ApproximateAgeOfOldestMessage`, consumer lag 등 +- **Binary가 아닌 점진적 차단**: 수락률을 100% → 80% → 50%로 AIMD 방식 +- **거절 비용이 큼**: 사용자가 게임 자체를 못 하게 됨 → 거절보다 긴 대기 + 다른 모드 유도 선호 + +### 3.3 계층적 대응 패턴 + +| 적체 정도 | 대응 | +|---|---| +| 정상 | 통상 동작 | +| 약간 (lag > 30s) | DGS fleet 확장 트리거, 알람 | +| 심각 (lag > 2min) | 신규 티켓 수락률 점진 감소 (50%~10%) | +| 위험 (lag > 5min) | 신규 티켓 완전 차단, drain 모드 | +| 복구 | 점진적 ramp-up (slow ramp으로 oscillation 방지) | + +핵심: +- 신호는 **queue depth × age** +- 대응은 **계층적/점진적** +- 차단은 **mode/region별 isolated** +- 복구는 **slow ramp** + +--- + +## 4. 아키텍처 진화 + +### 4.1 OpenMatch 스타일 출발 + +초기 구상: + +- **API Gateway**: 모든 API 요청 처리 +- **MatchingEngine (다수 병렬)**: pool별 매치 후보 생성 +- **Evaluator**: 후보 중 품질 최고 매치 선별 + 중복 제거 + +### 4.2 다중 Pool 멤버십 모델 + +한 큐에 여러 pool이 있고, 한 ticket이 여러 pool에 동시 속할 수 있음 (Pool 범위 overlap). + +이 모델에서: +- ticket이 여러 pool에 등장 → 같은 ticket이 여러 MatchingEngine의 후보가 됨 → **중복 제거가 구조적으로 필요** → Evaluator 필수 + +### 4.3 책임 분리: API Gateway는 큐/풀 설정을 모른다 + +**핵심 통찰**: 부하나 성능이 아닌 **bounded context 분리** 관점. + +| Gateway가 알아야 함 | Gateway가 몰라도 됨 | +|---|---| +| RPC schema (gRPC proto) | Pool 정의 | +| Queue ID (라우팅용) | Pool 분류 룰 | +| 인증 정보 | Attribute schema | +| Ticket의 raw payload | CEL 룰 | + +이점: +- 큐/풀 설정 변경이 Gateway 재배포 불필요 +- Gateway는 진짜 stateless + dumb +- 멀티 테넌시 자연스러움 +- 외부 노출 면에서 비즈니스 로직 격리 (보안) +- 설정 검증의 단일 소유자 + +### 4.4 Pull 모델 vs Push 모델 + +**Pull 모델 (DB-centric)**: +- ME가 DB에서 SELECT (with SKIP LOCKED) +- Pool 멤버십을 DB에 영속화 (`ticket_pool_membership` 테이블) +- 운영자 친화적, SQL로 디버깅 + +**Push 모델 (Director-centric)**: +- Director가 batch pull → 메모리에서 분류 → ME로 push +- Pool 멤버십이 Director의 in-memory 상태 +- 설정 버전을 ticket과 함께 전파 가능 (일관성 우월) +- Director가 SPOF에 가까움 + +**최종 선택: Hybrid (push 모델 채택)**: +- Ticket lifecycle은 DB에 (관찰성) +- Pool 분류는 Director 메모리에 (유연성) +- Director가 dispatch에 config_version 명시 + +--- + +## 5. 최종 컴포넌트 구조 + +### 5.1 컴포넌트 목록 + +| 컴포넌트 | 역할 | 인스턴스 | +|---|---|---| +| **API Gateway** | gRPC API, 티켓 영속화, 큐/풀 무지 | 다수 (stateless) | +| **Director** | 큐 설정 캐싱, batch pull, pool 분류, ME로 dispatch | 1 active + standby | +| **MatchingEngine (ME)** | 자기 pool의 매치 후보 생성 | pool 수만큼 | +| **Evaluator** | 후보 aggregation, 중복 제거, 최종 매치 확정 | 1 active + standby | +| **Config Syncer** | Config Store polling, 컴포넌트에 config API 제공 | 1 active + standby | +| **Entity Store (Postgres)** | tickets, matches, ticket lifecycle | 단일 DB | +| **Config Store (Postgres)** | 큐/풀/룰 설정, 변경 이력 | 별도 DB | + +### 5.2 데이터 흐름 + +``` +[Client] → [Game Backend] → [API Gateway] + ↓ Insert Tickets + [Entity Store] + ↑ Polling (read tickets) + [Director] ← Sync Config ← [Config Syncer] ← Polling ← [Config Store] + ↓ Distribute Tickets (by pool, with config_version) + [Matching Engine × N] + ↓ Aggregate Match Candidates + [Evaluator] + ↓ Matches + [Entity Store] + ↑ Find Matches (polling, claim batch) + [External DGS Allocator] + ↓ + [DGS Fleet] +``` + +### 5.3 DB 분리 결정 + +**runtime DB와 config DB를 분리**: + +이유: +- Config가 *런타임 critical path*에 들어옴 (라이브 튜닝) +- 변경 빈도와 패턴이 다름 (운영 DB는 빈번, config는 분~시간 단위) +- Config DB 장애 시 cached config로 매칭 계속 가능 (장애 격리) +- 다른 백업/보존 정책 (config는 long retention) +- 다른 권한 모델 + +둘 다 Postgres이므로 운영 부담 큰 차이 없음. + +--- + +## 6. Config 관리 + +### 6.1 런타임 변경 요구사항 + +YAML/Git 기반은 부적합. 이유: + +1. **큐 설정과 게임 서버 파라미터의 바인딩** — 롤백 시 게임 서버도 롤백 필요, 무중단 롤백 어려움 +2. **라이브 운영 이슈 대응** — MMR 분포 튜닝 등 분 단위 사이클 필요 + +따라서 **DB 기반 런타임 변경 API**가 정답. + +### 6.2 Config Syncer 도입 + +- Config Store와 컴포넌트 사이의 캐싱 + API 계층 +- Config Store 장애 시 cached config로 매칭 계속 가능 (격리) +- 검증/변환 로직 단일화 +- 컴포넌트가 Config Store 스키마를 몰라도 됨 + +### 6.3 동기화 방식: Polling 채택 + +**판단 근거**: +- Director cycle이 1초이므로 그보다 빠른 실시간성 무의미 +- 실제 매치 대기 시간이 수십 초~수 분이므로 config 반영 1~2초 지연은 무시 가능 +- 운영자가 변경 후 효과 관찰까지 분 단위 소요됨 +- Polling이 단순함, 장애 복구 자명, 운영자 mental model 단순 + +**Stale cache 정책**: +- Syncer는 마지막 성공 config를 무기한 유지 +- Config Store 장애 중에도 매칭 계속 동작 +- 라이브 게임에서 매칭 중단보다 약간의 stale config가 훨씬 나음 + +### 6.4 ME의 lazy config fetch + +ME 동기화 전략: +- ME는 단일 config version만 cache +- Director가 보낸 ticket의 config_version이 cache와 다르면 → Syncer에서 해당 version fetch → cache 교체 +- 매칭은 항상 ticket에 명시된 version으로 진행 + +장점: +- Memory footprint 최소 +- Director가 권위 있는 version 발급자 (일관성 보장) +- Syncer 부담 낮음 (변경 시에만 fetch) +- 자동 동기화 (별도 broadcast 불필요) +- Pull-based versioning 패턴 (K8s resource version, Git commit hash와 동일 원리) + +### 6.5 Config version의 의미 + +큐별 version 권장: +- 큐 X가 변경되면 큐 X의 version++ +- 다른 큐는 영향 없음 +- ME가 큐별로 cache 가능 + +``` +Config Syncer API: + GET /config/queues/{queue_id}/versions/latest + GET /config/queues/{queue_id}/versions/{version} + +Ticket dispatch metadata: + { + ticket_id, pool_id, queue_id, + config_version: { queue_id: 'rank-4v4', version: 124 } + } +``` + +### 6.6 부분 업데이트 방지 + +Config 변경은 단일 트랜잭션으로: + +```sql +begin; + update queues set ... where queue_id = 'X'; + update pools set ... where queue_id = 'X' and pool_id = 'Y'; + insert into pools (...); + update config_meta set version = version + 1 where queue_id = 'X'; +commit; +``` + +Syncer는 version 기반 polling으로 atomic 변경 보장. + +--- + +## 7. 통신 모델 + +### 7.1 외부 통신: Polling 강제 + +매치메이커의 외부 인터페이스(`API Gateway` 노출)는 **polling 기반**: + +- gRPC streaming 대비 폴링의 장점: + - 표준 HTTP/2 unary RPC (일반 LB로 충분) + - L4/L7 로드밸런서 설정 단순 + - Keep-alive 튜닝 부담 없음 + - 클라이언트 재시도 시맨틱 자명 + - 디버깅 단순 (각 요청 독립) +- 설치형 OSS의 운영 부담을 최소화하는 결정 + +### 7.2 게임 서버 통보 모델 + +**중요**: 클라이언트는 매치메이커를 직접 호출하지 않음. + +``` +[Client] → [Game Backend] → [Matchmaker API Gateway] + ← [DGS Allocator (외부)] ← polling for matches + → [DGS Fleet] +``` + +- 게임 백엔드가 ticket 생성 요청 +- DGS Allocator가 매치메이커를 polling하여 매치 가져감 +- DGS Allocator가 fleet에 게임 서버 할당 +- 게임 서버 정보가 ticket에 박힘 → 게임 백엔드가 ticket polling으로 확인 → 클라이언트에 통보 + +### 7.3 부하 재정리 + +매치메이커 시스템 전체 부하 (100만 동접 기준): + +| 컴포넌트 | 부하 | +|---|---| +| API Gateway: CreateTicket | ~556 QPS | +| Director: Entity Store polling | 1 QPS (1초 cycle, batch) | +| Director → ME: dispatch | 1 cycle/s | +| ME → Evaluator: candidates | 1 cycle/s | +| Evaluator → Entity Store: match insert | ~139 매치/s | +| DGS Allocator: match polling | 1 QPS (batch dequeue ~139개) | +| Config Syncer: Config Store polling | ~0.2 QPS | + +**유일한 의미 있는 부하는 CreateTicket의 ~556 QPS**. 단일 Postgres가 압도적으로 여유 있게 처리. + +--- + +## 8. DGS Allocator 통합 + +### 8.1 Allocator-agnostic 설계 + +특정 Allocator(Agones, GameLift, PlayFab 등)에 바인딩하지 않음. + +### 8.2 Pull-only 인터페이스 + +매치메이커는 외부 Allocator API를 호출하지 않음. Allocator가 매치메이커를 polling. + +```protobuf +service MatchmakerBackend { + rpc ClaimMatches(ClaimMatchesRequest) returns (ClaimMatchesResponse); + rpc ReportAssignment(ReportAssignmentRequest) returns (ReportAssignmentResponse); + rpc ReleaseMatch(ReleaseMatchRequest) returns (ReleaseMatchResponse); +} + +message ClaimMatchesRequest { + string allocator_id = 1; + repeated string queue_ids = 2; + int32 max_matches = 3; + map labels = 4; // region, capability 등 +} + +message ClaimMatchesResponse { + repeated Match matches = 1; + string lease_token = 2; + google.protobuf.Timestamp lease_expires_at = 3; +} +``` + +### 8.3 Lease 패턴 + +ClaimMatches로 가져간 매치는 임시 locked. lease 시간 내 `ReportAssignment` 없으면 자동 복원 (`state = 'ready'`로 reaper가 되돌림). + +### 8.4 매치 dequeue 쿼리 + +```sql +update matches set + state = 'assigning', + assigned_to_allocator = $allocator_id, + claimed_at = now() +where id in ( + select id from matches + where state = 'ready' + order by created_at + limit 500 + for update skip locked +) +returning *; +``` + +### 8.5 Adapter 패턴 + +OSS 배포 시 참조 구현 제공: + +``` +matchmaker/ +├── helm/ +├── proto/ +├── server/ # 매치메이커 본체 +└── examples/ + ├── agones-adapter/ + ├── kubernetes-adapter/ + └── README.md +``` + +Adapter는 매치메이커 외부에서: +- `ClaimMatches` polling +- 자기 Allocator API 호출 +- `ReportAssignment`로 결과 통보 + +--- + +## 9. 데이터 모델 + +### 9.1 핵심 테이블 + +```sql +-- Entity Store +create table tickets ( + id uuid primary key, + queue_id text not null, + payload jsonb not null, + state text not null, -- created, in_director, matched, assigned, ... + created_at timestamptz default now(), + expires_at timestamptz, + matched_at timestamptz, + config_version int -- 분류된 시점의 version +); +create index on tickets (state, created_at); +create index on tickets (queue_id, state); + +create table matches ( + id uuid primary key, + queue_id text not null, + ticket_ids uuid[] not null, + quality_score float, + state text not null, -- ready, assigning, assigned, completed + config_version int, + created_at timestamptz default now(), + claimed_at timestamptz, + assigned_to_allocator text, + dgs_info jsonb +); +create index on matches (state, created_at); + +-- Config Store (별도 DB) +create table queues ( + queue_id text primary key, + config jsonb not null, + version int not null, + updated_at timestamptz default now() +); + +create table config_history ( + queue_id text not null, + version int not null, + config jsonb not null, + changed_at timestamptz default now(), + changed_by text, + primary key (queue_id, version) +); +``` + +### 9.2 Ticket State Machine + +``` +created (Gateway INSERT) + ↓ +in_director (Director batch pull, lease) + ↓ +matched (Evaluator 확정) + ↓ +assigned (Allocator가 DGS 할당 완료) + ↓ +in_game / completed / cancelled / expired +``` + +### 9.3 Match State Machine + +``` +forming (Director가 매칭 중) + ↓ +ready (Evaluator 확정, Allocator 할당 대기) + ↓ +assigning (Allocator가 claim, lease 중) + ↓ +assigned (DGS 할당 완료) + ↓ +active → completed / failed / backfilling +``` + +--- + +## 10. Open Match와의 차별점 + +| 측면 | Open Match | 본 설계 | +|---|---|---| +| 의존성 | Redis + K8s | Postgres + Helm | +| Director | 사용자 구현 | 내장 | +| MMF (매칭 함수) | 사용자 구현 (gRPC) | CEL 룰 (config) | +| Evaluator | 사용자 구현 | 내장 | +| 백필 | 약함 | 일급 시민 | +| 라이브 룰 튜닝 | 재배포 필요 | 런타임 변경 API | +| 진입 장벽 | 높음 (Director/MMF 직접 구현) | 낮음 (config만) | +| 트랜잭션 일관성 | Lua 등 우회 필요 | Postgres native | + +**포지셔닝**: Open Match가 못 채우는 시장 (라이브 운영 친화적, 백필 강함, 진입장벽 낮음) 공략. + +--- + +## 11. 미해결 / 다음 단계 + +### 11.1 추가 설계 결정거리 + +1. **Director batch pull 전략** — limit N, lease timeout 튜닝 +2. **ME dispatch 단위** — pool 단위 통째 vs 더 작은 분할 +3. **Evaluator cycle 동기화** — Director cycle과의 맞물림 +4. **Ticket TTL과 expiry** — 너무 오래 기다린 ticket 처리 +5. **Region/label 기반 routing** — 다중 Allocator 운영 시 +6. **백필 데이터 흐름** — 활성 게임에 ticket 추가 시 통보 경로 + +### 11.2 다이어그램 보강 필요 + +- Ticket lifecycle 상태 전이 +- Match lifecycle 상태 전이 +- 백필 데이터 흐름 +- Director의 active-passive HA +- Config version 흐름 +- Reaper / TTL cleanup 메커니즘 + +### 11.3 다음 산출물 후보 + +- 상태 머신 다이어그램 (Ticket, Match, Director lease) +- 시퀀스 다이어그램 (happy path + 장애 시나리오) +- gRPC proto 정의 초안 +- Deployment 다이어그램 (인스턴스 다중성, leader election) + +### 11.4 백필 통보 경로 (미정) + +``` +일반 매치: Director → ME → Evaluator → matches table → Allocator polling → DGS +백필 매치: Director → ME → Evaluator → backfill table → ??? → 기존 DGS +``` + +백필은 활성 게임 인스턴스로 직접 통보되어야 하므로 별도 데이터 흐름 설계 필요. + +--- + +## 12. 핵심 원칙 요약 + +1. **Minimalistic 의존성**: Postgres 2대 (runtime + config) + 컴포넌트들. Redis, Kafka, NATS 없음. +2. **Polling 기반 통신**: 운영 견고성과 단순성 우선. Streaming은 필요한 곳만. +3. **Bounded context 분리**: API Gateway는 큐/풀 무지, Director가 큐/풀 권위자, ME는 매칭 로직, Evaluator는 일관성 수문장. +4. **Pull + lease 패턴**: SKIP LOCKED로 분산 컨슈머, lease로 장애 복구. +5. **Pull-based config versioning**: Director가 version 발급, 컴포넌트가 lazy fetch. +6. **외부 Allocator는 추상화**: Pull-only API로 매치메이커는 Allocator 모름. +7. **라이브 운영 first-class**: Config 런타임 변경, stale cache fallback, 점진적 backpressure. \ No newline at end of file diff --git a/docs/design/matchmaker_0523rev3.png b/docs/design/matchmaker_0523rev3.png new file mode 100644 index 0000000000000000000000000000000000000000..2439487fc504562c58b23c223d7ba7f847dbded7 GIT binary patch literal 360301 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
7tkA8A^D*udL(B_p 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%0A6|EC~7)s+Jslb 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%0A{Z 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 {E14N1XRUQO`5L6A6z93m5?_Lm>U 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 UPR(Yw)4|sXO 시스템 전체를 한 페이지로 요약. 상세 의사결정은 `docs/decisions/`, 컴포넌트별 디테일은 `docs/design/components/`에 분리. + +## 1. 목표 + +Postgres 기반 minimalistic 매치메이커. Open Match의 대안 포지셔닝. + +**기능 목표**: +- 동접 100만 명, 600 RPS 처리 +- 백필 지원 (일급 시민) +- 큐별 동적 커스텀 어트리뷰트 +- CEL 기반 동적 매칭 룰 (런타임 변경 가능) +- 관제 기능 +- gRPC 인터페이스 +- Helm chart 설치형 배포 + +**비기능 목표**: +- 의존성 최소화 (Postgres만, 외부 큐/캐시 없음) +- 라이브 운영 친화적 (재배포 없이 룰 튜닝) +- Allocator-agnostic (Agones, GameLift, PlayFab 등 모두 통합 가능) + +## 2. 시스템 경계 + +``` +┌─────────────────────────── 외부 ────────────────────────────┐ +│ │ +│ Game Client → Game Backend ──┐ │ +│ ├──gRPC──→ ┌──────────────┐ │ +│ External DGS Allocator ──────┘ │ API Gateway │ │ +│ └──────┬───────┘ │ +│ │ │ +└───────────────────────────────────────────────────┼──────────┘ + │ +┌─────────────────────────── 내부 ──────────────────┼──────────┐ +│ ▼ │ +│ ┌──────────────┐ │ +│ │ Entity Store │ │ +│ │ (Postgres) │ │ +│ └──┬───────────┘ │ +│ │ polling │ +│ ┌──────────▼─────────┐ │ +│ │ Director │◄─────┼─┐ +│ └──────────┬─────────┘ │ │ +│ │ dispatch │ │ +│ ┌──────────▼─────────┐ │ │ +│ │ MatchingEngine × N │◄─────┼─┤ +│ └──────────┬─────────┘ │ │ +│ │ candidates │ │ +│ ┌──────────▼─────────┐ │ │ +│ │ Evaluator │◄─────┼─┤ +│ └──────────┬─────────┘ │ │ +│ │ matches │ │ +│ ┌──────────▼─────────┐ │ │ +│ │ Entity Store │ │ │ +│ └────────────────────┘ │ │ +│ │ │ +│ ┌────────────────────┐ │ │ +│ │ Config Syncer │──────sync config─────┼─┘ +│ └──────────┬─────────┘ │ +│ │ polling │ +│ ┌──────────▼─────────┐ │ +│ │ Config Store │ │ +│ │ (Postgres) │ │ +│ └────────────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────┘ +``` + +**핵심 원칙**: 외부 시스템은 오직 API Gateway만 통해 매치메이커와 통신. 내부 컴포넌트는 외부에 노출되지 않음 (ADR-003). + +## 3. 컴포넌트 + +| 컴포넌트 | 역할 | 인스턴스 | 상태 | +|---|---|---|---| +| **API Gateway** | 외부 진입점, 인증, ticket CRUD, 매치 dequeue | 다수 (stateless, 수평 확장) | stateless | +| **Director** | 큐 설정 캐싱, batch pull, pool 분류, ME로 dispatch | 1 active + standby | stateful (lease) | +| **MatchingEngine** | pool당 1개, 매치 후보 생성 | pool 수만큼 | stateless (config cache만) | +| **Evaluator** | 후보 aggregation, 충돌 해결, 최종 매치 확정 | 1 active + standby | stateless | +| **Config Syncer** | Config Store polling 캐싱, 컴포넌트에 config API 제공 | 1 active + standby | cache | +| **Entity Store** | tickets, matches 영속화 | Postgres 1대 | stateful | +| **Config Store** | 큐/풀/룰 설정 + 변경 이력 | Postgres 1대 (runtime DB와 분리) | stateful | + +## 4. 외부 API + +API Gateway가 노출하는 gRPC service: + +```protobuf +service MatchmakerFrontend { + // Game Backend가 호출 + rpc CreateTicket(CreateTicketRequest) returns (CreateTicketResponse); + rpc GetTicket(GetTicketRequest) returns (GetTicketResponse); // polling + rpc CancelTicket(CancelTicketRequest) returns (CancelTicketResponse); +} + +service MatchmakerBackend { + // External DGS Allocator가 호출 + rpc ClaimMatches(ClaimMatchesRequest) returns (ClaimMatchesResponse); + rpc ReportAssignment(ReportAssignmentRequest) returns (ReportAssignmentResponse); + rpc ReleaseMatch(ReleaseMatchRequest) returns (ReleaseMatchResponse); +} + +service MatchmakerAdmin { + // 운영자가 호출 (라이브 룰 튜닝) + rpc UpsertQueueConfig(UpsertQueueConfigRequest) returns (UpsertQueueConfigResponse); + rpc GetQueueConfig(GetQueueConfigRequest) returns (GetQueueConfigResponse); + rpc ListConfigHistory(ListConfigHistoryRequest) returns (ListConfigHistoryResponse); + rpc RollbackConfig(RollbackConfigRequest) returns (RollbackConfigResponse); +} +``` + +모두 gRPC unary RPC (streaming 없음, ADR-002). + +## 5. 데이터 모델 (핵심만) + +### Ticket lifecycle + +``` +created (Gateway INSERT) + ↓ +in_director (Director batch pull, lease) + ↓ +matched (Evaluator 확정) + ↓ +assigned (Allocator가 DGS 할당 완료) + ↓ +in_game → completed / cancelled / expired +``` + +### Match lifecycle + +``` +forming (Director가 매칭 중, 메모리 only) + ↓ +ready (Evaluator 확정, Allocator 할당 대기) + ↓ +assigning (Allocator가 claim, lease 중) + ↓ +assigned (DGS 정보 박힘, ticket에 전파) + ↓ +active → completed / failed / backfilling +``` + +### 핵심 테이블 요약 + +```sql +-- Entity Store (runtime DB) +tickets -- id, queue_id, payload (jsonb), state, config_version, timestamps +matches -- id, queue_id, ticket_ids[], state, quality_score, + -- config_version, dgs_info (jsonb), assignment 메타 + +-- Config Store (별도 DB) +queues -- queue_id, config (jsonb), version, updated_at +config_history -- queue_id, version, config, changed_at, changed_by +``` + +상세는 `docs/design/data-model.md` 참조 (작성 예정). + +## 6. 핵심 흐름 + +### 6.1 매칭 happy path + +1. Game Backend → Gateway: `CreateTicket(queue_id, payload)` +2. Gateway → Entity Store: ticket INSERT (state=`created`) +3. Game Backend → Gateway: `GetTicket(ticket_id)` polling (1초) +4. Director (1초 cycle): + - Entity Store에서 `created` ticket batch pull (state=`in_director`) + - 메모리에서 큐별 config로 pool 분류 + - `{ticket, pool_id, config_version}`을 해당 ME로 push +5. MatchingEngine: + - 받은 ticket batch를 자기 pool 후보로 누적 + - config_version이 cache와 다르면 Syncer에서 fetch + - CEL 룰로 매치 후보 생성 + - Evaluator로 push +6. Evaluator: + - 모든 ME의 후보 수집 (cycle 단위) + - 중복 ticket 충돌 해결 (quality score 기반) + - 트랜잭션: ticket state=`matched`, match INSERT (state=`ready`) +7. External DGS Allocator → Gateway: `ClaimMatches(...)` polling +8. Gateway → Entity Store: `SELECT ... FOR UPDATE SKIP LOCKED` (state=`assigning`, lease) +9. Allocator: 자기 fleet에서 DGS 인스턴스 시작 +10. Allocator → Gateway: `ReportAssignment(match_id, dgs_address)` +11. Gateway → Entity Store: match state=`assigned`, ticket에 dgs_info 박음 +12. Game Backend의 `GetTicket` polling이 dgs_info 받음 → 클라이언트에 통보 + +### 6.2 Config 변경 흐름 + +1. 운영자 → Gateway → Config Store: `UpsertQueueConfig(...)` 트랜잭션 (version++) +2. Config Syncer가 polling으로 변경 감지 → cache 갱신 +3. Director가 Syncer에서 새 config fetch (다음 cycle) +4. Director가 새 config_version으로 ticket 분류 +5. ME가 ticket의 config_version이 cache와 다르면 Syncer에서 lazy fetch + +상세는 `docs/design/flows/` 참조 (작성 예정). + +## 7. 부하 특성 + +100만 동접 기준 추정: + +| 컴포넌트 | QPS | 비고 | +|---|---|---| +| Gateway CreateTicket | ~556 | 티켓 발급률 | +| Gateway GetTicket | ~수천 | Game Backend의 polling | +| Gateway ClaimMatches | 1~수십 | Allocator polling, batch | +| Director → Entity Store | 1 | 1초 cycle | +| Evaluator → matches insert | ~139 | 매치 생성률 | +| Config Syncer → Config Store | ~0.2 | 5초 polling | + +**유일한 의미 있는 부하는 Gateway의 CreateTicket + GetTicket**. 단일 Postgres가 압도적으로 여유 있게 처리. 부하 측면에서 Postgres scaling 우려 없음. + +## 8. 운영 특성 + +### 가용성 + +- **Gateway**: stateless, 다수 인스턴스 + LB +- **Director, Evaluator, Syncer**: active-passive + Postgres advisory lock 기반 leader election +- **ME**: pool당 1개 active + standby 또는 multi-active (큐 분할) +- **Entity Store/Config Store**: 사용자 운영 책임 (read replica는 선택) + +### Stale config 정책 + +Config Store 일시 장애 시 Syncer는 마지막 성공 config를 무기한 유지. 매칭은 계속 동작. "최근 변경한 룰이 늦게 반영됨"이 "매칭 시스템 다운"보다 낫다는 원칙. + +### Lease + Reaper 패턴 + +- Director가 ticket batch pull 시 `in_director` 상태 + leased_at +- Allocator가 ClaimMatches 시 `assigning` 상태 + claimed_at +- 별도 reaper job이 stale lease를 원래 상태로 복원 (Director/Allocator 다운 대응) + +### 백프레셔 + +매치 큐 (`state='ready'`) depth와 oldest age를 모니터링: +- 약간 적체 → DGS fleet autoscale 트리거 +- 심각 적체 → 신규 ticket 수락률 점진 감소 +- 위험 적체 → ticket 수락 완전 차단 + drain 모드 +- 복구 시 slow ramp (oscillation 방지) + +## 9. 디렉토리 구조 (계획) + +``` +matchmaker/ +├── CLAUDE.md +├── proto/ # gRPC 정의 +├── services/ # 매치메이커 본체 +│ ├── gateway/ +│ ├── director/ +│ ├── engine/ +│ ├── evaluator/ +│ └── syncer/ +├── helm/ # Helm chart +├── examples/ +│ ├── agones-adapter/ +│ └── kubernetes-adapter/ +└── docs/ + ├── design/ + │ ├── overview.md # 이 문서 + │ ├── data-model.md # 스키마, 인덱스, 상태 머신 상세 + │ ├── api.md # gRPC proto + 시맨틱 + │ ├── components/ # 컴포넌트별 디테일 + │ ├── flows/ # 시퀀스 다이어그램, 장애 시나리오 + │ └── operations/ # 배포, 모니터링, 튜닝 + ├── decisions/ # ADR + └── design-discussion.md # 초기 토론 전문 +``` + +## 10. 미해결 항목 + +- **백필 데이터 흐름**: 활성 게임 인스턴스로의 통보 경로 (일반 매치는 Allocator polling, 백필은 기존 DGS 직접 통보 필요) +- **Region/label 기반 routing**: 다중 Allocator 운영 시 매치 분배 정책 +- **Evaluator cycle 동기화**: Director cycle과의 정확한 맞물림 (현재 1초 cycle 가정) +- **충돌 해결 알고리즘**: greedy vs maximum weight matching +- **Ticket TTL**: 너무 오래 기다린 ticket 처리 정책 + +## 11. 참조 + +- ADR-001: Postgres-only 의존성 +- ADR-002: Polling 기반 통신 +- ADR-003: API Gateway는 유일한 외부 진입점이자 큐/풀 설정 무지 +- ADR-004: Director Push 모델 +- ADR-005: Config 런타임 변경 (DB 기반) +- ADR-006: Runtime DB와 Config DB 분리 +- ADR-007: Allocator-agnostic Pull-only API + +Open Match와의 차별점은 ADR 전반에서 다룸. 요약하면: 의존성 최소(Postgres만), Director/Evaluator 내장, 라이브 룰 튜닝, 백필 일급, 진입 장벽 낮음. \ No newline at end of file diff --git a/gen/go/common/v1/attribute.pb.go b/gen/go/common/v1/attribute.pb.go deleted file mode 100644 index a486443..0000000 --- a/gen/go/common/v1/attribute.pb.go +++ /dev/null @@ -1,461 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: common/v1/attribute.proto - -package v1 - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Attribute struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Value: - // - // *Attribute_DoubleValue - // *Attribute_StringValue - // *Attribute_BoolValue - // *Attribute_StringListValue - // *Attribute_DoubleListValue - // *Attribute_StringMapValue - // *Attribute_DoubleMapValue - Value isAttribute_Value `protobuf_oneof:"value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Attribute) Reset() { - *x = Attribute{} - mi := &file_common_v1_attribute_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Attribute) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Attribute) ProtoMessage() {} - -func (x *Attribute) ProtoReflect() protoreflect.Message { - mi := &file_common_v1_attribute_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Attribute.ProtoReflect.Descriptor instead. -func (*Attribute) Descriptor() ([]byte, []int) { - return file_common_v1_attribute_proto_rawDescGZIP(), []int{0} -} - -func (x *Attribute) GetValue() isAttribute_Value { - if x != nil { - return x.Value - } - return nil -} - -func (x *Attribute) GetDoubleValue() float64 { - if x != nil { - if x, ok := x.Value.(*Attribute_DoubleValue); ok { - return x.DoubleValue - } - } - return 0 -} - -func (x *Attribute) GetStringValue() string { - if x != nil { - if x, ok := x.Value.(*Attribute_StringValue); ok { - return x.StringValue - } - } - return "" -} - -func (x *Attribute) GetBoolValue() bool { - if x != nil { - if x, ok := x.Value.(*Attribute_BoolValue); ok { - return x.BoolValue - } - } - return false -} - -func (x *Attribute) GetStringListValue() *StringList { - if x != nil { - if x, ok := x.Value.(*Attribute_StringListValue); ok { - return x.StringListValue - } - } - return nil -} - -func (x *Attribute) GetDoubleListValue() *DoubleList { - if x != nil { - if x, ok := x.Value.(*Attribute_DoubleListValue); ok { - return x.DoubleListValue - } - } - return nil -} - -func (x *Attribute) GetStringMapValue() *StringMap { - if x != nil { - if x, ok := x.Value.(*Attribute_StringMapValue); ok { - return x.StringMapValue - } - } - return nil -} - -func (x *Attribute) GetDoubleMapValue() *DoubleMap { - if x != nil { - if x, ok := x.Value.(*Attribute_DoubleMapValue); ok { - return x.DoubleMapValue - } - } - return nil -} - -type isAttribute_Value interface { - isAttribute_Value() -} - -type Attribute_DoubleValue struct { - DoubleValue float64 `protobuf:"fixed64,1,opt,name=double_value,json=doubleValue,proto3,oneof"` -} - -type Attribute_StringValue struct { - StringValue string `protobuf:"bytes,2,opt,name=string_value,json=stringValue,proto3,oneof"` -} - -type Attribute_BoolValue struct { - BoolValue bool `protobuf:"varint,3,opt,name=bool_value,json=boolValue,proto3,oneof"` -} - -type Attribute_StringListValue struct { - StringListValue *StringList `protobuf:"bytes,4,opt,name=string_list_value,json=stringListValue,proto3,oneof"` -} - -type Attribute_DoubleListValue struct { - DoubleListValue *DoubleList `protobuf:"bytes,5,opt,name=double_list_value,json=doubleListValue,proto3,oneof"` -} - -type Attribute_StringMapValue struct { - StringMapValue *StringMap `protobuf:"bytes,6,opt,name=string_map_value,json=stringMapValue,proto3,oneof"` -} - -type Attribute_DoubleMapValue struct { - DoubleMapValue *DoubleMap `protobuf:"bytes,7,opt,name=double_map_value,json=doubleMapValue,proto3,oneof"` -} - -func (*Attribute_DoubleValue) isAttribute_Value() {} - -func (*Attribute_StringValue) isAttribute_Value() {} - -func (*Attribute_BoolValue) isAttribute_Value() {} - -func (*Attribute_StringListValue) isAttribute_Value() {} - -func (*Attribute_DoubleListValue) isAttribute_Value() {} - -func (*Attribute_StringMapValue) isAttribute_Value() {} - -func (*Attribute_DoubleMapValue) isAttribute_Value() {} - -type StringList struct { - state protoimpl.MessageState `protogen:"open.v1"` - Values []string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StringList) Reset() { - *x = StringList{} - mi := &file_common_v1_attribute_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StringList) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StringList) ProtoMessage() {} - -func (x *StringList) ProtoReflect() protoreflect.Message { - mi := &file_common_v1_attribute_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StringList.ProtoReflect.Descriptor instead. -func (*StringList) Descriptor() ([]byte, []int) { - return file_common_v1_attribute_proto_rawDescGZIP(), []int{1} -} - -func (x *StringList) GetValues() []string { - if x != nil { - return x.Values - } - return nil -} - -type DoubleList struct { - state protoimpl.MessageState `protogen:"open.v1"` - Values []float64 `protobuf:"fixed64,1,rep,packed,name=values,proto3" json:"values,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DoubleList) Reset() { - *x = DoubleList{} - mi := &file_common_v1_attribute_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DoubleList) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DoubleList) ProtoMessage() {} - -func (x *DoubleList) ProtoReflect() protoreflect.Message { - mi := &file_common_v1_attribute_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DoubleList.ProtoReflect.Descriptor instead. -func (*DoubleList) Descriptor() ([]byte, []int) { - return file_common_v1_attribute_proto_rawDescGZIP(), []int{2} -} - -func (x *DoubleList) GetValues() []float64 { - if x != nil { - return x.Values - } - return nil -} - -type StringMap struct { - state protoimpl.MessageState `protogen:"open.v1"` - Values map[string]string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StringMap) Reset() { - *x = StringMap{} - mi := &file_common_v1_attribute_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StringMap) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StringMap) ProtoMessage() {} - -func (x *StringMap) ProtoReflect() protoreflect.Message { - mi := &file_common_v1_attribute_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StringMap.ProtoReflect.Descriptor instead. -func (*StringMap) Descriptor() ([]byte, []int) { - return file_common_v1_attribute_proto_rawDescGZIP(), []int{3} -} - -func (x *StringMap) GetValues() map[string]string { - if x != nil { - return x.Values - } - return nil -} - -type DoubleMap struct { - state protoimpl.MessageState `protogen:"open.v1"` - Values map[string]float64 `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"fixed64,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DoubleMap) Reset() { - *x = DoubleMap{} - mi := &file_common_v1_attribute_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DoubleMap) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DoubleMap) ProtoMessage() {} - -func (x *DoubleMap) ProtoReflect() protoreflect.Message { - mi := &file_common_v1_attribute_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DoubleMap.ProtoReflect.Descriptor instead. -func (*DoubleMap) Descriptor() ([]byte, []int) { - return file_common_v1_attribute_proto_rawDescGZIP(), []int{4} -} - -func (x *DoubleMap) GetValues() map[string]float64 { - if x != nil { - return x.Values - } - return nil -} - -var File_common_v1_attribute_proto protoreflect.FileDescriptor - -const file_common_v1_attribute_proto_rawDesc = "" + - "\n" + - "\x19common/v1/attribute.proto\x12\tcommon.v1\"\x8d\x03\n" + - "\tAttribute\x12#\n" + - "\fdouble_value\x18\x01 \x01(\x01H\x00R\vdoubleValue\x12#\n" + - "\fstring_value\x18\x02 \x01(\tH\x00R\vstringValue\x12\x1f\n" + - "\n" + - "bool_value\x18\x03 \x01(\bH\x00R\tboolValue\x12C\n" + - "\x11string_list_value\x18\x04 \x01(\v2\x15.common.v1.StringListH\x00R\x0fstringListValue\x12C\n" + - "\x11double_list_value\x18\x05 \x01(\v2\x15.common.v1.DoubleListH\x00R\x0fdoubleListValue\x12@\n" + - "\x10string_map_value\x18\x06 \x01(\v2\x14.common.v1.StringMapH\x00R\x0estringMapValue\x12@\n" + - "\x10double_map_value\x18\a \x01(\v2\x14.common.v1.DoubleMapH\x00R\x0edoubleMapValueB\a\n" + - "\x05value\"$\n" + - "\n" + - "StringList\x12\x16\n" + - "\x06values\x18\x01 \x03(\tR\x06values\"$\n" + - "\n" + - "DoubleList\x12\x16\n" + - "\x06values\x18\x01 \x03(\x01R\x06values\"\x80\x01\n" + - "\tStringMap\x128\n" + - "\x06values\x18\x01 \x03(\v2 .common.v1.StringMap.ValuesEntryR\x06values\x1a9\n" + - "\vValuesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x80\x01\n" + - "\tDoubleMap\x128\n" + - "\x06values\x18\x01 \x03(\v2 .common.v1.DoubleMap.ValuesEntryR\x06values\x1a9\n" + - "\vValuesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\x01R\x05value:\x028\x01B4Z2github.com/chaewonkong/matchmaker/gen/go/common/v1b\x06proto3" - -var ( - file_common_v1_attribute_proto_rawDescOnce sync.Once - file_common_v1_attribute_proto_rawDescData []byte -) - -func file_common_v1_attribute_proto_rawDescGZIP() []byte { - file_common_v1_attribute_proto_rawDescOnce.Do(func() { - file_common_v1_attribute_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_v1_attribute_proto_rawDesc), len(file_common_v1_attribute_proto_rawDesc))) - }) - return file_common_v1_attribute_proto_rawDescData -} - -var file_common_v1_attribute_proto_msgTypes = make([]protoimpl.MessageInfo, 7) -var file_common_v1_attribute_proto_goTypes = []any{ - (*Attribute)(nil), // 0: common.v1.Attribute - (*StringList)(nil), // 1: common.v1.StringList - (*DoubleList)(nil), // 2: common.v1.DoubleList - (*StringMap)(nil), // 3: common.v1.StringMap - (*DoubleMap)(nil), // 4: common.v1.DoubleMap - nil, // 5: common.v1.StringMap.ValuesEntry - nil, // 6: common.v1.DoubleMap.ValuesEntry -} -var file_common_v1_attribute_proto_depIdxs = []int32{ - 1, // 0: common.v1.Attribute.string_list_value:type_name -> common.v1.StringList - 2, // 1: common.v1.Attribute.double_list_value:type_name -> common.v1.DoubleList - 3, // 2: common.v1.Attribute.string_map_value:type_name -> common.v1.StringMap - 4, // 3: common.v1.Attribute.double_map_value:type_name -> common.v1.DoubleMap - 5, // 4: common.v1.StringMap.values:type_name -> common.v1.StringMap.ValuesEntry - 6, // 5: common.v1.DoubleMap.values:type_name -> common.v1.DoubleMap.ValuesEntry - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name -} - -func init() { file_common_v1_attribute_proto_init() } -func file_common_v1_attribute_proto_init() { - if File_common_v1_attribute_proto != nil { - return - } - file_common_v1_attribute_proto_msgTypes[0].OneofWrappers = []any{ - (*Attribute_DoubleValue)(nil), - (*Attribute_StringValue)(nil), - (*Attribute_BoolValue)(nil), - (*Attribute_StringListValue)(nil), - (*Attribute_DoubleListValue)(nil), - (*Attribute_StringMapValue)(nil), - (*Attribute_DoubleMapValue)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_v1_attribute_proto_rawDesc), len(file_common_v1_attribute_proto_rawDesc)), - NumEnums: 0, - NumMessages: 7, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_common_v1_attribute_proto_goTypes, - DependencyIndexes: file_common_v1_attribute_proto_depIdxs, - MessageInfos: file_common_v1_attribute_proto_msgTypes, - }.Build() - File_common_v1_attribute_proto = out.File - file_common_v1_attribute_proto_goTypes = nil - file_common_v1_attribute_proto_depIdxs = nil -} diff --git a/gen/go/common/v1/entity.pb.go b/gen/go/common/v1/entity.pb.go deleted file mode 100644 index 41ebe5d..0000000 --- a/gen/go/common/v1/entity.pb.go +++ /dev/null @@ -1,363 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: common/v1/entity.proto - -package v1 - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Player struct { - state protoimpl.MessageState `protogen:"open.v1"` - PlayerId string `protobuf:"bytes,1,opt,name=player_id,json=playerId,proto3" json:"player_id,omitempty"` - Attributes map[string]*Attribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Player) Reset() { - *x = Player{} - mi := &file_common_v1_entity_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Player) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Player) ProtoMessage() {} - -func (x *Player) ProtoReflect() protoreflect.Message { - mi := &file_common_v1_entity_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Player.ProtoReflect.Descriptor instead. -func (*Player) Descriptor() ([]byte, []int) { - return file_common_v1_entity_proto_rawDescGZIP(), []int{0} -} - -func (x *Player) GetPlayerId() string { - if x != nil { - return x.PlayerId - } - return "" -} - -func (x *Player) GetAttributes() map[string]*Attribute { - if x != nil { - return x.Attributes - } - return nil -} - -type Ticket struct { - state protoimpl.MessageState `protogen:"open.v1"` - TicketId string `protobuf:"bytes,1,opt,name=ticket_id,json=ticketId,proto3" json:"ticket_id,omitempty"` - Players []*Player `protobuf:"bytes,2,rep,name=players,proto3" json:"players,omitempty"` - Attributes map[string]*Attribute `protobuf:"bytes,3,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Ticket) Reset() { - *x = Ticket{} - mi := &file_common_v1_entity_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Ticket) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Ticket) ProtoMessage() {} - -func (x *Ticket) ProtoReflect() protoreflect.Message { - mi := &file_common_v1_entity_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Ticket.ProtoReflect.Descriptor instead. -func (*Ticket) Descriptor() ([]byte, []int) { - return file_common_v1_entity_proto_rawDescGZIP(), []int{1} -} - -func (x *Ticket) GetTicketId() string { - if x != nil { - return x.TicketId - } - return "" -} - -func (x *Ticket) GetPlayers() []*Player { - if x != nil { - return x.Players - } - return nil -} - -func (x *Ticket) GetAttributes() map[string]*Attribute { - if x != nil { - return x.Attributes - } - return nil -} - -type Team struct { - state protoimpl.MessageState `protogen:"open.v1"` - TeamId string `protobuf:"bytes,1,opt,name=team_id,json=teamId,proto3" json:"team_id,omitempty"` - Tickets []*Ticket `protobuf:"bytes,2,rep,name=tickets,proto3" json:"tickets,omitempty"` - Attributes map[string]*Attribute `protobuf:"bytes,3,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Team) Reset() { - *x = Team{} - mi := &file_common_v1_entity_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Team) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Team) ProtoMessage() {} - -func (x *Team) ProtoReflect() protoreflect.Message { - mi := &file_common_v1_entity_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Team.ProtoReflect.Descriptor instead. -func (*Team) Descriptor() ([]byte, []int) { - return file_common_v1_entity_proto_rawDescGZIP(), []int{2} -} - -func (x *Team) GetTeamId() string { - if x != nil { - return x.TeamId - } - return "" -} - -func (x *Team) GetTickets() []*Ticket { - if x != nil { - return x.Tickets - } - return nil -} - -func (x *Team) GetAttributes() map[string]*Attribute { - if x != nil { - return x.Attributes - } - return nil -} - -type Match struct { - state protoimpl.MessageState `protogen:"open.v1"` - MatchId string `protobuf:"bytes,1,opt,name=match_id,json=matchId,proto3" json:"match_id,omitempty"` - Teams []*Team `protobuf:"bytes,2,rep,name=teams,proto3" json:"teams,omitempty"` - Attributes map[string]*Attribute `protobuf:"bytes,3,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Match) Reset() { - *x = Match{} - mi := &file_common_v1_entity_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Match) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Match) ProtoMessage() {} - -func (x *Match) ProtoReflect() protoreflect.Message { - mi := &file_common_v1_entity_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Match.ProtoReflect.Descriptor instead. -func (*Match) Descriptor() ([]byte, []int) { - return file_common_v1_entity_proto_rawDescGZIP(), []int{3} -} - -func (x *Match) GetMatchId() string { - if x != nil { - return x.MatchId - } - return "" -} - -func (x *Match) GetTeams() []*Team { - if x != nil { - return x.Teams - } - return nil -} - -func (x *Match) GetAttributes() map[string]*Attribute { - if x != nil { - return x.Attributes - } - return nil -} - -var File_common_v1_entity_proto protoreflect.FileDescriptor - -const file_common_v1_entity_proto_rawDesc = "" + - "\n" + - "\x16common/v1/entity.proto\x12\tcommon.v1\x1a\x19common/v1/attribute.proto\"\xbd\x01\n" + - "\x06Player\x12\x1b\n" + - "\tplayer_id\x18\x01 \x01(\tR\bplayerId\x12A\n" + - "\n" + - "attributes\x18\x02 \x03(\v2!.common.v1.Player.AttributesEntryR\n" + - "attributes\x1aS\n" + - "\x0fAttributesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12*\n" + - "\x05value\x18\x02 \x01(\v2\x14.common.v1.AttributeR\x05value:\x028\x01\"\xea\x01\n" + - "\x06Ticket\x12\x1b\n" + - "\tticket_id\x18\x01 \x01(\tR\bticketId\x12+\n" + - "\aplayers\x18\x02 \x03(\v2\x11.common.v1.PlayerR\aplayers\x12A\n" + - "\n" + - "attributes\x18\x03 \x03(\v2!.common.v1.Ticket.AttributesEntryR\n" + - "attributes\x1aS\n" + - "\x0fAttributesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12*\n" + - "\x05value\x18\x02 \x01(\v2\x14.common.v1.AttributeR\x05value:\x028\x01\"\xe2\x01\n" + - "\x04Team\x12\x17\n" + - "\ateam_id\x18\x01 \x01(\tR\x06teamId\x12+\n" + - "\atickets\x18\x02 \x03(\v2\x11.common.v1.TicketR\atickets\x12?\n" + - "\n" + - "attributes\x18\x03 \x03(\v2\x1f.common.v1.Team.AttributesEntryR\n" + - "attributes\x1aS\n" + - "\x0fAttributesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12*\n" + - "\x05value\x18\x02 \x01(\v2\x14.common.v1.AttributeR\x05value:\x028\x01\"\xe0\x01\n" + - "\x05Match\x12\x19\n" + - "\bmatch_id\x18\x01 \x01(\tR\amatchId\x12%\n" + - "\x05teams\x18\x02 \x03(\v2\x0f.common.v1.TeamR\x05teams\x12@\n" + - "\n" + - "attributes\x18\x03 \x03(\v2 .common.v1.Match.AttributesEntryR\n" + - "attributes\x1aS\n" + - "\x0fAttributesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12*\n" + - "\x05value\x18\x02 \x01(\v2\x14.common.v1.AttributeR\x05value:\x028\x01B4Z2github.com/chaewonkong/matchmaker/gen/go/common/v1b\x06proto3" - -var ( - file_common_v1_entity_proto_rawDescOnce sync.Once - file_common_v1_entity_proto_rawDescData []byte -) - -func file_common_v1_entity_proto_rawDescGZIP() []byte { - file_common_v1_entity_proto_rawDescOnce.Do(func() { - file_common_v1_entity_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_v1_entity_proto_rawDesc), len(file_common_v1_entity_proto_rawDesc))) - }) - return file_common_v1_entity_proto_rawDescData -} - -var file_common_v1_entity_proto_msgTypes = make([]protoimpl.MessageInfo, 8) -var file_common_v1_entity_proto_goTypes = []any{ - (*Player)(nil), // 0: common.v1.Player - (*Ticket)(nil), // 1: common.v1.Ticket - (*Team)(nil), // 2: common.v1.Team - (*Match)(nil), // 3: common.v1.Match - nil, // 4: common.v1.Player.AttributesEntry - nil, // 5: common.v1.Ticket.AttributesEntry - nil, // 6: common.v1.Team.AttributesEntry - nil, // 7: common.v1.Match.AttributesEntry - (*Attribute)(nil), // 8: common.v1.Attribute -} -var file_common_v1_entity_proto_depIdxs = []int32{ - 4, // 0: common.v1.Player.attributes:type_name -> common.v1.Player.AttributesEntry - 0, // 1: common.v1.Ticket.players:type_name -> common.v1.Player - 5, // 2: common.v1.Ticket.attributes:type_name -> common.v1.Ticket.AttributesEntry - 1, // 3: common.v1.Team.tickets:type_name -> common.v1.Ticket - 6, // 4: common.v1.Team.attributes:type_name -> common.v1.Team.AttributesEntry - 2, // 5: common.v1.Match.teams:type_name -> common.v1.Team - 7, // 6: common.v1.Match.attributes:type_name -> common.v1.Match.AttributesEntry - 8, // 7: common.v1.Player.AttributesEntry.value:type_name -> common.v1.Attribute - 8, // 8: common.v1.Ticket.AttributesEntry.value:type_name -> common.v1.Attribute - 8, // 9: common.v1.Team.AttributesEntry.value:type_name -> common.v1.Attribute - 8, // 10: common.v1.Match.AttributesEntry.value:type_name -> common.v1.Attribute - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name -} - -func init() { file_common_v1_entity_proto_init() } -func file_common_v1_entity_proto_init() { - if File_common_v1_entity_proto != nil { - return - } - file_common_v1_attribute_proto_init() - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_v1_entity_proto_rawDesc), len(file_common_v1_entity_proto_rawDesc)), - NumEnums: 0, - NumMessages: 8, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_common_v1_entity_proto_goTypes, - DependencyIndexes: file_common_v1_entity_proto_depIdxs, - MessageInfos: file_common_v1_entity_proto_msgTypes, - }.Build() - File_common_v1_entity_proto = out.File - file_common_v1_entity_proto_goTypes = nil - file_common_v1_entity_proto_depIdxs = nil -} diff --git a/gen/go/gateway/v1/messages.pb.go b/gen/go/gateway/v1/messages.pb.go deleted file mode 100644 index 2a34e50..0000000 --- a/gen/go/gateway/v1/messages.pb.go +++ /dev/null @@ -1,653 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: gateway/v1/messages.proto - -package v1 - -import ( - v1 "github.com/chaewonkong/matchmaker/gen/go/common/v1" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type CreateTicketRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Tickets []*v1.Ticket `protobuf:"bytes,1,rep,name=tickets,proto3" json:"tickets,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateTicketRequest) Reset() { - *x = CreateTicketRequest{} - mi := &file_gateway_v1_messages_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateTicketRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateTicketRequest) ProtoMessage() {} - -func (x *CreateTicketRequest) ProtoReflect() protoreflect.Message { - mi := &file_gateway_v1_messages_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateTicketRequest.ProtoReflect.Descriptor instead. -func (*CreateTicketRequest) Descriptor() ([]byte, []int) { - return file_gateway_v1_messages_proto_rawDescGZIP(), []int{0} -} - -func (x *CreateTicketRequest) GetTickets() []*v1.Ticket { - if x != nil { - return x.Tickets - } - return nil -} - -type CreateTicketResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - TicketId string `protobuf:"bytes,1,opt,name=ticket_id,json=ticketId,proto3" json:"ticket_id,omitempty"` - EstimatedWaitTimeSec float64 `protobuf:"fixed64,2,opt,name=estimated_wait_time_sec,json=estimatedWaitTimeSec,proto3" json:"estimated_wait_time_sec,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateTicketResponse) Reset() { - *x = CreateTicketResponse{} - mi := &file_gateway_v1_messages_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateTicketResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateTicketResponse) ProtoMessage() {} - -func (x *CreateTicketResponse) ProtoReflect() protoreflect.Message { - mi := &file_gateway_v1_messages_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateTicketResponse.ProtoReflect.Descriptor instead. -func (*CreateTicketResponse) Descriptor() ([]byte, []int) { - return file_gateway_v1_messages_proto_rawDescGZIP(), []int{1} -} - -func (x *CreateTicketResponse) GetTicketId() string { - if x != nil { - return x.TicketId - } - return "" -} - -func (x *CreateTicketResponse) GetEstimatedWaitTimeSec() float64 { - if x != nil { - return x.EstimatedWaitTimeSec - } - return 0 -} - -type CancelTicketRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - TicketId string `protobuf:"bytes,1,opt,name=ticket_id,json=ticketId,proto3" json:"ticket_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CancelTicketRequest) Reset() { - *x = CancelTicketRequest{} - mi := &file_gateway_v1_messages_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CancelTicketRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CancelTicketRequest) ProtoMessage() {} - -func (x *CancelTicketRequest) ProtoReflect() protoreflect.Message { - mi := &file_gateway_v1_messages_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CancelTicketRequest.ProtoReflect.Descriptor instead. -func (*CancelTicketRequest) Descriptor() ([]byte, []int) { - return file_gateway_v1_messages_proto_rawDescGZIP(), []int{2} -} - -func (x *CancelTicketRequest) GetTicketId() string { - if x != nil { - return x.TicketId - } - return "" -} - -type CancelTicketResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - TicketId string `protobuf:"bytes,1,opt,name=ticket_id,json=ticketId,proto3" json:"ticket_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CancelTicketResponse) Reset() { - *x = CancelTicketResponse{} - mi := &file_gateway_v1_messages_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CancelTicketResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CancelTicketResponse) ProtoMessage() {} - -func (x *CancelTicketResponse) ProtoReflect() protoreflect.Message { - mi := &file_gateway_v1_messages_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CancelTicketResponse.ProtoReflect.Descriptor instead. -func (*CancelTicketResponse) Descriptor() ([]byte, []int) { - return file_gateway_v1_messages_proto_rawDescGZIP(), []int{3} -} - -func (x *CancelTicketResponse) GetTicketId() string { - if x != nil { - return x.TicketId - } - return "" -} - -type AcknowledgeMatchRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - MatchId string `protobuf:"bytes,1,opt,name=match_id,json=matchId,proto3" json:"match_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AcknowledgeMatchRequest) Reset() { - *x = AcknowledgeMatchRequest{} - mi := &file_gateway_v1_messages_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AcknowledgeMatchRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AcknowledgeMatchRequest) ProtoMessage() {} - -func (x *AcknowledgeMatchRequest) ProtoReflect() protoreflect.Message { - mi := &file_gateway_v1_messages_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AcknowledgeMatchRequest.ProtoReflect.Descriptor instead. -func (*AcknowledgeMatchRequest) Descriptor() ([]byte, []int) { - return file_gateway_v1_messages_proto_rawDescGZIP(), []int{4} -} - -func (x *AcknowledgeMatchRequest) GetMatchId() string { - if x != nil { - return x.MatchId - } - return "" -} - -type AcknowledgeMatchResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - MatchId string `protobuf:"bytes,1,opt,name=match_id,json=matchId,proto3" json:"match_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AcknowledgeMatchResponse) Reset() { - *x = AcknowledgeMatchResponse{} - mi := &file_gateway_v1_messages_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AcknowledgeMatchResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AcknowledgeMatchResponse) ProtoMessage() {} - -func (x *AcknowledgeMatchResponse) ProtoReflect() protoreflect.Message { - mi := &file_gateway_v1_messages_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AcknowledgeMatchResponse.ProtoReflect.Descriptor instead. -func (*AcknowledgeMatchResponse) Descriptor() ([]byte, []int) { - return file_gateway_v1_messages_proto_rawDescGZIP(), []int{5} -} - -func (x *AcknowledgeMatchResponse) GetMatchId() string { - if x != nil { - return x.MatchId - } - return "" -} - -type SubmitMatchResultRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Match *v1.Match `protobuf:"bytes,1,opt,name=match,proto3" json:"match,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SubmitMatchResultRequest) Reset() { - *x = SubmitMatchResultRequest{} - mi := &file_gateway_v1_messages_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SubmitMatchResultRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubmitMatchResultRequest) ProtoMessage() {} - -func (x *SubmitMatchResultRequest) ProtoReflect() protoreflect.Message { - mi := &file_gateway_v1_messages_proto_msgTypes[6] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SubmitMatchResultRequest.ProtoReflect.Descriptor instead. -func (*SubmitMatchResultRequest) Descriptor() ([]byte, []int) { - return file_gateway_v1_messages_proto_rawDescGZIP(), []int{6} -} - -func (x *SubmitMatchResultRequest) GetMatch() *v1.Match { - if x != nil { - return x.Match - } - return nil -} - -type SubmitMatchResultResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - MatchId string `protobuf:"bytes,1,opt,name=match_id,json=matchId,proto3" json:"match_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SubmitMatchResultResponse) Reset() { - *x = SubmitMatchResultResponse{} - mi := &file_gateway_v1_messages_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SubmitMatchResultResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubmitMatchResultResponse) ProtoMessage() {} - -func (x *SubmitMatchResultResponse) ProtoReflect() protoreflect.Message { - mi := &file_gateway_v1_messages_proto_msgTypes[7] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SubmitMatchResultResponse.ProtoReflect.Descriptor instead. -func (*SubmitMatchResultResponse) Descriptor() ([]byte, []int) { - return file_gateway_v1_messages_proto_rawDescGZIP(), []int{7} -} - -func (x *SubmitMatchResultResponse) GetMatchId() string { - if x != nil { - return x.MatchId - } - return "" -} - -type WatchMatchCandidatesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - TicketId string `protobuf:"bytes,1,opt,name=ticket_id,json=ticketId,proto3" json:"ticket_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WatchMatchCandidatesRequest) Reset() { - *x = WatchMatchCandidatesRequest{} - mi := &file_gateway_v1_messages_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WatchMatchCandidatesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WatchMatchCandidatesRequest) ProtoMessage() {} - -func (x *WatchMatchCandidatesRequest) ProtoReflect() protoreflect.Message { - mi := &file_gateway_v1_messages_proto_msgTypes[8] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WatchMatchCandidatesRequest.ProtoReflect.Descriptor instead. -func (*WatchMatchCandidatesRequest) Descriptor() ([]byte, []int) { - return file_gateway_v1_messages_proto_rawDescGZIP(), []int{8} -} - -func (x *WatchMatchCandidatesRequest) GetTicketId() string { - if x != nil { - return x.TicketId - } - return "" -} - -type WatchMatchCandidatesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Event: - // - // *WatchMatchCandidatesResponse_Match - // *WatchMatchCandidatesResponse_Expired - Event isWatchMatchCandidatesResponse_Event `protobuf_oneof:"event"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WatchMatchCandidatesResponse) Reset() { - *x = WatchMatchCandidatesResponse{} - mi := &file_gateway_v1_messages_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WatchMatchCandidatesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WatchMatchCandidatesResponse) ProtoMessage() {} - -func (x *WatchMatchCandidatesResponse) ProtoReflect() protoreflect.Message { - mi := &file_gateway_v1_messages_proto_msgTypes[9] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WatchMatchCandidatesResponse.ProtoReflect.Descriptor instead. -func (*WatchMatchCandidatesResponse) Descriptor() ([]byte, []int) { - return file_gateway_v1_messages_proto_rawDescGZIP(), []int{9} -} - -func (x *WatchMatchCandidatesResponse) GetEvent() isWatchMatchCandidatesResponse_Event { - if x != nil { - return x.Event - } - return nil -} - -func (x *WatchMatchCandidatesResponse) GetMatch() *v1.Match { - if x != nil { - if x, ok := x.Event.(*WatchMatchCandidatesResponse_Match); ok { - return x.Match - } - } - return nil -} - -func (x *WatchMatchCandidatesResponse) GetExpired() *MatchExpired { - if x != nil { - if x, ok := x.Event.(*WatchMatchCandidatesResponse_Expired); ok { - return x.Expired - } - } - return nil -} - -type isWatchMatchCandidatesResponse_Event interface { - isWatchMatchCandidatesResponse_Event() -} - -type WatchMatchCandidatesResponse_Match struct { - Match *v1.Match `protobuf:"bytes,1,opt,name=match,proto3,oneof"` -} - -type WatchMatchCandidatesResponse_Expired struct { - Expired *MatchExpired `protobuf:"bytes,2,opt,name=expired,proto3,oneof"` -} - -func (*WatchMatchCandidatesResponse_Match) isWatchMatchCandidatesResponse_Event() {} - -func (*WatchMatchCandidatesResponse_Expired) isWatchMatchCandidatesResponse_Event() {} - -type MatchExpired struct { - state protoimpl.MessageState `protogen:"open.v1"` - Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *MatchExpired) Reset() { - *x = MatchExpired{} - mi := &file_gateway_v1_messages_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *MatchExpired) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MatchExpired) ProtoMessage() {} - -func (x *MatchExpired) ProtoReflect() protoreflect.Message { - mi := &file_gateway_v1_messages_proto_msgTypes[10] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use MatchExpired.ProtoReflect.Descriptor instead. -func (*MatchExpired) Descriptor() ([]byte, []int) { - return file_gateway_v1_messages_proto_rawDescGZIP(), []int{10} -} - -func (x *MatchExpired) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -var File_gateway_v1_messages_proto protoreflect.FileDescriptor - -const file_gateway_v1_messages_proto_rawDesc = "" + - "\n" + - "\x19gateway/v1/messages.proto\x12\n" + - "gateway.v1\x1a\x16common/v1/entity.proto\"B\n" + - "\x13CreateTicketRequest\x12+\n" + - "\atickets\x18\x01 \x03(\v2\x11.common.v1.TicketR\atickets\"j\n" + - "\x14CreateTicketResponse\x12\x1b\n" + - "\tticket_id\x18\x01 \x01(\tR\bticketId\x125\n" + - "\x17estimated_wait_time_sec\x18\x02 \x01(\x01R\x14estimatedWaitTimeSec\"2\n" + - "\x13CancelTicketRequest\x12\x1b\n" + - "\tticket_id\x18\x01 \x01(\tR\bticketId\"3\n" + - "\x14CancelTicketResponse\x12\x1b\n" + - "\tticket_id\x18\x01 \x01(\tR\bticketId\"4\n" + - "\x17AcknowledgeMatchRequest\x12\x19\n" + - "\bmatch_id\x18\x01 \x01(\tR\amatchId\"5\n" + - "\x18AcknowledgeMatchResponse\x12\x19\n" + - "\bmatch_id\x18\x01 \x01(\tR\amatchId\"B\n" + - "\x18SubmitMatchResultRequest\x12&\n" + - "\x05match\x18\x01 \x01(\v2\x10.common.v1.MatchR\x05match\"6\n" + - "\x19SubmitMatchResultResponse\x12\x19\n" + - "\bmatch_id\x18\x01 \x01(\tR\amatchId\":\n" + - "\x1bWatchMatchCandidatesRequest\x12\x1b\n" + - "\tticket_id\x18\x01 \x01(\tR\bticketId\"\x87\x01\n" + - "\x1cWatchMatchCandidatesResponse\x12(\n" + - "\x05match\x18\x01 \x01(\v2\x10.common.v1.MatchH\x00R\x05match\x124\n" + - "\aexpired\x18\x02 \x01(\v2\x18.gateway.v1.MatchExpiredH\x00R\aexpiredB\a\n" + - "\x05event\"&\n" + - "\fMatchExpired\x12\x16\n" + - "\x06reason\x18\x01 \x01(\tR\x06reasonB5Z3github.com/chaewonkong/matchmaker/gen/go/gateway/v1b\x06proto3" - -var ( - file_gateway_v1_messages_proto_rawDescOnce sync.Once - file_gateway_v1_messages_proto_rawDescData []byte -) - -func file_gateway_v1_messages_proto_rawDescGZIP() []byte { - file_gateway_v1_messages_proto_rawDescOnce.Do(func() { - file_gateway_v1_messages_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_gateway_v1_messages_proto_rawDesc), len(file_gateway_v1_messages_proto_rawDesc))) - }) - return file_gateway_v1_messages_proto_rawDescData -} - -var file_gateway_v1_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 11) -var file_gateway_v1_messages_proto_goTypes = []any{ - (*CreateTicketRequest)(nil), // 0: gateway.v1.CreateTicketRequest - (*CreateTicketResponse)(nil), // 1: gateway.v1.CreateTicketResponse - (*CancelTicketRequest)(nil), // 2: gateway.v1.CancelTicketRequest - (*CancelTicketResponse)(nil), // 3: gateway.v1.CancelTicketResponse - (*AcknowledgeMatchRequest)(nil), // 4: gateway.v1.AcknowledgeMatchRequest - (*AcknowledgeMatchResponse)(nil), // 5: gateway.v1.AcknowledgeMatchResponse - (*SubmitMatchResultRequest)(nil), // 6: gateway.v1.SubmitMatchResultRequest - (*SubmitMatchResultResponse)(nil), // 7: gateway.v1.SubmitMatchResultResponse - (*WatchMatchCandidatesRequest)(nil), // 8: gateway.v1.WatchMatchCandidatesRequest - (*WatchMatchCandidatesResponse)(nil), // 9: gateway.v1.WatchMatchCandidatesResponse - (*MatchExpired)(nil), // 10: gateway.v1.MatchExpired - (*v1.Ticket)(nil), // 11: common.v1.Ticket - (*v1.Match)(nil), // 12: common.v1.Match -} -var file_gateway_v1_messages_proto_depIdxs = []int32{ - 11, // 0: gateway.v1.CreateTicketRequest.tickets:type_name -> common.v1.Ticket - 12, // 1: gateway.v1.SubmitMatchResultRequest.match:type_name -> common.v1.Match - 12, // 2: gateway.v1.WatchMatchCandidatesResponse.match:type_name -> common.v1.Match - 10, // 3: gateway.v1.WatchMatchCandidatesResponse.expired:type_name -> gateway.v1.MatchExpired - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name -} - -func init() { file_gateway_v1_messages_proto_init() } -func file_gateway_v1_messages_proto_init() { - if File_gateway_v1_messages_proto != nil { - return - } - file_gateway_v1_messages_proto_msgTypes[9].OneofWrappers = []any{ - (*WatchMatchCandidatesResponse_Match)(nil), - (*WatchMatchCandidatesResponse_Expired)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_gateway_v1_messages_proto_rawDesc), len(file_gateway_v1_messages_proto_rawDesc)), - NumEnums: 0, - NumMessages: 11, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_gateway_v1_messages_proto_goTypes, - DependencyIndexes: file_gateway_v1_messages_proto_depIdxs, - MessageInfos: file_gateway_v1_messages_proto_msgTypes, - }.Build() - File_gateway_v1_messages_proto = out.File - file_gateway_v1_messages_proto_goTypes = nil - file_gateway_v1_messages_proto_depIdxs = nil -} diff --git a/gen/go/gateway/v1/service.pb.go b/gen/go/gateway/v1/service.pb.go deleted file mode 100644 index 4e9ce21..0000000 --- a/gen/go/gateway/v1/service.pb.go +++ /dev/null @@ -1,88 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: gateway/v1/service.proto - -package v1 - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -var File_gateway_v1_service_proto protoreflect.FileDescriptor - -const file_gateway_v1_service_proto_rawDesc = "" + - "\n" + - "\x18gateway/v1/service.proto\x12\n" + - "gateway.v1\x1a\x19gateway/v1/messages.proto2\xe4\x03\n" + - "\x0eGatewayService\x12Q\n" + - "\fCreateTicket\x12\x1f.gateway.v1.CreateTicketRequest\x1a .gateway.v1.CreateTicketResponse\x12Q\n" + - "\fCancelTicket\x12\x1f.gateway.v1.CancelTicketRequest\x1a .gateway.v1.CancelTicketResponse\x12]\n" + - "\x10AcknowledgeMatch\x12#.gateway.v1.AcknowledgeMatchRequest\x1a$.gateway.v1.AcknowledgeMatchResponse\x12`\n" + - "\x11SubmitMatchResult\x12$.gateway.v1.SubmitMatchResultRequest\x1a%.gateway.v1.SubmitMatchResultResponse\x12k\n" + - "\x14WatchMatchCandidates\x12'.gateway.v1.WatchMatchCandidatesRequest\x1a(.gateway.v1.WatchMatchCandidatesResponse0\x01B5Z3github.com/chaewonkong/matchmaker/gen/go/gateway/v1b\x06proto3" - -var file_gateway_v1_service_proto_goTypes = []any{ - (*CreateTicketRequest)(nil), // 0: gateway.v1.CreateTicketRequest - (*CancelTicketRequest)(nil), // 1: gateway.v1.CancelTicketRequest - (*AcknowledgeMatchRequest)(nil), // 2: gateway.v1.AcknowledgeMatchRequest - (*SubmitMatchResultRequest)(nil), // 3: gateway.v1.SubmitMatchResultRequest - (*WatchMatchCandidatesRequest)(nil), // 4: gateway.v1.WatchMatchCandidatesRequest - (*CreateTicketResponse)(nil), // 5: gateway.v1.CreateTicketResponse - (*CancelTicketResponse)(nil), // 6: gateway.v1.CancelTicketResponse - (*AcknowledgeMatchResponse)(nil), // 7: gateway.v1.AcknowledgeMatchResponse - (*SubmitMatchResultResponse)(nil), // 8: gateway.v1.SubmitMatchResultResponse - (*WatchMatchCandidatesResponse)(nil), // 9: gateway.v1.WatchMatchCandidatesResponse -} -var file_gateway_v1_service_proto_depIdxs = []int32{ - 0, // 0: gateway.v1.GatewayService.CreateTicket:input_type -> gateway.v1.CreateTicketRequest - 1, // 1: gateway.v1.GatewayService.CancelTicket:input_type -> gateway.v1.CancelTicketRequest - 2, // 2: gateway.v1.GatewayService.AcknowledgeMatch:input_type -> gateway.v1.AcknowledgeMatchRequest - 3, // 3: gateway.v1.GatewayService.SubmitMatchResult:input_type -> gateway.v1.SubmitMatchResultRequest - 4, // 4: gateway.v1.GatewayService.WatchMatchCandidates:input_type -> gateway.v1.WatchMatchCandidatesRequest - 5, // 5: gateway.v1.GatewayService.CreateTicket:output_type -> gateway.v1.CreateTicketResponse - 6, // 6: gateway.v1.GatewayService.CancelTicket:output_type -> gateway.v1.CancelTicketResponse - 7, // 7: gateway.v1.GatewayService.AcknowledgeMatch:output_type -> gateway.v1.AcknowledgeMatchResponse - 8, // 8: gateway.v1.GatewayService.SubmitMatchResult:output_type -> gateway.v1.SubmitMatchResultResponse - 9, // 9: gateway.v1.GatewayService.WatchMatchCandidates:output_type -> gateway.v1.WatchMatchCandidatesResponse - 5, // [5:10] is the sub-list for method output_type - 0, // [0:5] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_gateway_v1_service_proto_init() } -func file_gateway_v1_service_proto_init() { - if File_gateway_v1_service_proto != nil { - return - } - file_gateway_v1_messages_proto_init() - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_gateway_v1_service_proto_rawDesc), len(file_gateway_v1_service_proto_rawDesc)), - NumEnums: 0, - NumMessages: 0, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_gateway_v1_service_proto_goTypes, - DependencyIndexes: file_gateway_v1_service_proto_depIdxs, - }.Build() - File_gateway_v1_service_proto = out.File - file_gateway_v1_service_proto_goTypes = nil - file_gateway_v1_service_proto_depIdxs = nil -} diff --git a/gen/go/gateway/v1/service_grpc.pb.go b/gen/go/gateway/v1/service_grpc.pb.go deleted file mode 100644 index 3bfa2fb..0000000 --- a/gen/go/gateway/v1/service_grpc.pb.go +++ /dev/null @@ -1,277 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.6.1 -// - protoc (unknown) -// source: gateway/v1/service.proto - -package v1 - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - GatewayService_CreateTicket_FullMethodName = "/gateway.v1.GatewayService/CreateTicket" - GatewayService_CancelTicket_FullMethodName = "/gateway.v1.GatewayService/CancelTicket" - GatewayService_AcknowledgeMatch_FullMethodName = "/gateway.v1.GatewayService/AcknowledgeMatch" - GatewayService_SubmitMatchResult_FullMethodName = "/gateway.v1.GatewayService/SubmitMatchResult" - GatewayService_WatchMatchCandidates_FullMethodName = "/gateway.v1.GatewayService/WatchMatchCandidates" -) - -// GatewayServiceClient is the client API for GatewayService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type GatewayServiceClient interface { - CreateTicket(ctx context.Context, in *CreateTicketRequest, opts ...grpc.CallOption) (*CreateTicketResponse, error) - CancelTicket(ctx context.Context, in *CancelTicketRequest, opts ...grpc.CallOption) (*CancelTicketResponse, error) - AcknowledgeMatch(ctx context.Context, in *AcknowledgeMatchRequest, opts ...grpc.CallOption) (*AcknowledgeMatchResponse, error) - SubmitMatchResult(ctx context.Context, in *SubmitMatchResultRequest, opts ...grpc.CallOption) (*SubmitMatchResultResponse, error) - WatchMatchCandidates(ctx context.Context, in *WatchMatchCandidatesRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[WatchMatchCandidatesResponse], error) -} - -type gatewayServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewGatewayServiceClient(cc grpc.ClientConnInterface) GatewayServiceClient { - return &gatewayServiceClient{cc} -} - -func (c *gatewayServiceClient) CreateTicket(ctx context.Context, in *CreateTicketRequest, opts ...grpc.CallOption) (*CreateTicketResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(CreateTicketResponse) - err := c.cc.Invoke(ctx, GatewayService_CreateTicket_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *gatewayServiceClient) CancelTicket(ctx context.Context, in *CancelTicketRequest, opts ...grpc.CallOption) (*CancelTicketResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(CancelTicketResponse) - err := c.cc.Invoke(ctx, GatewayService_CancelTicket_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *gatewayServiceClient) AcknowledgeMatch(ctx context.Context, in *AcknowledgeMatchRequest, opts ...grpc.CallOption) (*AcknowledgeMatchResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(AcknowledgeMatchResponse) - err := c.cc.Invoke(ctx, GatewayService_AcknowledgeMatch_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *gatewayServiceClient) SubmitMatchResult(ctx context.Context, in *SubmitMatchResultRequest, opts ...grpc.CallOption) (*SubmitMatchResultResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SubmitMatchResultResponse) - err := c.cc.Invoke(ctx, GatewayService_SubmitMatchResult_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *gatewayServiceClient) WatchMatchCandidates(ctx context.Context, in *WatchMatchCandidatesRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[WatchMatchCandidatesResponse], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &GatewayService_ServiceDesc.Streams[0], GatewayService_WatchMatchCandidates_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[WatchMatchCandidatesRequest, WatchMatchCandidatesResponse]{ClientStream: stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type GatewayService_WatchMatchCandidatesClient = grpc.ServerStreamingClient[WatchMatchCandidatesResponse] - -// GatewayServiceServer is the server API for GatewayService service. -// All implementations must embed UnimplementedGatewayServiceServer -// for forward compatibility. -type GatewayServiceServer interface { - CreateTicket(context.Context, *CreateTicketRequest) (*CreateTicketResponse, error) - CancelTicket(context.Context, *CancelTicketRequest) (*CancelTicketResponse, error) - AcknowledgeMatch(context.Context, *AcknowledgeMatchRequest) (*AcknowledgeMatchResponse, error) - SubmitMatchResult(context.Context, *SubmitMatchResultRequest) (*SubmitMatchResultResponse, error) - WatchMatchCandidates(*WatchMatchCandidatesRequest, grpc.ServerStreamingServer[WatchMatchCandidatesResponse]) error - mustEmbedUnimplementedGatewayServiceServer() -} - -// UnimplementedGatewayServiceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedGatewayServiceServer struct{} - -func (UnimplementedGatewayServiceServer) CreateTicket(context.Context, *CreateTicketRequest) (*CreateTicketResponse, error) { - return nil, status.Error(codes.Unimplemented, "method CreateTicket not implemented") -} -func (UnimplementedGatewayServiceServer) CancelTicket(context.Context, *CancelTicketRequest) (*CancelTicketResponse, error) { - return nil, status.Error(codes.Unimplemented, "method CancelTicket not implemented") -} -func (UnimplementedGatewayServiceServer) AcknowledgeMatch(context.Context, *AcknowledgeMatchRequest) (*AcknowledgeMatchResponse, error) { - return nil, status.Error(codes.Unimplemented, "method AcknowledgeMatch not implemented") -} -func (UnimplementedGatewayServiceServer) SubmitMatchResult(context.Context, *SubmitMatchResultRequest) (*SubmitMatchResultResponse, error) { - return nil, status.Error(codes.Unimplemented, "method SubmitMatchResult not implemented") -} -func (UnimplementedGatewayServiceServer) WatchMatchCandidates(*WatchMatchCandidatesRequest, grpc.ServerStreamingServer[WatchMatchCandidatesResponse]) error { - return status.Error(codes.Unimplemented, "method WatchMatchCandidates not implemented") -} -func (UnimplementedGatewayServiceServer) mustEmbedUnimplementedGatewayServiceServer() {} -func (UnimplementedGatewayServiceServer) testEmbeddedByValue() {} - -// UnsafeGatewayServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to GatewayServiceServer will -// result in compilation errors. -type UnsafeGatewayServiceServer interface { - mustEmbedUnimplementedGatewayServiceServer() -} - -func RegisterGatewayServiceServer(s grpc.ServiceRegistrar, srv GatewayServiceServer) { - // If the following call panics, it indicates UnimplementedGatewayServiceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } - s.RegisterService(&GatewayService_ServiceDesc, srv) -} - -func _GatewayService_CreateTicket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateTicketRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(GatewayServiceServer).CreateTicket(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: GatewayService_CreateTicket_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(GatewayServiceServer).CreateTicket(ctx, req.(*CreateTicketRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _GatewayService_CancelTicket_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CancelTicketRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(GatewayServiceServer).CancelTicket(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: GatewayService_CancelTicket_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(GatewayServiceServer).CancelTicket(ctx, req.(*CancelTicketRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _GatewayService_AcknowledgeMatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AcknowledgeMatchRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(GatewayServiceServer).AcknowledgeMatch(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: GatewayService_AcknowledgeMatch_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(GatewayServiceServer).AcknowledgeMatch(ctx, req.(*AcknowledgeMatchRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _GatewayService_SubmitMatchResult_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SubmitMatchResultRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(GatewayServiceServer).SubmitMatchResult(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: GatewayService_SubmitMatchResult_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(GatewayServiceServer).SubmitMatchResult(ctx, req.(*SubmitMatchResultRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _GatewayService_WatchMatchCandidates_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(WatchMatchCandidatesRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(GatewayServiceServer).WatchMatchCandidates(m, &grpc.GenericServerStream[WatchMatchCandidatesRequest, WatchMatchCandidatesResponse]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type GatewayService_WatchMatchCandidatesServer = grpc.ServerStreamingServer[WatchMatchCandidatesResponse] - -// GatewayService_ServiceDesc is the grpc.ServiceDesc for GatewayService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var GatewayService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "gateway.v1.GatewayService", - HandlerType: (*GatewayServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "CreateTicket", - Handler: _GatewayService_CreateTicket_Handler, - }, - { - MethodName: "CancelTicket", - Handler: _GatewayService_CancelTicket_Handler, - }, - { - MethodName: "AcknowledgeMatch", - Handler: _GatewayService_AcknowledgeMatch_Handler, - }, - { - MethodName: "SubmitMatchResult", - Handler: _GatewayService_SubmitMatchResult_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "WatchMatchCandidates", - Handler: _GatewayService_WatchMatchCandidates_Handler, - ServerStreams: true, - }, - }, - Metadata: "gateway/v1/service.proto", -} diff --git a/go.mod b/go.mod deleted file mode 100644 index 9015982..0000000 --- a/go.mod +++ /dev/null @@ -1,15 +0,0 @@ -module github.com/chaewonkong/matchmaker - -go 1.26 - -require ( - google.golang.org/grpc v1.79.2 - google.golang.org/protobuf v1.36.11 -) - -require ( - golang.org/x/net v0.48.0 // indirect - golang.org/x/sys v0.39.0 // indirect - golang.org/x/text v0.32.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 25c7c0f..0000000 --- a/go.sum +++ /dev/null @@ -1,38 +0,0 @@ -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= -golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= -golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= -google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= diff --git a/mise.toml b/mise.toml deleted file mode 100644 index ea92f40..0000000 --- a/mise.toml +++ /dev/null @@ -1,3 +0,0 @@ -[tools] -go = "1.26.1" -buf = "1.66.0" diff --git a/proto/common/v1/attribute.proto b/proto/common/v1/attribute.proto deleted file mode 100644 index c127536..0000000 --- a/proto/common/v1/attribute.proto +++ /dev/null @@ -1,20 +0,0 @@ -syntax = "proto3"; -package common.v1; -option go_package = "github.com/chaewonkong/matchmaker/gen/go/common/v1"; - -message Attribute { - oneof value { - double double_value = 1; - string string_value = 2; - bool bool_value = 3; - StringList string_list_value = 4; - DoubleList double_list_value = 5; - StringMap string_map_value = 6; - DoubleMap double_map_value = 7; - } -} - -message StringList { repeated string values = 1; } -message DoubleList { repeated double values = 1; } -message StringMap { map values = 1; } -message DoubleMap { map values = 1; } diff --git a/proto/common/v1/entity.proto b/proto/common/v1/entity.proto deleted file mode 100644 index a1dacd6..0000000 --- a/proto/common/v1/entity.proto +++ /dev/null @@ -1,28 +0,0 @@ -syntax = "proto3"; -package common.v1; -option go_package = "github.com/chaewonkong/matchmaker/gen/go/common/v1"; - -import "common/v1/attribute.proto"; - -message Player { - string player_id = 1; - map attributes = 2; -} - -message Ticket { - string ticket_id = 1; - repeated Player players = 2; - map attributes = 3; -} - -message Team { - string team_id = 1; - repeated Ticket tickets = 2; - map attributes = 3; -} - -message Match { - string match_id = 1; - repeated Team teams = 2; - map attributes = 3; -} diff --git a/proto/gateway/v1/messages.proto b/proto/gateway/v1/messages.proto deleted file mode 100644 index 991299d..0000000 --- a/proto/gateway/v1/messages.proto +++ /dev/null @@ -1,54 +0,0 @@ -syntax = "proto3"; -package gateway.v1; -option go_package = "github.com/chaewonkong/matchmaker/gen/go/gateway/v1"; - -import "common/v1/entity.proto"; - -message CreateTicketRequest { - repeated common.v1.Ticket tickets = 1; -} - -message CreateTicketResponse { - string ticket_id = 1; - double estimated_wait_time_sec = 2; -} - -message CancelTicketRequest { - string ticket_id = 1; -} - -message CancelTicketResponse { - string ticket_id = 1; -} - -message AcknowledgeMatchRequest { - string match_id = 1; -} - -message AcknowledgeMatchResponse { - string match_id = 1; -} - -message SubmitMatchResultRequest { - common.v1.Match match = 1; -} - -message SubmitMatchResultResponse { - string match_id = 1; -} - -message WatchMatchCandidatesRequest { - string ticket_id = 1; -} - -message WatchMatchCandidatesResponse { - oneof event { - common.v1.Match match = 1; - MatchExpired expired = 2; - } -} - - -message MatchExpired { - string reason = 1; -} \ No newline at end of file diff --git a/proto/gateway/v1/service.proto b/proto/gateway/v1/service.proto deleted file mode 100644 index 4e3acd6..0000000 --- a/proto/gateway/v1/service.proto +++ /dev/null @@ -1,15 +0,0 @@ -syntax = "proto3"; -package gateway.v1; -option go_package = "github.com/chaewonkong/matchmaker/gen/go/gateway/v1"; - -import "gateway/v1/messages.proto"; - -service GatewayService { - rpc CreateTicket(CreateTicketRequest) returns (CreateTicketResponse); - rpc CancelTicket(CancelTicketRequest) returns (CancelTicketResponse); - rpc AcknowledgeMatch(AcknowledgeMatchRequest) returns (AcknowledgeMatchResponse); - rpc SubmitMatchResult(SubmitMatchResultRequest) returns (SubmitMatchResultResponse); - - rpc WatchMatchCandidates(WatchMatchCandidatesRequest) - returns (stream WatchMatchCandidatesResponse); -} \ No newline at end of file