Managing Room User State
This guide details how to manage per-user state within a room in Agent8. Room user state lets you store and update individualized profiles, settings, or any other room-scoped data. You can access and modify these states using the following room API functions:
$room.getMyState()$room.updateMyState(state: Object)$room.getUserState(account: string)$room.updateUserState(account: string, state: Object)
By default, every room user state contains an account field that uniquely identifies the user.
Every room user state contains an account field holding the
user’s unique identifier.
Server API
The server API offers the following methods to manage room user state:
$sender.account: string— The unique account identifier of the user sending the request.$sender.roomId: string— The room ID associated with the request.$room.getMyState(): Promise<Object>— Retrieves your room-specific state.$room.updateMyState(state: Object): Promise<Object>— Updates your room-specific state.$room.getUserState(account: string): Promise<Object>— Retrieves a particular user’s room state.$room.updateUserState(account: string, state: Object): Promise<Object>— Updates a particular user’s room state.$room.countUsers(): Promise<number>- Retrieves number of joined users in the room.$room.getAllUserStates(): Promise<Object[]>- Retrieves all user states in the room.
class Server {
async getMyState() {
return await $room.getMyState();
}
async updateMyState(state) {
return await $room.updateMyState(state);
}
async getUserState(account) {
return await $room.getUserState(account);
}
async updateUserState(account, state) {
return await $room.updateUserState(account, state);
}
async getAllUserStates() {
return await $room.getAllUserStates();
}
}Client API
subscriptions
server.subscribeRoomMyState(roomId: string, (state: any) => {}): UnsubscribeFunction- roomId is required.server.subscribeRoomUserState(roomId: string, account: string, (state: any) => {}): UnsubscribeFunction- roomId is required.server.subscribeRoomAllUserStates(roomId: string, (states: { ...state: any, account: string, updated: boolean }[]) => {}): UnsubscribeFunction- roomId is required. The changed state, which is the cause of the subscription, is set toupdated: true.
hooks
const myState = useRoomMyState()- The hook always gets the current room. No room ID is required.const userState = useRoomUserState(account: string)- The hook always gets the current room. No room ID is required.
On the client side, you can receive real-time updates for room-specific user states using subscription methods. This allows you to subscribe to your own state changes, updates for a specific user, or even get a snapshot of states for all users in the room.
Subscribe to Room User State Updates
import { useGameServer } from "@agent8/gameserver";
import { useEffect } from "react";
function RoomUserStateComponent({ roomId }) {
const { connected, server } = useGameServer();
useEffect(() => {
if (!connected) return;
// Subscribe to updates for your own room state
const unsubscribeMyState = server.subscribeRoomMyState(roomId, (state) => {
console.log("My room state updated:", state);
});
// Subscribe to updates for a specific user's room state
const userAccount = "some-user-account";
const unsubscribeUserState = server.subscribeRoomUserState(
roomId,
userAccount,
(state) => {
console.log(`Room user state updated for ${userAccount}:`, state);
}
);
// Subscribe to updates for all user states in the room (including yours)
const unsubscribeAllUserStates = server.subscribeRoomAllUserStates(
roomId,
(states: { ...state: any, account: string; }[]) => {
console.log("All room users state updated:", users);
}
);
// Cleanup subscriptions when component unmounts
return () => {
unsubscribeMyState();
unsubscribeUserState();
unsubscribeAllUserStates();
};
}, [connected, server]);
return <div>Room User State Component</div>;
}Sync Room User State with React Hooks
For real-time synchronization of room user state, the following hooks are available:
import {
useRoomState,
useRoomUserState,
useRoomAllUserStates,
} from "@agent8/gameserver";
function RoomUserStateDisplay() {
const roomState = useRoomState(); // Retrieves your room-specific state
const userAccount = "some-user-account";
const userState = useRoomUserState(userAccount); // Retrieves a particular user's state
const states = useRoomAllUserStates(); // Retrieves states for all states as { ...state: any, account: string }[]
return (
<div>
<h2>My Room State</h2>
<pre>{JSON.stringify(roomState, null, 2)}</pre>
<h2>User State for {account}</h2>
<pre>{JSON.stringify(userState, null, 2)}</pre>
<h2>All User States in Room</h2>
<p>Total user states: {states.length}</p>
<ul>
{states.map((state) => (
<li key={state.account}>
<strong>Account:</strong> {state.account}
<pre>{JSON.stringify(state, null, 2)}</pre>
</li>
))}
</ul>
</div>
);
}The useRoomAllUserStates hook returns an object with a states array where each item
has the structure { ...state: any, account: string }.
Example: Syncing User Positions
In this example, when a user moves their mouse, the new position is sent to the server via the remote function call updateMyPosition(position). The server then updates the user’s room state with the new position, and all user positions are rendered as circles on the screen.
class Server {
async joinRoom(roomId) {
await $room.join(roomId);
}
async updateMyPosition(position) {
// 'position' should be an object: { x: number, y: number }
return await $room.updateMyState({ position });
}
}import {
useGameServer,
useRoomState,
useRoomAllUserStates,
} from "@agent8/gameserver";
import { useEffect } from "react";
function PositionTracker({ roomId }: { roomId: string }) {
const { connected, server } = useGameServer();
const myState = useRoomState();
const states = useRoomAllUserStates(); // users is of type { ...state: any, account: string }[]
useEffect(() => {
if (!connected) return;
// Join the room when component mounts
server.remoteFunction("joinRoom", [roomId]).catch(console.error);
// Optional: Additional subscription setup if needed beyond the hooks
const unsubscribe = server.subscribeRoomAllUserStates(roomId,
(states: { ...state: any; account: string; updated: boolean }[]) => {
}
);
return () => {
unsubscribe();
};
}, [connected, server, roomId]);
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (!connected) return;
const position = { x: e.clientX, y: e.clientY };
server
.remoteFunction("updateMyPosition", [position], { throttle: 100 })
.catch(console.error);
};
if (!connected) return <p>Loading...</p>;
return (
<div
style={{
position: "absolute",
left: 0,
top: 0,
width: "100%",
height: "100vh",
}}
onMouseMove={handleMouseMove}
>
{states.map((state) => (
<div
key={state.account}
style={{
position: "absolute",
top: state.position?.y || 0,
left: state.position?.x || 0,
width: 20,
height: 20,
borderRadius: "50%",
background: state.account === myState.account ? "blue" : "red",
}}
/>
))}
</div>
);
}
export default function App() {
return <PositionTracker roomId="room1" />;
}Conclusion
By leveraging room-specific state functions and subscription methods, you can efficiently manage and synchronize per-user data within a room. This approach enables real-time updates for both your own data and that of other users, giving you a complete, aggregated view of the room’s state.