PageSourceSearch

https://sharedgametimer.com/_nuxt/-6D7fPNV.js

js sharedgametimer.com collected 2026-10-02 06:20:11 UTC 39,476 bytes, 9 lines download raw bytes

1import{gA as Q,af as L,ax as A,dt as R,av as E,as as t,ar as $,ag as M,aw as V,fC as K,W as z,r as X,du as n,aq as l,am as o,ay as C,dI as Z,an as r}from"./mCBnZlwl.js";import{E as d}from"./BXOCeNrz.js";(function(){try{var T=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},a=new T.Error().stack;a&&(T._sentryDebugIds=T._sentryDebugIds||{},T._sentryDebugIds[a]="5e73fb0e-3a99-4d93-8610-e38e32de2aae",T._sentryDebugIdIdentifier="sentry-dbid-5e73fb0e-3a99-4d93-8610-e38e32de2aae")}catch{}})();const c=Q("/guides/tft-sandwich/tft-sandwich.jpg"),_=Q("/guides/hex-table/hex-table.jpg"),ee={key:0},te={key:0,class:"title"},ne={key:1,class:"caption"},oe={key:1},le={key:2,class:"footer"},ae=L({__name:"DefinitionList",props:{title:{},caption:{}},setup(T){return(a,S)=>(M(),A("figure",null,[a.title||a.caption?(M(),A("figcaption",ee,[a.title?(M(),A("p",te,R(a.title),1)):E("",!0),a.caption?(M(),A("p",ne,R(a.caption),1)):E("",!0)])):E("",!0),t("dl",null,[$(a.$slots,"default",{},void 0,!0)]),a.$slots.footer?(M(),A("hr",oe)):E("",!0),a.$slots.footer?(M(),A("div",le,[$(a.$slots,"footer",{},void 0,!0)])):E("",!0)]))}}),f=V(ae,[["__scopeId","data-v-1359f3dd"]]),u=L({__name:"JsonData",props:{maxLineLength:{}},setup(T){const a=T,S=K("code");z(()=>{var y,v,i;if((y=S.value)!=null&&y.innerHTML)try{S.value.innerHTML=J(JSON.parse((v=S.value)==null?void 0:v.innerHTML),{maxLineLength:a.maxLineLength})}catch(w){console.error("Failed to parse HTML",(i=S.value)==null?void 0:i.innerHTML),console.error(w)}});const e={maxLineLength:100,indent:"  ",inlineMaxLength:80,inlineMaxItems:4,tokenClasses:{key:"json-key",string:"json-string",number:"json-number",boolean:"json-boolean",null:"json-null"}};function p(y){return y.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}const B=/^"(.*)"$/;function k(y,v){const i=B.exec(y);return i?`"<span class="${v}">${i[1]}</span>"`:`<span class="${v}">${y}</span>`}function J(y,v={}){const i={...e,...v,tokenClasses:{...e.tokenClasses,...v.tokenClasses}},w=(s,m=0)=>s===null?k("null",i.tokenClasses.null):typeof s=="string"?k(p(JSON.stringify(s)),i.tokenClasses.string):typeof s=="number"?k(String(s),i.tokenClasses.number):typeof s=="boolean"?k(String(s),i.tokenClasses.boolean):Array.isArray(s)?G(s,m):typeof s=="object"?H(s,m):k(p(JSON.stringify(String(s))),i.tokenClasses.string),j=s=>s.replace(/<[^>]+>/g,""),q=(s,m,I=0)=>{if(s.length===0)return!0;if(s.length>i.inlineMaxItems)return!1;const g=`[${s.map(b=>w(b,m+1)).join(", ")}]`,h=j(g);return!h.includes(`
2`)&&h.length+m*i.indent.length+I<=i.maxLineLength},F=(s,m,I=0)=>{const g=Object.entries(s);if(g.length===0)return!0;if(g.length>i.inlineMaxItems)return!1;const h=`{ ${g.map(([D,P])=>`${k(p(JSON.stringify(D)),i.tokenClasses.key)}: `+w(P,m+1)).join(", ")} }`,b=j(h);return!b.includes(`
3`)&&b.length+m*i.indent.length+I<=i.maxLineLength},G=(s,m,I=0)=>{if(s.length===0)return"[]";if(q(s,m,I))return`[${s.map(b=>w(b,m+1)).join(", ")}]`;const g=i.indent.repeat(m+1),h=i.indent.repeat(m);return`[
4${g}${s.map(b=>w(b,m+1)).join(`,
5${g}`)}
6${h}]`},H=(s,m,I=0)=>{const g=Object.entries(s);if(g.length===0)return"{}";if(F(s,m,I))return`{ ${g.map(([x,O])=>`${k(p(JSON.stringify(x)),i.tokenClasses.key)}: ${w(O,m+1)}`).join(", ")} }`;const h=i.indent.repeat(m+1),b=i.indent.repeat(m),D=g.map(([P,x],O,N)=>{const W=k(p(JSON.stringify(P)),i.tokenClasses.key),U=JSON.stringify(P),Y=Array.isArray(x)?G(x,m+1,U.length+2):x&&typeof x=="object"?H(x,m+1,U.length+2+(O+1>=N.length?0:1)):w(x,m+1);return`${W}: ${Y}`}).join(`,
7${h}`);return`{
8${h}${D}
9${b}}`};return w(y)}return(y,v)=>(M(),A("code",{ref_key:"code",ref:S,class:"json-code"},[$(y.$slots,"default")],512))}}),se={class:"page"},ie={class:"guides"},re={class:"guide"},de={class:"guide"},ue=L({__name:"index",setup(T){const a=X(66);return(S,e)=>{const p=Z;return M(),A("div",se,[e[192]||(e[192]=t("p",null," MQTT is a popular protocol for connecting IoT (Internet of Things) devices. The Shared Game Timer (SGT) can connect to a MQTT broker to both read commands (e.g. 'End Turn') as well as publish the current state of a timer (e.g. current player color). ",-1)),e[193]||(e[193]=t("p",null,"Here are some examples of what you could do:",-1)),e[194]||(e[194]=t("ul",null,[t("li",null,"Set the color of a smart lightbulb to the current player'
9s color."),t("li",null,"Three minutes into someone's turn, tell your alert bracelet to vibrate."),t("li",null,"Six minutes into someone's turn, tell the doorbell to ring."),t("li",null,"When you press some button, end your turn."),t("li",null,"When you shake the device, perform Undo."),t("li",null,"During Admin Time, play elevator music on your stereo.")],-1)),t("p",null,[e[1]||(e[1]=n(" Popular ways to do the above is to either activate MQTT integration with your ")),e[2]||(e[2]=t("a",{href:"https://www.home-assistant.io/"},"Home Assistant",-1)),e[3]||(e[3]=n(" or program a micro-controller to act as a remote control. If you use Home Assistant, see the dedicated ")),l(p,{to:"/remote-control/home-assistant"},{default:o(()=>e[0]||(e[0]=[n("Home Assistant integration")])),_:1}),e[4]||(e[4]=n(" page — SGT can appear in Home Assistant as a device with ready-made sensors and buttons, no manual MQTT configuration needed. "))]),e[195]||(e[195]=C('<nav class="tos" data-v-db280981><ul data-v-db280981><li data-v-db280981><a href="#projects" data-v-db280981>Example Projects</a></li><li data-v-db280981><a href="#overview" data-v-db280981>Summary of Setup</a></li><li data-v-db280981><a href="#read" data-v-db280981>Reading the state of the game</a></li><li data-v-db280981><a href="#write" data-v-db280981>Sending commands to the SGT</a></li></ul></nav><p data-v-db280981> After activating the MQTT integration, SGT will start writing the state of your open timers to the <em data-v-db280981>{userid}/game</em> feed (in JSON format), which your IoT devices can subscribe to. The SGT will in turn subscribe to the <em data-v-db280981>{userid}/commands</em> where your IoT devices can post messages to do things like pause the game, end your turn etc. </p>',2)),t("p",null,[e[6]||(e[6]=n(" You can use your own MQTT broker or a free broker that SGT has set up via ")),e[7]||(e[7]=t("a",{href:"https://www.hivemq.com/"},"HiveMQ",-1)),e[8]||(e[8]=n(". The shared one is fine for trying things out, but it has usage caps and its credentials are shared by every SGT user, so for anything permanent you want your own. Setting one up is free and takes about five minutes — ")),l(p,{to:"/remote-control/mqtt/broker-setup"},{default:o(()=>e[5]||(e[5]=[n("see the MQTT Broker Setup page")])),_:1}),e[9]||(e[9]=n(". "))]),e[196]||(e[196]=t("h2",{id:"projects"},"Example Projects",-1)),e[197]||(e[197]=t("p",null," Below are example projects that uses the MQTT integration. Let me know if you want to add your own here. ",-1)),t("div",ie,[t("div",re,[l(p,{to:"/remote-control/mqtt/tft-sandwich"},{default:o(()=>e[10]||(e[10]=[t("h3",null,"TFT Sandwich",-1),t("img",{src:c,height:"160",width:"212",alt:""},null,-1),t("p",null,"A remote with 3 buttons, a screen and buzzer.",-1)])),_:1})]),t("div",de,[l(p,{to:"/remote-control/mqtt/hex-table"},{default:o(()=>e[11]||(e[11]=[t("h3",null,"The Hex Table",-1),t("img",{src:_,height:"160",width:"212",alt:""},null,-1),t("p",null,"A six-sided game table with animated LED lights and buttons.",-1)])),_:1})])]),e[198]||(e[198]=t("h2",{id:"overview"},"Summary of Setup",-1)),t("p",null,[e[13]||(e[13]=n(" You must be logged in to activate the MQTT integration. (your user ID is part of the MQTT topic that will be read/written) Once logged in, navigate to the ")),l(p,{to:"/mqtt"},{default:o(()=>e[12]||(e[12]=[n("MQTT Setup")])),_:1}),e[14]||(e[14]=n(" page and activate the integration. You should do this on only one device (e.g. your phone), and the activation is active for only that device. This MQTT enabled device should be the one you have open during the game so it can 1) write updates to the ")),e[15]||(e[15]=t("em",null,"{userID}/game",-1)),e[16]||(e[16]=n(" feed and 2) perform commands written to the ")),e[17]||(e[17]=t("em",null,"{userID}/commands",-1)),e[18]||(e[18]=n(" feed. "))]),e[199]||(e[199]=t("h2",{id:"read"},"Reading the state of the game",-1)),e[200]||(e[200]=t("p",null,[n(" Whenever you enter a game timer on a MQTT enabled device or when the game state changes, a "),t("em",null,"retained"),n(" message is written to the "),t("em",null,"{userID}/game"),n(" feed. Since it is retained, any IoT device that subscribes to the "),t("em",null,"{userID}/game"),n(" feed will get the latest game state message, even if it was sent earlier. ")],-1)),e[201]||(e[201]=t("p",null," When you exit the game by e.g. navigating away, it will clear the game state message. However, if you simply close the browser abruptly, the game message will remain for some time. ",-1)),e[202]||(e[202]=t("p",null,"The game state message is in JSON format. All times are in seconds. There are four kind of messages sent, depending on the timer mode and if the game is finished or not.",-1)),l(f,{title:"Common fields for all messages"},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[19]||(e[19]=[n(' { "gameStateVersion": 1, "ts": 1777623085, "state": "st", "players": "-- Omitted. See its own section for details. --", "tokens": "-- Omitted. See its own section for details. --" } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[20]||(e[20]=t("dt",null,"gameStateVersion",-1)),e[21]||(e[21]=t("dd",null,[n("Integer incrementing with every new game state message. Should be returned in messages sent to "),t("em",null,"{userID}/commands")],-1)),e[22]||(e[22]=t("dt",null,"ts",-1)),e[23]||(e[23]=t("dd",null,"A unix timestamp of when this 
9state was created. Add lapsed seconds to active timers to get the 'current' time.",-1)),e[24]||(e[24]=t("dt",null,"state",-1)),e[25]||(e[25]=t("dd",null,"The current state of the game. Each section below describes this further.",-1)),e[26]||(e[26]=t("dt",null,"players",-1)),e[27]||(e[27]=t("dd",null,[n("Player information like color, name etc. "),t("a",{href:"#players"},"Details below.")],-1)),e[28]||(e[28]=t("dt",null,"tokens",-1)),e[29]||(e[29]=t("dd",null,[n("Token information, if your game uses them. "),t("a",{href:"#tokens"},"Details below.")],-1))]),_:1}),e[203]||(e[203]=t("p",null,[n("Games go from 'Starting' to 'In Progress' to 'Ended'. A game that is either starting (meaning it is still in the 'Create Game' screen) or ended ('meaning you are looking at the scoresheet') has no additional fields. Only the "),t("code",null,"state"),n(" field changes.")],-1)),l(f,{title:"A starting / ended game"},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[30]||(e[30]=[n(' { "gameStateVersion": 1, "ts": 1777623085, "state": "en", "players": "-- Omitted. See its own section for details. --", "tokens": "-- Omitted. See its own section for details. --" } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[31]||(e[31]=t("dt",null,"state",-1)),e[32]||(e[32]=t("dd",null,[t("code",null,"st/en"),n(" = starting / ended")],-1))]),_:1}),e[204]||(e[204]=t("p",null,"In progress games have messages that look quite different depending on which type of timer is used. There are four of them:",-1)),e[205]||(e[205]=t("ul",null,[t("li",null,"Sand Timer"),t("li",null,"No Timer"),t("li",null,"Count Up"),t("li",null,"Count Down")],-1)),e[206]||(e[206]=t("p",null,"The last three are all 'Turn Based' games, while the Sand Timer is very different. So let's start with the Sand Timer.",-1)),l(f,{title:"Sand Timer"},{footer:o(()=>[e[34]||(e[34]=t("p",{style:{"margin-bottom":"0.5em"}},[t("em",null,"Common fields omitted above and from example below.")],-1)),l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[33]||(e[33]=[n(' { "timerMode": "st", "state": "nr", "turnTime": 0, "playerTime": 30, "actions": "-- Omitted. See its own section for details. --" } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[35]||(e[35]=t("dt",null,"timerMode",-1)),e[36]||(e[36]=t("dd",null,[t("code",null,"st"),n(" - Use this to know this is a Sand Timer.")],-1)),e[37]||(e[37]=t("dt",null,"state",-1)),e[38]||(e[38]=t("dd",null,[t("code",null,"ru/nr/pa"),n(" = running / not running / paused")],-1)),e[39]||(e[39]=t("dt",null,"turnTime",-1)),e[40]||(e[40]=t("dd",null,"Time taken out of the sand timer.",-1)),e[41]||(e[41]=t("dt",null,"playerTime",-1)),e[42]||(e[42]=t("dd",null,"Sand-timer reset size.",-1)),e[43]||(e[43]=t("dt",null,"actions",-1)),e[44]||(e[44]=t("dd",null,[n("Current primary game actions. "),t("a",{href:"#actions"},"Details below.")],-1))]),_:1}),e[207]||(e[207]=t("p",null,"The rest of the timer modes are all turn based timers, and they differ only in how time-fields are interpreted.",-1)),e[208]||(e[208]=t("p",null,"Let's start with the No Timer mode. It is a mode for players who doesn't want time pressure of a timer, but still want to use other features of the SGT. As such, it excludes all timer fields apart from the 'Audible Long Turn' reminders.",-1)),l(f,{title:"No Timer (Turn-Based Timer)"},{footer:o(()=>[e[46]||(e[46]=t("p",{style:{"margin-bottom":"0.5em"}},[t("em",null,"Common fields omitted above and from example below.")],-1)),l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[45]||(e[45]=[n(' { "timerMode": "nt", "state": "pl", "color": "486bfa", "colorHsv": "a2b6fa", "name": "Player 1", "seat": 1, "timeReminders":[300,180,60], "actions": "-- Omitted. See its own section for details. --" } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[47]||(e[47]=t("dt",null,"timerMode",-1)),e[48]||(e[48]=t("dd",null,[t("code",null,"st"),n(" - Use this to know this is a 'No Timer' Turn-Based Timer.")],-1)),e[49]||(e[49]=t("dt",null,"state",-1)),e[50]||(e[50]=t("dd",null,[t("code",null,"pa/ad/pl/si"),n(" = paused / admin time / playing / simultaneous turn")],-1)),e[51]||(e[51]=t("dt",null,"stateType",-1)),e[52]||(e[52]=t("dd",null,[n(" Differentiate between types of states."),t("br"),n("For 'bg', "),t("code",null,"state=pa"),n(". All others, "),t("code",null,"state=ad"),n("."),t("br"),t("
9code",null,"bg"),n(" = Before Game (You just left the create game screen.)"),t("br"),t("code",null,"se"),n(" = Setup (Admin time before the first 'move'.)"),t("br"),t("code",null,"mt"),n(" = Mid-Turn (Admin time started during a normal turn.)"),t("br"),t("code",null,"ms"),n(" = Mid-Sim-Turn (Admin time started during a Sim. Turn.)"),t("br"),t("code",null,"et"),n(" = End-of-Turn (If using Admin Time between turns feature.)"),t("br"),t("code",null,"er"),n(" = End-of-Round (A round ended, and the next hasn't started.)"),t("br"),t("code",null,"sr"),n(" = Start-of-Round (New round started, before first 'move'.)"),t("br")],-1)),e[53]||(e[53]=t("dt",null,"seat",-1)),e[54]||(e[54]=t("dd",null,[n(" seat of player (or next up) as "),t("code",null,"number"),t("br"),n(" if Sim. Turn: seats of players involved as "),t("code",null,"number[]"),t("br"),n(" if neither of above: the number "),t("code",null,"0")],-1)),e[55]||(e[55]=t("dt",null,"name",-1)),e[56]||(e[56]=t("dd",null,[n(" seat > 0: name of player at that seat"),t("br"),n(" seat = 0: 'Admin Time'"),t("br"),n(" seat is array: 'Sim. Turn' ")],-1)),e[57]||(e[57]=t("dt",null,"color",-1)),e[58]||(e[58]=t("dd",null,[n(" active player (or next up) color as "),t("em",null,"rrggbb"),n(" string"),t("br"),n(" seat = 0 or array: Admin Color ")],-1)),e[59]||(e[59]=t("dt",null,"colorHsv",-1)),e[60]||(e[60]=t("dd",null,[n("same as above, but color as "),t("em",null,"hhssvv"),n(" string")],-1)),e[61]||(e[61]=t("dt",null,"timeReminders",-1)),e[62]||(e[62]=t("dd",null,"Audible time reminders game setting, as array of seconds",-1)),e[63]||(e[63]=t("dt",null,"actions",-1)),e[64]||(e[64]=t("dd",null,[n("Current primary game actions. "),t("a",{href:"#actions"},"Details below.")],-1))]),_:1}),e[209]||(e[209]=t("p",null,[n("The remaining two timer modes, Count-Up and Count-Down, adds three time fields that need to be viewed in light of the timer counting up or down. To display a running clock on a device, you need to calculate the lapsed time from the messages "),t("code",null,"ts"),n(" field and add or subtract that from the timers, assuming the relevant time field is not expected to be paused. You'll need to look at the "),t("code",null,"state"),n(" field for that.")],-1)),l(f,{title:"Count Up/Down (Turn-Based Timer)"},{footer:o(()=>[e[66]||(e[66]=t("p",{style:{"margin-bottom":"0.5em"}},[t("em",null,"Fields also found in the No Timer message omitted above and from example below.")],-1)),l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[65]||(e[65]=[n(' { "timerMode": "cu", "turnTime": 220, "playerTime": 220, "totalPlayTime": 226 } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[67]||(e[67]=t("dt",null,"timerMode",-1)),e[68]||(e[68]=t("dd",null,[t("code",null,"cu/cd"),n(" = count-up / count-down")],-1)),e[69]||(e[69]=t("dt",null,"turnTime",-1)),e[70]||(e[70]=t("dd",null,[n(" Count-Up: time taken this turn (or pause/admin time)"),t("br"),n(" Count-Down: same as above, but negative during Delay Time ")],-1)),e[71]||(e[71]=t("dt",null,"playerTime",-1)),e[72]||(e[72]=t("dd",null,[n(" Count-Up: total time taken"),t("br"),n(" Count-Down: remaining time bank"),t("br"),n(" During admin/pause: Blank ")],-1)),e[73]||(e[73]=t("dt",null,"totalPlayTime",-1)),e[74]||(e[74]=t("dd",null," Total play time, excluding current turn and admin/pause time ",-1))]),_:1}),e[210]||(e[210]=t("h3",{id:"players"},"The Players Field",-1)),e[211]||(e[211]=t("p",null,[n("Every MQTT message has a "),t("code",null,"players"),n(" field, which is an array of objects with information about each player. "),t("strong",null,"The order of the array is the current player order.")],-1)),l(f,{title:"The Players Field"},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[75]||(e[75]=[n(' { "players": [ { "seat": 1, "name": "Player 1", "color": "486bfa", "colorHsv": "a2b6fa", "tokens": [4,-5] }, { "seat": 2, "name": "Player 2", "color": "dd3b34", "colorHsv": "02c3dd", "tokens": [9,10], "action": "se" } ] } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[76]||(e[76]=t("dt",null,"name",-1)),e[77]||(e[77]=t("dd",null,"The name of the player.",-1)),e[78]||(e[78]=t("dt",null,"seat",-1)),e[79]||(e[79]=t("dd",null,"The seat number of the player. Acts as the unique ID of the player when issuing commands.",-1)),e[80]||(e[80]=t("dt",null,"color",-1)),e[81]||(e[81]=t("dd",null,[n("Player color as a "),t("em",null,"rrggbb"),n(" string")],-1)),e[82]||(e[82]=t("dt",null,"colorHsv",-1)),e[83]||(e[83]=t("dd",null,[n("Player color as a "),t("em",null,"hhssvv"),n(" string")],-1)),e[84]||(e[84]=t("dt",null,"action",-1)),e[85]||(e[85]=t("dd",null,[t("code",null,"pr"),n(" = This is the active player, and can only perform the 'Primary' action in the "),t("code",null,"actions"),n(" map."),t("br"),t("
9code",null,"se"),n(" = This is the active player, and can perform both the 'Primary' and 'Secondary' action in the "),t("code",null,"actions"),n(" map."),t("br"),t("code",null,"in"),n(" = Not the active player, or player is still involved in a Sim. Turn. Player can perform the 'Inactive Player' action in the "),t("code",null,"actions"),n(" map "),t("strong",null,"but must supply the 'seat' argument!")],-1)),e[86]||(e[86]=t("dt",null,"tokens",-1)),e[87]||(e[87]=t("dd",null,[n("Array of token amounts, "),t("strong",null,"in the same order as the 'global' tokens array.")],-1))]),_:1}),e[212]||(e[212]=C('<h3 id="actions" data-v-db280981>The Actions Map</h3><p data-v-db280981> The <code data-v-db280981>actions</code> map details the most common actions available at any given time. The keys correspond to the placement in the main SGT UI where you would expect to find the buttons to do those actions. </p><p data-v-db280981>The values are objects with two properties:</p><ul data-v-db280981><li data-v-db280981><em data-v-db280981>label</em>: Human friendly description of the action, e.g. &#39;End Turn&#39;. These change often, for example from &#39;Start Round&#39; to &#39;End Turn&#39;. You can display these if your remote device has a screen.</li><li data-v-db280981><em data-v-db280981>action</em>: An ID of the action e.g. &#39;Primary&#39;. These can be sent to the commands topic to execute and are constant for each key of the <code data-v-db280981>actions</code> map.</li></ul>',4)),l(f,{title:"Action keys"},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[88]||(e[88]=[n(' { "actions": { "primary": {"label": "End Turn","action": "game/endTurn"}, "secondary": {"label": "End Round","action": "game/endRound"}, "inactive": {"label": "Take Turn","action": "game/startTurn"}, "admin": {"label": "Start mid-turn admin","action": "game/startMidTurnAdmin"}, "pause": {"label": "Pause","action": "game/pause"}, "undo": {"label": "Undo","action": "game/undo"} } } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[89]||(e[89]=t("dt",null,"primary",-1)),e[90]||(e[90]=t("dd",null,"The primary active player action e.g. End Turn.",-1)),e[91]||(e[91]=t("dt",null,"secondary",-1)),e[92]||(e[92]=t("dd",null,"The secondary active player action e.g. Pass.",-1)),e[93]||(e[93]=t("dt",null,"inactive",-1)),e[94]||(e[94]=t("dd",null,"Actions for inactive players, or players currently in a Sim. Turn.",-1)),e[95]||(e[95]=t("dt",null,"admin",-1)),e[96]||(e[96]=t("dd",null,"The action on the Admin Time tile.",-1)),e[97]||(e[97]=t("dt",null,"pause",-1)),e[98]||(e[98]=t("dd",null,"The current pause action.",-1)),e[99]||(e[99]=t("dt",null,"undo",-1)),e[100]||(e[100]=t("dd",null,"The undo action.",-1))]),_:1}),e[213]||(e[213]=C('<h3 id="tokens" data-v-db280981>The Tokens Array</h3><p data-v-db280981> The <code data-v-db280981>tokens</code> array is only present if your game uses tokens. Otherwise it and the related <code data-v-db280981>tokens</code> array for each player object are omitted. </p><p data-v-db280981>The array contains objects with information about each token. <strong data-v-db280981>The order of the array is important.</strong> It is the same order as they appear in the SGT UI, and it is also the order that the amounts of tokens appear in the players <code data-v-db280981>tokens</code> field. So, to understand what <code data-v-db280981>[5,-3]</code> means there, you have to index into the &#39;global&#39; tokens array to find out which token is related to which number.</p>',3)),l(f,{title:"Token Object Keys"},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[101]||(e[101]=[n(' { "tokens": [ { "id": "⭐" }, { "id": "💰", "altText": "Money", "currentStock": 86, "image": "http://example.com/image" } ] } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[102]||(e[102]=t("dt",null,"id",-1)),e[103]||(e[103]=t("dd",null,"The token id, usually an emoji.",-1)),e[104]||(e[104]=t("dt",null,"altText",-1)),e[105]||(e[105]=t("dd",null,"The token altText field, if given.",-1)),e[106]||(e[106]=t("dt",null,"image",-1)),e[107]||(e[107]=t("dd",null,"The URL of the image, if you gave it a custom icon.",-1)),e[108]||(e[108]=t("dt",null,"currentStock",-1)),e[109]||(e[109]=t("dd",null,"Tokens left in the limited stock of tokens, if limited.",-1))]),_:1}),e[214]||(e[214]=C('<h3 id="status" data-v-db280981>The Status Feed</h3><p data-v-db280981> In addition to the game feed, SGT maintains a <em data-v-db280981>retained</em> message on the <em data-v-db280981>{userID}/status</em> feed with the value <code data-v-db280981>online</code> or <code data-v-db280981>offline</code>. It is <code data-v-db280981>online</code> while an MQTT-enabled SGT device is connected and broadcasting a game, and <code data-v-db280981>offline</code> otherwise. Unlike the game feed, this also covers abrupt disconnections: SGT registers an MQTT <em data-v-db280981>Last Will</em> with the broker, so if the browser is closed or loses its connection without cleaning up, the broker itself flips the status to <code data-v-db280981>offline</code>. </p><p data-v-db280981> Use this feed to show a connection indicator on your device, or to power battery-saving tricks like keeping a wireless button awake only while a game is running. </p><h2 id="write" data-v-db280981>Sending commands to the SGT</h2><p data-v-db280981>To execute a command in the <em data-v-db280981>action map</em>, publish a JSON-formatted string to the <em data-v-db280981>{userID}/commands</em> topic.</p>',5)),l(f,{title:"Basic Command Message Fields"},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[110]||(e[110]=[n(' { "gameStateVersion": 7, "action": "Primary" } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[111]||(e[111]=t("dt",null,"gameStateVersion",-1)),e[112]||(e[112]=t("dd",null,[n("The current "),t("code",null,"gameStateVersion"),n(". If the SGT version and the command versions don't match, the command will be ignored. You can omit this field, but you run the risk of the action executing twice or run a race condition with another player pressing a button.")],-1)),e[113]||(e[113]=t("dt",null,"action",-1)),e[114]||(e[114]=t("dd",null,"The action to execute, e.g. 'Primary'.",-1))]),_:1}),e[215]||(e[215]=t("p",null,"Some actions have additional optional or mandatory arguments which we'll cover below.",-1)),e[216]||(e[216]=t("p",null,"I will structure this section by the type of remote control you want to build. There are two kinds of remotes: pass-around and seat-specific controllers. A third category would be advanced game-table controllers. You may dream up other categories of controllers, but this serves as a useful division to teach the different kinds of commands you are likely to send to the SGT.",-1)),e[217]||(e[217]=t("h3",null,"Pass-Around Controllers",-1)),e[218]||(e[218]=t("p",null,"A pass-around controller is one that you are likely to pass around the table, and you are most likely only going to have one of them. It probably has a set of buttons, but it could also have sensors that detect when it is turned upside down or shaken.",-1)),e[219]||(e[219]=t("p",null,"When a button is pressed, or a momentary sensor is triggered, it blindly sends its related command.",-1)),l(f,{title:"Main Momentary Actions"},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[115]||(e[115]=[n(' { "gameStateVersion": 7, "action": "ToggleAdmin" } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[116]||(e[116]=t("dt",null,"Primary",-1)),e[117]||(e[117]=t("dd",null,"Performs main action of the current player.",-1)),e[118]||(e[118]=t("dt",null,"Secondary",-1)),e[119]||(e[119]=t("dd",null,"Performs secondary action of current player.",-1)),e[120]||(e[120]=t("dt",null,"ToggleAdmin",-1)),e[121]||(e[121]=t("dd",null,"Performs current Admin action, usually enter/exit admin time but in rare situations, End Round.",-1)),e[122]||(e[122]=t("dt",null,"TogglePause",-1)),e[123]||(e[123]=t("dd",null,"Toggles Pause on/off.",-1)),e[124]||(e[124]=t("dt",null,"Undo",-1)),e[125]||(e[125]=t("dd",null,"Undoes the last action.",-1))]),_:1}),e[220]||(e[220]=t("p",null,[n("It is fine to send commands that are currently unavailable, e.g. a "),t("code",null,"Secondary"),n(" when there is no such action. SGT will simply ignore it.")],-1)),e[221]||(e[221]=t("p",null,[n("If you have a LED or a screen next to the buttons, then you can indicate that the button is 'active' or even write out what the action is by inspecting the "),t("code",null,"actions"),n(" map on the latest MQTT message.")],-1)),t("p",null,[e[127]||(e[127]=n("If you have a latching button or a sensor that can be in two states, then there are additional commands you can send. An example is the ")),l(p,{to:"/remote-control/mqtt/tft-sandwich"},{default:o(()=>e[126]||(e[126]=[n("TFT Sandwich")])),_:1}),e[128]||(e[128]=n(" which has an accelerometer that lets it know if it is face down or not. I wanted it to pause the game when turned face down (and stay paused if it was already) and unpause the game when turned the right way up (and not do anything if the game was not paused at that time). "))]),e[222]||(e[222]=t("p",null,[n("For these actions, I may consider omitting the "),t("code",null,"gameStateVersion"),n(" field. There isn't really a reason to prevent the action that I can think of. This is up to you.")],-1)),l(f,{title:"Main One
9-Way Toggle Actions"},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[129]||(e[129]=[n(' { "action": "AdminOn" } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[130]||(e[130]=t("dt",null,"AdminOn",-1)),e[131]||(e[131]=t("dd",null,"Turns Admin Time on, if possible.",-1)),e[132]||(e[132]=t("dt",null,"AdminOff",-1)),e[133]||(e[133]=t("dd",null,"Turns Admin Time off, if possible.",-1)),e[134]||(e[134]=t("dt",null,"PauseOn",-1)),e[135]||(e[135]=t("dd",null,"Pauses the game, if possible.",-1)),e[136]||(e[136]=t("dt",null,"PauseOff",-1)),e[137]||(e[137]=t("dd",null,"Unpauses the game, if possible.",-1))]),_:1}),e[223]||(e[223]=C("<p data-v-db280981>The main limitation with pass-around controllers is that you cannot perform &#39;Inactive Player&#39; actions or use them for Simultaneous Turns, as that requires the device to identify which player it wants to perform the action for. Pass-Around controllers always controls the current player, or performs &#39;global&#39; actions such as Undo, Pause, Admin. Let&#39;s move on to the more powerful Seat-Aware controllers!</p><h3 data-v-db280981>Seat-Aware Controllers</h3><p data-v-db280981>A seat-aware controller is a controller that can issue commands tied to <strong data-v-db280981>seat numbers</strong>. The controller might be set to only control one specific seat, or it may be an entire board game table with multiple seats. The important thing is that each command is for a specific seat.</p><p data-v-db280981>The actions are the same as for pass-around controllers, but the command MQTT message must include a <code data-v-db280981>seat</code> argument, which should be the seat number of the player the button is for.</p><p data-v-db280981>Below, a player is either &#39;active&#39; or &#39;inactive&#39;.</p><p data-v-db280981>The <em data-v-db280981>Active</em> player is the one that has the active turn (which could be interrupted by admin time or paused), or if the game is in between turns (e.g. between rounds) then it is the next-up player who has the right to start the next turn. There is maximum one active player, but there may be none, for example between turns if you use free-form turn order.</p><p data-v-db280981>A player is an <em data-v-db280981>Inactive</em> player if they are not the Active player.</p><p data-v-db280981>During Sim. Turns, no one is the the active player. The players who are involved in the Sim. Turn and have yet not ended their Sim. Turn are all considered as &#39;Inactive&#39; players. I know, the name &#39;inactive&#39; is a bad name, but it is now stuck!</p>",8)),l(f,{title:"Seat-Specific Actions"},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[138]||(e[138]=[n(' { "gameStateVersion": 7, "action": "Primary", "seat": 2 } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[139]||(e[139]=t("dt",null,"Primary",-1)),e[140]||(e[140]=t("dd",null,[t("em",null,"Seat is Active"),n(": Do the primary action, e.g. End Turn"),t("br"),t("em",null,"Seat is Inactive"),n(": Do the inactive player action, e.g. End Sim.Turn ")],-1)),e[141]||(e[141]=t("dt",null,"Secondary",-1)),e[142]||(e[142]=t("dd",null,[t("em",null,"Seat is Active"),n(": Do the secondary action, e.g. Pass"),t("br"),t("em",null,"Seat is Inactive"),n(": If using Rounds with 'Pass' as round-end method, then toggle target player'
9s 'pass' status, if possible. ")],-1)),e[143]||(e[143]=t("dt",null,"ToggleAdmin",-1)),e[144]||(e[144]=t("dd",null,[n(" Performs current Admin action, usually enter/exit admin time but in rare situations, End Round."),t("br"),t("em",null,"Does nothing if this action would resume another player's turn.")],-1)),e[145]||(e[145]=t("dt",null,"AdminOn",-1)),e[146]||(e[146]=t("dd",null,"Turns Admin Time on, if possible.",-1)),e[147]||(e[147]=t("dt",null,"AdminOff",-1)),e[148]||(e[148]=t("dd",null,[n(" Turns Admin Time off, if possible."),t("br"),t("em",null,"Does nothing if this action would resume another player's turn.")],-1)),e[149]||(e[149]=t("dt",null,"TogglePause",-1)),e[150]||(e[150]=t("dd",null,[n(" Toggles Pause on/off."),t("br"),t("em",null,"Does nothing if this action would resume another player's turn.")],-1)),e[151]||(e[151]=t("dt",null,"PauseOn",-1)),e[152]||(e[152]=t("dd",null,"Pauses the game, if possible.",-1)),e[153]||(e[153]=t("dt",null,"PauseOff",-1)),e[154]||(e[154]=t("dd",null,[n(" Unpauses the game, if possible."),t("br"),t("em",null,"Does nothing if this action would resume another player's turn.")],-1)),e[155]||(e[155]=t("dt",null,"Undo",-1)),e[156]||(e[156]=t("dd",null,"Undoes the last action.",-1))]),_:1}),e[224]||(e[224]=t("h3",null,"Special Actions",-1)),e[225]||(e[225]=t("p",null,"There are some advanced or special actions. These are mostly used in full-on board game table integrations.",-1)),e[226]||(e[226]=t("p",null,"The first three below can be used mid-game.",-1)),l(f,{title:"action='Reorder'",caption:"Changes the player turn order. (Can also be used during game setup.)"},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[157]||(e[157]=[n(' { "action": "Reorder", "seats": [2,1,3] } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[158]||(e[158]=t("dt",null,"seats",-1)),e[159]||(e[159]=t("dd",null,"The new turn order as an array of seat numbers",-1))]),_:1}),l(f,{title:"action='StartSimTurn'",caption:"Start a mid-turn simultaneous turn."},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[160]||(e[160]=[n(' { "action": "StartSimTurn", "seats": [1,3] } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[161]||(e[161]=t("dt",null,"seats",-1)),e[162]||(e[162]=t("dd",null,"Seats of players to partake in the SimTurn.",-1))]),_:1}),l(f,{title:"action='AddTokenTransaction'",caption:"Adds a token transaction."},{footer:o(()=>[e[166]||(e[166]=t("p",null,"Note: If using limited tokens, a initiator/associate of 0 means the Stock.",-1)),l(d,{summary:"Example Message (Pay 10 💰 to bank)"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[163]||(e[163]=[n(' { "action": "AddTokenTransaction", "token": "💰", "amount": -10, "initiator": 2 } ')])),_:1},8,["max-line-length"])]),_:1}),l(d,{summary:"Example Message (Take a 🐑 from player at seat 3)"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[164]||(e[164]=[n(' { "action": "AddTokenTransaction", "token": "🐑", "amount": 1, "initiator": 2, "associate": 3 } ')])),_:1},8,["max-line-length"])]),_:1}),l(d,{summary:"Example Message (Take 3 💧 from the limited stock)"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[165]||(e[165]=[n(' { "action": "AddTokenTransaction", "token": "💧", "amount": 3, "initiator": 2, "associate": 0 } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[167]||(e[167]=t("dt",null,"token",-1)),e[168]||(e[168]=t("dd",null,"The token involved in the transaction.",-1)),e[169]||(e[169]=t("dt",null,"amount",-1)),e[170]||(e[170]=t("dd",null,"The amount as a number or a formula string, of tokens the initiator receives, or if negative, looses.",-1)),e[171]||(e[171]=t("dt",null,"initiator",-1)),e[172]||(e[172]=t("dd",null,"The seat of the player who performs the transaction. The one to receive (+) or loose (-) tokens.",-1)),e[173]||(e[173]=t("dt",null,"associate",-1)),e[174]||(e[174]=t("dd",null,"(optional) If given, then the initiator takes tokens from (+) or gives tokens to (-) the associate.",
9-1))]),_:1}),e[227]||(e[227]=t("p",null,"The remaining ones can only be done during game setup.",-1)),l(f,{title:"action='StartGame'",caption:"Start Game, with optional start-of-game turn order update."},{footer:o(()=>[l(d,{summary:"Example Message (Start game with turn order unchanged)"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[175]||(e[175]=[n(' { "action": "StartGame" } ')])),_:1},8,["max-line-length"])]),_:1}),l(d,{summary:"Example Message (Start game with new turn order)"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[176]||(e[176]=[n(' { "action": "StartGame", "playerOrderAsSeats": [2,1,3] } ')])),_:1},8,["max-line-length"])]),_:1}),l(d,{summary:"Example Message (Start game with seat 2 as first player)"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[177]||(e[177]=[n(' { "action": "StartGame", "firstPlayerSeat": 2 } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[178]||(e[178]=t("dt",null,"playerOrderAsSeats",-1)),e[179]||(e[179]=t("dd",null,"(optional) Set turn order to the one given (as array of seat numbers)",-1)),e[180]||(e[180]=t("dt",null,"firstPlayerSeat",-1)),e[181]||(e[181]=t("dd",null,"(optional) Maintain relative player order, but make the given seat the first player. So if order is [1,2,3] and seat 2 is made the first player, the turn order at game start will be [2,3,1]",-1))]),_:1}),l(f,{title:"action='AddHotseatPlayer'",caption:"Adds a new hot-seat player."},{footer:o(()=>[l(d,{summary:"Example Message (Add hot-seat player, with seat number auto-assigned)"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[182]||(e[182]=[n(' { "action": "AddHotseatPlayer" } ')])),_:1},8,["max-line-length"])]),_:1}),l(d,{summary:"Example Message (Add hot-seat player, with given seat number)"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[183]||(e[183]=[n(' { "action": "AddHotseatPlayer", "seat": 4 } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[184]||(e[184]=t("dt",null,"seat",-1)),e[185]||(e[185]=t("dd",null,"(optional) The seat to give the newly added player.",-1))]),_:1}),l(f,{title:"action='CyclePlayerColor'",caption:"Changes the color of the given player during game setup. If using a template, it uses the template colors, otherwise a generic palette."},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[186]||(e[186]=[n(' { "action": "CyclePlayerColor", "seat": 3 } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[187]||(e[187]=t("dt",null,"seat",-1)),e[188]||(e[188]=t("dd",null,"The player whose color to cycle.",-1))]),_:1}),l(f,{title:"action='RemovePlayer'",caption:"Removes a player during game setup."},{footer:o(()=>[l(d,{summary:"Example Message"},{default:o(()=>[l(u,{"max-line-length":r(a)},{default:o(()=>e[189]||(e[189]=[n(' { "action": "RemovePlayer", "seat": 3 } ')])),_:1},8,["max-line-length"])]),_:1})]),default:o(()=>[e[190]||(e[190]=t("dt",null,"seat",-1)),e[191]||(e[191]=t("dd",null,"The player to remove.",-1))]),_:1})])}}}),pe=V(ue,[["__scopeId","data-v-db280981"]]);export{pe as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.