Managing Room Collection Data
This guide explains how to manage collection data scoped to a room in Agent8. Collections provide a structured way to store and retrieve persistent room-specific data—such as leaderboards, game records, or other room-related information. The main difference compared to global collections is that the API functions here are prefixed with $room instead of $global.
Room collections operate almost identically to global collections but their data is scoped to an individual room.
Server API
-
$room.getCollectionItems(collectionId: string, options?: CollectionOptions): Promise<Item[]>
Retrieves multiple items from a room collection based on filtering, sorting, and pagination options. -
$room.getCollectionItem(collectionId: string, itemId: string): Promise<Item>
Retrieves a single item from the room collection using its unique ID. -
$room.addCollectionItem(collectionId: string, item: any): Promise<Item>
Adds a new item to the specified room collection and returns the added item with its unique identifier. -
$room.updateCollectionItem(collectionId: string, item: any): Promise<Item>
Updates an existing item in the room collection and returns the updated item. -
$room.deleteCollectionItem(collectionId: string, itemId: string): Promise<{ __id: string }>
Deletes an item from the room collection and returns a confirmation containing the deleted item’s ID.
class Server {
async addRoomCollectionItem(collectionId, item) {
return await $room.addCollectionItem(collectionId, item);
}
async updateRoomCollectionItem(collectionId, item) {
return await $room.updateCollectionItem(collectionId, item);
}
async deleteRoomCollectionItem(collectionId, itemId) {
return await $room.deleteCollectionItem(collectionId, itemId);
}
async getRoomCollectionItems(collectionId, options = {}) {
return await $room.getCollectionItems(collectionId, options);
}
async getRoomCollectionItem(collectionId, itemId) {
return await $room.getCollectionItem(collectionId, itemId);
}
}Client API
subscriptions
server.subscribeRoomCollection(roomId, collectionId: string, ({ items, changes }: { items: any[], changes: { op: 'add'|'update'|'delete'|'deleteAll', items: any[]} }) => {}): UnsubscribeFunction
hooks
const { items } = useRoomCollection(collectionId)
Subscribe to Room Collection Updates
import { useGameServer } from "@agent8/gameserver";
import { useEffect } from "react";
function RoomCollectionComponent() {
const { connected, server } = useGameServer();
const roomId = "my-room";
const collectionId = "room-scores";
useEffect(() => {
if (!connected) return;
const unsubscribe = server.subscribeRoomCollection(
roomId,
collectionId,
({ items, changes }) => {
console.log("all items", items);
console.log("Updated items:", changes.items);
// Handle different operations
if (changes.op === "add") {
// Handle added items
} else if (changes.op === "update") {
// Handle updated items
} else if (changes.op === "delete") {
// Handle deleted items
}
}
);
// Cleanup subscription when component unmounts
return () => unsubscribe();
}, [connected, server, collectionId]);
return <div>Room Collection Component</div>;
}Sync Room Collection with React Hooks
To synchronize room collections in real-time using hooks:
import { useRoomCollection } from "@agent8/gameserver";
function RoomCollectionDisplay() {
const roomId = "my-room";
const collectionId = "room-scores";
const { items } = useRoomCollection(collectionId);
return (
<div>
<h2>Room Collection Items</h2>
<ul>
{items.map((item) => (
<li key={item.__id}>{JSON.stringify(item)}</li>
))}
</ul>
</div>
);
}The useRoomCollection hook automatically handles subscriptions and
unsubscriptions, making it the recommended approach for React applications.
Example: Room Scoreboard
This example demonstrates a simple room-specific scoreboard that tracks player scores.
class Server {
async addScore(points) {
const myState = await $room.getMyState();
const scoreItem = {
playerName: myState.name || "Anonymous",
playerAccount: $sender.account,
score: points,
timestamp: Date.now(),
};
return await $room.addCollectionItem("scores", scoreItem);
}
async getTopScores(limit = 10) {
return await $room.getCollectionItems("scores", {
limit,
orderBy: [{ field: "score", direction: "desc" }],
});
}
}import { useGameServer, useRoomCollection } from "@agent8/gameserver";
import { useState } from "react";
export default function Scoreboard() {
const { connected, server } = useGameServer();
const { items: scores } = useRoomCollection("scores");
const [newScore, setNewScore] = useState(0);
if (!connected) return <p>Loading...</p>;
const handleAddScore = async () => {
if (newScore > 0) {
await server.remoteFunction("addScore", [newScore]);
setNewScore(0);
}
};
// Sort scores by highest first
const sortedScores = [...scores].sort((a, b) => b.score - a.score);
return (
<div>
<h2>Room Scoreboard</h2>
<div>
<input
type="number"
value={newScore}
onChange={(e) => setNewScore(parseInt(e.target.value) || 0)}
min="0"
/>
<button onClick={handleAddScore}>Add Score</button>
</div>
<table style={{ width: "100%", marginTop: "20px" }}>
<thead>
<tr>
<th>Rank</th>
<th>Player</th>
<th>Score</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{sortedScores.map((score, index) => (
<tr key={score.__id}>
<td>{index + 1}</td>
<td>{score.playerName}</td>
<td>{score.score}</td>
<td>{new Date(score.timestamp).toLocaleString()}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}For additional collection features like filtering, sorting, and pagination, please refer to the Global Collection documentation.