DataDecorator
Integrates the external Data Decorator minigame, showing a live host scoreboard while players join and play inside an embedded page.
Example structures
Basic
{
"type": "DataDecorator",
"data": {
"url": "https://dd.humuus.de/",
"gameSettings": {
"RoundLength": 180,
"maxRounds": 4,
"ProgressDisplayDuration": 5,
"targetScore": 1000
}
}
}
Full (with persona)
{
"type": "DataDecorator",
"data": {
"url": "https://dd.humuus.de/",
"gameSettings": {
"RoundLength": 120,
"maxRounds": 5,
"ProgressDisplayDuration": 8,
"targetScore": 1500,
"persona": 2
}
}
}
Properties
| Key | Type | Default | Description |
|---|---|---|---|
data.url | string | "" | Base URL of the Data Decorator web app that will be iframed for players. |
data.gameSettings.RoundLength | number | 180 | Seconds per round inside the minigame. |
data.gameSettings.maxRounds | number | 4 | Maximum rounds the session should play. |
data.gameSettings.ProgressDisplayDuration | number | 5 | Seconds to show inter-round progress. |
data.gameSettings.targetScore | number | 1000 | Target score for the session. |
data.gameSettings.persona | number | 0 | Optional persona preset for the minigame. |
What players and host see
Players (participant view) An iframe loads the external app. The node builds the URL with required query parameters:
{url}?humuus=true
&matchid={ddMatchId}
&deviceid={LocalStorageKeys.DEVICE_ID}
&host={NEXT_PUBLIC_NAKAMA_SERVER_HOST}
&https={NEXT_PUBLIC_NAKAMA_USE_SSL}
Host A compact scoreboard card shows the current round and four progress metrics as percentages:
- Daten Sortieren →
pM1 - Daten minen →
pM2 - Daten analyzieren →
pM3 - Daten Visualizieren →
pM4
The host view also shows the current round as round / maxRounds.
Lifecycle and server loop
- init() creates a dedicated sub-match for the minigame:
ddMatchId = nk.matchCreate(GAMETYPE_dataDecorator, { creator, joincode: "-1", humuus: true }). - Start gating in
loop()waits until either all expected players are present or a join timeout is reached. Timeout:workshop_DataDecorator_JoinMatchWaitTime = 30seconds. - Start signal once ready: sends
{ code: 1, msg: { gameSettings } }to the sub-match. - Polling every
workshop_DataDecorator_SignalInterval = 30seconds: sends{ code: 2 }to request an update and setsgameStatefrom the reply, then pushes the new state to the host only.
Default node sound is disabled for this node.
Client protocol (workshop node opcodes)
The React component sends an immediate join request on mount:
const WorkshopNodeOpcode_DataDecorator = { JoinMatch: 0, GameState: 1 };
props.sendDataToNode(WorkshopNodeOpcode_DataDecorator.JoinMatch, {});
On the server, the node maps these to match-layer opcodes:
- JoinMatch (200): server replies to the requester with
{ ddMatchId }. - GameState (201): reserved (no action in the shown code).
Data model
Validated state sent/stored by the node
interface DataDecoratorNodeState {
ddMatchId: string;
ddMatchStarted: boolean;
ddMatchTimeWaited: number;
url: string;
gameSettings: {
RoundLength: number;
maxRounds: number;
ProgressDisplayDuration: number;
targetScore: number;
persona: number;
};
gameState: {
pM1: number; pM2: number; pM3: number; pM4: number;
round: number;
};
}
Incoming config
interface DataDecoratorNodeState_Storage {
url: string;
gameSettings: {
RoundLength: number;
maxRounds: number;
ProgressDisplayDuration: number;
targetScore: number;
persona: number;
};
}
Message envelope used with the sub-match
interface Workshop_DataDecorator_MessageObject {
code: number; // 1 start, 2 poll
msg: any;
}
Behavior details
- Join readiness compares the sub-match size to
workshop_connectedPlayers(state, false)or uses the 30s fallback timer. - Host receives periodic updates only after the sub-match is started.
- Players load the external app once they receive
ddMatchIdvia the node’s response toJoinMatch. - The iframe is full-viewport in the player view and responsive.
Use cases
- Drop-in minigame between workshop sections with a live progress board for the facilitator.
- Team-based or class-based rounds where the external app handles the heavy logic and the workshop shell handles orchestration.
tip
LocalStorageKeys.DEVICE_IDmust be present; ensure your device ID is initialized before entering the node.- The external app must honor the query parameters and connect to the given Nakama host/match. Cross-origin settings need to allow embedding.
- If fewer players arrive than expected, the node still starts after 30 seconds to avoid deadlock.
- Host labels in the scoreboard are currently German; adapt if you need localized UI.
Integration example
[
{
"type": "Slide",
"data": { "headline": "Mini-Game Time", "text": "Let’s play Data Decorator!" }
},
{
"type": "DataDecorator",
"data": {
"url": "https://dd.humuus.de/",
"gameSettings": {
"RoundLength": 150,
"maxRounds": 3,
"ProgressDisplayDuration": 6,
"targetScore": 1200,
"persona": 1
}
}
},
{
"type": "Scoreboard",
"data": { "showPodium": false }
}
]