Specialized components
Matchmaking system
Match assignment
22 min
overview the match assignment is the final output of the ir engine's matchmaking system that provides players with the information needed to join their game session it contains the connection details for the assigned game server and any additional game specific information required by the client by delivering this crucial information to players, match assignments complete the matchmaking process, enabling players to transition from searching for a game to actively participating in one this chapter explores the structure, delivery, and usage of match assignments within the matchmaking architecture core concepts connection information the primary purpose of a match assignment is to provide connection information server address the ip address or hostname of the game server port number the network port for connecting to the server connection string often formatted as "address \ port " (e g , "192 168 1 100 7777") protocol details sometimes includes information about the connection protocol authentication tokens may include tokens for secure server access this connection information enables the game client to establish a network connection to the assigned game server assignment delivery match assignments are delivered to players through a defined process polling clients periodically check for assignments using their ticket id response handling clients process the assignment when received connection initiation clients use the assignment to connect to the game server timeout handling clients manage cases where assignments are delayed or never arrive error recovery clients handle connection failures and retry mechanisms this delivery process ensures that players receive their assignments and can join their games implementation assignment structure the match assignment is defined with a specific schema // from src/match ticket assignment schema ts import { type } from '@sinclair/typebox'; export const matchticketassignmentschema = type object( { // the game server connection information connection type string(), // optional additional information extensions type optional( type record( type string(), // extension key type object({ // extension value typeurl type string(), // type information value type string() // encoded value }) ) ) }, { additionalproperties false } ); // type definition for typescript export type matchticketassignmenttype = { connection string; extensions? record\<string, { typeurl string; value string; }>; }; this structure defines the connection field for server address information provides an extensions field for additional game specific data uses typescript types for type safety in the client code restricts additional properties for consistent data handling retrieving assignments clients retrieve match assignments by polling the frontend service // from src/functions ts import { matchticketassignmenttype } from ' /match ticket assignment schema'; / gets the assignment for a specific ticket @param ticketid the id of the ticket to check @returns a promise resolving to the ticket assignment (if any) / async function getticketsassignment(ticketid string) promise\<matchticketassignmenttype> { console log(`checking assignment for ticket ${ticketid}`); try { // create an abortcontroller for timeout handling const controller = new abortcontroller(); const timeoutid = settimeout(() => controller abort(), 10000); // 10 second timeout // send get request to check for assignment const response = await fetch( `${frontend service url}/tickets/${ticketid}/assignments`, { signal controller signal } ); // clear the timeout cleartimeout(timeoutid); // check for errors if (!response ok) { throw new error(`http error ${response status}`); } // parse the response const data = await readstreamfirstdata(response body); return data as matchticketassignmenttype; } catch (error) { if (error name === 'aborterror') { console error('request timed out'); return { connection '' } as matchticketassignmenttype; } console error('failed to get ticket assignment ', error); throw new error('failed to get ticket assignment'); } } / helper function to read the first chunk of data from a stream / async function readstreamfirstdata(stream) { const reader = stream getreader(); const { value, done } = await reader read(); reader releaselock(); if (done) { return null; } // parse the json data const text = new textdecoder() decode(value); return json parse(text); } this function takes a ticket id parameter that identifies the player's match request sends an http get request to the frontend service's assignments endpoint includes timeout handling to prevent indefinite waiting parses the response to extract assignment information returns an assignment object that may contain connection details if a match was found polling for assignments since matchmaking takes time, clients typically poll for assignments // example of assignment polling in a game client async function waitforgameassignment(ticketid string, maxattempts number = 30) promise\<string> { console log(`waiting for game assignment for ticket ${ticketid}`); // initial check let assignment = await getticketsassignment(ticketid); let attempts = 0; // keep checking until we get an assignment or reach max attempts while (assignment connection === '' && attempts < maxattempts) { console log(`no assignment yet, waiting (attempt ${attempts + 1}/${maxattempts}) `); // wait before trying again await new promise(resolve => settimeout(resolve, 5000)); // 5 second delay // check again assignment = await getticketsassignment(ticketid); attempts++; } // check if we got an assignment if (assignment connection !== '') { console log(`game assignment received ${assignment connection}`); return assignment connection; } else { console log('failed to get game assignment within the time limit'); throw new error('matchmaking timeout'); } } this function takes a ticket id and an optional maximum number of attempts checks for an assignment initially if no assignment is found, waits and checks again periodically continues until an assignment is received or the maximum attempts are reached returns the connection string when an assignment is found throws an error if no assignment is received within the time limit assignment creation the director creates match assignments when finalizing matches // simplified from open match custom pods/director/main go func assign(be pb backendserviceclient, p pb matchprofile, matches \[] pb match) error { for , match = range matches { // extract ticket ids from the match ticketids = \[]string{} for , ticket = range match gettickets() { ticketids = append(ticketids, ticket id) } // get game mode from profile extensions gamemode = "" if p extensions != nil { if profiledatamsg, ok = p extensions\["profiledata"]; ok { profiledata = \&common profiledatamessage{} if err = profiledatamsg unmarshalto(profiledata); err == nil { gamemode = profiledata getmode() } } } // get server connection information // in a real system, this would come from a game server allocation service serverconnectioninfo = "gameserver " + uuid new() string() + " 7777" // create game mode extension gamemodevalue = \&wrappers stringvalue{value gamemode} gamemodeany, err = anypb new(gamemodevalue) if err != nil { return fmt errorf("failed to marshal game mode %v", err) } // create the assignment assignment = \&pb assignment{ // connection information for the game server connection serverconnectioninfo, // additional game specific information extensions map\[string] anypb any{ "gamemode" gamemodeany, }, } // create the assignment request req = \&pb assignticketsrequest{ assignments \[] pb assignmentgroup{{ ticketids ticketids, assignment assignment, }}, } // send the assignment request to open match backend if , err = be assigntickets(context background(), req); err != nil { return fmt errorf("failed to assign tickets %v", err) } log printf("assigned tickets %v to server %s", ticketids, serverconnectioninfo) } return nil } this function processes each match proposal individually extracts the ticket ids from the match retrieves the game mode from the profile extensions determines the game server connection information creates an assignment with the connection information and game mode sends the assignment to the open match backend logs the assignment for monitoring and debugging assignment workflow the complete assignment workflow follows this sequence sequencediagram participant client as game client participant frontend as frontend service participant backend as open match backend participant director participant server as game server client >>frontend create ticket (post /tickets) frontend >>backend register ticket backend >>frontend ticket created frontend >>client return ticket id loop until assignment received or timeout client >>frontend check assignment (get /tickets/{id}/assignments) frontend >>backend query assignment backend >>frontend no assignment yet frontend >>client empty connection client >>client wait and retry end note over director,backend meanwhile director >>backend fetchmatches(profile) backend >>director match proposals director >>server allocate server for match server >>director server connection info director >>backend assigntickets(ticketids, serverinfo) client >>frontend check assignment (get /tickets/{id}/assignments) frontend >>backend query assignment backend >>frontend assignment found frontend >>client server connection info client >>server connect to game server this diagram illustrates the client creates a ticket and receives a ticket id the client periodically checks for an assignment initially, no assignment is available the director processes match proposals and assigns tickets to a server the client checks again and receives the assignment the client uses the connection information to join the game server using assignments in game clients game clients use assignments to connect to game servers // example of using assignments in a game client async function joingame() { try { // step 1 create a match ticket console log("finding a game "); const ticket = await createticket("capturetheflag"); console log(`ticket created with id ${ticket id}`); // step 2 wait for an assignment console log("waiting for game assignment "); const serveraddress = await waitforgameassignment(ticket id); // step 3 connect to the game server console log(`connecting to game server ${serveraddress}`); // parse the connection string const \[host, portstr] = serveraddress split(' '); const port = parseint(portstr, 10); // connect to the server (implementation depends on the game) const connection = await connecttogameserver(host, port); console log("connected to game server successfully!"); return connection; } catch (error) { console error("failed to join game ", error); throw error; } } // example game server connection function async function connecttogameserver(host string, port number) { // implementation depends on the game's networking library // this is just a placeholder example return new promise((resolve, reject) => { try { const connection = new gameconnection(); connection connect(host, port, { onconnect () => resolve(connection), onerror (err) => reject(err) }); } catch (error) { reject(error); } }); } this implementation creates a match ticket for the desired game mode waits for an assignment by polling the frontend service parses the connection string to extract the host and port establishes a connection to the game server handles errors that might occur during the process integration with other components the match assignment integrates with several other components of the matchmaking system frontend service the frontend service delivers assignments to clients // example of frontend service integration import express from 'express'; import { openmatchclient } from ' /open match client'; const app = express(); const openmatch = new openmatchclient(); // endpoint to get assignments for a ticket app get('/tickets/\ ticketid/assignments', async (req, res) => { try { const { ticketid } = req params; // get the assignment from open match const assignment = await openmatch getassignment(ticketid); // if no assignment is found, return an empty connection if (!assignment) { return res json({ connection '' }); } // return the assignment return res json({ connection assignment connection, extensions assignment extensions }); } catch (error) { console error(`error getting assignment for ticket ${req params ticketid} `, error); return res status(500) json({ error 'failed to get assignment' }); } }); this integration provides an endpoint for clients to check for assignments retrieves assignments from open match returns assignments in a standardized format handles error cases appropriately director the director creates assignments when finalizing matches // example of director integration import ( "github com/google/uuid" "open match dev/open match/pkg/pb" ) // function to create an assignment func createassignment(serverinfo string, gamemode string) ( pb assignment, error) { // create game mode extension gamemodevalue = \&wrappers stringvalue{value gamemode} gamemodeany, err = anypb new(gamemodevalue) if err != nil { return nil, err } // create the assignment assignment = \&pb assignment{ connection serverinfo, extensions map\[string] anypb any{ "gamemode" gamemodeany, }, } return assignment, nil } this integration creates assignments with server connection information includes game specific information in extensions formats assignments according to the open match protocol provides a consistent structure for assignments game server allocation in a production environment, the director would integrate with a game server allocation service // example of game server allocation integration import ( "context" "github com/example/gameserver allocator/client" ) // function to allocate a game server func allocategameserver(gamemode string, playercount int) (string, error) { // create allocation client allocator = client newallocatorclient() // request a game server allocation, err = allocator allocateserver(context background(), \&client allocationrequest{ gamemode gamemode, playercount playercount, }) if err != nil { return "", err } // return the connection string return allocation connectionstring, nil } this integration requests a game server from an allocation service provides game specific requirements for the server receives connection information for the allocated server includes the connection information in the assignment benefits of match assignments the match assignment system provides several key advantages connection facilitation provides the essential information for clients to join game servers decoupling separates matchmaking from game server management extensibility supports additional game specific information through extensions standardization creates a consistent format for server connection information asynchronous operation allows matchmaking to proceed independently of client polling scalability supports large numbers of concurrent matchmaking requests flexibility accommodates various game server deployment models these benefits make match assignments an essential component for completing the matchmaking process and connecting players to their games conclusion the match assignment represents the successful culmination of the matchmaking process it provides players with the essential connection information needed to join their assigned game server, completing the journey from requesting a match to actively participating in a game session throughout this documentation, we've explored the complete matchmaking system the frontend service interaction that handles player requests the match ticket that represents a player's desire to join a game the match profile that defines the requirements for valid matches the match function that groups players into potential matches the director that orchestrates the matchmaking process the match assignment that connects players to game servers together, these components form a comprehensive matchmaking system that efficiently connects players for multiplayer gaming experiences