// DataStoreCounter.cs // A persistent, world-wide click counter backed by the script DATA STORE (SaveData / LoadData). The total survives // world restarts and platform rebuilds, because it lives in the server's database, not in the scene file. // // How it works: // * On Start the script LOADS the saved total from the data store and shows it on an overlay panel. // * Every time any player clicks the object (or ClickTarget), the total goes up for everyone and is SAVED again. // * The storage key is prefixed with a [Secret] string so the key never ships to any player's browser and another // creator can't guess it to read or overwrite your data. (Not a hard security boundary — just keeps keys hidden.) // // Set DataKey to any unique string of your own in the Inspector (it is a [Secret] field, so it is stored server-side // only). Everything the script stores is keyed under it. using IHXR.Simulation; public class DataStoreCounter : ScriptBase { [Header("Storage")] [Secret] [Tooltip("A unique, private prefix for this counter's data. Stored server-side only (never downloaded by players).")] public string DataKey = "my-world-counter-v1"; [Header("What players click")] [Tooltip("The object players click to add one. Empty = the object this script is on.")] public SceneObject ClickTarget; public string ClickHint = "Add one"; [Tooltip("How much each click adds.")] public int Step = 1; [Header("On-screen counter")] public string Label = "Clicks"; public TextureItem Icon; ScriptPanel panel; PanelItem counter; int total; bool loaded; // The full key everything is stored under (your secret prefix keeps it unique and hidden). string TotalKey => DataKey + ":total"; public override void Start() { panel = CreatePanel("datastore-counter", 260); panel.SetMode(PanelMode.Overlay) .SetScreenPosition(70f, 6f, PanelAnchor.TopCenter) .SetBackgroundColor(Color.clear) .SetClosable(false) .SetLayout(1, 4, 8, PanelAlign.Center) .SetFont(PanelFont.Heavy, 30); counter = panel.AddCounter(Text(), Icon); panel.ShowToEveryone(); // Load the saved total from the data store (async: the callback runs when the DB replies). LoadInt(TotalKey, 0, value => { total = value; loaded = true; counter.SetText(Text()); Log("[DataStoreCounter] loaded total = " + total); }); // Any player clicking adds to the shared total (the callback runs once, on the server). if (ClickTarget != null && !ClickTarget.IsNull) MakeInteractable(ClickTarget, click => Add(), true, null, true, 12f, ClickHint); else MakeInteractable(click => Add(), true, null, true, 12f, ClickHint); } void Add() { if (!loaded) return; // wait until the saved value is in, so we don't overwrite it with 0 total += Step; counter.SetText(Text()); // update everyone's screen now // Persist the new total. onDone(true) means it was written to the database. SaveData(TotalKey, total, ok => { if (!ok) LogWarning("[DataStoreCounter] could not save the total (is the data store reachable?)."); }); } string Text() => Label + ": " + total; }