Skip to main content

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

KeyTypeDefaultDescription
data.urlstring""Base URL of the Data Decorator web app that will be iframed for players.
data.gameSettings.RoundLengthnumber180Seconds per round inside the minigame.
data.gameSettings.maxRoundsnumber4Maximum rounds the session should play.
data.gameSettings.ProgressDisplayDurationnumber5Seconds to show inter-round progress.
data.gameSettings.targetScorenumber1000Target score for the session.
data.gameSettings.personanumber0Optional 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 = 30 seconds.
  • Start signal once ready: sends { code: 1, msg: { gameSettings } } to the sub-match.
  • Polling every workshop_DataDecorator_SignalInterval = 30 seconds: sends { code: 2 } to request an update and sets gameState from 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 ddMatchId via the node’s response to JoinMatch.
  • 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_ID must 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 }
}
]