diff --git a/CHANGELOG.md b/CHANGELOG.md index 0eb781f0c8..7385290279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [keep a changelog](http://keepachangelog.com) and this pr - Independent config flags for socket read and write buffer sizes. - Expose session fields in authoritative match join attempt contexts. - Add group ID to content of in-app notifications relating to groups. +- New runtime function to get a single match by ID. ### Changed - Replace metrics implementation. diff --git a/server/match_registry.go b/server/match_registry.go index 2a386c0a7b..91228ccd64 100644 --- a/server/match_registry.go +++ b/server/match_registry.go @@ -50,6 +50,7 @@ var ( MatchLabelMaxBytes = 2048 ErrCannotEncodeParams = errors.New("error creating match: cannot encode params") + ErrMatchIdInvalid = errors.New("match id invalid") ErrMatchLabelTooLong = errors.New("match label too long, must be 0-2048 bytes") ErrDeferredBroadcastFull = errors.New("too many deferred message broadcasts per tick") ) @@ -71,13 +72,11 @@ type MatchRegistry interface { CreateMatch(ctx context.Context, logger *zap.Logger, createFn RuntimeMatchCreateFunction, module string, params map[string]interface{}) (string, error) // Register and initialise a match that's ready to run. NewMatch(logger *zap.Logger, id uuid.UUID, core RuntimeMatchCore, stopped *atomic.Bool, params map[string]interface{}) (*MatchHandler, error) - // Return a match handler by ID, only from the local node. - GetMatch(id uuid.UUID) *MatchHandler + // Return a match by ID. + GetMatch(ctx context.Context, id string) (*api.Match, error) // Remove a tracked match and ensure all its presences are cleaned up. // Does not ensure the match process itself is no longer running, that must be handled separately. RemoveMatch(id uuid.UUID, stream PresenceStream) - // Get the label for a match. - GetMatchLabel(ctx context.Context, id uuid.UUID, node string) (string, error) // Update the label entry for a given match. UpdateMatchLabel(id uuid.UUID, label string) error // List (and optionally filter) currently running matches. @@ -191,12 +190,47 @@ func (r *LocalMatchRegistry) NewMatch(logger *zap.Logger, id uuid.UUID, core Run return match, nil } -func (r *LocalMatchRegistry) GetMatch(id uuid.UUID) *MatchHandler { - mh, ok := r.matches.Load(id) +func (r *LocalMatchRegistry) GetMatch(ctx context.Context, id string) (*api.Match, error) { + // Validate the match ID. + idComponents := strings.SplitN(id, ".", 2) + if len(idComponents) != 2 { + return nil, ErrMatchIdInvalid + } + matchID, err := uuid.FromString(idComponents[0]) + if err != nil { + return nil, ErrMatchIdInvalid + } + + // Relayed match. + if idComponents[1] == "" { + size := r.tracker.CountByStream(PresenceStream{Mode: StreamModeMatchRelayed, Subject: matchID}) + if size == 0 { + return nil, nil + } + + return &api.Match{ + MatchId: id, + Size: int32(size), + }, nil + } + + // Authoritative match. + if idComponents[1] != r.node { + return nil, nil + } + + mh, ok := r.matches.Load(matchID) if !ok { - return nil + return nil, nil } - return mh.(*MatchHandler) + handler := mh.(*MatchHandler) + + return &api.Match{ + MatchId: handler.IDStr, + Authoritative: true, + Label: &wrappers.StringValue{Value: handler.Label()}, + Size: int32(handler.PresenceList.Size()), + }, nil } func (r *LocalMatchRegistry) RemoveMatch(id uuid.UUID, stream PresenceStream) { @@ -220,20 +254,6 @@ func (r *LocalMatchRegistry) RemoveMatch(id uuid.UUID, stream PresenceStream) { } } -func (r *LocalMatchRegistry) GetMatchLabel(ctx context.Context, id uuid.UUID, node string) (string, error) { - if node != r.node { - // Match does not exist. - return "", nil - } - - mh, ok := r.matches.Load(id) - if !ok { - // Match does not exist, or has already ended. - return "", nil - } - return mh.(*MatchHandler).Label(), nil -} - func (r *LocalMatchRegistry) UpdateMatchLabel(id uuid.UUID, label string) error { if len(label) > MatchLabelMaxBytes { return ErrMatchLabelTooLong diff --git a/server/runtime_go_nakama.go b/server/runtime_go_nakama.go index 238e60c278..5ae9a07325 100644 --- a/server/runtime_go_nakama.go +++ b/server/runtime_go_nakama.go @@ -861,6 +861,10 @@ func (n *RuntimeGoNakamaModule) MatchCreate(ctx context.Context, module string, return n.matchRegistry.CreateMatch(ctx, n.logger, fn, module, params) } +func (n *RuntimeGoNakamaModule) MatchGet(ctx context.Context, id string) (*api.Match, error) { + return n.matchRegistry.GetMatch(ctx, id) +} + func (n *RuntimeGoNakamaModule) MatchList(ctx context.Context, limit int, authoritative bool, label string, minSize, maxSize *int, query string) ([]*api.Match, error) { authoritativeWrapper := &wrappers.BoolValue{Value: authoritative} var labelWrapper *wrappers.StringValue diff --git a/server/runtime_lua_nakama.go b/server/runtime_lua_nakama.go index 3dbd245c7e..8fc05c47d8 100644 --- a/server/runtime_lua_nakama.go +++ b/server/runtime_lua_nakama.go @@ -187,6 +187,7 @@ func (n *RuntimeLuaNakamaModule) Loader(l *lua.LState) int { "stream_send_raw": n.streamSendRaw, "session_disconnect": n.sessionDisconnect, "match_create": n.matchCreate, + "match_get": n.matchGet, "match_list": n.matchList, "notification_send": n.notificationSend, "notifications_send": n.notificationsSend, @@ -3217,6 +3218,35 @@ func (n *RuntimeLuaNakamaModule) matchCreate(l *lua.LState) int { return 1 } +func (n *RuntimeLuaNakamaModule) matchGet(l *lua.LState) int { + // Parse match ID. + id := l.CheckString(1) + + result, err := n.matchRegistry.GetMatch(l.Context(), id) + if err != nil { + l.RaiseError(fmt.Sprintf("failed to get match: %s", err.Error())) + return 0 + } + + if result == nil { + l.Push(lua.LNil) + return 1 + } + + match := l.CreateTable(0, 4) + match.RawSetString("match_id", lua.LString(result.MatchId)) + match.RawSetString("authoritative", lua.LBool(result.Authoritative)) + if result.Label == nil { + match.RawSetString("label", lua.LNil) + } else { + match.RawSetString("label", lua.LString(result.Label.Value)) + } + match.RawSetString("size", lua.LNumber(result.Size)) + + l.Push(match) + return 1 +} + func (n *RuntimeLuaNakamaModule) matchList(l *lua.LState) int { // Parse limit. limit := l.OptInt(1, 1)