Core components
Networking & multiplayer infra...
Hyperflux state & action system
27 min
overview the hyperflux state & action system is a core component of the ir engine's multiplayer infrastructure that provides a reactive state management solution for tracking and synchronizing application state it enables different parts of the system to maintain awareness of current conditions and coordinate responses to events in a structured manner by implementing a centralized approach to state management with a clear action dispatch mechanism, hyperflux creates a predictable data flow that enhances maintainability and scalability this chapter explores the implementation, patterns, and benefits of the hyperflux system within the ir engine core concepts state management hyperflux provides a structured approach to managing application state state containers dedicated objects that store specific pieces of information immutability state changes create new state objects rather than modifying existing ones centralization single source of truth for each piece of state reactivity automatic notifications when state changes accessibility consistent patterns for accessing state throughout the application this approach creates a predictable and maintainable state management system action based communication actions serve as the primary mechanism for signaling events and requesting changes action definition structured objects that describe what happened or what should happen action dispatch a process for sending actions to the system action queues collections of actions waiting to be processed type safety strongly typed actions with defined payloads traceability clear path of action creation and handling this pattern creates a decoupled communication system between components reactive systems reactive systems (or reactors) respond to state changes and actions continuous monitoring systems that watch for specific state changes or actions selective processing handling only relevant actions or state updates side effects performing operations in response to changes state updates modifying state based on actions or other state changes component coordination enabling different parts of the system to work together this reactive approach creates a dynamic and responsive application architecture implementation state definition states are defined using a declarative pattern // 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 } }); this code defines a state container named instanceserverstate specifies the initial values for each state property uses typescript for type safety creates a centralized location for instance related state state access and modification hyperflux provides utilities for accessing and modifying state // example of state access and modification import { getstate, getmutablestate } from '@ir engine/hyperflux'; import { instanceserverstate } from ' /instanceserverstate'; // read only access to state function checkifinstanceready() boolean { const instancestate = getstate(instanceserverstate); return instancestate ready; } // modifying state function markinstanceasready() void { const mutableinstancestate = getmutablestate(instanceserverstate); mutableinstancestate ready set(true); console log('instance is now ready for players'); } this code uses getstate for read only access to state uses getmutablestate for modifying state provides a clear distinction between reading and writing follows a consistent pattern for state operations action definition actions are defined using a structured pattern // example of action definitions import { defineaction } from '@ir engine/hyperflux'; // define a namespace for related actions export const mediasoupmediaproduceractions = { // action to request a new producer requestproducer defineaction({ type 'ee mediasoup producer request', // unique type identifier // function to create the action with payload creator (payload { networkid string; peerid string; kind 'audio' | 'video'; rtpparameters any; appdata? any; }) => { return { payload }; } }), // action to notify that a producer was created producercreated defineaction({ type 'ee mediasoup producer created', creator (payload { networkid string; peerid string; producerid string; kind 'audio' | 'video'; }) => { return { payload }; } }) }; this code defines a namespace for related actions creates actions with unique type identifiers specifies the payload structure for each action provides creator functions for type safe action creation action dispatch actions are dispatched to signal events or request changes // example of action dispatch import { dispatchaction } from '@ir engine/hyperflux'; import { mediasoupmediaproduceractions } from ' /mediasoupmediaproduceractions'; // function to handle a client request to produce media function handleclientproducerrequest( networkid string, peerid string, kind 'audio' | 'video', rtpparameters any ) void { // dispatch an action to request a producer dispatchaction(mediasoupmediaproduceractions requestproducer({ networkid, peerid, kind, rtpparameters })); console log(`dispatched request for ${kind} producer from peer ${peerid}`); } this code uses dispatchaction to send an action to the system creates the action using the defined creator function includes all necessary payload data follows a consistent pattern for action dispatch action queues action queues collect actions for processing // example of action queue definition import { defineactionqueue } from '@ir engine/hyperflux'; import { mediasoupmediaproduceractions } from ' /mediasoupmediaproduceractions'; // create a queue for producer request actions const requestproduceractionqueue = defineactionqueue( mediasoupmediaproduceractions requestproducer matches ); // create a queue for producer created actions const producercreatedactionqueue = defineactionqueue( mediasoupmediaproduceractions producercreated matches ); this code creates queues for specific action types uses action matchers to filter actions provides a way to collect actions for processing separates action dispatch from action handling reactive systems systems react to actions and state changes // simplified from src/mediasoupserversystem tsx import { definesystem, usehookstate } from '@ir engine/hyperflux'; import { mediasoupmediaproduceractions } from ' /mediasoupmediaproduceractions'; import { mediasoupmediaproducerstate } from ' /mediasoupmediaproducerstate'; // define action queues const requestproduceractionqueue = defineactionqueue( mediasoupmediaproduceractions requestproducer matches ); // define the system export const mediasoupserversystem = definesystem({ uuid 'ee instanceserver mediasoupserversystem', execute () => { // process all producer request actions for (const action of requestproduceractionqueue()) { handlerequestproducer(action); } // other processing logic } }); // handler for producer requests async function handlerequestproducer(action) { const { networkid, peerid, kind, rtpparameters } = action payload; try { // get the transport for this peer const transport = gettransportforpeer(networkid, peerid); // create the producer const producer = await transport produce({ kind, rtpparameters }); // update state const producerstate = getmutablestate(mediasoupmediaproducerstate); producerstate producers\[networkid] merge({ \[producer id] { id producer id, peerid, kind } }); // dispatch a success action dispatchaction(mediasoupmediaproduceractions producercreated({ networkid, peerid, producerid producer id, kind })); console log(`created ${kind} producer ${producer id} for peer ${peerid}`); } catch (error) { console error(`failed to create producer `, error); // handle error } } this code defines a system with a unique identifier implements an execute function that runs regularly processes actions from the action queue updates state based on action results dispatches new actions to signal completion react style components hyperflux supports react style components for reactive logic // example of a react style component import { usehookstate } from '@ir engine/hyperflux'; import { networkstate } from ' /networkstate'; // component that reacts to network changes const networkmonitor = () => { // subscribe to network state const networks = usehookstate(getmutablestate(networkstate) networks); // effect that runs when networks change useeffect(() => { console log('network state changed ', networks value); // check for disconnected peers for (const \[networkid, network] of object entries(networks value)) { for (const \[peerid, peer] of object entries(network peers)) { if (peer connected && date now() peer lastseents > 10000) { console log(`peer ${peerid} may be disconnected`); // handle potential disconnection } } } }, \[networks]); // this component doesn't render anything return null; }; this code uses usehookstate to subscribe to state changes implements a useeffect hook that runs when state changes performs logic in response to state changes follows react patterns for reactive programming state and action workflow the complete hyperflux workflow follows this sequence sequencediagram participant client as game client participant server as server endpoint participant dispatcher as action dispatcher participant queue as action queue participant system as reactive system participant state as state container client >>server request (e g , start sending video) server >>dispatcher dispatch action (requestproducer) dispatcher >>queue add action to queue loop system execution cycle system >>queue check for actions queue >>system return queued actions alt actions found system >>system process action system >>state update state state >>system state updated system >>dispatcher dispatch result action end end state >>server react to state change server >>client send response this diagram illustrates the client sends a request to the server the server dispatches an action the action is added to a queue a system checks for actions during its execution cycle the system processes the action and updates state the system may dispatch a result action other components react to the state change the server sends a response to the client example microphone muting a practical example of hyperflux in action is microphone muting // example of microphone muting with hyperflux import { dispatchaction, getmutablestate } from '@ir engine/hyperflux'; import { mediaactions } from ' /mediaactions'; import { usermediastate } from ' /usermediastate'; // action definition const mediaactions = { togglemute defineaction({ type 'ee media toggle mute', creator (payload { userid string }) => ({ payload }) }) }; // state definition const usermediastate = definestate({ name 'ee media usermediastate', initial {} as record\<string, { userid string, ismuted boolean, producerid string }> }); // client request handler function handlemuterequest(userid string) { // dispatch the toggle mute action dispatchaction(mediaactions togglemute({ userid })); } // system that handles mute actions const mediasystem = definesystem({ uuid 'ee media mediasystem', execute () => { // process toggle mute actions for (const action of defineactionqueue(mediaactions togglemute matches)()) { const { userid } = action payload; // get the current state const usermediastate = getmutablestate(usermediastate); const usermedia = usermediastate\[userid]; if (usermedia) { // toggle the mute state const newmutestate = !usermedia ismuted; usermediastate\[userid] ismuted set(newmutestate); // get the producer const producer = getproducerbyid(usermedia producerid); // pause or resume the producer if (newmutestate) { producer pause(); } else { producer resume(); } console log(`user ${userid} microphone ${newmutestate ? 'muted' 'unmuted'}`); } } } }); this example defines an action for toggling mute state creates a state container for user media information implements a handler for client mute requests defines a system that processes mute actions updates state and performs the actual muting operation integration with other components the hyperflux system integrates with several other components of the multiplayer infrastructure instance lifecycle management hyperflux manages instance state // example of instance lifecycle integration import { getmutablestate } from '@ir engine/hyperflux'; import { instanceserverstate } from ' /instanceserverstate'; // function to initialize an instance async function initializeinstance(instanceid string) { // get instance data const instancedata = await fetchinstancedata(instanceid); // update instance state const instancestate = getmutablestate(instanceserverstate); instancestate instance set(instancedata); // load the scene await loadscene(instancedata sceneid); // mark the instance as ready instancestate ready set(true); console log(`instance ${instanceid} initialized and ready`); } this integration uses hyperflux state to track instance status updates state as the instance progresses through its lifecycle enables other components to react to instance state changes provides a centralized source of truth for instance information webrtc communication hyperflux coordinates webrtc operations // example of webrtc integration import { dispatchaction, getmutablestate } from '@ir engine/hyperflux'; import { mediasouptransportstate } from ' /mediasouptransportstate'; import { mediasoupmediaproduceractions } from ' /mediasoupmediaproduceractions'; // function to create a webrtc transport async function createtransport(networkid string, direction 'send' | 'recv') { // create the transport const transport = await router createwebrtctransport({ listenips \[{ ip '0 0 0 0', announcedip null }], enableudp true, enabletcp true, preferudp true }); // update transport state const transportstate = getmutablestate(mediasouptransportstate); transportstate transports\[networkid]\[direction] merge({ id transport id, iceparameters transport iceparameters, icecandidates transport icecandidates, dtlsparameters transport dtlsparameters }); console log(`created ${direction} transport ${transport id} for network ${networkid}`); return transport; } // function to handle a producer request function handleproducerrequest(networkid string, peerid string, kind 'audio' | 'video', rtpparameters any) { // dispatch an action to request a producer dispatchaction(mediasoupmediaproduceractions requestproducer({ networkid, peerid, kind, rtpparameters })); } this integration uses hyperflux state to store webrtc transport information dispatches actions to request media producers enables coordination between webrtc components maintains a consistent approach to state management user connection and authorization hyperflux tracks user connection state // example of user connection integration import { getmutablestate } from '@ir engine/hyperflux'; import { networkstate } from ' /networkstate'; // function to handle a user connection function handleuserconnection(networkid string, peerid string, userid string) { // update network state const networkstate = getmutablestate(networkstate); networkstate networks\[networkid] peers\[peerid] merge({ userid, connected true, lastseents date now() }); console log(`user ${userid} connected as peer ${peerid} on network ${networkid}`); } // function to handle a user disconnection function handleuserdisconnection(networkid string, peerid string) { // update network state const networkstate = getmutablestate(networkstate); // check if the peer exists if (networkstate networks\[networkid]? peers\[peerid]) { // get the user id before removing const userid = networkstate networks\[networkid] peers\[peerid] userid value; // mark as disconnected networkstate networks\[networkid] peers\[peerid] connected set(false); console log(`user ${userid} disconnected (peer ${peerid} on network ${networkid})`); } } this integration uses hyperflux state to track user connections updates state when users connect or disconnect enables other components to react to connection changes provides a centralized record of connected users benefits of hyperflux the hyperflux state & action system provides several key advantages decoupling components interact through state and actions rather than direct dependencies centralization provides a single source of truth for application state predictability creates a clear flow of data and actions reactivity enables automatic responses to state changes testability makes it easier to test components in isolation scalability supports complex state management as the application grows traceability provides clear paths for debugging state changes these benefits make hyperflux an essential foundation for the ir engine's multiplayer infrastructure next steps with an understanding of how the system manages state and actions, the next chapter explores a specific application of these concepts in media stream recording next media stream recording docid\ kmm8wwcdv0ua7i0ujs e