Core components
Networking & multiplayer infra...
Instance lifecycle management
25 min
overview the instance lifecycle management system is a core component of the ir engine's multiplayer infrastructure that handles the creation, initialization, operation, and termination of game server instances it ensures that game environments are efficiently provisioned when needed and properly released when no longer in use by managing the complete lifecycle of server instances, this system enables dynamic scaling of multiplayer games while optimizing resource utilization this chapter explores the implementation, stages, and workflow of instance lifecycle management within the ir engine core concepts game instances in the ir engine, an instance represents a discrete game environment game session a specific match or gameplay session (e g , a 5v5 team match) world shard a portion of a larger persistent game world media room a communication space with audio/video capabilities dedicated environment a self contained space for specific gameplay activities resource boundary a unit of server resources allocated to a specific purpose each instance operates independently with its own state, players, and resources lifecycle stages game instances progress through several defined stages request and creation the initial request and allocation of server resources initialization setting up the game environment and required systems ready state the instance is prepared and available for players to join active gameplay the instance is hosting active gameplay with connected players updates and maintenance modifications to the running instance as needed shutdown and cleanup termination of the instance and release of resources these stages ensure proper management of server resources throughout the instance's existence orchestration integration the lifecycle management system often integrates with container orchestration platforms kubernetes container orchestration for managing server deployments agones game server specific extension for kubernetes fleet management grouping and scaling of similar server instances health monitoring tracking the status and performance of instances resource allocation efficient distribution of computing resources this integration enables scalable and reliable deployment of game servers implementation instance state management the system tracks instance state through a dedicated state structure // from src/instanceserverstate ts import { definestate } from '@ir engine/hyperflux'; import { instancetype } from ' /types/instancetype'; export const instanceserverstate = definestate({ name 'ee instanceserver instanceserverstate', initial { // whether the instance is ready for players ready false, // details about this specific instance instance null! as instancetype, // whether this is a media only instance ismediainstance false, // the current scene being hosted currentscene null! as { name string, sceneid string }, // connection information address '', port 1, // other state properties as needed } }); this state structure tracks whether the instance is ready for players stores details about the specific instance being hosted indicates whether it's a media only instance maintains information about the current scene records network connection details provides a central reference point for instance status instance initialization the initialization process prepares a server for hosting a specific game instance // simplified from src/channels ts async function initializeinstance(app, instanceid) { const logger = app get('logger'); logger info(`initializing instance ${instanceid}`); // get instance data from the database const instanceresult = await app service('instance') get(instanceid); // update the instance state const instanceserverstate = getmutablestate(instanceserverstate); instanceserverstate instance set(instanceresult); // if using kubernetes/agones, mark this server as allocated if (config kubernetes enabled) { const serverstate = app get('serverstate'); await serverstate agonessdk allocate(); logger info('agones server allocated'); } // initialize the game engine await initializeengine(app); // load the appropriate scene await loadscene(instanceresult sceneid); // mark the instance as ready instanceserverstate ready set(true); logger info(`instance ${instanceid} initialized and ready`); return instanceresult; } this function retrieves instance data from the database updates the instance state with the retrieved data allocates the server through agones if using kubernetes initializes the game engine with appropriate settings loads the required scene for this instance marks the instance as ready for players to join scene loading loading the appropriate scene is a critical part of instance initialization // simplified from src/channels ts async function loadscene(sceneid) { const logger = app get('logger'); logger info(`loading scene ${sceneid}`); // get scene data from the database const sceneresult = await app service('scene') get(sceneid); // update the current scene in the instance state const instanceserverstate = getmutablestate(instanceserverstate); instanceserverstate currentscene set({ name sceneresult name, sceneid sceneresult id }); // load the scene in the engine scenestate loadscene(sceneresult url, sceneresult id); // initialize scene specific systems initializescenesystems(sceneresult); logger info(`scene ${sceneid} loaded successfully`); } this function retrieves scene data from the database updates the current scene information in the instance state loads the scene into the game engine initializes any scene specific systems or components logs the successful scene loading user connection handling the system manages user connections to instances // simplified from src/channels ts async function updateinstance(app, user, instanceid) { const logger = app get('logger'); // check if the instance exists const instanceresult = await app service('instance') get(instanceid); if (!instanceresult) { throw new error(`instance ${instanceid} not found`); } // get the current instance state const instanceserverstate = getstate(instanceserverstate); // if the instance is not initialized, initialize it if (!instanceserverstate instance || instanceserverstate instance id !== instanceid) { await initializeinstance(app, instanceid); } // check if the instance is ready if (!instanceserverstate ready) { throw new error(`instance ${instanceid} is not ready`); } // check if the user is authorized to join this instance if (!isuserauthorized(user, instanceresult)) { throw new error(`user ${user id} is not authorized to join instance ${instanceid}`); } // add the user to the instance await addusertoinstance(app, user, instanceid); logger info(`user ${user id} connected to instance ${instanceid}`); return instanceresult; } this function verifies that the requested instance exists checks if the instance is already initialized on this server initializes the instance if necessary ensures the instance is ready for players verifies that the user is authorized to join adds the user to the instance returns the instance details user disconnection and cleanup the system handles user disconnections and potential instance shutdown // simplified from src/channels ts async function handleuserdisconnect(app, user) { const logger = app get('logger'); logger info(`user ${user id} disconnected`); // get the current instance state const instanceserverstate = getstate(instanceserverstate); const instanceid = instanceserverstate instance? id; if (!instanceid) { logger warn('no active instance found during user disconnect'); return; } // remove the user from the instance await removeuserfrominstance(app, user, instanceid); // check if the instance is now empty const connectedusers = await getinstanceusers(app, instanceid); // if the instance is empty and configured for auto shutdown, shut it down if (connectedusers length === 0 && instanceserverstate instance? autoshutdownwhenempty) { logger info(`instance ${instanceid} is empty, initiating shutdown`); await shutdownserver(app, instanceid); } } this function logs the user disconnection identifies the current instance removes the user from the instance checks if the instance is now empty initiates server shutdown if the instance is empty and configured for auto shutdown server shutdown the shutdown process terminates an instance and releases resources // simplified from src/channels ts async function shutdownserver(app, instanceid) { const logger = app get('logger'); logger info(`shutting down instance ${instanceid}`); // mark the instance as ended in the database await app service('instance') patch(instanceid, { ended true, endedat new date() }); // disconnect any remaining users await disconnectallusers(app, instanceid); // perform any necessary cleanup await cleanupinstance(app, instanceid); // if using kubernetes/agones, tell it we're shutting down if (config kubernetes enabled) { const serverstate = app get('serverstate'); await serverstate agonessdk shutdown(); logger info('agones server shutdown initiated'); } logger info(`instance ${instanceid} shutdown complete`); } this function marks the instance as ended in the database disconnects any remaining users performs necessary cleanup operations signals to agones that the server is shutting down logs the completion of the shutdown process server process initialization the server process itself is initialized when it first starts // simplified from src/start ts import { agonessdk } from '@google cloud/agones sdk'; async function start() { const logger = app get('logger'); logger info('starting instance server'); // initialize the application const app = await createapp(); // if using kubernetes/agones, connect to the agones sdk if (config kubernetes enabled) { const agonessdk = new agonessdk(); // connect to the agones system await agonessdk connect(); logger info('connected to agones sdk'); // tell agones this server is ready to host a game await agonessdk ready(); logger info('reported ready status to agones'); // set up a health check heartbeat setinterval(() => agonessdk health(), 1000); // store the sdk for later use app set('serverstate', { agonessdk }); } // start listening for connections const server = app listen(app get('port')); logger info(`instance server listening on port ${app get('port')}`); return { app, server }; } this function creates the application instance connects to the agones sdk if using kubernetes reports ready status to agones sets up a health check heartbeat starts listening for connections returns the application and server instances instance lifecycle workflow the complete instance lifecycle follows this sequence sequencediagram participant player participant api as api server participant ilm as instance lifecycle manager participant agones participant instance as game instance api >>ilm request new instance (gamemode, sceneid) ilm >>agones allocate server agones >>ilm server allocated (address, port) ilm >>instance initialize (instanceid) instance >>instance load scene instance >>instance set up networking instance >>instance initialize game systems instance >>ilm ready status (instanceserverstate ready = true) ilm >>api instance ready (address, port) api >>player connection details player >>instance connect to instance note over instance active gameplay player >>instance disconnect instance >>instance check if empty alt instance is empty instance >>instance initiate shutdown instance >>agones report shutdown instance >>instance cleanup resources agones >>ilm server released end this diagram illustrates the api server requests a new instance the instance lifecycle manager allocates a server through agones the instance initializes and loads the required scene the instance reports ready status the player connects to the instance active gameplay occurs the player disconnects if the instance is empty, shutdown is initiated the instance reports shutdown to agones resources are cleaned up and released integration with other components the instance lifecycle management system integrates with several other components of the multiplayer infrastructure mediasoup webrtc the webrtc communication system is initialized during instance startup // simplified integration with mediasoup async function initializeengine(app) { // initialize the webrtc system const { routers, workers } = await startwebrtc(); app set('routers', routers); app set('workers', workers); // register the mediasoup server system engine instance systemregistry add(mediasoupserversystem); logger info('webrtc communication system initialized'); } this integration initializes the mediasoup webrtc system during instance startup makes communication capabilities available to connected players ensures real time interaction is possible within the instance user connection and authorization the instance lifecycle interacts with user authentication // simplified integration with user connection app on('connection', (connection) => { // when a user connects connection on('login', async (authresult) => { try { // authenticate the user const user = await authenticateuser(authresult); // get the requested instance id const { instanceid } = connection handshake query; // update or initialize the instance await updateinstance(app, user, instanceid); // notify the user of successful connection connection emit('instance ready', { status 'success', instanceid }); } catch (error) { connection emit('instance ready', { status 'error', message error message }); } }); // when a user disconnects connection on('disconnect', async () => { const user = connection user; if (user) { await handleuserdisconnect(app, user); } }); }); this integration processes user authentication when connecting to an instance ensures the instance is initialized and ready handles user disconnection and potential instance shutdown maintains the relationship between users and instances feathersjs services the instance lifecycle uses feathersjs services for data management // simplified integration with feathersjs services async function initializeinstance(app, instanceid) { // get instance data from the database using the instance service const instanceresult = await app service('instance') get(instanceid); // get scene data using the scene service const sceneresult = await app service('scene') get(instanceresult sceneid); // update instance status using the instance service await app service('instance') patch(instanceid, { status 'active', currentusers 0 }); // other initialization steps } this integration uses feathersjs services to access and update instance data maintains consistent state between the server and database leverages the service architecture for data operations benefits of instance lifecycle management the instance lifecycle management system provides several key advantages resource efficiency ensures server resources are only used when needed dynamic scaling supports creating and removing instances based on demand isolation keeps game sessions separate and independent reliability handles the complete lifecycle including proper cleanup orchestration integrates with container orchestration for robust deployment automation reduces manual intervention in server management observability provides visibility into instance status and health these benefits make the instance lifecycle management system an essential component for creating scalable and efficient multiplayer games next steps with an understanding of how game instances are managed, the next chapter explores how users connect to these instances and get authorized to participate next user connection and authorization docid\ l88afdioqjhcb9adle9gz