From bcb1673549c75b5231aac2a3d6a93efc3f6e38a5 Mon Sep 17 00:00:00 2001 From: Andrei Mihu Date: Mon, 6 Nov 2017 16:16:05 +0000 Subject: [PATCH] Add JSON encoding support over WebSocket connections. Merge #122 --- CHANGELOG.md | 3 +- main.go | 18 ++++- pkg/multicode/client_instance.go | 5 +- pkg/multicode/netcode_conn.go | 3 +- pkg/multicode/server.go | 5 +- server/config.go | 3 +- server/message_router.go | 64 ++++++++++++++---- server/pipeline_friend.go | 1 + server/pipeline_group.go | 1 + server/pipeline_match.go | 2 + server/pipeline_matchmake.go | 2 +- server/pipeline_runtime.go | 3 +- server/pipeline_topic.go | 1 + server/session.go | 9 ++- server/session_auth.go | 26 +++---- server/session_registry.go | 12 ++-- server/session_udp.go | 9 ++- server/session_ws.go | 112 +++++++++++++++++++++---------- server/tracker.go | 1 + tests/matchmaker_test.go | 2 +- 20 files changed, 197 insertions(+), 85 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca8fde014..06c238e8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,10 @@ The format is based on [keep a changelog](http://keepachangelog.com/) and this p ## [Unreleased] ### Added - RUDP transport option for client connections. +- Accept JSON payload over WebSocket connections. ### Changed -- Consistently Use strings for all ID types. +- Consistently use strings for all ID types. - Improve runtime hook lookup behaviour. ### [1.1.0] - 2017-10-17 diff --git a/main.go b/main.go index 4bb997b2b..77eef7133 100644 --- a/main.go +++ b/main.go @@ -32,11 +32,13 @@ import ( "nakama/pkg/social" + "runtime" + "github.com/armon/go-metrics" + "github.com/gogo/protobuf/jsonpb" _ "github.com/lib/pq" "github.com/satori/go.uuid" "go.uber.org/zap" - "runtime" ) const ( @@ -84,11 +86,21 @@ func main() { // Check migration status and log if the schema has diverged. cmd.MigrationStartupCheck(multiLogger, db) + jsonpbMarshaler := &jsonpb.Marshaler{ + EnumsAsInts: true, + EmitDefaults: false, + Indent: "", + OrigName: false, + } + jsonpbUnmarshaler := &jsonpb.Unmarshaler{ + AllowUnknownFields: false, + } + trackerService := server.NewTrackerService(config.GetName()) statsService := server.NewStatsService(jsonLogger, config, semver, trackerService, startedAt) matchmakerService := server.NewMatchmakerService(config.GetName()) sessionRegistry := server.NewSessionRegistry(jsonLogger, config, trackerService, matchmakerService) - messageRouter := server.NewMessageRouterService(sessionRegistry) + messageRouter := server.NewMessageRouterService(jsonpbMarshaler, sessionRegistry) presenceNotifier := server.NewPresenceNotifier(jsonLogger, config.GetName(), trackerService, messageRouter) trackerService.AddDiffListener(presenceNotifier.HandleDiff) notificationService := server.NewNotificationService(jsonLogger, db, trackerService, messageRouter, config.GetSocial().Notification) @@ -101,7 +113,7 @@ func main() { socialClient := social.NewClient(5 * time.Second) purchaseService := server.NewPurchaseService(jsonLogger, multiLogger, db, config.GetPurchase()) pipeline := server.NewPipeline(config, db, trackerService, matchmakerService, messageRouter, sessionRegistry, socialClient, runtimePool, purchaseService, notificationService) - authService := server.NewAuthenticationService(jsonLogger, config, db, statsService, sessionRegistry, socialClient, pipeline, runtimePool) + authService := server.NewAuthenticationService(jsonLogger, config, db, jsonpbMarshaler, jsonpbUnmarshaler, statsService, sessionRegistry, socialClient, pipeline, runtimePool) dashboardService := server.NewDashboardService(jsonLogger, multiLogger, semver, config, statsService) gaenabled := len(os.Getenv("NAKAMA_TELEMETRY")) < 1 diff --git a/pkg/multicode/client_instance.go b/pkg/multicode/client_instance.go index dfeef5043..a1930930c 100644 --- a/pkg/multicode/client_instance.go +++ b/pkg/multicode/client_instance.go @@ -34,11 +34,12 @@ package multicode import ( "errors" "fmt" - "github.com/wirepair/netcode" - "go.uber.org/zap" "net" "sync" "time" + + "github.com/wirepair/netcode" + "go.uber.org/zap" ) const ( diff --git a/pkg/multicode/netcode_conn.go b/pkg/multicode/netcode_conn.go index ad4833b2c..ac9f67f86 100644 --- a/pkg/multicode/netcode_conn.go +++ b/pkg/multicode/netcode_conn.go @@ -32,9 +32,10 @@ package multicode import ( + "net" + "github.com/wirepair/netcode" "go.uber.org/zap" - "net" ) type NetcodeData struct { diff --git a/pkg/multicode/server.go b/pkg/multicode/server.go index 51c6dc115..9006061f9 100644 --- a/pkg/multicode/server.go +++ b/pkg/multicode/server.go @@ -32,12 +32,13 @@ package multicode import ( - "github.com/wirepair/netcode" - "go.uber.org/zap" "net" "sync" "sync/atomic" "time" + + "github.com/wirepair/netcode" + "go.uber.org/zap" ) // Not a true connection limit, only used to initialise/allocate various buffer and data structure sizes. diff --git a/server/config.go b/server/config.go index cc6b20512..da63c8a1e 100644 --- a/server/config.go +++ b/server/config.go @@ -23,10 +23,11 @@ import ( "io/ioutil" "nakama/pkg/flags" + "net" + "github.com/go-yaml/yaml" "github.com/satori/go.uuid" "go.uber.org/zap" - "net" ) // Config interface is the Nakama Core configuration diff --git a/server/message_router.go b/server/message_router.go index 125a0d68d..7804456fe 100644 --- a/server/message_router.go +++ b/server/message_router.go @@ -15,6 +15,7 @@ package server import ( + "github.com/gogo/protobuf/jsonpb" "github.com/gogo/protobuf/proto" "go.uber.org/zap" ) @@ -25,12 +26,14 @@ type MessageRouter interface { } type messageRouterService struct { - registry *SessionRegistry + jsonpbMarshaler *jsonpb.Marshaler + registry *SessionRegistry } -func NewMessageRouterService(registry *SessionRegistry) *messageRouterService { +func NewMessageRouterService(jsonpbMarshaler *jsonpb.Marshaler, registry *SessionRegistry) *messageRouterService { return &messageRouterService{ - registry: registry, + jsonpbMarshaler: jsonpbMarshaler, + registry: registry, } } @@ -39,21 +42,56 @@ func (m *messageRouterService) Send(logger *zap.Logger, ps []Presence, msg proto return } - payload, err := proto.Marshal(msg) - if err != nil { - logger.Error("Could not marshall message to byte[]", zap.Error(err)) - return + // Group together target sessions by format. + jsonSessionIDs := make([]string, 0) + protobufSessionIDs := make([]string, 0) + for _, p := range ps { + switch p.Meta.Format { + case SessionFormatJson: + jsonSessionIDs = append(jsonSessionIDs, p.ID.SessionID) + default: + protobufSessionIDs = append(protobufSessionIDs, p.ID.SessionID) + } } - for _, p := range ps { - session := m.registry.Get(p.ID.SessionID) - if session != nil { + // Encode and route together for Protobuf format. + if len(protobufSessionIDs) != 0 { + payload, err := proto.Marshal(msg) + if err != nil { + logger.Error("Could not marshall message to byte[]", zap.Error(err)) + return + } + for _, sessionID := range protobufSessionIDs { + session := m.registry.Get(sessionID) + if session == nil { + logger.Warn("No session to route to", zap.Any("sid", sessionID)) + continue + } err := session.SendBytes(payload, reliable) if err != nil { - logger.Error("Failed to route to", zap.Any("p", p), zap.Error(err)) + logger.Error("Failed to route to", zap.Any("sid", sessionID), zap.Error(err)) + } + } + } + + // Encode and route together for JSON format. + if len(jsonSessionIDs) != 0 { + payload, err := m.jsonpbMarshaler.MarshalToString(msg) + if err != nil { + logger.Error("Could not marshall message to json", zap.Error(err)) + return + } + payloadBytes := []byte(payload) + for _, sessionID := range jsonSessionIDs { + session := m.registry.Get(sessionID) + if session == nil { + logger.Warn("No session to route to", zap.Any("sid", sessionID)) + continue + } + err := session.SendBytes(payloadBytes, reliable) + if err != nil { + logger.Error("Failed to route to", zap.Any("sid", sessionID), zap.Error(err)) } - } else { - logger.Warn("No session to route to", zap.Any("p", p)) } } } diff --git a/server/pipeline_friend.go b/server/pipeline_friend.go index 3f0c444b2..e28d76802 100644 --- a/server/pipeline_friend.go +++ b/server/pipeline_friend.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" + "github.com/lib/pq" "go.uber.org/zap" ) diff --git a/server/pipeline_group.go b/server/pipeline_group.go index d8d956113..f3d3003bf 100644 --- a/server/pipeline_group.go +++ b/server/pipeline_group.go @@ -25,6 +25,7 @@ import ( "strings" "fmt" + "github.com/lib/pq" "go.uber.org/zap" ) diff --git a/server/pipeline_match.go b/server/pipeline_match.go index a880c6301..62d698756 100644 --- a/server/pipeline_match.go +++ b/server/pipeline_match.go @@ -34,6 +34,7 @@ func (p *pipeline) matchCreate(logger *zap.Logger, session session, envelope *En p.tracker.Track(session.ID(), "match:"+matchID, session.UserID(), PresenceMeta{ Handle: handle, + Format: session.Format(), }) self := &UserPresence{ @@ -123,6 +124,7 @@ func (p *pipeline) matchJoin(logger *zap.Logger, session session, envelope *Enve p.tracker.Track(session.ID(), topic, session.UserID(), PresenceMeta{ Handle: handle, + Format: session.Format(), }) userPresences := make([]*UserPresence, len(ps)+1) diff --git a/server/pipeline_matchmake.go b/server/pipeline_matchmake.go index 92727df60..d52c404b4 100644 --- a/server/pipeline_matchmake.go +++ b/server/pipeline_matchmake.go @@ -54,7 +54,7 @@ func (p *pipeline) matchmakeAdd(logger *zap.Logger, session session, envelope *E } matchmakerProfile := &MatchmakerProfile{ - Meta: PresenceMeta{Handle: session.Handle()}, + Meta: PresenceMeta{Handle: session.Handle(), Format: session.Format()}, RequiredCount: int(requiredCount), Properties: properties, Filters: filters, diff --git a/server/pipeline_runtime.go b/server/pipeline_runtime.go index fe540b3a9..dd02d3567 100644 --- a/server/pipeline_runtime.go +++ b/server/pipeline_runtime.go @@ -15,9 +15,10 @@ package server import ( + "strings" + "github.com/yuin/gopher-lua" "go.uber.org/zap" - "strings" ) func (p *pipeline) rpc(logger *zap.Logger, session session, envelope *Envelope) { diff --git a/server/pipeline_topic.go b/server/pipeline_topic.go index 7bc287886..795cea8fa 100644 --- a/server/pipeline_topic.go +++ b/server/pipeline_topic.go @@ -137,6 +137,7 @@ func (p *pipeline) topicJoin(logger *zap.Logger, session session, envelope *Enve // Track the presence, and gather current member list. isNewPresence := p.tracker.Track(session.ID(), trackerTopic, session.UserID(), PresenceMeta{ Handle: handle, + Format: session.Format(), }) presences := p.tracker.ListByTopic(trackerTopic) diff --git a/server/session.go b/server/session.go index 2e52cf014..eb3bc411b 100644 --- a/server/session.go +++ b/server/session.go @@ -16,6 +16,13 @@ package server import "go.uber.org/zap" +type SessionFormat int + +const ( + SessionFormatProtobuf SessionFormat = 0 + SessionFormatJson = 1 +) + type session interface { Logger() *zap.Logger ID() string @@ -26,10 +33,10 @@ type session interface { Lang() string Expiry() int64 - Consume(func(logger *zap.Logger, session session, envelope *Envelope, reliable bool)) Unregister() + Format() SessionFormat Send(envelope *Envelope, reliable bool) error SendBytes(payload []byte, reliable bool) error diff --git a/server/session_auth.go b/server/session_auth.go index 115707f3a..1263a2685 100644 --- a/server/session_auth.go +++ b/server/session_auth.go @@ -29,10 +29,13 @@ import ( "strings" "time" + "nakama/pkg/httputil" "nakama/pkg/multicode" "nakama/pkg/social" "encoding/base64" + "net" + "github.com/dgrijalva/jwt-go" "github.com/gogo/protobuf/jsonpb" "github.com/gogo/protobuf/proto" @@ -44,8 +47,6 @@ import ( "github.com/yuin/gopher-lua" "go.uber.org/zap" "golang.org/x/crypto/bcrypt" - "nakama/pkg/httputil" - "net" ) const ( @@ -86,7 +87,7 @@ type authenticationService struct { } // NewAuthenticationService creates a new AuthenticationService -func NewAuthenticationService(logger *zap.Logger, config Config, db *sql.DB, statService StatsService, registry *SessionRegistry, socialClient *social.Client, pipeline *pipeline, runtimePool *RuntimePool) *authenticationService { +func NewAuthenticationService(logger *zap.Logger, config Config, db *sql.DB, jsonpbMarshaler *jsonpb.Marshaler, jsonpbUnmarshaler *jsonpb.Unmarshaler, statService StatsService, registry *SessionRegistry, socialClient *social.Client, pipeline *pipeline, runtimePool *RuntimePool) *authenticationService { a := &authenticationService{ logger: logger, config: config, @@ -107,15 +108,8 @@ func NewAuthenticationService(logger *zap.Logger, config Config, db *sql.DB, sta WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { return true }, }, - jsonpbMarshaler: &jsonpb.Marshaler{ - EnumsAsInts: true, - EmitDefaults: false, - Indent: "", - OrigName: false, - }, - jsonpbUnmarshaler: &jsonpb.Unmarshaler{ - AllowUnknownFields: false, - }, + jsonpbMarshaler: jsonpbMarshaler, + jsonpbUnmarshaler: jsonpbUnmarshaler, } a.configure() @@ -183,6 +177,12 @@ func (a *authenticationService) configure() { lang = "en" } + sformat := SessionFormatProtobuf + format := r.URL.Query().Get("format") + if format == "json" { + sformat = SessionFormatJson + } + conn, err := a.upgrader.Upgrade(w, r, nil) if err != nil { // http.Error is invoked automatically from within the Upgrade func @@ -190,7 +190,7 @@ func (a *authenticationService) configure() { return } - a.registry.addWS(uid, handle, lang, exp, conn, a.pipeline.processRequest) + a.registry.addWS(uid, handle, lang, sformat, exp, conn, a.jsonpbMarshaler, a.jsonpbUnmarshaler, a.pipeline.processRequest) }).Methods("GET", "OPTIONS") a.mux.HandleFunc("/runtime/{path}", func(w http.ResponseWriter, r *http.Request) { diff --git a/server/session_registry.go b/server/session_registry.go index 531e86d8c..c1fc099b5 100644 --- a/server/session_registry.go +++ b/server/session_registry.go @@ -17,9 +17,11 @@ package server import ( "sync" + "nakama/pkg/multicode" + + "github.com/gogo/protobuf/jsonpb" "github.com/gorilla/websocket" "go.uber.org/zap" - "nakama/pkg/multicode" ) // SessionRegistry maintains a list of sessions to their IDs. This is thread-safe. @@ -67,14 +69,14 @@ func (a *SessionRegistry) Get(sessionID string) session { return s } -func (a *SessionRegistry) addWS(userID string, handle string, lang string, expiry int64, conn *websocket.Conn, processRequest func(logger *zap.Logger, session session, envelope *Envelope, reliable bool)) { - s := NewWSSession(a.logger, a.config, userID, handle, lang, expiry, conn, a.remove) +func (a *SessionRegistry) addWS(userID string, handle string, lang string, format SessionFormat, expiry int64, conn *websocket.Conn, jsonpbMarshaler *jsonpb.Marshaler, jsonpbUnmarshaler *jsonpb.Unmarshaler, processRequest func(logger *zap.Logger, session session, envelope *Envelope, reliable bool)) { + s := NewWSSession(a.logger, a.config, userID, handle, lang, format, expiry, conn, jsonpbMarshaler, jsonpbUnmarshaler, a.remove) a.Lock() a.sessions[s.ID()] = s a.Unlock() // Register the session for notifications. - a.tracker.Track(s.ID(), "notifications:"+userID, userID, PresenceMeta{Handle: handle}) + a.tracker.Track(s.ID(), "notifications:"+userID, userID, PresenceMeta{Handle: handle, Format: s.Format()}) // Allow the server to begin processing incoming messages from this session. s.Consume(processRequest) @@ -87,7 +89,7 @@ func (a *SessionRegistry) addUDP(userID string, handle string, lang string, expi a.Unlock() // Register the session for notifications. - a.tracker.Track(s.ID(), "notifications:"+userID, userID, PresenceMeta{Handle: handle}) + a.tracker.Track(s.ID(), "notifications:"+userID, userID, PresenceMeta{Handle: handle, Format: s.Format()}) // Allow the server to begin processing incoming messages from this session. s.Consume(processRequest) diff --git a/server/session_udp.go b/server/session_udp.go index 43719d189..bb47fbd9b 100644 --- a/server/session_udp.go +++ b/server/session_udp.go @@ -22,9 +22,10 @@ import ( "github.com/gogo/protobuf/proto" + "nakama/pkg/multicode" + "go.uber.org/atomic" "go.uber.org/zap" - "nakama/pkg/multicode" ) type udpSession struct { @@ -158,11 +159,14 @@ func (s *udpSession) pingNow() bool { return true } +func (s *udpSession) Format() SessionFormat { + return SessionFormatProtobuf +} + func (s *udpSession) Send(envelope *Envelope, reliable bool) error { s.logger.Debug(fmt.Sprintf("Sending %T message", envelope.Payload), zap.String("cid", envelope.CollationId)) payload, err := proto.Marshal(envelope) - if err != nil { s.logger.Warn("Could not marshall Response to byte[]", zap.Error(err)) return err @@ -183,7 +187,6 @@ func (s *udpSession) SendBytes(payload []byte, reliable bool) error { err := s.clientInstance.Send(payload, reliable) if err != nil { s.logger.Warn("Could not write message", zap.Error(err)) - // TODO investigate whether we need to cleanupClosedConnection if write fails } return err diff --git a/server/session_ws.go b/server/session_ws.go index 817ac42e7..9fe992b77 100644 --- a/server/session_ws.go +++ b/server/session_ws.go @@ -23,46 +23,56 @@ import ( "github.com/gogo/protobuf/proto" "github.com/gorilla/websocket" + "bytes" + + "github.com/gogo/protobuf/jsonpb" "go.uber.org/atomic" "go.uber.org/zap" ) type wsSession struct { sync.Mutex - logger *zap.Logger - config Config - id string - userID string - handle *atomic.String - lang string - expiry int64 - stopped bool - conn *websocket.Conn - pingTicker *time.Ticker - pingTickerStopCh chan bool - unregister func(s session) + logger *zap.Logger + config Config + id string + userID string + handle *atomic.String + lang string + format SessionFormat + expiry int64 + stopped bool + conn *websocket.Conn + jsonpbMarshaler *jsonpb.Marshaler + jsonpbUnmarshaler *jsonpb.Unmarshaler + pingTicker *time.Ticker + pingTickerStopCh chan bool + unregister func(s session) } // NewWSSession creates a new session which encapsulates a WebSocket connection. -func NewWSSession(logger *zap.Logger, config Config, userID string, handle string, lang string, expiry int64, websocketConn *websocket.Conn, unregister func(s session)) session { +func NewWSSession(logger *zap.Logger, config Config, userID string, handle string, lang string, format SessionFormat, expiry int64, websocketConn *websocket.Conn, jsonpbMarshaler *jsonpb.Marshaler, + jsonpbUnmarshaler *jsonpb.Unmarshaler, unregister func(s session)) session { sessionID := generateNewId() sessionLogger := logger.With(zap.String("uid", userID), zap.String("sid", sessionID)) sessionLogger.Debug("New WS session connected") return &wsSession{ - logger: sessionLogger, - config: config, - id: sessionID, - userID: userID, - handle: atomic.NewString(handle), - lang: lang, - expiry: expiry, - conn: websocketConn, - stopped: false, - pingTicker: time.NewTicker(time.Duration(config.GetSocket().PingPeriodMs) * time.Millisecond), - pingTickerStopCh: make(chan bool), - unregister: unregister, + logger: sessionLogger, + config: config, + id: sessionID, + userID: userID, + handle: atomic.NewString(handle), + lang: lang, + format: format, + expiry: expiry, + conn: websocketConn, + jsonpbMarshaler: jsonpbMarshaler, + jsonpbUnmarshaler: jsonpbUnmarshaler, + stopped: false, + pingTicker: time.NewTicker(time.Duration(config.GetSocket().PingPeriodMs) * time.Millisecond), + pingTickerStopCh: make(chan bool), + unregister: unregister, } } @@ -117,9 +127,20 @@ func (s *wsSession) Consume(processRequest func(logger *zap.Logger, session sess } request := &Envelope{} - err = proto.Unmarshal(data, request) + switch s.format { + case SessionFormatJson: + err = s.jsonpbUnmarshaler.Unmarshal(bytes.NewReader(data), request) + default: + err = proto.Unmarshal(data, request) + } + if err != nil { - s.logger.Warn("Received malformed payload", zap.Any("data", data)) + if s.format == SessionFormatJson { + s.logger.Warn("Received malformed payload", zap.String("data", string(data))) + } else { + s.logger.Warn("Received malformed payload", zap.Any("data", data)) + } + s.Send(ErrorMessage(request.CollationId, UNRECOGNIZED_PAYLOAD, "Unrecognized payload"), true) } else { // TODO Add session-global context here to cancel in-progress operations when the session is closed. @@ -171,33 +192,50 @@ func (s *wsSession) pingNow() bool { return true } +func (s *wsSession) Format() SessionFormat { + return s.format +} + func (s *wsSession) Send(envelope *Envelope, reliable bool) error { + // NOTE: WebSocket sessions ignore the reliable flag and will always deliver messages reliably. s.logger.Debug(fmt.Sprintf("Sending %T message", envelope.Payload), zap.String("cid", envelope.CollationId)) - payload, err := proto.Marshal(envelope) - - if err != nil { - s.logger.Warn("Could not marshall Response to byte[]", zap.Error(err)) - return err + switch s.format { + case SessionFormatJson: + payload, err := s.jsonpbMarshaler.MarshalToString(envelope) + if err != nil { + s.logger.Warn("Could not marshall Response to json", zap.Error(err)) + return err + } + return s.SendBytes([]byte(payload), reliable) + default: + payload, err := proto.Marshal(envelope) + if err != nil { + s.logger.Warn("Could not marshall Response to byte[]", zap.Error(err)) + return err + } + return s.SendBytes(payload, reliable) } - - return s.SendBytes(payload, reliable) } func (s *wsSession) SendBytes(payload []byte, reliable bool) error { // NOTE: WebSocket sessions ignore the reliable flag and will always deliver messages reliably. - // TODO Improve on mutex usage here. s.Lock() defer s.Unlock() if s.stopped { return nil } + var err error s.conn.SetWriteDeadline(time.Now().Add(time.Duration(s.config.GetSocket().WriteWaitMs) * time.Millisecond)) - err := s.conn.WriteMessage(websocket.BinaryMessage, payload) + switch s.format { + case SessionFormatJson: + err = s.conn.WriteMessage(websocket.TextMessage, payload) + default: + err = s.conn.WriteMessage(websocket.BinaryMessage, payload) + } if err != nil { s.logger.Warn("Could not write message", zap.Error(err)) - // TODO investigate whether we need to cleanupClosedConnection if write fails } return err diff --git a/server/tracker.go b/server/tracker.go index 9044c5a40..7924e3842 100644 --- a/server/tracker.go +++ b/server/tracker.go @@ -26,6 +26,7 @@ type PresenceID struct { type PresenceMeta struct { Handle string + Format SessionFormat } type Presence struct { diff --git a/tests/matchmaker_test.go b/tests/matchmaker_test.go index 83cc4ce1f..55081c0e6 100644 --- a/tests/matchmaker_test.go +++ b/tests/matchmaker_test.go @@ -36,7 +36,7 @@ func add(props map[string]interface{}, filters map[string]server.MatchmakerFilte func addRequest(count int, props map[string]interface{}, filters map[string]server.MatchmakerFilter) (string, map[server.MatchmakerKey]*server.MatchmakerProfile, []*server.MatchmakerAcceptedProperty) { userID := uuid.NewV4().String() profile := &server.MatchmakerProfile{ - Meta: server.PresenceMeta{userID}, + Meta: server.PresenceMeta{Handle: userID, Format: server.SessionFormatProtobuf}, RequiredCount: count, Properties: props, Filters: filters, -- GitLab