package matching
import "github.com/Diarkis/diarkis/matching"
Package matching ▷
Distributed memory matchmaking module
Highly scalable and extremely fast matchmaking based on application defined rules (matchmaking profiles).
MatchMaker scales in and out as you scale your Diarkis server cluster and it maintains both the performance and flexibility.
Configurations
MatchMaker configurations are explained below.
{ "distributionRate": 30, "targetNodeType": "HTTP" "profiles": { "$(matchmakingProfileID)": { "$(propertyA)": $(1), "$(propertyB)":$(100) } } } distributionRate - Default is 30
Configures how many servers to distribute the matchmaking data duplicates by percentage.
If set to 30, it will MatchMaker will distribute data duplicates to the 30% of the servers in the Diarkis server cluster.
targetNodeType - Default is "HTTP"
Configures which servers to store matchmaking data by server type.
profiles - Optional configuration to define matchmaking profiles from the configuration file.
"profiles" an optional configuration that allows you to define matchmaking profiles.
The format of the JSON is as shown below:
$(...) represents a variable
{ "profiles": { "$(profileID)": { "$(profileName)": $(profileValue) ... } ... } } "$(profileID)" is the unique matchmaking profile ID and the object that follows will define the properties of that profile as key and value pairs.
Matchmaking Rule Examples
In order to perform matchmaking, you must define the rules for matchmakings.
The matchmaking rules are called profiles.
These rules will dictate how matchmaking should be conditioned.
You may combine multiple matchmaking rules and create more complex matchmaking conditions as well.
You must define matchmaking rule profiles before invoking diarkis.Start.
The example below shows a matchmaking rule that uses level and creates buckets of matchmaking pools by the range of 10.
With this profile, each level bucket will pool users with level 1 to 10, 11 to 20, 21 to 30 and so forth...
The string name given as LevelMatch is the unique ID to represents the profile.
levelMatchProfile := make(map[string]int) levelMatchProfile["level"] = 10 matching.Define("LevelMatch", levelMatchProfile)
The diagrams below explain how matchmaking profiles work:
┏━━━┓ ┃ 8 ┃ 8 falls between 0 and 10, so it matches with items in this bucket ┗━┳━┛ ┃ │ ▼ │ │ │ │ 1 ~ 10 │ 11 ~ 20 │ 21 ~ 30 │ └─────────┴─────────┴─────────┘ ┏━━━┓ ┃ 3 ┃ 3 falls on 3, so it matches with items in this bucket ┗━┳━┛ ┃ │ │ │ ▼ │ │ 1 │ 2 │ 3 │ └─────────┴─────────┴─────────┘
▶︎ Multiple properties:
Tables below explains how matchmaking profile with multiple properties work.
┌──────────┬──────┬────────┐ │ Player │ Rank │ League │ ╞══════════╪══════╪════════╡ │ Player A │ 5 │ 1 │ │ Player B │ 10 │ 2 │ │ Player C │ 9 │ 1 │ │ Player D │ 30 │ 2 │ │ Player E │ 7 │ 2 │ │ Player F │ 24 │ 2 │ └──────────┴──────┴────────┘ ┌──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐ │░░░░░░░░░░│ Player A │ Player B │ Player C │ Player D │ Player E │ Player F │ ╞══════════╪══════════╪══════════╪══════════╪══════════╪══════════╪══════════╡ │ Player A │░░░░░░░░░░│ x │ ○ │ x │ x │ x │ │ Player B │ x │░░░░░░░░░░│ x │ x │ ○ │ x │ │ Player C │ ○ │ x │░░░░░░░░░░│ x │ x │ x │ │ Player D │ x │ x │ x │░░░░░░░░░░│ x │ ○ │ │ Player E │ x │ ○ │ x │ x │░░░░░░░░░░│ x │ │ Player F │ x │ x │ x │ ○ │ x │░░░░░░░░░░│ └──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
▶︎ Range search
All matchmaking profile properties are "bucket". What it means is that it does not allow search by ranged values.
For example, if search value is 10 and a matchmaking bucket is 0 ~ 10, it will match items of 0 to 10, but it does not allow search by ±5.
In order for MatchMaker to search by range, you must use SearchWithRange function.
Diagram below shows how it works:
┏━━━┓ ┃ 8 ┃ 8 with ±3 would fall into 5 ~ 11 and that means it matches with items in the bucket of 0 to 10 and 11 to 20 ┗━┳━┛ ┏━┻━━━━┓ │ ▼ │ ▼ │ │ │ 0 ~ 10 │ 11 ~ 20 │ 21 ~ 30 │ └─────────┴─────────┴─────────┘ ╒═══════════════════════════════════════════════════════════════════════╕ │ NOTE │ ├───────────────────────────────────────────────────────────────────────┤ │ To make ranged search more precise, │ │ consider using SetOnTicketAllowMatchIf callback for MatchMaker Ticket.│ │ │ │ SetOnTicketAllowMatchIf callback is invoked on every match found │ │ and it allows the application's custom logic │ │ via callback to either accept or reject the match found. │ ╘═══════════════════════════════════════════════════════════════════════╛
Debugging
MatchMaker allows you to see what is held in memory by sending a SIGUSR1 signal to a target server process.
Follow the steps below to output all matchmaking data in stdout stream:
1. Create a file called DIARKIS_SIGUSR1 in /tmp/ directory and write MatchMakerDump in the file.
2. Send SIGUSR1 signal to the target server process. By Default, HTTP servers act as memory storage for MatchMaker.
3. All data held in memory for MatchMaker will be dumped in stdout stream.
How to read the dumped matchmaking data:
=-=-=-=-=-=[ RankMatch2\t\tv100\t3\t1 ]=-=-=-=-=-= 1 => ID:68d425979add41e28f2ecc4993cfde2e7f0000011fa5cae3767e Value:map[maxMembers:2 roomID:68d425979add41e28f2ecc4993cfde2e7f0000011fa5cae3767e uniqueID:111] TTL:1675730081 TTLRemaining:2 seconds Expired:false
Above example output explains how to read and understand the matchmaking data held in memory.
=-=-=-=-=-=[ RankMatch2\t\tv100\t3\t1 ]=-=-=-=-=-=
RankMatch2:v100/3/1 is the matching condition key with a tag. Let's break it down:
RanMatch2 - Matching schema (definition) ID. v100 - Tag. 3\t1 - This key is generated by the condition values given to Add function along with the schema values defined by Define function.
The search conditions must match the key in order to find matches.
ID:68d425979add41e28f2ecc4993cfde2e7f0000011fa5cae3767e
ID represents the matchmaking data to identify the entry. Each ID is provided by the application code.
Value:map[maxMembers:2 roomID:68d425979add41e28f2ecc4993cfde2e7f0000011fa5cae3767e uniqueID:111] The value of the matchmaking data provided by the application. TTL:1675730081 TTLRemaining:2 seconds Expired:false
TTL of the matchmaking data entry and maximum TTL lasts 60 seconds.
MatchMaker Ticket Custom Logic
MatchMaker offers plethora of callbacks to add customized logic for your applications.
▶︎ Create/Issue a new MatchMaker Ticket
If you are writing your own custom MatchMaker Ticket logic and command, you will need to use StartTicket(ticketType uint8, userData *user.User).
The example below shows you how it is done.
// SetOnIssueTicket callback is required to create a new ticket when executing StartTicket // The callback will be invoked when you execute StartTicket with matching ticketType matching.SetOnIssueTicket(ticketType, func(userData *user.User) *matching.TicketParams { return &matching.TicketParams{ ProfileIDs: []string{"RankMatch"}, MaxMembers: 2, SearchInterval: 300, // 300 milliseconds interval of search SearchTries: 4, // allow 4 consecutive empty search results up to 4 times before moving on to wait TicketDuration: 20, // ticket lasts for 20 seconds HowMany: 20, // up to 20 search results Tags: []string{"Tag1"}, // if we want to group tickets using tag add the string value here AddProperties: &map[string]int{ "Rank": 3 }, // wait for other users to find me and my rank is 3 SearchProperties: &map[string][]int{ "Rank": &[]int{ 1, 2, 3, 4, 5 } }, // search for other users within the range of 1 to 5 and property is "Rank" } }) // Example of custom command implementation to create a new ticket // This example custom command expects the client to send ticket type in the payload server.HandleCommand(ver, cmd, func(ver uint8, cmd uint16, payload []byte, userData *user.User, next func(error)) { ticketType := uint8(0) if len(payload) > 0 { ticketType = uint8(payload[0]) } err := StartTicket(ticketType, userData) if err != nil { next(err) return } next(nil) })
Once you create/issue a new ticket, the rest will be handled by MatchMaker Ticket.
Control matches
MatchMaker Ticket allows the application to decide if found matches should proceed to become actual matches or not by adding a custom callback.
// This callback is applied to all potential matches found for the given ticketType. matching.SetOnTicketAllowMatchIf(ticketType, func (ticketProps *TicketProperties, owner, candidate *user.User) bool) { // Add your custom logic here to determine if this potential match should become an actual match or not. // By returning true, MatchMaker will proceed to make this candidate as an actual match. return true })
Control completion of matchmaking ticket
The callback is invoked on every match made and allows you to execute your custom logic to decide if the application should mark the ticket as complete or not. This allows the application to complete a matchmaking ticket even if you do not have enough number of matches required by the ticket.
// This callback is applied all tickets with the given ticketType. matching.SetOnTicketMatch(ticketType, func (ticket *Ticket, matchedUser, ownerUser *user.User, roomID string, memberIDs []string) bool { // By returning true, the ticket will be marked as complete and notifies the matched users. return true })
Additional custom logic on every match made
You may execute additional custom logic on every match made. This maybe useful if you want to send a message to all matched members when a new match is made to show a progress of matchmaking until it is complete.
// This callback is applied all tickets with the given ticketType. matching.SetOnTicketMemberJoined(ticketType, func (ticket *Ticket, joinedUser, ownerUser *user.User, memberIDs []string) { // This example sends a message to all matched users ver := 2 // server to client command version cmd := 100 // server to client command ID msg := []byte(fmt.Sprintf("Hello I am %s", userData.ID)) // a message to be sent to all matched users matching.TicketBroadcast(ticketType, userData, ver, cmd, msg) })
Detect when a matched user has left the match
You may execute a custom callback function when a matched user leaves a match.
// This callback is applied all tickets with the given ticketType. matching.SetOnTicketMemberLeave(ticketType, func (ticket *Ticket, leftUserData, ownerUser *user.User, roomID string, memberIDs []string) { // This example sends a message to all matched users except for the leftUserData ver := 2 // server to client command version cmd := 100 // server to client command ID msg := []byte(fmt.Sprintf("Bye bye from %s", userData.ID)) // a message to be sent to all matched users matching.TicketBroadcast(ticketType, userData, ver, cmd, msg) })
Matchmaking ticket completion notification
[IMPORTANT] This callback must be assigned in order to send matchmaking completion notification to all matched client users.
MatchMaker Ticket offers a callback to send a notification message to all matched users when a matchmaking ticket is marked as complete (either it has matched required number of users or matching.SetOnTicketMatch returned true). The callback returns a byte array to be sent as a notification message so that you may customize the message to be sent.
// This callback is applied all tickets with the given ticketType. matching.SetOnTicketComplete(ticketType, func (ticketProps *TicketProperties, owner *user.User) []byte { message := []byte("Matchmaking ticket has completed") return message })
Callback when canceling a ticket
You may assign a callback to be invoked when the owner (creator) of the ticket is canceled.
This callback is invoked for the owner of the ticket only.
// This callback is applied all tickets with the given ticketType. // The callback is invoked for the owner user of the ticket ONLY. matching.SetOnTicketCanceled(ticketType uint8, cb func(owner *user.User) { // custom logic here. })
Callback when the owner of the ticket cancels the ticket you matched
You may assign a callback to be invoked when the owner (creator) of the ticket cancels the ticket that you matched.
This callback is invoked for the non-owner users of the ticket that has been canceled.
// This callback is applied all tickets with the given ticketType. // The callback is invoked for the non-owner users of the ticket ONLY. matching.SetOnMatchedTicketCanceled(ticketType uint8, cb func(ownerID string, userData *user.User) { // custom log here. })
Detect and handle Matchmaking ticket timeout
You may assign a callback when your matchmaking ticket is timed out.
This callback is invoked for the owner of the ticket only. It is useful when you want to notify all the matched users of your ticket that the ticket has timed out before completing.
// This callback is applied all tickets with the given ticketType. // The callback is invoked for the owner user of the ticket ONLY. matching.SetOnTicketTimeout(ticketType, func (owner *user.User) { // custom log here. })
Index
- func Add(mid, uqid, tag string, props map[string]int, value map[string]interface{}, ...) error
- func CancelTicket(ticketType uint8, userData *user.User) error
- func ClaimMatchmaking(userData *user.User, roomID string, message []byte, next func(error))
- func ClearDefinition(matchingID string)
- func ClearMatchmakingHost(userData *user.User, profileID string, uniqueIDs []string, haltFlag bool, ...) error
- func ControlTicketParams(params *TicketParams) (int64, int64)
- func CreateKeyByProperties(matchingID string, tag string, properties map[string]int) string
- func DebugDataDump() (map[string][]*searchItem, error)
- func DebugDataDumpWriter(writer io.Writer) error
- func Define(profileID string, props map[string]int)
- func DefineByJSON(jsonBytes []byte)
- func ExposeCommands()
- func ExposePufferCommands()
- func GenerateTicketFailurePayload(message string, code uint16) []byte
- func GetMatchingID(ticketType uint8, userData *user.User) string
- func GetTicketMemberIDs(ticketType uint8, userData *user.User) ([]string, bool)
- func GetTicketMemberSIDs(ticketType uint8, userData *user.User) ([]string, bool)
- func GetTicketProperties(ticketType uint8, userData *user.User, keys []string) (map[string]interface{}, bool)
- func GetTicketProperty(ticketType uint8, userData *user.User, key string) (interface{}, bool)
- func GetTicketPropertyByMatchingID(matchingID string, key string) (interface{}, bool)
- func GetTicketTypeByMatchingID(matchingID string) (uint8, bool)
- func HasTicket(ticketType uint8, userData *user.User) bool
- func HostMatchmaking(userData *user.User, profileID, tag string, maxMembers uint16, ...) (string, error)
- func IsAddError(err error) bool
- func IsExportTicketError(err error) bool
- func IsImportTicketError(err error) bool
- func IsMarkAsCompleteError(err error) bool
- func IsSearchError(err error) bool
- func IsTicketAddError(err error) bool
- func IsTicketBackfillError(err error) bool
- func IsTicketBroadcastError(err error) bool
- func IsTicketCancelError(err error) bool
- func IsTicketCreateError(err error) bool
- func IsTicketJoinError(err error) bool
- func IsTicketKickError(err error) bool
- func IsTicketLeaveError(err error) bool
- func IsTicketOwner(ticketType uint8, userData *user.User) bool
- func IsTicketPropertySetError(err error) bool
- func IsTicketPropertyUpdateError(err error) bool
- func IsTicketSearchError(err error) bool
- func IsTicketStartError(err error) bool
- func IsUserInTicketMatchmaking(ticketType uint8, userData *user.User) bool
- func JoinRoomByID(ticketType uint8, id string, userData *user.User, cb func(error))
- func KickFromMatchmaking(userData *user.User, targetUID string, message []byte) error
- func KickoutFromTicket(ticketType uint8, owner *user.User, targetUserID string, cb func(err error))
- func LeaveFromTicketMatchmaking(ticketType uint8, userData *user.User) bool
- func LeaveFromTicketMatchmakingWithCallback(ticketType uint8, userData *user.User, cb func(err error))
- func MarkTicketAsComplete(ticketType uint8, userData *user.User) error
- func MarkTicketAsCompleteWhenExpire(ticketType uint8, userData *user.User) bool
- func MigrateTicketByUser(userData *user.User) error
- func ParseTicketFailurePayload(payload []byte) (string, uint16, error)
- func Remove(matchingID string, uniqueIDList []string, limit int)
- func Search(profileIDList []string, tag string, props map[string]int, limit int, ...)
- func SearchWithRange(profileIDList []string, tag string, searchProps map[string][]int, limit int, ...)
- func SearchWithRangeWithTags(profileIDList []string, tags []string, searchProps map[string][]int, limit int, ...)
- func SetCustomJoinCondition(callback func(string, *user.User) bool)
- func SetOnDeleteTicketRoom(ticketType uint8, userData *user.User, callback func(id string)) bool
- func SetOnIssueTicket(ticketType uint8, cb func(userData *user.User) *TicketParams) bool
- func SetOnLeaveTicketRoom(ticketType uint8, userData *user.User, ...) bool
- func SetOnMatchedTicketCanceled(ticketType uint8, cb func(ownerID string, userData *user.User)) bool
- func SetOnMatchedTicketTimeout(ticketType uint8, cb func(ownerID string, userData *user.User)) bool
- func SetOnTicketAllowMatchIf(ticketType uint8, ...) bool
- func SetOnTicketCanceled(ticketType uint8, cb func(owner *user.User)) bool
- func SetOnTicketComplete(ticketType uint8, ...) bool
- func SetOnTicketCompleteWithProfileID(ticketType uint8, ...) bool
- func SetOnTicketMatch(ticketType uint8, ...) bool
- func SetOnTicketMemberJoined(ticketType uint8, ...) bool
- func SetOnTicketMemberJoinedAnnounce(ticketType uint8, ...) bool
- func SetOnTicketMemberLeave(ticketType uint8, ...) bool
- func SetOnTicketMemberLeaveAnnounce(ticketType uint8, ...) bool
- func SetOnTicketTimeout(ticketType uint8, cb func(owner *user.User)) bool
- func SetTicketProperties(ticketType uint8, userData *user.User, data map[string]interface{}) error
- func SetTicketProperty(ticketType uint8, userData *user.User, key string, value interface{}) error
- func SetTicketPropertyIfNotExists(ticketType uint8, userData *user.User, key string, value interface{}) bool
- func Setup(confpath string)
- func StartTicket(ticketType uint8, userData *user.User) error
- func StartTicketBackfill(ticketType uint8, owner *user.User) error
- func StopTicketBackfill(ticketType uint8, owner *user.User) error
- func SyncMatchmakingMembers(userData *user.User, message []byte) error
- func TTLTest(src []int64) []int64
- func Test(method string, data map[string]interface{}) ([]byte, error)
- func TestDebugDataDump() map[string][]*searchItem
- func TicketBroadcast(ticketType uint8, userData *user.User, ver uint8, cmd uint16, msg []byte) error
- func UpdateTicketProperties(ticketType uint8, userData *user.User, data map[string]interface{}, ...) error
- func UpdateTicketProperty(ticketType uint8, userData *user.User, key string, value interface{}, ...) error
- type AddData
- type Client
- type FindOwnerData
- type JoinRoomData
- type LeaveRoomData
- type MigratedRoomIDData
- type PartialRemoveData
- type RemoveData
-
type Room
- func DecodeRoom(encoded []byte) *Room
- func (r *Room) CancelBackfill() error
- func (r *Room) CancelReservation(userData *user.User) bool
- func (r *Room) Encode() ([]byte, error)
- func (r *Room) GetID() string
- func (r *Room) GetMemberIDs() []string
- func (r *Room) GetMemberMeshAddrByUID(uid string) string
- func (r *Room) GetMemberMeshAddrList() []string
- func (r *Room) GetMemberSIDByUID(uid string) string
- func (r *Room) GetMemberSIDs() []string
- func (r *Room) GetMemberUsers() []*user.User
- func (r *Room) GetOwnerUser() (*user.User, bool)
- func (r *Room) GetProperties(keys []string) map[string]interface{}
- func (r *Room) GetProperty(key string) (interface{}, bool)
- func (r *Room) InitiateBackfill() error
- func (r *Room) IsBackfilling() bool
- func (r *Room) IsFull() bool
- func (r *Room) IsMemberReconnecting() (isReconnecting bool)
- func (r *Room) MakeReservation(userData *user.User) bool
- func (r *Room) RemoveOnDeleted(callback func(id string))
- func (r *Room) RemoveOnJoin(callback func(id string, userData *user.User) bool)
- func (r *Room) RemoveOnJoined(callback func(id string, userData *user.User))
- func (r *Room) RemoveOnLeft(callback func(id string, userData *user.User))
- func (r *Room) SetOnCancelBackfill(cb func(id string)) bool
- func (r *Room) SetOnDeleted(cb func(id string)) bool
- func (r *Room) SetOnInitiateBackfill(cb func(id string)) bool
- func (r *Room) SetOnJoin(cb func(id string, userData *user.User) bool) bool
- func (r *Room) SetOnJoined(cb func(id string, userData *user.User)) bool
- func (r *Room) SetOnLeft(cb func(id string, userData *user.User)) bool
- func (r *Room) SetOnTick(interval uint16, cb func(id string)) bool
- func (r *Room) SetOnTickStop(interval uint16, cb func(id string)) bool
- func (r *Room) SetProperties(data map[string]interface{})
- func (r *Room) SetProperty(key string, value interface{})
- func (r *Room) SetPropertyIfNotExists(key string, value interface{}) bool
- func (r *Room) StopAllTicks() bool
- func (r *Room) UpdateProperties(data map[string]interface{}, ...)
- func (r *Room) UpdateProperty(key string, value interface{}, ...)
- type RoomBroadcastData
- type SearchData
- type SearchReturnData
- type SearchReturnItemData
- type Ticket
- type TicketHolder
- type TicketParams
- type TicketProperties
- type UpdateUserData
Functions
func Add
func Add(mid, uqid, tag string, props map[string]int, value map[string]interface{}, ttl int64, limit int) error
Add Adds a searchable matching candidate data.
[NOTE] In order to have long lasting (TTL longer than 60 seconds) searchable items, the application must "add" searchable items repeatedly with TTL=60. [NOTE] Uses mutex lock internally.
Error Cases
╒═════════════════════╤══════════════════════════════════════════════════════════╕ │ Error │ Reason │ ╞═════════════════════╪══════════════════════════════════════════════════════════╡ │ Invalid profile IDs │ Either missing or invalid matchmaking profile IDs given. │ ├─────────────────────┼──────────────────────────────────────────────────────────┤ │ Unique ID missing │ Unique ID must not be an empty string. │ ├─────────────────────┼──────────────────────────────────────────────────────────┤ │ Properties missing │ Properties must not be a nil. │ ├─────────────────────┼──────────────────────────────────────────────────────────┤ │ Invalid limit value │ Limit must be greater than 1. │ ╘═════════════════════╧══════════════════════════════════════════════════════════╛
Parameters
mid - Matching profile ID to add the searchable data to. unqiueID - Unique ID of the searchable ID. tag - Tag is used to isolate and group add and search of matchmaking. If an empty string is given, it will be ignored. props - Searchable condition properties. value - Searchable data to be returned along with the search results. ttl - TTL of the searchable item to be added in seconds. Maximum 60 seconds. For a given profile ID diarkis expects the TTL to be constant, otherwise searchable data might be discarded too early. limit - Number of search node to propagate the searchable item data at a time.
func CancelTicket
func CancelTicket(ticketType uint8, userData *user.User) error
CancelTicket stops ticket-based matchmaking started by StartTicket.
Canceling a ticket will disband the matchmaking and remove already-matched users from it.
[IMPORTANT] Only the owner user of the ticket are allowed to cancel the ticket. [IMPORTANT] CancelTicket will fail after the completion of the ticket. [NOTE] Uses mutex lock internally.
Error Cases
┌───────────────────────┬───────────────────────────────────────────────────────────┐ │ Error │ Reason │ ╞═══════════════════════╪═══════════════════════════════════════════════════════════╡ │ Ticket not found │ The ticket to cancel is not available. │ │ Ticket failed to stop │ The ticket is not active (already completed or canceled). │ │ User not found │ The user was not found. │ └───────────────────────┴───────────────────────────────────────────────────────────┘
Parameters
ticketType - Ticket type of the ticket to cancel. userData - The owner user of the ticket to cancel.
func ClaimMatchmaking
func ClaimMatchmaking(userData *user.User, roomID string, message []byte, next func(error))
ClaimMatchmaking commits user to matchmaking room and joins at its room ID.
Parameters
userData - User attempting to commit to matchmaking room. roomID - Room ID of the matchmaking room to commit to. message - Message broadcast on matchmaking room join.
func ClearDefinition
func ClearDefinition(matchingID string)
ClearDefinition clears already defined match making definition
matchingID - Matching profile ID to clear the definition.
func ClearMatchmakingHost
func ClearMatchmakingHost(userData *user.User, profileID string, uniqueIDs []string, haltFlag bool, message []byte) error
ClearMatchmakingHost removes the current matchmaking room from matchmaking search and optionally deletes it.
Parameters
userData - Host user of the room to clear from matchmaking. profileID - The profile ID for which the host's matchmaking room is searchable by, and for which to remove. uniqueIDs - Unique ID list of searchable items for which to remove. haltFlag - If true, the room will only be removed from matchmaking without deleting it. message - Message announced on matchmaking room deletion.
func ControlTicketParams
func ControlTicketParams(params *TicketParams) (int64, int64)
ControlTicketParams [INTERNALLY USED ONLY]
func CreateKeyByProperties
func CreateKeyByProperties(matchingID string, tag string, properties map[string]int) string
CreateKeyByProperties generates a key from tag and search or add properties.
This is meant to be used along with DebugDataDump for test and debugging.
The key generated is the actual search condition data used internally to find matches.
func DebugDataDump
func DebugDataDump() (map[string][]*searchItem, error)
DebugDataDump returns the entire matchmaking data held in memory of the server.
[IMPORTANT] This is a debug function and must NOT be used in production code at all. [NOTE] In order to evaluate key, use CreateKeyByProperties to reproduce the same key with property values that match. [NOTE] Uses mutex lock internally.
Example of evaluating the dump data keys:
dump := matching.DebugDataDump() for key, searchItems := range dump { // expected key expectedKey := matching.CreateKeyByProperties(profileID, tag, expectedProperties) // check to see if the key matches the expected key if key == expectedKey { // good } else { // bad... } }
func DebugDataDumpWriter
func DebugDataDumpWriter(writer io.Writer) error
DebugDataDumpWriter writes the entire matchmaking data held in memory of the server to io.Writer stream.
[IMPORTANT] This is a debug function and must NOT be used in production code at all. [NOTE] In order to evaluate key, use CreateKeyByProperties to reproduce the same key with property values that match. [NOTE] Uses mutex lock internally.
func Define
func Define(profileID string, props map[string]int)
Define defines a match making search schema:
[IMPORTANT] This must be defined on the server that is specified by "targetNodeType" configuration because all match making data is stored on those servers. [IMPORTANT] Profile ID must not contain "\t".
Parameters
profileID - Unique matching profile ID. props - Matching profile condition properties.
In order to perform matchmaking, you must define the rules for matchmakings.
The matchmaking rules are called profiles.
These rules will dictate how matchmaking should be conditioned.
You may combine multiple matchmaking rules and create more complex matchmaking conditions as well.
You must define matchmaking rule profiles before invoking diarkis.Start.
The example below shows a matchmaking rule that uses level and creates buckets of matchmaking pools by the range of 10.
With this profile, each level bucket will pool users with level 1 to 10, 11 to 20, 21 to 30 and so forth...
The string name given as LevelMatch is the unique ID to represents the profile.
levelMatchProfile := make(map[string]int) levelMatchProfile["level"] = 10 matching.Define("LevelMatch", levelMatchProfile)
You may define as many matching definition as you require as long as each profileID is unique.
func DefineByJSON
func DefineByJSON(jsonBytes []byte)
DefineByJSON defines multiple match making definitions from JSON string.
$(...) represents a variable.
{ "$(profile ID)": { "$(property name)": $(property value as int) "$(property name)": $(property value as int) "$(property name)": $(property value as int) } } jsonBytes - Matching profile byte array data to be used to define the profile.
func ExposeCommands
func ExposeCommands()
ExposeCommands exposes built-in commands to the client.
func ExposePufferCommands
func ExposePufferCommands()
ExposePufferCommands exposes built-in Puffer commands to the client.
func GenerateTicketFailurePayload
func GenerateTicketFailurePayload(message string, code uint16) []byte
GenerateTicketFailurePayload generates a payload for ticket failure.
func GetMatchingID
func GetMatchingID(ticketType uint8, userData *user.User) string
GetMatchingID returns the matching ID of the given ticket type and user.
func GetTicketMemberIDs
func GetTicketMemberIDs(ticketType uint8, userData *user.User) ([]string, bool)
GetTicketMemberIDs returns the list of matched member user IDs.
Cases for the second return value to be false
- When the internal room is missing.
- When non-owner user executes the function.
Highlights
[IMPORTANT] This function is available ONLY for the owner of the ticket. [NOTE] Uses mutex lock internally.
func GetTicketMemberSIDs
func GetTicketMemberSIDs(ticketType uint8, userData *user.User) ([]string, bool)
GetTicketMemberSIDs returns the list of matched member user SIDs.
Cases for the second return value to be false
- When the internal room is missing.
- When non-owner user executes the function.
Highlights
[IMPORTANT] This function is available ONLY for the owner of the ticket. [IMPORTANT] If non-user uses this function it returns an empty array. [NOTE] Uses mutex lock internally.
func GetTicketProperties
func GetTicketProperties(ticketType uint8, userData *user.User, keys []string) (map[string]interface{}, bool)
GetTicketProperties returns key and value pairs as a map.
Cases for the second return value to be false
- When the internal room is missing.
- When non-owner user executes the function.
- When the given property key does not exist.
Highlights
[IMPORTANT] This function is available ONLY for the owner of the ticket. [IMPORTANT] Properties are only primitive values and does not support reference type data such as array and map. [IMPORTANT] If a value of a given key does not exist, the returned map will have a nil as a value of the key. [IMPORTANT] The returned property value is an interface{}, in order to type assert safely, please use Diarkis' util package functions. [NOTE] Uses mutex lock internally.
Example
values, ok := GetTicketProperties(ticketType, userData, []string{ "someKey" }) if !ok { // handle error here } for key, v := range values { // If the value data type is an uint8, of course ;) value, ok := util.ToUint8(v) }
func GetTicketProperty
func GetTicketProperty(ticketType uint8, userData *user.User, key string) (interface{}, bool)
GetTicketProperty returns the value of the given key and if the key does not exist, the second return value will be a false.
Cases for the second return value to be false
- When the internal room is missing.
- When non-owner user executes the function.
- When the given property key does not exist.
Highlights
[IMPORTANT] This function is available ONLY for the owner of the ticket. [IMPORTANT] Properties are only primitive values and does not support reference type data such as array and map. [IMPORTANT] The returned property value is an interface{}, in order to type assert safely, please use Diarkis' util package functions. [NOTE] Uses mutex lock internally.
Example
v, ok := GetTicketProperty(ticketType, userData, "someKey") if !ok { // handle error here } // If the value data type is an uint8, of course ;) v, ok := util.ToUint8(v)
func GetTicketPropertyByMatchingID
func GetTicketPropertyByMatchingID(matchingID string, key string) (interface{}, bool)
GetTicketPropertyByMatchingID returns the value of the given key and if the key does not exist, the second return value will be a false.
Cases for the second return value to be false
- When the internal room is missing.
- When the given property key does not exist.
Highlights
[IMPORTANT] Properties are only primitive values and does not support reference type data such as array and map. [IMPORTANT] The returned property value is an interface{}, in order to type assert safely, please use Diarkis' util package functions. [NOTE] Uses mutex lock internally.
Example
v, ok := GetTicketPropertyByMatchingID(matchingID, "someKey") if !ok { // handle error here } // If the value data type is an uint8, of course ;) v, ok := util.ToUint8(v)
func GetTicketTypeByMatchingID
func GetTicketTypeByMatchingID(matchingID string) (uint8, bool)
GetTicketTypeByMatchingID returns the ticket type of the given matching ID.
func HasTicket
func HasTicket(ticketType uint8, userData *user.User) bool
HasTicket returns true if the user given has a matchmaking ticket of the given type in progress.
func HostMatchmaking
func HostMatchmaking(userData *user.User, profileID, tag string, maxMembers uint16, reserveOnly bool, ttl uint8, props map[string]int, rawMetadata []byte) (string, error)
HostMatchmaking initializes a matchmaking room and makes it searchable for the provided conditionals.
Parameters
userData - User that will serve as the host of the matchmaking room. profileID - The profile ID for which to make the host's matchmaking room searchable by. tag - The tag for which to make the host's matchmaking room searchable by. maxMembers - Maximum number of members allowed to join the matchmaking room. reserveOnly - If true, the room will only be reserved for the host and not automatically joined (allowEmpty=true) ttl - TTL of the room when it is empty in seconds. The value must be greater than 0. Minimum is 10 seconds. props - Properties of map[string]int associated with the matchmaking room. rawMetadata - Serialized metadata associated with the matchmaking room.
func IsAddError
func IsAddError(err error) bool
IsAddError returns true if the given error is or contain Add error.
func IsExportTicketError
func IsExportTicketError(err error) bool
IsExportTicketError returns true if the given error is or contain ExportTicket error.
func IsImportTicketError
func IsImportTicketError(err error) bool
IsImportTicketError returns true if the given error is or contain ImportTicket error.
func IsMarkAsCompleteError
func IsMarkAsCompleteError(err error) bool
IsMarkAsCompleteError returns true if the given error is or contain MarkAsComplete error.
func IsSearchError
func IsSearchError(err error) bool
IsSearchError returns true if the given error is or contain Search error.
func IsTicketAddError
func IsTicketAddError(err error) bool
IsTicketAddError returns true if the given error is or contain TicketAdd error.
func IsTicketBackfillError
func IsTicketBackfillError(err error) bool
IsTicketBackfillError returns true if the given error is or contain TicketBackfill error.
func IsTicketBroadcastError
func IsTicketBroadcastError(err error) bool
IsTicketBroadcastError returns true if the given error is or contain TicketBroadcast error.
func IsTicketCancelError
func IsTicketCancelError(err error) bool
IsTicketCancelError returns true if the given error is or contain TicketCancel error.
func IsTicketCreateError
func IsTicketCreateError(err error) bool
IsTicketCreateError returns true if the given error is or contain TicketCreate error.
func IsTicketJoinError
func IsTicketJoinError(err error) bool
IsTicketJoinError returns true if the given error is or contain TicketJoin error.
func IsTicketKickError
func IsTicketKickError(err error) bool
IsTicketKickError returns true if the given error is or contain TicketKick error.
func IsTicketLeaveError
func IsTicketLeaveError(err error) bool
IsTicketLeaveError returns true if the given error is or contain TicketLeave error.
func IsTicketOwner
func IsTicketOwner(ticketType uint8, userData *user.User) bool
IsTicketOwner returns true if the given user is the owner of its ticket
[NOTE] Uses mutex lock internally.
func IsTicketPropertySetError
func IsTicketPropertySetError(err error) bool
IsTicketPropertySetError returns true if the given error is or contain TicketPropertySet error.
func IsTicketPropertyUpdateError
func IsTicketPropertyUpdateError(err error) bool
IsTicketPropertyUpdateError returns true if the given error is or contain TicketPropertyUpdate error.
func IsTicketSearchError
func IsTicketSearchError(err error) bool
IsTicketSearchError returns true if the given error is or contain TicketSearch error.
func IsTicketStartError
func IsTicketStartError(err error) bool
IsTicketStartError returns true if the given error is or contain TicketStart error.
func IsUserInTicketMatchmaking
func IsUserInTicketMatchmaking(ticketType uint8, userData *user.User) bool
IsUserInTicketMatchmaking returns true if the user is matched with at least one another user in ticket matchmaking of the given ticket type.
The function returns true when:
1. The ticket the user created moves to "waiting" phase.
2. The ticket finds and joins another ticket.
[NOTE] If the user is in a ticket matchmaking, the user is able to send and receive TicketBroadcast messages and perform other ticket-related operations. [NOTE] Uses mutex lock internally.
func JoinRoomByID
func JoinRoomByID(ticketType uint8, id string, userData *user.User, cb func(error))
JoinRoomByID allows the user given to join a MatchMaker Ticket Room.
[IMPORTANT] MatchMaker Ticket Rooms are NOT the same as Room module.
func KickFromMatchmaking
func KickFromMatchmaking(userData *user.User, targetUID string, message []byte) error
KickFromMatchmaking kicks a single user by user ID from matchmaking room.
Parameters
userData - Host user attempting to perform matchmaking room kick. targetUID - User ID of the target user to kick from the matchmaking room. message - Message announced to matchmaking room via kicked user on kick.
func KickoutFromTicket
func KickoutFromTicket(ticketType uint8, owner *user.User, targetUserID string, cb func(err error))
KickoutFromTicket forcefully removes a matched member from the matchmaking ticket.
[IMPORTANT] Only the owner of the ticket may execute this operation. [NOTE] Uses mutex lock internally.
Error Cases
╒═══════════════════════════╤═════════════════════════════════════════════════════════════════════════════╕ │ Error │ Reason │ ╞═══════════════════════════╪═════════════════════════════════════════════════════════════════════════════╡ │ MatchMaker room not found │ The owner user is not in a matchmaking ticket room. │ ├───────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤ │ Must be the owner │ Only the owner of the ticket may kick out matched members. │ ├───────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤ │ Target user not found │ The target user to kick out is not a matched member. │ ├───────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤ │ Cannot kick owner │ The owner of a matchmaking ticket room cannot kick self. │ ╘═══════════════════════════╧═════════════════════════════════════════════════════════════════════════════╛
func LeaveFromTicketMatchmaking
func LeaveFromTicketMatchmaking(ticketType uint8, userData *user.User) bool
LeaveFromTicketMatchmaking makes the target user leave the matchmaking that the user has matched and joined.
[NOTE] Uses mutex lock internally.
func LeaveFromTicketMatchmakingWithCallback
func LeaveFromTicketMatchmakingWithCallback(ticketType uint8, userData *user.User, cb func(err error))
LeaveFromTicketMatchmakingWithCallback makes the target user leave the matchmaking that the user has matched and joined.
[NOTE] Uses mutex lock internally.
func MarkTicketAsComplete
func MarkTicketAsComplete(ticketType uint8, userData *user.User) error
MarkTicketAsComplete finishes the ticket as complete immediately.
[IMPORTANT] Only the owner user of the ticket may execute this function. [NOTE] Uses mutex lock internally.
Error Cases
╒═══════════════════════╤═════════════════════════════════════════════════════════════════════════════╕ │ Error │ Reason │ ╞═══════════════════════╪═════════════════════════════════════════════════════════════════════════════╡ │ Ticket not found │ Either the ticket is not available or the given user does not own a ticket. │ ├───────────────────────┼─────────────────────────────────────────────────────────────────────────────┤ │ User is not owner │ User given is not the owner of the ticket. │ ├───────────────────────┼─────────────────────────────────────────────────────────────────────────────┤ │ Ticket room not found │ Internal room of the ticket is missing. │ │ │ Most likely due of an internal bug causing the ticket data to be corrupt. │ ╘═══════════════════════╧═════════════════════════════════════════════════════════════════════════════╛
Parameters
ticketType - Ticket type. userData - Owner user of the ticket to mark as complete.
func MarkTicketAsCompleteWhenExpire
func MarkTicketAsCompleteWhenExpire(ticketType uint8, userData *user.User) bool
MarkTicketAsCompleteWhenExpire marks the ticket as complete, but it will wait until the ticket expires.
[IMPORTANT] Only the owner of the ticket may execute this function. [NOTE] Uses mutex lock internally.
This is useful when you need to have alternative conditions for matchmaking completion without having all expected members match.
func MigrateTicketByUser
func MigrateTicketByUser(userData *user.User) error
MigrateTicketByUser migrates all tickets and ticket rooms to another server.
[IMPORTANT] Ticket Migration is automatically triggered when the user calls Reconnect. Call this function only when it should be triggered by the server. [IMPORTANT] This function does not work if the user is not in matchmaking. [IMPORTANT] Migration will change the room ID.
Error Cases
┌─────────────────────────────────────────┬────────────────────────────────────────────────────────────────────┐ │ Error │ Reason │ ╞═════════════════════════════════════════╪════════════════════════════════════════════════════════════════════╡ │ Node (server) to migrate room not found │ The server to migrate the room to is not found. │ │ User transfer failed │ The owner user failed to transfer to another server for migration. │ └─────────────────────────────────────────┴────────────────────────────────────────────────────────────────────┘
When a user reconnects to the other server, their tickets are transferred to the new server. If the user is the owner of the room, the room is also transferred to the new server. This is handled by Diarkis internally, but it is useful to understand the mechanism of it.
The diagram below shows the sequence of operations and callbacks as well as where they are executed.
The number in < > indicates the order of execution of callbacks and operations.
┌──────────────────────────┐ <2> Migrate User ┌────────────────────────┐ │ UDP/TCP server A │─────────────────────────▶︎│ UDP/TCP server B │ <3> The user is copied to the new server first. │ │ <4> Migrate Room/Ticket │ │ │ [ Room A exists here ] │─────────────────────────▶︎│ [ Room A migrated ] │ <5> Room is copied here with the new room ID. │ [ Ticket A exists here ] │ <8> Leave Room │ [Ticket A migrated ] │ The node propagates the room ID to the other nodes. └──────────────────────────┘─────────────────────────▶︎└────────────────────────┘ Ticket is copied here and continues add/search process. ▲ │ ▲ ▲ │ │ │ │ │<1> Reconnect Request │ <8> Re-connect │ │ │ │ Join the new migrated room. │ │<6> Reconnect Response (w/ new endpoint) │ Other members keep in the room without re-connection with the new room ID propagated. │ │ │ │ │ │ │<7> Disconnect │ │ │ │ │ │ ▼ │ │ ╭────────╮ │ │ Client │──────────────────────────────────────────────────┘ ╰────────╯(Client re-connects to the new server following the re-connect response)
func ParseTicketFailurePayload
func ParseTicketFailurePayload(payload []byte) (string, uint16, error)
ParseTicketFailurePayload parses the payload of ticket failure push.
func Remove
func Remove(matchingID string, uniqueIDList []string, limit int)
Remove removes a list of searchable matching candidate items by their unique IDs
[NOTE] This function is very expensive as it will send a message to all related mesh nodes [NOTE] There maybe some delay with removal operation on multiple servers as the instruction to remove must traverse all servers in the cluster.
Parameters
matchingID - Target matching profile ID to remove items from uniqueIDList - A list of unique IDs of the searchable items to remove limit - Mesh network relay limit
func Search
func Search(profileIDList []string, tag string, props map[string]int, limit int, callback func(err error, results []interface{}))
Search searches for matched data based on the given props' values
[IMPORTANT] Search performs search operations on remote servers and the number of servers to perform the search is calculated based on the number of the server and distributionRate configuration. This means that there is a chance that Search function may miss the server(s) with the desired matchmaking data resulting in not finding the intended matchmaking data. [NOTE] Uses mutex lock internally.
Error Cases
╒═════════════════════╤══════════════════════════════════════════════════════════════╕ │ Error │ Reason │ ╞═════════════════════╪══════════════════════════════════════════════════════════════╡ │ Invalid profile IDs │ Either missing or invalid matchmaking profile IDs give. │ ├─────────────────────┼──────────────────────────────────────────────────────────────┤ │ Invalid properties │ Either missing or invalid properties given. │ ├─────────────────────┼──────────────────────────────────────────────────────────────┤ │ Reliable timeout │ Communication to another server process timed out or failed. │ ╘═════════════════════╧══════════════════════════════════════════════════════════════╛
Parameters
profileIDList - A list of matchmaking profile IDs to search by. The order of the list is the order of search attempts. tag - Tag is used to isolate and group search, meaning the search will not include a tag that does not match. props - A property map to act as match making conditions. limit - A hard limit to the number of results. callback - A callback with the search results or an error.
func SearchWithRange
func SearchWithRange(profileIDList []string, tag string, searchProps map[string][]int, limit int, callback func(err error, results []interface{}))
SearchWithRange searches for matched data based on the given values of props in range.
Each props will have an array of elements with property values that should range from minimum value to maximum value. The function will use those values per property to performance the search.
[IMPORTANT] SearchWithRange performs search operations on remote servers and the number of servers to perform the search is calculated based on the number of the server and distributionRate configuration. This means that there is a chance that SearchWithRange function may miss the server(s) with the desired matchmaking data resulting in not finding the intended matchmaking data. [IMPORTANT] The number of allowed range properties is limited to two.
Example:
searchProps["level"] = []int{ 1, 2, 3, 4, 5 } // range property searchProps["rank"] = []int{ 1, 2, 3 } // range property searchProps["matchType"] = []int{ 1 } // regular property searchProps["league"] = []int{ 10 } // regular property
Error Cases
╒═════════════════════╤══════════════════════════════════════════════════════════════╕ │ Error │ Reason │ ╞═════════════════════╪══════════════════════════════════════════════════════════════╡ │ Invalid profile IDs │ Either missing or invalid matchmaking profile IDs give. │ ├─────────────────────┼──────────────────────────────────────────────────────────────┤ │ Invalid properties │ Either missing or invalid properties given. │ ├─────────────────────┼──────────────────────────────────────────────────────────────┤ │ Reliable timeout │ Communication to another server process timed out or failed. │ ╘═════════════════════╧══════════════════════════════════════════════════════════════╛
Parameters
profileIDList - A list of matchmaking profile IDs to search by. The order of the list is the order of search attempts. tag - Tag is used to isolate and group search, meaning the search will not include a tag that does not match. searchProps - A map with search condition values. You are allowed to have maximum two properties with range (more than 1 element in the int array). If you have more than two elements with range, it will give you an error. limit - A hard limit to the number of results. callback - A callback with the search results or an error.
Diagram below shows how it works:
┏━━━┓ ┃ 8 ┃ 8 with ±3 would fall into 5 ~ 11 and that means it matches with items in the bucket of 0 to 10 and 11 to 20 ┗━┳━┛ ┏━┻━━━━┓ │ ▼ │ ▼ │ │ │ 0 ~ 10 │ 11 ~ 20 │ 21 ~ 30 │ └─────────┴─────────┴─────────┘
To make ranged search more precise, consider using SetOnTicketAllowMatchIf callback for MatchMaker Ticket.
func SearchWithRangeWithTags
func SearchWithRangeWithTags(profileIDList []string, tags []string, searchProps map[string][]int, limit int, callback func(err error, results []interface{}))
SearchWithRangeWithTags searches for matched data based on the given values of props in range with multiple tags. Search will be performed with the array of tags. Shuffle the tags before calling this function if needed. See SearchWithRange for more information.
func SetCustomJoinCondition
func SetCustomJoinCondition(callback func(string, *user.User) bool)
SetCustomJoinCondition assigns a on join evaluation callback to be called to evaluate if the user should join or not
func SetOnDeleteTicketRoom
func SetOnDeleteTicketRoom(ticketType uint8, userData *user.User, callback func(id string)) bool
SetOnDeleteTicketRoom assigns a callback which is triggered when a matching room is deleted.
[IMPORTANT] This function is available ONLY for the owner of the ticket. [NOTE] Uses mutex lock internally. [NOTE] The callback is invoked while the lock is still being held. Avoid using locks in the callback to prevent mutex deadlocks.
func SetOnIssueTicket
func SetOnIssueTicket(ticketType uint8, cb func(userData *user.User) *TicketParams) bool
SetOnIssueTicket assigns a callback to be invoked when the built-in command issue ticket is called.
Parameters
ticketType - Ticket type is used to group tickets. It will assign the callback to the given ticket type. cb - Callback to be invoked and expected to create and return TicketParams for a new ticket.
The callback is invoked for the owner user only.
Calling StartTicket triggers this callback and TicketParams that it returns will be used to create a new ticket.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] This callback must be assigned to the appropriate ticket type in order for StartTicket(ticketType uint8, userData *user.User) to work properly. [IMPORTANT] userData maybe nil if the user disconnects from the server.
func SetOnLeaveTicketRoom
func SetOnLeaveTicketRoom(ticketType uint8, userData *user.User, callback func(id string, userData *user.User)) bool
SetOnLeaveTicketRoom assigns a callback which is triggered when a member leaves a matching room.
ticket.OnMatchedMemberLeave is valid only while ticket exists whereas this will be triggered also after the ticket completion.
[IMPORTANT] This function is available ONLY for the owner of the ticket. [NOTE] Uses mutex lock internally. [NOTE] The callback is invoked while the lock is still being held. Avoid using locks in the callback to prevent mutex deadlocks.
func SetOnMatchedTicketCanceled
func SetOnMatchedTicketCanceled(ticketType uint8, cb func(ownerID string, userData *user.User)) bool
SetOnMatchedTicketCanceled assigns a callback to be invoked when a ticket that the user has joined is canceled.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] The callback is invoked for the non-owner users of the ticket only. For the ticket owner, OnTicketCancled will be invoked. [IMPORTANT] When the owner of the ticket either disconnects or re-connects to another server, the ticket will be canceled automatically. Cancel event is raised event after the completion of the ticket when the owner of the ticket disconnects or re-connects..
Parameters
ticketType - Ticket type is used to group tickets. It will assign the callback to the given ticket type. cb - Callback to be invoked when a ticket is canceled.
func SetOnMatchedTicketTimeout
func SetOnMatchedTicketTimeout(ticketType uint8, cb func(ownerID string, userData *user.User)) bool
SetOnMatchedTicketTimeout assigns a callback to be invoked when a ticket that the user has joined is timed out.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] The callback is invoked for the non-owner users of the ticket only. For the ticket owner, OnTicketTimeout will be invoked.
Parameters
ticketType - Ticket type is used to group tickets. It will assign the callback to the given ticket type. cb - Callback to be invoked when a ticket times out.
func SetOnTicketAllowMatchIf
func SetOnTicketAllowMatchIf(ticketType uint8, cb func(ticketProps *TicketProperties, owner, candidate *user.User) bool) bool
SetOnTicketAllowMatchIf assigns a callback to be invoked before a match is made to add a custom logic to control if the match found should proceed forward to become an actual match or not..
ticketType - Ticket type is used to group tickets. It will assign the callback to the given ticket type. callback - Callback to be called func(ticketProps *TicketProperties, owner *user.User, candidate *user.User) bool
If the callback returns true, a match will be made.
The callback is invoked for the owner user only.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] The callback is invoked while the ticket is holding the mutex lock, In order to avoid mutex deadlock, you must not invoke functions that uses mutex lock or retrieve a mutex lock in the callback. [IMPORTANT] Candidate is the user that has been matched and attempting to join the matched member room. [IMPORTANT] owner and/or candidate maybe nil if owner and/or candidate disconnects from the server.
func SetOnTicketCanceled
func SetOnTicketCanceled(ticketType uint8, cb func(owner *user.User)) bool
SetOnTicketCanceled assigns a callback to be invoked when an issued ticket has been canceled.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] The callback is invoked only for the owner user of the ticket. For the non-owner users, OnMatchedTicketCancled will be invoked. [IMPORTANT] Owner may be nil if owner is disconnected from the server.
Parameters
ticketType - Ticket type is used to group tickets. It will assign the callback to the given ticket type. cb - Callback to be invoked when a ticket is canceled.
func SetOnTicketComplete
func SetOnTicketComplete(ticketType uint8, cb func(ticketProps *TicketProperties, owner *user.User) []byte) bool
SetOnTicketComplete assigns a callback to be invoked when an issued ticket successfully completes.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] The callback is invoked only for the owner user of the ticket. [IMPORTANT] Owner maybe nil if owner disconnects from the server. [IMPORTANT] This callback and SetOnTicketCompleteWithProfileID are mutually exclusive. Make sure to only set one.
Parameters
ticketType - Ticket type is used to group tickets. It will assign the callback to the given ticket type. cb - Callback to be invoked when a matchmaking is complete.
The callback is invoked for the owner user of the ticket only.
The callback function must return a message byte array to be sent to all matched user clients.
func SetOnTicketCompleteWithProfileID
func SetOnTicketCompleteWithProfileID(ticketType uint8, cb func(ticketProps *TicketProperties, owner *user.User, tag []string, profileID string) []byte) bool
SetOnTicketCompleteWithProfileID assigns a callback to be invoked when an issued ticket successfully completes.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] The callback is invoked only for the owner user of the ticket. [IMPORTANT] Owner maybe nil if owner disconnects from the server. [IMPORTANT] This callback and SetOnTicketComplete are mutually exclusive. Make sure to only set one.
Parameters
ticketType - Ticket type is used to group tickets. It will assign the callback to the given ticket type. cb - Callback to be invoked when a matchmaking is complete.
The callback is invoked for the owner user of the ticket only.
The callback function must return a message byte array to be sent to all matched user clients.
func SetOnTicketMatch
func SetOnTicketMatch(ticketType uint8, cb func(ticket *Ticket, matchedUser, ownerUser *user.User, roomID string, memberIDs []string) bool) bool
SetOnTicketMatch assigns a callback to be invoked when a new match is made.
ticketType - Ticket type is used to group tickets. It will assign the callback to the given ticket type. cb - Callback to control if the match completes the matchmaking or not. Room ID passed to the callback is NOT Diarkis Room's ID, but it is the ID of the internal matchmaking room. func(ticket *Ticket, matchedUserData *user.User, owner *user.User, roomID string, memberIDs []string) bool
This callback is meant to execute a custom logic for matchmaking completion on every match found.
Having the callback return true will automatically completes the matchmaking.
The callback is invoked for the owner user only.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] callback is invoked for the owner of the ticket only. [IMPORTANT] matchedUser is a copy of the actual user data of *user.User because the matched user maybe on a different server. [IMPORTANT] ownerUser and/or matchedUser maybe nil if ownerUser and/or matchedUser disconnects from the server.
func SetOnTicketMemberJoined
func SetOnTicketMemberJoined(ticketType uint8, cb func(ticket *Ticket, joinedUser, ownerUser *user.User, memberIDs []string)) bool
SetOnTicketMemberJoined assigns a callback to be invoked when a matched member joins.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] The callback is invoked for the owner user only. [IMPORTANT] joinedUser is a copy of the actual user data of *user.User because the matched user maybe on a different server. [IMPORTANT] ownerUser and/or joinedUser maybe nil if ownerUser and/or joinedUser disconnects from the server.
Parameters
ticketType - Ticket type is used to group tickets. It will assign the callback to the given ticket type. cb - Callback to be invoked when a matched member joins the match.
func SetOnTicketMemberJoinedAnnounce
func SetOnTicketMemberJoinedAnnounce(ticketType uint8, cb func(ticket *Ticket, joinedUser, ownerUser *user.User, memberIDs []string) (ver uint8, cmd uint16, message []byte)) bool
SetOnTicketMemberJoinedAnnounce assigns a callback to be invoked when a matched member joins and returns ver, cmd, and message to be used as an announcement.
An announcement is sent to all matched users.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] The event is invoked for the owner of the ticket only.
Parameters
ticketType - Apply the callback to the given ticket type. cb - Callback to be invoked when a new user successfully match and join. ticket - The matchmaking ticket. joinedUser - The user that joined the matchmaking. [IMPORTANT] This is a copy data of the joined user and it is not the actual joined user data. ownerUser - The owner user of the matchmaking ticket. memberIDs - an array of matched member user IDs.
Example:
added := matching.SetOnTicketMemberJoinedAnnounce(ticketType, func(ticket *matching.Ticket, joinedUser, ownerUser *user.User, memberIDs []string) (ver uint8, cmd uint16, message []byte) { // we will be sending a notification message to all matched users with the following: ver = uint8(2) // the notification message will be sent with command ver 2 cmd = uint16(1010) // the notification message will be sent with command ID 1010 message = []byte(strings.Join(messageIDs, ",")) // the notification message will send a message with comma separated list of matched member user IDs. return ver, cmd, message }) if !added { // failed to assign the callback... }
func SetOnTicketMemberLeave
func SetOnTicketMemberLeave(ticketType uint8, cb func(ticket *Ticket, leftUserData, ownerUser *user.User, roomID string, memberIDs []string)) bool
SetOnTicketMemberLeave assigns a callback to be invoked when a matched member leaves.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] The callback is invoked for the owner user only. [IMPORTANT] leftUser is a copy of the actual user data of *user.User because the matched user maybe on a different server. [IMPORTANT] ownerUser and/or leftUserData maybe nil if ownerUser and/or leftUserData disconnects from the server.
Parameters
ticketType - Ticket type is used to group tickets. It will assign the callback to the given ticket type. cb - Callback to be invoked when a matched member leaves the match.
func SetOnTicketMemberLeaveAnnounce
func SetOnTicketMemberLeaveAnnounce(ticketType uint8, cb func(ticket *Ticket, leftUser, ownerUser *user.User, memberIDs []string) (ver uint8, cmd uint16, message []byte)) bool
SetOnTicketMemberLeaveAnnounce assigns a callback to be invoked when a matched member leaves and returns ver, cmd, and message to be used as an announcement.
An announcement is sent to all matched users.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] The event is invoked for the owner of the ticket only.
Parameters
ticketType - Apply the callback to the given ticket type. cb - Callback to be invoked when a matched user leaves. ticket - The matchmaking ticket. leftUser - The user that left the matchmaking. [IMPORTANT] This is a copy of the left user data and it is not the actual left user data. ownerUser - The owner user of the matchmaking ticket. memberIDs - an array of matched member user IDs.
Example:
added := matching.SetOnTicketMemberLeaveAnnounce(ticketType, func(ticket *matching.Ticket, leftUser, ownerUser *user.User, memberIDs []string) (ver uint8, cmd uint16, message []byte) { // we will be sending a notification message to all matched users with the following: ver = uint8(2) // the notification message will be sent with command ver 2 cmd = uint16(1010) // the notification message will be sent with command ID 1010 message = []byte(strings.Join(messageIDs, ",")) // the notification message will send a message with comma separated list of matched member user IDs. return ver, cmd, message }) if !added { // failed to assign the callback... }
func SetOnTicketTimeout
func SetOnTicketTimeout(ticketType uint8, cb func(owner *user.User)) bool
SetOnTicketTimeout assigns a callback to be invoked when an issued ticket has timed out.
[CRITICALLY IMPORTANT] Using pointer variables that are defined outside of the callback closure in the callback closure will cause those pointers to be not garbage collected leading to memory leak. [IMPORTANT] The callback is invoked for the owner user of the ticket only. [IMPORTANT] Owner may be nil if owner disconnects from the server.
Parameters
ticketType - Ticket type is used to group tickets. It will assign the callback to the given ticket type. cb - Callback to be invoked when a ticket times out.
func SetTicketProperties
func SetTicketProperties(ticketType uint8, userData *user.User, data map[string]interface{}) error
SetTicketProperties stores a collection of keys and their values to ticket as properties.
Error Cases
┌────────────────┬────────────────────────────────────────────────────────────────┐ │ Error │ Reason │ ╞════════════════╪════════════════════════════════════════════════════════════════╡ │ Room not found │ MatchMaker ticket is corrupt. │ │ Must be owner │ Only the ticket owner user is allowed to execute the function. │ └────────────────┴────────────────────────────────────────────────────────────────┘ [IMPORTANT] This function is available ONLY for the owner of the ticket. [IMPORTANT] If the same key exists, it overwrites the existing value of the same key. [NOTE] Uses mutex lock internally.
Properties are only primitive values and does not support reference type data such as array and map.
func SetTicketProperty
func SetTicketProperty(ticketType uint8, userData *user.User, key string, value interface{}) error
SetTicketProperty stores a key and a value as a property to the ticket.
Error Cases
┌────────────────┬────────────────────────────────────────────────────────────────┐ │ Error │ Reason │ ╞════════════════╪════════════════════════════════════════════════════════════════╡ │ Room not found │ MatchMaker ticket is corrupt. │ │ Must be owner │ Only the ticket owner user is allowed to execute the function. │ └────────────────┴────────────────────────────────────────────────────────────────┘ [IMPORTANT] This function is available ONLY for the owner of the ticket. [NOTE] Uses mutex lock internally.
If the same key exists, it overwrites the existing value of the same key.
Properties are only primitive values and does not support reference type data such as array and map.
func SetTicketPropertyIfNotExists
func SetTicketPropertyIfNotExists(ticketType uint8, userData *user.User, key string, value interface{}) bool
SetTicketPropertyIfNotExists stores a key and a value as a property to the ticket if the same key does not exist.
[IMPORTANT] This function is available ONLY for the owner of the ticket. [NOTE] Uses mutex lock internally.
Properties are only primitive values and does not support reference type data such as array and map.
func Setup
func Setup(confpath string)
Setup sets up MatchMaker on the server. You must call this at the start of the server process.
confpath - Absolute path of the configuration file to be loaded.
func StartTicket
func StartTicket(ticketType uint8, userData *user.User) error
StartTicket creates and starts a new ticket-based matchmaking with the given ticket type.
[IMPORTANT] If the owner user (user that created the ticket) disconnects or re-connects, the ticket will be canceled automatically. This also means that if you use Diarkis modules (Diarkis Field and Diarkis Room) that may require the user to re-connect such as Room and Field, it may cause the ticket to be canceled unexpected when the user re-connects. [IMPORTANT] Complex search properties with large number of properties with long range (lengthy array of values) may have negative impact on server performance. [NOTE] Uses mutex lock internally.
Error Cases
╒════════════════════════════════════════════════════╤════════════════════════════════════════════════════════════════════════════════╕ │ Error │ Reason │ ╞════════════════════════════════════════════════════╪════════════════════════════════════════════════════════════════════════════════╡ │ MatchMaker is not setup correctly │ SetOnIssueTicket callback for the ticketType must be assigned. │ ├────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤ │ MatchMaker ticket cannot be issued more than once │ The user is not allowed to issue more than one ticket of the same ticket type. │ ├────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤ │ Failed to create a ticket │ Given nil for *TicketParams. │ ├────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────┤ │ Failed to issue a new ticket │ The user is still in a previous matchmaking room of the same ticket type. │ ╘════════════════════════════════════════════════════╧════════════════════════════════════════════════════════════════════════════════╛
Parameters
ticketType - Ticket type is used to group tickets. You may issue multiple tickets of different ticket types. userData - The user that issues and starts the ticket and becomes the owner of the ticket.
How Ticket Works Internally
Ticket manages MatchMaker's Add and Search internally, so that you do not have to manage and balance their calls.
Ticket has two phases: "Search" and "Waiting"
Ticket starts out in "Search" phase and moves to "Waiting" phase.
The diagram below shows how a ticket operations internally:
┌──────────────┐ │ Start Ticket │ └──────┬───────┘ │ ▼ ┌──────────────┐ │ Search │ └──────┬───────┘ │ ┌──────────┴──────────┐ │ │ Ticket actively searches for ▼ ▼ other tickets that are ┌───────────────┐ ┌────────┐ Ticket creates a waiting room for in "Wait" phase to match │ Found Matches │ │ Wait │ the ticket and waits for └──────┬────────┘ └──┬──┬──┘ other tickets to "Search" it and match ╭────────────────────────────────────╮ │ │ │ │ Ticket found other tickets │ └────────┐ ┌──────┘ └──────────┐ │ to join and matched ticket ├──────────▷│ │◁──────────┐ │ │ met the required number of users │ │ │ │ │ ╰────────────────────────────────────╯ ▼ ▼ │ ▼ ╔═══════════════════════╗ │ ┌─────────┐ If required number of users are not met ║ Matchmaking Completed ║ │ │ Timeout │ and ticket duration expires, ╚═══════════════════════╝ │ └─────────┘ the ticket times out. In order to continue, ┌──────────┘ The user must issue and start a new ticket │ ╭──────────────────────────────────────────────────┴─╮ │ Other tickets searched and found the ticket │ │ and the required number of users have been matched │ ╰────────────────────────────────────────────────────╯
Diagram below visually explains how these two phases of a ticket work and how some of the parameters affect these two phases.
▶︎ SearchInterval 200ms ▶︎ SearchTries 10 ▶︎ TicketDuration 4s ▷ Search Phase: The ticket will search every 200ms 10 times ▷ Wait Phase: If the ticket does not find match, it will wait for the remainder of time until the ticket duration expires 200ms x 10 searches ┌────── Search Phase ──────┐ ┌─────── Waiting Phase ───────┐ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┰────────────────────────────────┐ │ │ │ │ │ │ │ │ │ │ ┃ │ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┸────────────────────────────────┘ Total 2 seconds of searching Total 2 seconds of waiting │ │ └─────────── Ticket duration is 4 seconds in total ────────────┘ This means that a ticket in search phase will only match with tickets in wait phase and vice versa.
Matchmaking result notifications
The table below explains the notifications that the client receives from the server.
┌───────────────────┬──────────────────┬─────────────────┬───────────────────────────────────────────────────────────────────────────┐ │ Notification Type │ Push Command Ver │ Push Command ID │ Description │ ╞═══════════════════╪══════════════════╪═════════════════╪═══════════════════════════════════════════════════════════════════════════╡ │ Success │ 1 │ 220 │ Matchmaking ticket has been successfully completed │ │ │ │ │ and all matched user clients receive this server push. │ ├───────────────────┼──────────────────┼─────────────────┼───────────────────────────────────────────────────────────────────────────┤ │ Timeout │ 1 │ 219 │ Matchmaking ticket has failed and it has been discarded. │ │ │ │ │ All user clients that matched receives this server push. │ ├───────────────────┼──────────────────┼─────────────────┼───────────────────────────────────────────────────────────────────────────┤ │ Cancel │ 1 │ 222 │ Matchmaking ticket has been canceled │ │ │ │ │ and all user clients that matched receives this server push. │ ├───────────────────┼──────────────────┼─────────────────┼───────────────────────────────────────────────────────────────────────────┤ │ Broadcast │ 1 │ 224 │ Matchmaking ticket sends a broadcast message to all matched user clients. │ └───────────────────┴──────────────────┴─────────────────┴───────────────────────────────────────────────────────────────────────────┘
Calling StartTicket raises The callback assigned by SetOnIssueTicket and a new ticket will be created using the given ticket parameters.
Example with SetOnIssueTicket callback
// the callback will be invoked by matching.StartTicket matching.SetOnIssueTicket(sampleTicketType, func(userData *user.User) *matching.TicketParams { return &matching.TicketParams{ ProfileIDs: []string{"RankMatch"}, MaxMembers: 2, SearchInterval: 300, // 300 milliseconds interval of search SearchTries: 4, // allow 4 consecutive empty search results up to 4 times before moving on to wait TicketDuration: 20, // ticket lasts for 20 seconds HowMany: 20, // up to 20 search results Tags: []string{"tag1"}, // if we want to group tickets using tag(s) add the string value(s) here AddProperties: &map[string]int{ "Rank": 3 }, // wait for other users to find me and my rank is 3 SearchProperties: &map[string][]int{ "Rank": &[]int{ 1, 2, 3, 4, 5 } }, // search for other users within the range of 1 to 5 and property is "Rank" } }) err := matching.StartTicket(sampleTicketType, userData) if err != nil { // error... }
▶︎ SearchPropeties
ServerProperties dictate conditions for searches that is performed internally.
[IMPORTANT] The number of allowed range properties is limited to two. When you have multiple elements in a search property, it is considered as a range property. [IMPORTANT] SearchProperties operates AND operations. It means that with multiple search properties, the search must satisfy all search properties in order to match.
Diagram below shows how each search property operates:
┏━━━┓ ┃ 8 ┃ 8 with ±3 would fall into 5 ~ 11 and that means it matches with items in the bucket of 0 to 10 and 11 to 20 ┗━┳━┛ ┏━┻━━━━┓ │ ▼ │ ▼ │ │ │ 0 ~ 10 │ 11 ~ 20 │ 21 ~ 30 │ └─────────┴─────────┴─────────┘
Example:
searchProps["level"] = []int{ 1, 2, 3, 4, 5 } // range property searchProps["rank"] = []int{ 1, 2, 3 } // range property searchProps["matchType"] = []int{ 1 } // regular property searchProps["league"] = []int{ 10 } // regular property
Order of range search property's search:
The range search properties will look for matches in the order of the array. It means that if []int{ 1, 2, 3, 4, 5 } is given, it will start from 1 and continue up to 5 until it finds it matches.
▶︎ Callbacks
Every callback is assigned to given ticket type and invoked based on assigned ticket type.
func StartTicketBackfill
func StartTicketBackfill(ticketType uint8, owner *user.User) error
StartTicketBackfill starts "backfill" on a ticket that has already been completed.
Backfill allows other users to match and join "after" the completion of the ticket.
When ticket is completed, the ticket itself will be deleted, but the matched users remain in the ticket room.
The owner user that wishes to start backfill must be a member of this ticket room.
[IMPORTANT] If the ticket is full, no users may match and join even with backfill started. [IMPORTANT] In order to stop backfill, you must invoke StopTicketBackfill. [NOTE] Uses mutex lock internally.
Error Cases
┌────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────┐ │ Error │ Reason │ ╞════════════════════════════════════════════════════╪════════════════════════════════════════════════════════════════════════════════╡ │ Completed ticket room must be available │ The ticket and its internal room have been discarded. │ │ Ticket already exists │ Either the ticket has not been completed or backfill has already been started. │ │ MatchMaker is not setup correctly │ SetOnIssueTicket callback for the ticketType must be assigned. │ │ MatchMaker ticket cannot be issued more than once │ The user is not allowed to issue more than one ticket of the same ticket type. │ │ Failed to create a ticket │ Given nil for *TicketParams. │ │ Failed to issue a new ticket │ The user is still in a previous matchmaking room of the same ticket type. │ └────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────┘
Parameters
ticketType - Type of the completed ticket to start backfill. owner - Ticket's owner user.
func StopTicketBackfill
func StopTicketBackfill(ticketType uint8, owner *user.User) error
StopTicketBackfill stops backfill ticket.
[NOTE] Uses mutex lock internally. Error Cases ┌────────────────────────────────┬─────────────────────────────────────────────────────────────────────────┐ │ Error │ Reason │ ╞════════════════════════════════╪═════════════════════════════════════════════════════════════════════════╡ │ Backfill ticket not found │ Backfill ticket to stop does not exist. │ │ Backfill ticket Room not found │ Backfill ticket Room has been discarded. (All users have disconnected.) │ └────────────────────────────────┴─────────────────────────────────────────────────────────────────────────┘
Parameters
ticketType - Backfill ticket type to stop backfill. owner - Backfill ticket owner user.
func SyncMatchmakingMembers
func SyncMatchmakingMembers(userData *user.User, message []byte) error
SyncMatchmakingMembers syncs a message to all matchmaking room members.
Parameters
userData - User attempting to sync message across matchmaking room. message - Message announced to matchmaking room upon sync.
func TTLTest
func TTLTest(src []int64) []int64
TTLTest this is used ONLY in tests
func Test
func Test(method string, data map[string]interface{}) ([]byte, error)
Test this is used ONLY in tests
func TestDebugDataDump
func TestDebugDataDump() map[string][]*searchItem
TestDebugDataDump is used ONLY in internal tests.
func TicketBroadcast
func TicketBroadcast(ticketType uint8, userData *user.User, ver uint8, cmd uint16, msg []byte) error
TicketBroadcast sends a reliable message to all matched users with the given ver, cmd, and message byte array.
[NOTE] This function can be executed by any matched member user. [NOTE] Uses mutex lock internally.
Parameters
ticketType - MatchMaker Ticket's type. userData - Matched member user of the ticket. ver - Broadcast message command version. cmd - Broadcast message command ID. msg - Broadcast message data in byte array format.
func UpdateTicketProperties
func UpdateTicketProperties( ticketType uint8, userData *user.User, data map[string]interface{}, cb func(exists bool, storedValue interface{}, newValue interface{}) (updateValue interface{})) error
UpdateTicketProperties changes the existing property values of ticket.
The callback is invoked while the internal lock is still held, locking inside the callback may cause mutex deadlock.
Error Cases
┌────────────────┬────────────────────────────────────────────────────────────────┐ │ Error │ Reason │ ╞════════════════╪════════════════════════════════════════════════════════════════╡ │ Room not found │ MatchMaker ticket is corrupt. │ │ Must be owner │ Only the ticket owner user is allowed to execute the function. │ └────────────────┴────────────────────────────────────────────────────────────────┘
Highlights
[IMPORTANT] This function is available ONLY for the owner of the ticket. [IMPORTANT] This function is NOT asynchronous. [IMPORTANT] Properties are only primitive values and does not support reference type data such as array and map. [NOTE] Uses mutex lock internally. [NOTE] The callback is invoked while the lock is still being held. Avoid using locks in the callback to prevent mutex deadlocks.
Parameters
ticketType - Ticket type is used to find the ticket. userData - Owner user data of the ticket. data - A map of key and value pair to be stored or updated as properties. cb - Callback to be invoked on every key and value pair to handle the update. func(exists bool, storedValue interface{}, updateValue interface{}) (updatedValue interface{}) - exists - Indicates if the same key already exists or not - storedValue - Existing value that is stored as a property. If the key does not exist it is a nil. - updateValue - The value to be used to update/replace or set.
func UpdateTicketProperty
func UpdateTicketProperty( ticketType uint8, userData *user.User, key string, value interface{}, cb func(exists bool, storedValue interface{}, newValue interface{}) (updateValue interface{})) error
UpdateTicketProperty changes the existing property value of ticket.
Error Cases
┌────────────────┬────────────────────────────────────────────────────────────────┐ │ Error │ Reason │ ╞════════════════╪════════════════════════════════════════════════════════════════╡ │ Room not found │ MatchMaker ticket is corrupt. │ │ Must be owner │ Only the ticket owner user is allowed to execute the function. │ └────────────────┴────────────────────────────────────────────────────────────────┘
Highlights
[IMPORTANT] This function is available ONLY for the owner of the ticket. [IMPORTANT] This function is NOT asynchronous. [IMPORTANT] Properties are only primitive values and does not support reference type data such as array and map. [NOTE] Uses mutex lock internally. [NOTE] The callback is invoked while the lock is still being held. Avoid using locks in the callback to prevent mutex deadlocks.
Parameters
ticketType - Ticket type is used to find the ticket. userData - Owner user data of the ticket. key - A key of the property to be updated. value - A value of the property to be updated with. cb - Callback to be invoked on every key and value pair to handle the update. func(exists bool, storedValue interface{}, updateValue interface{}) (updatedValue interface{}) - exists - Indicates if the same key already exists or not - storedValue - Existing value that is stored as a property. If the key does not exist it is a nil. - updateValue - The value to be used to update/replace or set.
Types
type AddData
type AddData struct { InternalID string `json:"internalID"` MatchingID string `json:"matchingID"` Tag string `json:"tag"` Props map[string]int `json:"props"` Value map[string]interface{} `json:"value"` TTL int64 `json:"ttl"` }
AddData represents internally used matchmaking data
type Client
type Client struct { ID string SID string // Available ONLY for UDP PublicAddress string // Available ONLY for UDP PrivateAddressBytes []byte // User property data copied when the user joins a matchmaking ticket UserData map[string]interface{} }
Client represents matchmaking ticket matched member user client.
func GetTicketMemberClients
func GetTicketMemberClients(ticketType uint8, userData *user.User) ([]*Client, bool)
GetTicketMemberClients returns the list of matched member user clients.
Cases for the second return value to be false
- When the internal room is missing.
- When non-owner user executes the function.
Highlights
[IMPORTANT] This function is available ONLY for the owner of the ticket. [IMPORTANT] If non-user uses this function it returns an empty array. [NOTE] Uses mutex lock internally.
type FindOwnerData
type FindOwnerData struct { SID string `json:"sid"` }
FindOwnerData represents internally used data
type JoinRoomData
type JoinRoomData struct { TicketType uint8 `json:"ticketType"` ID string `json:"id"` SID string `json:"sid"` MeshAddr string `json:"meshAddr"` UserData map[string]interface{} `json:"userData"` }
JoinRoomData represents internally used data
type LeaveRoomData
type LeaveRoomData struct { TicketType uint8 `json:"ticketType"` ID string `json:"id"` UID string `json:"uid"` SID string `json:"sid"` }
LeaveRoomData represents internally used data
type MigratedRoomIDData
type MigratedRoomIDData struct { TicketType uint8 `json:"ticketType"` NewID string `json:"id"` MemberSIDs []string `json:"memberSIDs"` }
MigratedRoomIDData represents internally used data
type PartialRemoveData
type PartialRemoveData struct { MatchingID string `json:"m"` UniqueIDs []string `json:"u"` Tags []string `json:"t"` }
PartialRemoveData represents internally used matchmaking removal data
type RemoveData
type RemoveData struct { MatchingID string `json:"matchingID"` UniqueIDs []string `json:"uniqueIDs"` }
RemoveData represents internally used matchmaking removal data
type Room
type Room struct { sync.RWMutex // contains filtered or unexported fields }
Room represents matchmaker ticket room that is used internally
func DecodeRoom
func DecodeRoom(encoded []byte) *Room
DecodeRoom decodes the given data string to the room.
func (*Room) CancelBackfill
func (r *Room) CancelBackfill() error
CancelBackfill stops the backfill process.
func (*Room) CancelReservation
func (r *Room) CancelReservation(userData *user.User) bool
CancelReservation removes the reservation of the given user from the ticket room.
[NOTE] Uses mutex lock internally.
func (*Room) Encode
func (r *Room) Encode() ([]byte, error)
Encode returns the encoded string of the room.
func (*Room) GetID
func (r *Room) GetID() string
GetID returns the room ID.
func (*Room) GetMemberIDs
func (r *Room) GetMemberIDs() []string
GetMemberIDs returns an array of matched member IDs.
[NOTE] Uses mutex lock internally.
func (*Room) GetMemberMeshAddrByUID
func (r *Room) GetMemberMeshAddrByUID(uid string) string
GetMemberMeshAddrByUID returns a mesh address of a member.
Returns an empty string if the member is not found or invalid.
[NOTE] Uses mutex lock internally.
func (*Room) GetMemberMeshAddrList
func (r *Room) GetMemberMeshAddrList() []string
GetMemberMeshAddrList returns an array of internal server address of each matched user.
[NOTE] Uses mutex lock internally.
func (*Room) GetMemberSIDByUID
func (r *Room) GetMemberSIDByUID(uid string) string
GetMemberSIDByUID returns the member's SID.
It returns and empty string if the member is not found or invalid.
[NOTE] Uses mutex lock internally.
func (*Room) GetMemberSIDs
func (r *Room) GetMemberSIDs() []string
GetMemberSIDs returns an array of matched member SIDs.
[NOTE] Uses mutex lock internally.
func (*Room) GetMemberUsers
func (r *Room) GetMemberUsers() []*user.User
GetMemberUsers returns an array of matched member user copies.
[IMPORTANT] The return array contains copies of member users. [NOTE] Uses mutex lock internally.
func (*Room) GetOwnerUser
func (r *Room) GetOwnerUser() (*user.User, bool)
GetOwnerUser returns the ticket owner user.
[IMPORTANT] Returned owner user is NOT a copy. [NOTE] Uses mutex lock internally.
func (*Room) GetProperties
func (r *Room) GetProperties(keys []string) map[string]interface{}
GetProperties returns key and value pairs as a map.
[NOTE] Properties are only primitive values and does not support reference type data such as array and map. [NOTE] Uses mutex lock internally.
If a value of a given key does not exist, the returned map will have a nil as a value of the key.
The returned property value is an interface{}, in order to type assert safely, please use Diarkis' util package functions.
Example:
values, ok := r.GetProperties([]string{ "someKey" }) if !ok { // handle error here } for key, v := range values { // If the value data type is an uint8, of course ;) value, ok := util.ToUint8(v) }
func (*Room) GetProperty
func (r *Room) GetProperty(key string) (interface{}, bool)
GetProperty returns the value of the given key and if the key does not exist, the second return value will be a false.
[NOTE] Properties are only primitive values and does not support reference type data such as array and map. [NOTE] Uses mutex lock internally.
The returned property value is an interface{}, in order to type assert safely, please use Diarkis' util package functions.
Example:
v, ok := r.GetProperty("someKey") if !ok { // handle error here } // If the value data type is an uint8, of course ;) v, ok := util.ToUint8(v)
func (*Room) InitiateBackfill
func (r *Room) InitiateBackfill() error
InitiateBackfill triggers the backfill process.
func (*Room) IsBackfilling
func (r *Room) IsBackfilling() bool
IsBackfilling returns true if the backfill process is ongoing.
func (*Room) IsFull
func (r *Room) IsFull() bool
IsFull returns true if the room is full.
func (*Room) IsMemberReconnecting
func (r *Room) IsMemberReconnecting() (isReconnecting bool)
IsMemberReconnecting returns true if the user is in the process of reconnecting.
func (*Room) MakeReservation
func (r *Room) MakeReservation(userData *user.User) bool
MakeReservation allows the user to reserve a spot in the ticket room.
[NOTE] Uses mutex lock internally.
func (*Room) RemoveOnDeleted
func (r *Room) RemoveOnDeleted(callback func(id string))
RemoveOnDeleted removes the given callback from the room.
[NOTE] Uses mutex lock internally.
func (*Room) RemoveOnJoin
func (r *Room) RemoveOnJoin(callback func(id string, userData *user.User) bool)
RemoveOnJoin removes the given callback from the room.
[NOTE] Uses mutex lock internally.
func (*Room) RemoveOnJoined
func (r *Room) RemoveOnJoined(callback func(id string, userData *user.User))
RemoveOnJoined removes the given callback from the room.
[NOTE] Uses mutex lock internally.
func (*Room) RemoveOnLeft
func (r *Room) RemoveOnLeft(callback func(id string, userData *user.User))
RemoveOnLeft removes the given callback from the room.
[NOTE] Uses mutex lock internally.
func (*Room) SetOnCancelBackfill
func (r *Room) SetOnCancelBackfill(cb func(id string)) bool
SetOnCancelBackfill assigns a callback to be invoked at every backfill tick.
[NOTE] You may assign multiple callbacks to a room.
func (*Room) SetOnDeleted
func (r *Room) SetOnDeleted(cb func(id string)) bool
SetOnDeleted assigns a callback on ticket room deletion.
[NOTE] You may assign multiple callbacks to a room. [NOTE] Uses mutex lock internally.
func (*Room) SetOnInitiateBackfill
func (r *Room) SetOnInitiateBackfill(cb func(id string)) bool
SetOnInitiateBackfill assigns a callback to be invoked when a backfill is triggered.
[NOTE] You may assign multiple callbacks to a room.
func (*Room) SetOnJoin
func (r *Room) SetOnJoin(cb func(id string, userData *user.User) bool) bool
SetOnJoin assigns a callback on ticket room to be invoked when a new member is matched and attempting to join the match.
The callback returns a bool and if you return false, the matched user will be rejected and will not match and join.
[NOTE] You may assign multiple callbacks to a room. [NOTE] Uses mutex lock internally.
func (*Room) SetOnJoined
func (r *Room) SetOnJoined(cb func(id string, userData *user.User)) bool
SetOnJoined assigns a callback on ticket room to be invoked when a new member is matched.
[NOTE] You may assign multiple callbacks to a room. [NOTE] Uses mutex lock internally.
func (*Room) SetOnLeft
func (r *Room) SetOnLeft(cb func(id string, userData *user.User)) bool
SetOnLeft assigns a callback on ticket room to be invoked when a member of matched user leaves the match.
[NOTE] You may assign multiple callbacks to a room. [NOTE] Uses mutex lock internally.
func (*Room) SetOnTick
func (r *Room) SetOnTick(interval uint16, cb func(id string)) bool
SetOnTick assigns a callback to be invoked at every given interval.
[IMPORTANT] A single callback may be assigned per tick interval. You may not assign multiple callback a tick with the same interval. [NOTE] Uses mutex lock internally.
Parameters
interval - Tick interval in seconds. cb - Callback to be invoked at every tick.
func (*Room) SetOnTickStop
func (r *Room) SetOnTickStop(interval uint16, cb func(id string)) bool
SetOnTickStop assigns a callback to be invoked when a tick of the room stops.
[IMPORTANT] Only one callback may be assigned to a room. [NOTE] Uses mutex lock internally.
func (*Room) SetProperties
func (r *Room) SetProperties(data map[string]interface{})
SetProperties stores a collection of keys and their values to ticket room.
If the same key exists, it overwrites the existing value of the same key.
[NOTE] Properties are only primitive values and does not support reference type data such as array and map. [NOTE] Uses mutex lock internally.
func (*Room) SetProperty
func (r *Room) SetProperty(key string, value interface{})
SetProperty stores a key and value data to ticket room.
If the same key exists, it overwrites the existing value of the same key.
[NOTE] Properties are only primitive values and does not support reference type data such as array and map. [NOTE] Uses mutex lock internally.
func (*Room) SetPropertyIfNotExists
func (r *Room) SetPropertyIfNotExists(key string, value interface{}) bool
SetPropertyIfNotExists stores a key and value data to ticket room if the same key does not exist.
[NOTE] Properties are only primitive values and does not support reference type data such as array and map. [NOTE] Uses mutex lock internally.
func (*Room) StopAllTicks
func (r *Room) StopAllTicks() bool
StopAllTicks stops all tick loops.
[NOTE] Uses mutex lock internally.
func (*Room) UpdateProperties
func (r *Room) UpdateProperties(data map[string]interface{}, cb func(bool, interface{}, interface{}) interface{})
UpdateProperties changes the existing property values of ticket room.
The callback is invoked while the internal lock is still held, locking inside the callback may cause mutex deadlock.
[NOTE] Properties are only primitive values and does not support reference type data such as array and map. [NOTE] Uses mutex lock internally.
Parameters
data - A map of key and value pair to be stored as properties. cb - Callback to be invoked on every key and value pair to handle the update. func(exists bool, storedValue interface{}, updateValue interface{}) (updatedValue interface{}) - exists - Indicates if the same key already exists or not - storedValue - Existing value that is stored as a property. If the key does not exist it is a nil. - updateValue - The value to be used to update/replace or set.
func (*Room) UpdateProperty
func (r *Room) UpdateProperty(key string, value interface{}, cb func(bool, interface{}, interface{}) interface{})
UpdateProperty changes the existing property value of ticket room.
The callback is invoked while the internal lock is still held, locking inside the callback may cause mutex deadlock.
[NOTE] Properties are only primitive values and does not support reference type data such as array and map. [NOTE] Uses mutex lock internally.
Parameters
key - A key of the property to be updated. value - A value of the property to be updated with. cb - Callback to be invoked on every key and value pair to handle the update. func(exists bool, storedValue interface{}, updateValue interface{}) (updatedValue interface{}) - exists - Indicates if the same key already exists or not - storedValue - Existing value that is stored as a property. If the key does not exist it is a nil. - updateValue - The value to be used to update/replace or set.
type RoomBroadcastData
type RoomBroadcastData struct { ID string `json:"id"` Ver uint8 `json:"ver"` Cmd uint16 `json:"cmd"` Msg []byte `json:"msg"` MemberSIDs []string `json:"memberSIDs"` }
RoomBroadcastData represents internally used broadcast message data
type SearchData
type SearchData struct { MatchingID string `json:"matchingID"` Tag string `json:"tag"` Props map[string][]int `json:"props"` Limit int `json:"limit"` }
SearchData represents internally used matchmaking data
type SearchReturnData
type SearchReturnData struct { Results []*SearchReturnItemData `json:"results"` }
SearchReturnData represents internally used data
type SearchReturnItemData
type SearchReturnItemData struct { ID string `json:"id"` TTL int64 `json:"ttl"` Value interface{} `json:"value"` }
SearchReturnItemData represents internally used data
type Ticket
type Ticket struct { OnMatch func(ticket *Ticket, userData *user.User, owner *user.User, roomID string, memberIDs []string) bool OnMatchedMemberJoined func(ticket *Ticket, userData *user.User, owner *user.User, memberIDs []string) OnMatchedMemberLeave func(ticket *Ticket, userData *user.User, owner *user.User, roomID string, memberIDs []string) OnTimeout func(userData *user.User) OnMatchedMemberJoinedAnnounce func(ticket *Ticket, userData, owner *user.User, memberIDs []string) (ver uint8, cmd uint16, message []byte) OnMatchedMemberLeaveAnnounce func(ticket *Ticket, userData, owner *user.User, memberIDs []string) (ver uint8, cmd uint16, message []byte) // contains filtered or unexported fields }
Ticket represents a matchmaking ticket that manages a life cycle of issued ticket
OnMatch - Raised when a remote user matches. By returning true, you may complete the ticket and raise OnComplete (OnComplete event is captured by matching.SetOnComplete callback) OnMatchedMemberJoined - Raised when a matched member completes join. OnMatchedMemberJoinedAnnounce - Raised when a matched member completes join and returns ver, cmd, and message to be sent to all matched members. OnMatchedMemberLeaveAnnounce - Raised when a matched member leaves and returns ver, cmd, and message to be sent to all matched members. OnMatchedMemberLeave - Raised when a matched member user leave the match. OnTimeout - Raised when the ticket times out.
func FindTicket
func FindTicket(ticketType uint8, userData *user.User) *Ticket
FindTicket returns the valid matchmaking ticket that the user has.
[IMPORTANT] This function works with the owner of the ticket only. [IMPORTANT] IF the user given does not own a ticket, it returns nil. [NOTE] Uses mutex lock internally. ticketType - MatchMaker ticket type. userData - The owner user of the ticket.
func (*Ticket) GetRoomID
func (t *Ticket) GetRoomID() string
GetRoomID returns the room ID of the ticket.
func (*Ticket) GetTicketType
func (t *Ticket) GetTicketType() uint8
GetTicketType returns the ticket type of the *Ticket instance.
func (*Ticket) IsTicketFinished
func (t *Ticket) IsTicketFinished() bool
IsTicketFinished returns true if the ticket has finished its entire operations.
func (*Ticket) Start
func (t *Ticket) Start() bool
Start starts the life cycle of a ticket.
[NOTE] Uses mutex lock internally.
func (*Ticket) Stop
func (t *Ticket) Stop() bool
Stop interrupts the ticket and stops all matchmaking operations.
[NOTE] Uses mutex lock internally.
func (*Ticket) StopWithoutPropagate
func (t *Ticket) StopWithoutPropagate()
StopWithoutPropagate does NOT check the state of the ticket and does NOT propagate ticket cancel. This is used when the ticket is being migrated. Ticket.Stop delete function is not suited for migrate because it will propagate the ticket cancel to other servers.
type TicketHolder
type TicketHolder struct { AddProperties map[string]int SearchProperties map[string][]int ApplicationData []byte }
TicketHolder represents add and search properties of the ticket holder user.
AddProperties - Add properties of the ticket: Add properties are used to be found by other tickets. SearchProperties - Search properties of the ticket: Search properties are used to search for other tickets. ApplicationData - May hold byte array encoded application data that maybe added from the application.
type TicketParams
type TicketParams struct { ProfileIDs []string AddProfileIDs []string SearchProfileIDs []string Tags []string AddProperties map[string]int SearchProperties map[string][]int ApplicationData []byte MaxMembers uint8 TicketDuration uint8 SearchInterval uint16 TimeoutExtensionOnMatchJoin uint8 SearchTries uint8 EmptySearches uint8 HowMany uint8 }
TicketParams parameter struct for issueTicket
[IMPORTANT] AddProperties and SearchProperties are limited to have up to 2 properties.
Properties
ProfileIDs - A list of profiles to add to and search against. AddProfileIDs - Optional list of profiles to add. If this is used, ProfileIDs will be overridden for add. SearchProfileIDs - Optional list of profiles to search. If this is used, ProfileIDs will be overridden for search. Tags - A string of tag(s) to group matchmaking data by the same tag(s). Data with different tag do NOT match even with the matching properties. Leave it with an empty string if no need. AddProperties - Matchmaking properties (conditions) for add (being a host waiting) SearchProperties - Matchmaking properties to used for search Each property may contain a range of property values i.e. []int{ 1, 2, 3, 4, 5 } etc. The number of properties allowed is 2, if you exceed the number of properties, two properties will be randomly chosen. ApplicationData - May hold application data that maybe added from the application. ApplicationData must NOT be a struct or must NOT contain struct. MaxMembers - Maximum number of matchmaking users per matchmaking. When matched users reach this number, the matchmaking will complete as success TicketDuration - Duration of the ticket to be valid in seconds. Minimum value for TicketDuration is 10 seconds. SearchInterval - The interval for search in milliseconds TimeoutExtensionOnMatchJoin - Timeout extension in seconds to be added every time a new match joins. Leave it with 0 if no need. SearchTries - Number of empty search results to tolerate before giving up and moving on to hosting (add) EmptySearches - If the number of empty search results reach EmptySearches, the ticket will forcefully change to add phase. If 0 is given, this feature will be disabled. Default is 0. HowMany - Matchmaking profile IDs to use for search and add Leave this empty if you do not need to repeat the operation set.
Breaking down how a ticket works under the hood
A ticket has two phases. When you start a ticket, it starts as a search phase where it actively searches other tickets that are in waiting phase. Once a certain time passes and the search yields no matches, ticket then switches to waiting phase where it waits for other searching tickets to find it.
Diagram below visually explains how these two phases of a ticket work and how some of the parameters affect these two phases.
▶︎ SearchInterval 200ms ▶︎ SearchTries 10 ▶︎ TicketDuration 4s ▷ Search Phase: The ticket will search every 200ms 10 times ▷ Wait Phase: If the ticket does not find match, it will wait for the remainder of time until the ticket duration expires 200ms x 10 searches ┌────── Search Phase ──────┐ ┌─────── Waiting Phase ───────┐ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┰────────────────────────────────┐ │ │ │ │ │ │ │ │ │ │ ┃ │ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┸────────────────────────────────┘ Total 2 seconds of searching Total 2 seconds of waiting │ │ └─────────── Ticket duration is 4 seconds in total ────────────┘ This means that a ticket in search phase will only match with tickets in wait phase and vice versa.
TicketParams Tip
It usually helps to have randomized values for SearchInterval, SearchTries, and TicketDuration.
This is because every ticket strictly follows search → wait flow. Having every ticket with different search and wait durations will help tickets find other tickets.
type TicketProperties
type TicketProperties struct { sync.RWMutex // contains filtered or unexported fields }
TicketProperties represents both the owner of the matched ticket and the candidate to be matched.
It is primarily meant to be used for SetOnTicketAllowMatchIf callback.
Owner - Represents ticket owner's add and search properties (user that perform add/waiting). Owner add and search properties are pointers to the original properties and changing the values may influence the matchmaking. Candidates - A map of match candidate's add and search properties (user that performs searches) by candidate's UID as keys. Candidate add and search properties are pointers to the original properties and changing the values may influence the matchmaking.
func (*TicketProperties) GetAllCandidates
func (t *TicketProperties) GetAllCandidates() map[string]*TicketHolder
GetAllCandidates returns all candidates' *TicketHolder instances as a map. The key of the map is UID.
[NOTE] Returned map of *TicketHolder is a copy of the actual candidate ticket holders.
func (*TicketProperties) GetCandidateByUID
func (t *TicketProperties) GetCandidateByUID(uid string) (*TicketHolder, bool)
GetCandidateByUID returns the given UID's *TicketHolder instance. If the second value is false, there is no candidate that matches the given UID.
[NOTE] Returned *TicketHolder is a copy of the actual candidate ticket holder.
func (*TicketProperties) GetOwner
func (t *TicketProperties) GetOwner() *TicketHolder
GetOwner returns the ticket owner's *TicketHolder instance
[NOTE] Returned *TicketHolder is a copy of the actual candidate ticket holder.
func (*TicketProperties) IsBackfill
func (t *TicketProperties) IsBackfill() bool
IsBackfill returns true if the ticket is set as backfilling
func (*TicketProperties) MarkAsBackfill
func (t *TicketProperties) MarkAsBackfill()
MarkAsBackfill marks the ticket as backfilling
type UpdateUserData
type UpdateUserData struct { RoomID string `json:"id"` UserID string `json:"ID"` SID string `json:"SID"` PublicAddr string `json:"PublicAddr"` PrivateAddrBytes []byte `json:"PrivateAddrBytes"` MeshAddr string `json:"meshAddr"` }
UpdateUserData represents internally used user data update