diff --git a/server/api_user.go b/server/api_user.go index 8e37fdb01e6f53e52de1d70fc004685c7fe3809b..731de87268684f4093247088b2592d36aaebb034 100644 --- a/server/api_user.go +++ b/server/api_user.go @@ -47,15 +47,13 @@ func (s *ApiServer) GetUsers(ctx context.Context, in *api.GetUsersRequest) (*api } } - if in.GetIds() == nil && in.GetUsernames() == nil && in.GetFacebookIds() == nil { + if len(in.GetIds()) == 0 && len(in.GetUsernames()) == 0 && len(in.GetFacebookIds()) == 0 { return &api.Users{}, nil } - ids := make([]string, 0) - usernames := make([]string, 0) - facebookIDs := make([]string, 0) - - if in.GetIds() != nil { + var ids []string + if len(in.GetIds()) != 0 { + ids = make([]string, 0, len(in.GetIds())) for _, id := range in.GetIds() { if _, uuidErr := uuid.FromString(id); uuidErr != nil { return nil, status.Error(codes.InvalidArgument, "ID '"+id+"' is not a valid system ID.") @@ -65,15 +63,7 @@ func (s *ApiServer) GetUsers(ctx context.Context, in *api.GetUsersRequest) (*api } } - if in.GetUsernames() != nil { - usernames = in.GetUsernames() - } - - if in.GetFacebookIds() != nil { - facebookIDs = in.GetFacebookIds() - } - - users, err := GetUsers(ctx, s.logger, s.db, s.tracker, ids, usernames, facebookIDs) + users, err := GetUsers(ctx, s.logger, s.db, s.tracker, ids, in.GetUsernames(), in.GetFacebookIds()) if err != nil { return nil, status.Error(codes.Internal, "Error retrieving user accounts.") } diff --git a/server/console_api_explorer.go b/server/console_api_explorer.go index 0093554ed5991a26791a6f779650db8cd3863893..58cebd4a7ad08e4db0f42ac58ba6b846e1161832 100644 --- a/server/console_api_explorer.go +++ b/server/console_api_explorer.go @@ -145,13 +145,12 @@ func (s *ConsoleServer) extractApiCallContext(ctx context.Context, in *console.C } func (s *ConsoleServer) ListApiEndpoints(ctx context.Context, _ *empty.Empty) (*console.ApiEndpointList, error) { - - endpointNames := make([]string, 0) + endpointNames := make([]string, 0, len(s.rpcMethodCache.endpoints)) for name := range s.rpcMethodCache.endpoints { endpointNames = append(endpointNames, string(name)) } sort.Strings(endpointNames) - var endpoints []*console.ApiEndpointDescriptor + endpoints := make([]*console.ApiEndpointDescriptor, 0, len(endpointNames)) for _, name := range endpointNames { endpoint := s.rpcMethodCache.endpoints[MethodName(name)] endpoints = append(endpoints, &console.ApiEndpointDescriptor{ @@ -160,12 +159,12 @@ func (s *ConsoleServer) ListApiEndpoints(ctx context.Context, _ *empty.Empty) (* }) } - rpcs := make([]string, 0) + rpcs := make([]string, 0, len(s.rpcMethodCache.rpcs)) for name := range s.rpcMethodCache.rpcs { rpcs = append(rpcs, string(name)) } sort.Strings(rpcs) - var rpcEndpoints []*console.ApiEndpointDescriptor + rpcEndpoints := make([]*console.ApiEndpointDescriptor, 0, len(rpcs)) for _, name := range rpcs { endpoint := s.rpcMethodCache.rpcs[MethodName(name)] rpcEndpoints = append(rpcEndpoints, &console.ApiEndpointDescriptor{ diff --git a/server/console_user.go b/server/console_user.go index 962ae2f75c58443d5090462c900a1cffe676991e..33e9349eebc8585602b3dc937398c374d659fbf6 100644 --- a/server/console_user.go +++ b/server/console_user.go @@ -104,7 +104,7 @@ func (s *ConsoleServer) ListUsers(ctx context.Context, in *empty.Empty) (*consol } func (s *ConsoleServer) dbListConsoleUsers(ctx context.Context) ([]*console.UserList_User, error) { - result := make([]*console.UserList_User, 0) + result := make([]*console.UserList_User, 0, 10) rows, err := s.db.QueryContext(ctx, "SELECT username, email, role FROM console_user") if err != nil { return nil, err diff --git a/server/core_account.go b/server/core_account.go index 815712f0ae8a59c6eeb5394e807763e5660008de..4807aa8fdafe29bbb3082c086ff2f444592a962e 100644 --- a/server/core_account.go +++ b/server/core_account.go @@ -401,7 +401,7 @@ func ExportAccount(ctx context.Context, logger *zap.Logger, db *sql.DB, userID u return nil, status.Error(codes.Internal, "An error occurred while trying to export user data.") } - groups := make([]*api.Group, 0) + groups := make([]*api.Group, 0, 1) groupUsers, err := ListUserGroups(ctx, logger, db, userID, 0, nil, "") if err != nil { logger.Error("Could not fetch groups that belong to the user", zap.Error(err), zap.String("user_id", userID.String())) diff --git a/server/core_authenticate.go b/server/core_authenticate.go index 3db9e6cc25f86cdd9596a8b863f3652d89d0d13e..dad3324a8dec3534a6390d60b5aa45a319ce6ea9 100644 --- a/server/core_authenticate.go +++ b/server/core_authenticate.go @@ -800,8 +800,8 @@ func importFacebookFriends(ctx context.Context, logger *zap.Logger, db *sql.DB, if err != nil { return err } - statements := make([]string, 0) - params := make([]interface{}, 0) + statements := make([]string, 0, 10) + params := make([]interface{}, 0, 10) for rows.Next() { var id string err = rows.Scan(&id) @@ -852,7 +852,7 @@ func importFacebookFriends(ctx context.Context, logger *zap.Logger, db *sql.DB, } var id string - possibleFriendIDs := make([]uuid.UUID, 0) + possibleFriendIDs := make([]uuid.UUID, 0, len(statements)) for rows.Next() { err = rows.Scan(&id) if err != nil { @@ -868,7 +868,7 @@ func importFacebookFriends(ctx context.Context, logger *zap.Logger, db *sql.DB, _ = rows.Close() // If the transaction is retried ensure we wipe any friend user IDs that may have been recorded by previous attempts. - friendUserIDs = make([]uuid.UUID, 0) + friendUserIDs = make([]uuid.UUID, 0, len(possibleFriendIDs)) for _, friendID := range possibleFriendIDs { position := fmt.Sprintf("%v", time.Now().UTC().UnixNano()) diff --git a/server/core_friend.go b/server/core_friend.go index fdc054d8a1e3de7fd68438a1c8c43bc801950d1f..ee4038ff9cf18b6c8ba947d6f6258228e07bf34b 100644 --- a/server/core_friend.go +++ b/server/core_friend.go @@ -57,7 +57,7 @@ FROM users, user_edge WHERE id = destination_id AND source_id = $1` } defer rows.Close() - friends := make([]*api.Friend, 0) + friends := make([]*api.Friend, 0, 10) for rows.Next() { var id string diff --git a/server/core_group.go b/server/core_group.go index da5385c3bae257c4b84b4d7c09cd61622a109c53..14ffb00b70088b553e170cbc687cb5453ecf2259 100644 --- a/server/core_group.go +++ b/server/core_group.go @@ -605,7 +605,7 @@ func AddGroupUsers(ctx context.Context, logger *zap.Logger, db *sql.DB, router M if err := ExecuteInTx(ctx, tx, func() error { // If the transaction is retried ensure we wipe any notifications/messages that may have been prepared by previous attempts. notifications = make(map[uuid.UUID][]*api.Notification, len(userIDs)) - messages = make([]*api.ChannelMessage, len(userIDs)) + messages = make([]*api.ChannelMessage, 0, len(userIDs)) for _, uid := range userIDs { if uid == caller { @@ -762,7 +762,7 @@ func BanGroupUsers(ctx context.Context, logger *zap.Logger, db *sql.DB, router M if err := ExecuteInTx(ctx, tx, func() error { // If the transaction is retried ensure we wipe any messages that may have been prepared by previous attempts. - messages = make([]*api.ChannelMessage, len(userIDs)) + messages = make([]*api.ChannelMessage, 0, len(userIDs)) // Position to use for new banned edges. position := time.Now().UTC().UnixNano() @@ -928,7 +928,7 @@ func KickGroupUsers(ctx context.Context, logger *zap.Logger, db *sql.DB, router if err := ExecuteInTx(ctx, tx, func() error { // If the transaction is retried ensure we wipe any messages that may have been prepared by previous attempts. - messages = make([]*api.ChannelMessage, len(userIDs)) + messages = make([]*api.ChannelMessage, 0, len(userIDs)) for _, uid := range userIDs { // Shouldn't kick self. @@ -1095,7 +1095,7 @@ func PromoteGroupUsers(ctx context.Context, logger *zap.Logger, db *sql.DB, rout if err := ExecuteInTx(ctx, tx, func() error { // If the transaction is retried ensure we wipe any messages that may have been prepared by previous attempts. - messages = make([]*api.ChannelMessage, len(userIDs)) + messages = make([]*api.ChannelMessage, 0, len(userIDs)) for _, uid := range userIDs { if uid == caller { @@ -1236,7 +1236,7 @@ func DemoteGroupUsers(ctx context.Context, logger *zap.Logger, db *sql.DB, route if err := ExecuteInTx(ctx, tx, func() error { // If the transaction is retried ensure we wipe any messages that may have been prepared by previous attempts. - messages = make([]*api.ChannelMessage, len(userIDs)) + messages = make([]*api.ChannelMessage, 0, len(userIDs)) for _, uid := range userIDs { if uid == caller { @@ -1734,7 +1734,7 @@ LIMIT $1` func groupConvertRows(rows *sql.Rows) ([]*api.Group, error) { defer rows.Close() - groups := make([]*api.Group, 0) + groups := make([]*api.Group, 0, 10) for rows.Next() { var id string @@ -1922,9 +1922,9 @@ WHERE group_edge.destination_id = $1` return err } - deleteGroupsAndRelationships := make([]uuid.UUID, 0) - deleteRelationships := make([]uuid.UUID, 0) - checkForOtherSuperadmins := make([]uuid.UUID, 0) + deleteGroupsAndRelationships := make([]uuid.UUID, 0, 5) + deleteRelationships := make([]uuid.UUID, 0, 5) + checkForOtherSuperadmins := make([]uuid.UUID, 0, 5) for rows.Next() { var id string diff --git a/server/core_notification.go b/server/core_notification.go index 3dc94781f14c96e45dbae2cfe572bd1435658470..2840538941e9ffc32a81d66c9065c237ec607d88 100644 --- a/server/core_notification.go +++ b/server/core_notification.go @@ -47,7 +47,7 @@ type notificationCacheableCursor struct { } func NotificationSend(ctx context.Context, logger *zap.Logger, db *sql.DB, messageRouter MessageRouter, notifications map[uuid.UUID][]*api.Notification) error { - persistentNotifications := make(map[uuid.UUID][]*api.Notification) + persistentNotifications := make(map[uuid.UUID][]*api.Notification, len(notifications)) for userID, ns := range notifications { for _, userNotification := range ns { // Select persistent notifications for storage. @@ -108,7 +108,7 @@ ORDER BY create_time ASC, id ASC`+limitQuery, params...) return nil, err } - notifications := make([]*api.Notification, 0) + notifications := make([]*api.Notification, 0, limit) var lastCreateTime int64 for rows.Next() { no := &api.Notification{Persistent: true, CreateTime: ×tamp.Timestamp{}} diff --git a/server/core_storage.go b/server/core_storage.go index d1f9785774e4d782f67a9d9729ddd3ed433561c2..a19f8350ddf1f791129170b31cdc145b8c467413 100644 --- a/server/core_storage.go +++ b/server/core_storage.go @@ -309,7 +309,7 @@ WHERE user_id = $1` } defer rows.Close() - funcObjects := make([]*api.StorageObject, 0) + funcObjects := make([]*api.StorageObject, 0, 10) for rows.Next() { o := &api.StorageObject{CreateTime: ×tamp.Timestamp{}, UpdateTime: ×tamp.Timestamp{}} var createTime pgtype.Timestamptz @@ -391,7 +391,7 @@ func storageListObjects(rows *sql.Rows, limit int) (*api.StorageObjectList, erro } func StorageReadObjects(ctx context.Context, logger *zap.Logger, db *sql.DB, caller uuid.UUID, objectIDs []*api.ReadStorageObjectId) (*api.StorageObjects, error) { - params := make([]interface{}, 0) + params := make([]interface{}, 0, len(objectIDs)*3) whereClause := "" for _, id := range objectIDs { @@ -436,7 +436,7 @@ WHERE } defer rows.Close() - funcObjects := &api.StorageObjects{Objects: make([]*api.StorageObject, 0)} + funcObjects := &api.StorageObjects{Objects: make([]*api.StorageObject, 0, len(objectIDs))} for rows.Next() { o := &api.StorageObject{CreateTime: ×tamp.Timestamp{}, UpdateTime: ×tamp.Timestamp{}} var createTime pgtype.Timestamptz diff --git a/server/core_user.go b/server/core_user.go index 5497332b37b0efdeeb485941cfd860e86977d4a7..3dc3de51988a4cabf233a2b83be9fc997c10e3ed 100644 --- a/server/core_user.go +++ b/server/core_user.go @@ -208,7 +208,7 @@ func convertUser(tracker Tracker, rows *sql.Rows) (*api.User, error) { } func fetchUserID(ctx context.Context, db *sql.DB, usernames []string) ([]string, error) { - ids := make([]string, 0) + ids := make([]string, 0, len(usernames)) if len(usernames) == 0 { return ids, nil } diff --git a/server/core_wallet.go b/server/core_wallet.go index 4a3ac31684a5b3d9f304d80ea90a4771f4b740f4..f25be8c3302f67c577c0f8ec4cbd6587376509da 100644 --- a/server/core_wallet.go +++ b/server/core_wallet.go @@ -307,7 +307,7 @@ func ListWalletLedger(ctx context.Context, logger *zap.Logger, db *sql.DB, userI } var outgoingCursor *walletLedgerListCursor - results := make([]*walletLedger, 0) + results := make([]*walletLedger, 0, 10) params := []interface{}{userID} query := "SELECT id, changeset, metadata, create_time, update_time FROM wallet_ledger WHERE user_id = $1::UUID" if incomingCursor != nil { diff --git a/server/leaderboard_cache.go b/server/leaderboard_cache.go index 62fcaadcf8cce2e5ea31cccfb40ae32de9c67b66..71586d72c0701bff0c885de2d1050cd5e4661986 100644 --- a/server/leaderboard_cache.go +++ b/server/leaderboard_cache.go @@ -190,8 +190,8 @@ FROM leaderboard` return err } - leaderboards := make(map[string]*Leaderboard) - tournamentList := make([]*Leaderboard, 0) + leaderboards := make(map[string]*Leaderboard, 10) + tournamentList := make([]*Leaderboard, 0, 10) for rows.Next() { var id string @@ -622,7 +622,9 @@ func (l *LocalLeaderboardCache) Delete(ctx context.Context, id string) error { if leaderboard.IsTournament() { for i, currentLeaderboard := range l.tournamentList { if currentLeaderboard.Id == id { - l.tournamentList = append(l.tournamentList[:i], l.tournamentList[i+1:]...) + copy(l.tournamentList[i:], l.tournamentList[i+1:]) + l.tournamentList[len(l.tournamentList)-1] = nil + l.tournamentList = l.tournamentList[:len(l.tournamentList)-1] break } } @@ -638,7 +640,9 @@ func (l *LocalLeaderboardCache) Remove(id string) { if leaderboard.IsTournament() { for i, currentLeaderboard := range l.tournamentList { if currentLeaderboard.Id == id { - l.tournamentList = append(l.tournamentList[:i], l.tournamentList[i+1:]...) + copy(l.tournamentList[i:], l.tournamentList[i+1:]) + l.tournamentList[len(l.tournamentList)-1] = nil + l.tournamentList = l.tournamentList[:len(l.tournamentList)-1] break } } diff --git a/server/leaderboard_rank_cache.go b/server/leaderboard_rank_cache.go index 2104624daa482cc37d54c5459b6c905151d6945c..9276848be80b5434f328800f0b47224e970605da 100644 --- a/server/leaderboard_rank_cache.go +++ b/server/leaderboard_rank_cache.go @@ -117,12 +117,11 @@ func NewLocalLeaderboardRankCache(startupLogger *zap.Logger, db *sql.DB, config startupLogger.Info("Initializing leaderboard rank cache") - skippedLeaderboards := make([]string, 0) - cachedLeaderboards := make([]string, 0) - nowTime := time.Now().UTC() + skippedLeaderboards := make([]string, 0, 10) leaderboards := leaderboardCache.GetAllLeaderboards() + cachedLeaderboards := make([]string, 0, len(leaderboards)) for _, leaderboard := range leaderboards { if _, ok := cache.blacklistIds[leaderboard.Id]; ok { startupLogger.Debug("Skip caching leaderboard ranks", zap.String("leaderboard_id", leaderboard.Id)) diff --git a/server/leaderboard_scheduler.go b/server/leaderboard_scheduler.go index e92b164cbb4619ece107cae39f2e298a7ffb4be6..44afbb17b15e1039183975d955b4fbce12cbde38 100644 --- a/server/leaderboard_scheduler.go +++ b/server/leaderboard_scheduler.go @@ -185,8 +185,8 @@ func (ls *LocalLeaderboardScheduler) Update() { earliestEndActive := int64(-1) earliestExpiry := int64(-1) - endActiveLeaderboardIds := make([]string, 0) - expiryLeaderboardIds := make([]string, 0) + endActiveLeaderboardIds := make([]string, 0, 1) + expiryLeaderboardIds := make([]string, 0, 1) for _, l := range leaderboards { if l.Duration > 0 { diff --git a/server/match_handler.go b/server/match_handler.go index e7840520f3cee1b0ccef35a486b1be07f9705f3f..31361bd561a6cd3501133974711ce94353abaf80 100644 --- a/server/match_handler.go +++ b/server/match_handler.go @@ -199,9 +199,9 @@ func NewMatchHandler(logger *zap.Logger, config Config, sessionRegistry SessionR // Disconnect all clients currently connected to the server. func (mh *MatchHandler) disconnectClients() { - presenceIDs := mh.PresenceList.ListPresenceIDs() - for _, presenceID := range presenceIDs { - _ = mh.sessionRegistry.Disconnect(context.Background(), presenceID.SessionID) + presences := mh.PresenceList.ListPresences() + for _, presence := range presences { + _ = mh.sessionRegistry.Disconnect(context.Background(), presence.SessionID) } } diff --git a/server/match_presence.go b/server/match_presence.go index de02c99d2cf51125ac0a46c81e20ccff3be6878b..b0c5a8f3147aea963697cffbdfcc5017be06b837 100644 --- a/server/match_presence.go +++ b/server/match_presence.go @@ -15,9 +15,10 @@ package server import ( + "sync" + "github.com/gofrs/uuid" "go.uber.org/atomic" - "sync" ) // Represents routing and identify information for a single match participant. @@ -87,7 +88,7 @@ func (m *MatchJoinMarkerList) Mark(sessionID uuid.UUID) { } func (m *MatchJoinMarkerList) ClearExpired(tick int64) []*MatchPresence { - presences := make([]*MatchPresence, 0) + presences := make([]*MatchPresence, 0, 1) m.Lock() for sessionID, joinMarker := range m.joinMarkers { if joinMarker.expiryTick <= tick { @@ -102,9 +103,10 @@ func (m *MatchJoinMarkerList) ClearExpired(tick int64) []*MatchPresence { // Maintains the match presences for routing and validation purposes. type MatchPresenceList struct { sync.RWMutex - size *atomic.Int32 - presences []*MatchPresenceListItem - presenceMap map[uuid.UUID]string + size *atomic.Int32 + presences []*MatchPresenceListItem + presenceMap map[uuid.UUID]string + presencesRead *atomic.Value } type MatchPresenceListItem struct { @@ -113,11 +115,14 @@ type MatchPresenceListItem struct { } func NewMatchPresenceList() *MatchPresenceList { - return &MatchPresenceList{ - size: atomic.NewInt32(0), - presences: make([]*MatchPresenceListItem, 0, 10), - presenceMap: make(map[uuid.UUID]string, 10), + m := &MatchPresenceList{ + size: atomic.NewInt32(0), + presences: make([]*MatchPresenceListItem, 0, 10), + presenceMap: make(map[uuid.UUID]string, 10), + presencesRead: &atomic.Value{}, } + m.presencesRead.Store(make([]*MatchPresence, 0)) + return m } func (m *MatchPresenceList) Join(joins []*MatchPresence) []*MatchPresence { @@ -136,8 +141,16 @@ func (m *MatchPresenceList) Join(joins []*MatchPresence) []*MatchPresence { processed = append(processed, join) } } + l := len(processed) + if l != 0 { + presencesRead := make([]*MatchPresence, 0, len(m.presences)) + for _, presence := range m.presences { + presencesRead = append(presencesRead, presence.Presence) + } + m.presencesRead.Store(presencesRead) + } m.Unlock() - if l := len(processed); l != 0 { + if l != 0 { m.size.Add(int32(l)) } return processed @@ -150,7 +163,9 @@ func (m *MatchPresenceList) Leave(leaves []*MatchPresence) []*MatchPresence { if _, ok := m.presenceMap[leave.SessionID]; ok { for i, presence := range m.presences { if presence.PresenceID.SessionID == leave.SessionID && presence.PresenceID.Node == leave.Node { - m.presences = append(m.presences[:i], m.presences[i+1:]...) + m.presences[i] = m.presences[len(m.presences)-1] + m.presences[len(m.presences)-1] = nil + m.presences = m.presences[:len(m.presences)-1] break } } @@ -158,8 +173,16 @@ func (m *MatchPresenceList) Leave(leaves []*MatchPresence) []*MatchPresence { processed = append(processed, leave) } } + l := len(processed) + if l != 0 { + presencesRead := make([]*MatchPresence, 0, len(m.presences)) + for _, presence := range m.presences { + presencesRead = append(presencesRead, presence.Presence) + } + m.presencesRead.Store(presencesRead) + } m.Unlock() - if l := len(processed); l != 0 { + if l != 0 { m.size.Sub(int32(l)) } return processed @@ -175,24 +198,22 @@ func (m *MatchPresenceList) Contains(presence *PresenceID) bool { return found } -func (m *MatchPresenceList) ListPresenceIDs() []*PresenceID { +func (m *MatchPresenceList) FilterPresenceIDs(ids []*PresenceID) []*PresenceID { m.RLock() - list := make([]*PresenceID, 0, len(m.presences)) - for _, presence := range m.presences { - list = append(list, presence.PresenceID) + for i := 0; i < len(ids); i++ { + if node, ok := m.presenceMap[ids[i].SessionID]; !ok || node != ids[i].Node { + ids[i] = ids[len(ids)-1] + ids[len(ids)-1] = nil + ids = ids[:len(ids)-1] + i-- + } } m.RUnlock() - return list + return ids } func (m *MatchPresenceList) ListPresences() []*MatchPresence { - m.RLock() - list := make([]*MatchPresence, 0, len(m.presences)) - for _, presence := range m.presences { - list = append(list, presence.Presence) - } - m.RUnlock() - return list + return m.presencesRead.Load().([]*MatchPresence) } func (m *MatchPresenceList) Size() int { diff --git a/server/message_router.go b/server/message_router.go index d857be8a87b0c7668e9af98d2c7f553f45704bbe..f17a60223c91f83d6aea3babafd66d964b93ad66 100644 --- a/server/message_router.go +++ b/server/message_router.go @@ -25,6 +25,7 @@ import ( // Deferred message expected to be batched with other deferred messages. // All deferred messages in a batch are expected to be for the same stream/mode and share a logger context. type DeferredMessage struct { + Stream *PresenceStream PresenceIDs []*PresenceID Envelope *rtapi.Envelope Reliable bool @@ -107,6 +108,10 @@ func (r *LocalMessageRouter) SendToStream(logger *zap.Logger, stream PresenceStr func (r *LocalMessageRouter) SendDeferred(logger *zap.Logger, messages []*DeferredMessage) { for _, message := range messages { - r.SendToPresenceIDs(logger, message.PresenceIDs, message.Envelope, message.Reliable) + if message.Stream != nil { + r.SendToStream(logger, *message.Stream, message.Envelope, message.Reliable) + } else { + r.SendToPresenceIDs(logger, message.PresenceIDs, message.Envelope, message.Reliable) + } } } diff --git a/server/party_handler.go b/server/party_handler.go index 4e64a4a03044a6f6deede48823b52eb1bc692237..1caf658813e3dbd30dd4089e65e4d75b9fdff727 100644 --- a/server/party_handler.go +++ b/server/party_handler.go @@ -226,8 +226,14 @@ func (p *PartyHandler) Leave(presences []*Presence) { } for i := 0; i < len(p.members); i++ { if p.members[i].SessionID == presence.ID.SessionID && p.members[i].Node == presence.ID.Node { - p.members = append(p.members[:i], p.members[i+1:]...) - p.memberUserPresences = append(p.memberUserPresences[:i], p.memberUserPresences[i+1:]...) + copy(p.members[i:], p.members[i+1:]) + p.members[len(p.members)-1] = nil + p.members = p.members[:len(p.members)-1] + + copy(p.memberUserPresences[i:], p.memberUserPresences[i+1:]) + p.memberUserPresences[len(p.memberUserPresences)-1] = nil + p.memberUserPresences = p.memberUserPresences[:len(p.memberUserPresences)-1] + break } } @@ -336,8 +342,15 @@ func (p *PartyHandler) Accept(sessionID, node string, presence *rtapi.UserPresen for i, joinRequest := range p.joinRequests { if joinRequest.ID.SessionID.String() == presence.SessionId && joinRequest.UserID.String() == presence.UserId && joinRequest.GetUsername() == presence.Username { joinRequestPresence = joinRequest - p.joinRequests = append(p.joinRequests[:i], p.joinRequests[i+1:]...) - p.joinRequestUserPresences = append(p.joinRequestUserPresences[:i], p.joinRequestUserPresences[i+1:]...) + + copy(p.joinRequests[i:], p.joinRequests[i+1:]) + p.joinRequests[len(p.joinRequests)-1] = nil + p.joinRequests = p.joinRequests[:len(p.joinRequests)-1] + + copy(p.joinRequestUserPresences[i:], p.joinRequestUserPresences[i+1:]) + p.joinRequestUserPresences[len(p.joinRequestUserPresences)-1] = nil + p.joinRequestUserPresences = p.joinRequestUserPresences[:len(p.joinRequestUserPresences)-1] + break } } @@ -385,8 +398,15 @@ func (p *PartyHandler) Remove(sessionID, node string, presence *rtapi.UserPresen for i, memberUserPresence := range p.memberUserPresences { if memberUserPresence.SessionId == presence.SessionId && memberUserPresence.UserId == presence.UserId && memberUserPresence.Username == presence.Username { removeMember = memberUserPresence - p.memberUserPresences = append(p.memberUserPresences[:i], p.memberUserPresences[i+1:]...) - p.members = append(p.members[:i], p.members[i+1:]...) + + copy(p.memberUserPresences[i:], p.memberUserPresences[i+1:]) + p.memberUserPresences[len(p.memberUserPresences)-1] = nil + p.memberUserPresences = p.memberUserPresences[:len(p.memberUserPresences)-1] + + copy(p.members[i:], p.members[i+1:]) + p.members[len(p.members)-1] = nil + p.members = p.members[:len(p.members)-1] + break } } @@ -395,8 +415,14 @@ func (p *PartyHandler) Remove(sessionID, node string, presence *rtapi.UserPresen for i, joinRequest := range p.joinRequests { if joinRequest.ID.SessionID.String() == presence.SessionId && joinRequest.UserID.String() == presence.UserId && joinRequest.GetUsername() == presence.Username { // Rejected join requests do not require stream removal, they were never part of the stream to begin with. - p.joinRequests = append(p.joinRequests[:i], p.joinRequests[i+1:]...) - p.joinRequestUserPresences = append(p.joinRequestUserPresences[:i], p.joinRequestUserPresences[i+1:]...) + copy(p.joinRequests[i:], p.joinRequests[i+1:]) + p.joinRequests[len(p.joinRequests)-1] = nil + p.joinRequests = p.joinRequests[:len(p.joinRequests)-1] + + copy(p.joinRequestUserPresences[i:], p.joinRequestUserPresences[i+1:]) + p.joinRequestUserPresences[len(p.joinRequestUserPresences)-1] = nil + p.joinRequestUserPresences = p.joinRequestUserPresences[:len(p.joinRequestUserPresences)-1] + p.Unlock() return nil } diff --git a/server/runtime.go b/server/runtime.go index 182497ed4e1530cba126422fb47bb78f6fead3c6..3c21613f1dafc6cd5cf2072c497c3791ac226c86 100644 --- a/server/runtime.go +++ b/server/runtime.go @@ -493,7 +493,7 @@ func GetRuntimePaths(logger *zap.Logger, rootPath string) ([]string, error) { return nil, err } - paths := make([]string, 0) + paths := make([]string, 0, 5) if err := filepath.Walk(rootPath, func(path string, f os.FileInfo, err error) error { if err != nil { logger.Error("Error listing runtime path", zap.String("path", path), zap.Error(err)) diff --git a/server/runtime_go_match_core.go b/server/runtime_go_match_core.go index 3dc16ef4f6a4c293c3ab1c614bcf98b83042b229..ec4498440129e24f727c110679dcaaea17858480 100644 --- a/server/runtime_go_match_core.go +++ b/server/runtime_go_match_core.go @@ -208,11 +208,15 @@ func (r *RuntimeGoMatchCore) BroadcastMessage(opCode int64, data []byte, presenc if err != nil { return err } - if len(presenceIDs) == 0 { + if msg == nil { return nil } - r.router.SendToPresenceIDs(r.logger, presenceIDs, msg, reliable) + if len(presenceIDs) == 0 { + r.router.SendToStream(r.logger, r.stream, msg, reliable) + } else { + r.router.SendToPresenceIDs(r.logger, presenceIDs, msg, reliable) + } return nil } @@ -226,10 +230,18 @@ func (r *RuntimeGoMatchCore) BroadcastMessageDeferred(opCode int64, data []byte, if err != nil { return err } - if len(presenceIDs) == 0 { + if msg == nil { return nil } + if len(presenceIDs) == 0 { + return r.deferMessageFn(&DeferredMessage{ + Stream: &r.stream, + Envelope: msg, + Reliable: reliable, + }) + } + return r.deferMessageFn(&DeferredMessage{ PresenceIDs: presenceIDs, Envelope: msg, @@ -248,7 +260,7 @@ func (r *RuntimeGoMatchCore) validateBroadcast(opCode int64, data []byte, presen presenceIDs = make([]*PresenceID, size) for i, presence := range presences { if presence == nil { - continue + return nil, nil, errors.New("Presence was nil") } sessionID, err := uuid.FromString(presence.GetSessionId()) @@ -287,6 +299,10 @@ func (r *RuntimeGoMatchCore) validateBroadcast(opCode int64, data []byte, presen if presenceIDs != nil { // Ensure specific presences actually exist to prevent sending bogus messages to arbitrary users. if len(presenceIDs) == 1 { + if presences == nil { + // Should not happen. + return nil, nil, nil + } // Shorter validation cycle if there is only one intended recipient. _, err := uuid.FromString(presences[0].GetUserId()) if err != nil { @@ -298,26 +314,7 @@ func (r *RuntimeGoMatchCore) validateBroadcast(opCode int64, data []byte, presen } } else { // Validate multiple filtered recipients. - actualPresenceIDs := r.presenceList.ListPresenceIDs() - for i := 0; i < len(presenceIDs); i++ { - found := false - presenceID := presenceIDs[i] - for j := 0; j < len(actualPresenceIDs); j++ { - if actual := actualPresenceIDs[j]; presenceID.SessionID == actual.SessionID && presenceID.Node == actual.Node { - // If it matches, drop it. - actualPresenceIDs[j] = actualPresenceIDs[len(actualPresenceIDs)-1] - actualPresenceIDs = actualPresenceIDs[:len(actualPresenceIDs)-1] - found = true - break - } - } - if !found { - // If this presence wasn't in the filters, it's not needed. - presenceIDs[i] = presenceIDs[len(presenceIDs)-1] - presenceIDs = presenceIDs[:len(presenceIDs)-1] - i-- - } - } + presenceIDs = r.presenceList.FilterPresenceIDs(presenceIDs) if len(presenceIDs) == 0 { // None of the target presenceIDs existed in the list of match members. return nil, nil, nil @@ -333,10 +330,6 @@ func (r *RuntimeGoMatchCore) validateBroadcast(opCode int64, data []byte, presen Reliable: reliable, }}} - if presenceIDs == nil { - presenceIDs = r.presenceList.ListPresenceIDs() - } - return presenceIDs, msg, nil } diff --git a/server/runtime_go_nakama.go b/server/runtime_go_nakama.go index 34f3f1445dc47a1814586ee624a6c56e9ee10de9..90ee0b553309a91e0e3d719a9564da35a10728ee 100644 --- a/server/runtime_go_nakama.go +++ b/server/runtime_go_nakama.go @@ -1138,7 +1138,7 @@ func (n *RuntimeGoNakamaModule) NotificationsSend(ctx context.Context, notificat no := ns[uid] if no == nil { - no = make([]*api.Notification, 0) + no = make([]*api.Notification, 0, 1) } no = append(no, &api.Notification{ Id: uuid.Must(uuid.NewV4()).String(), diff --git a/server/runtime_javascript_match_core.go b/server/runtime_javascript_match_core.go index 928d239823d94996cbcdcca266442ab339798750..41f1a783a7ebe9ec16108e2cb8a66533c1199877 100644 --- a/server/runtime_javascript_match_core.go +++ b/server/runtime_javascript_match_core.go @@ -428,7 +428,13 @@ func (rm *RuntimeJavaScriptMatchCore) broadcastMessage(r *goja.Runtime) func(goj } presenceIDs, msg, reliable := rm.validateBroadcast(r, f) - if len(presenceIDs) != 0 { + if msg == nil { + return goja.Undefined() + } + + if len(presenceIDs) == 0 { + rm.router.SendToStream(rm.logger, rm.stream, msg, reliable) + } else { rm.router.SendToPresenceIDs(rm.logger, presenceIDs, msg, reliable) } @@ -443,7 +449,19 @@ func (rm *RuntimeJavaScriptMatchCore) broadcastMessageDeferred(r *goja.Runtime) } presenceIDs, msg, reliable := rm.validateBroadcast(r, f) - if len(presenceIDs) != 0 { + if msg == nil { + return goja.Undefined() + } + + if len(presenceIDs) == 0 { + if err := rm.deferMessageFn(&DeferredMessage{ + Stream: &rm.stream, + Envelope: msg, + Reliable: reliable, + }); err != nil { + panic(r.NewGoError(fmt.Errorf("error deferring message broadcast: %v", err))) + } + } else { if err := rm.deferMessageFn(&DeferredMessage{ PresenceIDs: presenceIDs, Envelope: msg, @@ -577,26 +595,7 @@ func (rm *RuntimeJavaScriptMatchCore) validateBroadcast(r *goja.Runtime, f goja. return nil, nil, false } } else { - actualPresenceIDs := rm.presenceList.ListPresenceIDs() - for i := 0; i < len(presenceIDs); i++ { - found := false - presenceID := presenceIDs[i] - for j := 0; j < len(actualPresenceIDs); j++ { - if actual := actualPresenceIDs[j]; presenceID.SessionID == actual.SessionID && presenceID.Node == actual.Node { - // If it matches, drop it. - actualPresenceIDs[j] = actualPresenceIDs[len(actualPresenceIDs)-1] - actualPresenceIDs = actualPresenceIDs[:len(actualPresenceIDs)-1] - found = true - break - } - } - if !found { - // If this presence wasn't in the filters, it's not needed. - presenceIDs[i] = presenceIDs[len(presenceIDs)-1] - presenceIDs = presenceIDs[:len(presenceIDs)-1] - i-- - } - } + presenceIDs := rm.presenceList.FilterPresenceIDs(presenceIDs) if len(presenceIDs) == 0 { // None of the target presenceIDs existed in the list of match members. return nil, nil, false @@ -618,10 +617,6 @@ func (rm *RuntimeJavaScriptMatchCore) validateBroadcast(r *goja.Runtime, f goja. Reliable: reliable, }}} - if presenceIDs == nil { - presenceIDs = rm.presenceList.ListPresenceIDs() - } - return presenceIDs, msg, reliable } diff --git a/server/runtime_javascript_nakama.go b/server/runtime_javascript_nakama.go index 9d7462692761890aa8f8763a3f15098fb206edcf..f0ea02c497a2193ac850eda1840b4e4efb802f8b 100644 --- a/server/runtime_javascript_nakama.go +++ b/server/runtime_javascript_nakama.go @@ -2877,7 +2877,7 @@ func (n *runtimeJavascriptNakamaModule) notificationsSend(r *goja.Runtime) func( no := notifications[userID] if no == nil { - no = make([]*api.Notification, 0) + no = make([]*api.Notification, 0, 1) } no = append(no, notification) notifications[userID] = no diff --git a/server/runtime_lua_match_core.go b/server/runtime_lua_match_core.go index 676212f3f179974c03a652e3dfdfd5d7159bfbd8..be7ff2bfe12e9f2f3ca4cb69ff4342f9ee30eac6 100644 --- a/server/runtime_lua_match_core.go +++ b/server/runtime_lua_match_core.go @@ -571,7 +571,13 @@ func (r *RuntimeLuaMatchCore) broadcastMessage(l *lua.LState) int { } presenceIDs, msg, reliable := r.validateBroadcast(l) - if len(presenceIDs) != 0 { + if msg == nil { + return 0 + } + + if len(presenceIDs) == 0 { + r.router.SendToStream(r.logger, r.stream, msg, reliable) + } else { r.router.SendToPresenceIDs(r.logger, presenceIDs, msg, reliable) } @@ -585,7 +591,19 @@ func (r *RuntimeLuaMatchCore) broadcastMessageDeferred(l *lua.LState) int { } presenceIDs, msg, reliable := r.validateBroadcast(l) - if len(presenceIDs) != 0 { + if msg == nil { + return 0 + } + + if len(presenceIDs) == 0 { + if err := r.deferMessageFn(&DeferredMessage{ + Stream: &r.stream, + Envelope: msg, + Reliable: reliable, + }); err != nil { + l.RaiseError("error deferring message broadcast: %v", err) + } + } else { if err := r.deferMessageFn(&DeferredMessage{ PresenceIDs: presenceIDs, Envelope: msg, @@ -742,26 +760,7 @@ func (r *RuntimeLuaMatchCore) validateBroadcast(l *lua.LState) ([]*PresenceID, * return nil, nil, false } } else { - actualPresenceIDs := r.presenceList.ListPresenceIDs() - for i := 0; i < len(presenceIDs); i++ { - found := false - presenceID := presenceIDs[i] - for j := 0; j < len(actualPresenceIDs); j++ { - if actual := actualPresenceIDs[j]; presenceID.SessionID == actual.SessionID && presenceID.Node == actual.Node { - // If it matches, drop it. - actualPresenceIDs[j] = actualPresenceIDs[len(actualPresenceIDs)-1] - actualPresenceIDs = actualPresenceIDs[:len(actualPresenceIDs)-1] - found = true - break - } - } - if !found { - // If this presence wasn't in the filters, it's not needed. - presenceIDs[i] = presenceIDs[len(presenceIDs)-1] - presenceIDs = presenceIDs[:len(presenceIDs)-1] - i-- - } - } + presenceIDs = r.presenceList.FilterPresenceIDs(presenceIDs) if len(presenceIDs) == 0 { // None of the target presenceIDs existed in the list of match members. return nil, nil, false @@ -779,10 +778,6 @@ func (r *RuntimeLuaMatchCore) validateBroadcast(l *lua.LState) ([]*PresenceID, * Reliable: reliable, }}} - if presenceIDs == nil { - presenceIDs = r.presenceList.ListPresenceIDs() - } - return presenceIDs, msg, reliable } diff --git a/server/runtime_lua_nakama.go b/server/runtime_lua_nakama.go index db6f82e6f6ba449be2358cc4d29d1e1f82fb3c32..b0262bc036f9ad46f2e4254e2b17a56ef0ea3c69 100644 --- a/server/runtime_lua_nakama.go +++ b/server/runtime_lua_nakama.go @@ -4063,7 +4063,7 @@ func (n *RuntimeLuaNakamaModule) notificationsSend(l *lua.LState) int { no := notifications[userID] if no == nil { - no = make([]*api.Notification, 0) + no = make([]*api.Notification, 0, 1) } no = append(no, notification) notifications[userID] = no diff --git a/social/social.go b/social/social.go index 4fb8bb9e95cfd8b9a4bb2a4ee829eb7fd1d6c401..1d327406c31abd7a8ad256e38db53d9cedf06e90 100644 --- a/social/social.go +++ b/social/social.go @@ -288,7 +288,7 @@ func (c *Client) CheckGoogleToken(ctx context.Context, idToken string) (*GoogleP c.googleMutex.Unlock() return nil, err } - newCerts := make([]*rsa.PublicKey, 0, 3) + newCerts := make([]*rsa.PublicKey, 0, len(certs)) var newRefreshAt int64 for _, data := range certs { currentBlock, _ := pem.Decode([]byte(data))