cluaupp 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +5 -8
  3. package/docs/README.md +13 -10
  4. package/docs/architecture.md +18 -11
  5. package/docs/cli.md +2 -2
  6. package/docs/config.md +1 -1
  7. package/docs/cpp-advanced.md +10 -6
  8. package/docs/cpp-organization.md +3 -1
  9. package/docs/cpp-safety.md +11 -7
  10. package/docs/cpp-types.md +1 -1
  11. package/docs/examples/_category_.json +5 -0
  12. package/docs/examples/combat.md +239 -0
  13. package/docs/examples/data-boot.md +98 -0
  14. package/docs/examples/hud.md +96 -0
  15. package/docs/examples/index.md +43 -0
  16. package/docs/examples/leaderstats.md +140 -0
  17. package/docs/examples/shop.md +122 -0
  18. package/docs/examples/sword.md +108 -0
  19. package/docs/getting-started.md +12 -6
  20. package/docs/intellisense.md +31 -0
  21. package/docs/intro.md +2 -2
  22. package/docs/libraries/_category_.json +5 -0
  23. package/docs/libraries/dataservice.md +104 -0
  24. package/docs/libraries/index.md +22 -0
  25. package/docs/libraries/janitor.md +65 -0
  26. package/docs/libraries/more.md +79 -0
  27. package/docs/libraries/net.md +35 -0
  28. package/docs/libraries/promise.md +27 -0
  29. package/docs/oop/_category_.json +5 -0
  30. package/docs/oop/file-tags.md +49 -0
  31. package/docs/oop/index.md +22 -0
  32. package/docs/oop/modules.md +86 -0
  33. package/docs/oop/services.md +83 -0
  34. package/docs/print-cout.md +91 -0
  35. package/docs/syntax.md +59 -5
  36. package/editors/vscode/extension.js +146 -0
  37. package/editors/vscode/package.json +25 -0
  38. package/include/cluaupp/libs/janitor.hpp +7 -3
  39. package/include/cluaupp/roblox.hpp +19 -0
  40. package/package.json +66 -65
  41. package/runtime/Janitor/init.luau +4 -34
  42. package/src/architecture.js +108 -48
  43. package/src/cli.js +85 -10
  44. package/src/client/init.client.cpp +5 -0
  45. package/src/compile.js +20 -4
  46. package/src/editor-install.js +218 -0
  47. package/src/emit.js +320 -8
  48. package/src/intellisense.js +1368 -0
  49. package/src/layout.js +129 -0
  50. package/src/lex.js +17 -9
  51. package/src/libs.js +95 -16
  52. package/src/lsp.js +227 -0
  53. package/src/parse.js +187 -6
  54. package/src/preprocess.js +71 -3
  55. package/src/server/leaderstats.server.cpp +28 -0
  56. package/src/shared/config.cpp +5 -0
  57. package/src/shared/config.h +3 -0
  58. package/src/understand.js +64 -6
  59. package/templates/game/.clangd +11 -0
  60. package/templates/game/.vscode/c_cpp_properties.json +6 -2
  61. package/templates/game/.vscode/extensions.json +6 -0
  62. package/templates/game/.vscode/settings.json +16 -2
  63. package/templates/game/compile_flags.txt +2 -0
  64. package/docs/libraries.md +0 -80
@@ -0,0 +1,104 @@
1
+ ---
2
+ title: DataService
3
+ sidebar_position: 2
4
+ ---
5
+
6
+ # DataService
7
+
8
+ Player profiles (DataServiceV2). Header: `#include <cluaupp/libs/dataservice.hpp>`.
9
+
10
+ The **save shape is yours**. Do not put `Currencies` / `Money` on the library `DataPath` type. Define a struct in the game (shared header) and pass it as `.Template`.
11
+
12
+ ## 1. Template (shared)
13
+
14
+ ```cpp
15
+ #pragma once
16
+
17
+ struct TemplateData {
18
+ struct Currencies {
19
+ int Money = 0;
20
+ int Level = 1;
21
+ } Currencies;
22
+ };
23
+ ```
24
+
25
+ Luau `DataService.Paths` follows this table (`Paths.Currencies.Money`).
26
+
27
+ ## 2. Start the server
28
+
29
+ Call **once** from a server boot script (`DataBoot.server.cpp`). `void init()` is what Cluaupp runs (not `int main()`).
30
+
31
+ ```cpp
32
+ #include <cluaupp/roblox.hpp>
33
+ #include <cluaupp/libs/dataservice.hpp>
34
+ #include "../../shared/constants/TemplateData.hpp"
35
+
36
+ void init() {
37
+ TemplateData playerData = TemplateData {};
38
+ DataService::Server.Init(DataServiceOptions {
39
+ .Template = playerData,
40
+ .StoreName = "PlayerData",
41
+ .UseMock = true,
42
+ });
43
+ }
44
+ ```
45
+
46
+ | Field | Meaning |
47
+ | --- | --- |
48
+ | `.Template` | Default document. Must match the save struct. |
49
+ | `.StoreName` | DataStore name (or a version string you own). |
50
+ | `.UseMock` | `true` in Studio / tests so you do not hit the live store. |
51
+ | `.KeyPrefix` | Optional prefix on profile keys. |
52
+ | `.StrictPaths` | Reject unknown path segments. |
53
+ | `.AutoCreateMissingTables` | Create missing nested tables on write. |
54
+
55
+ ## 3. Start the client
56
+
57
+ ```cpp
58
+ #include <cluaupp/roblox.hpp>
59
+ #include <cluaupp/libs/dataservice.hpp>
60
+
61
+ void init() {
62
+ DataService::Client.Init();
63
+ }
64
+ ```
65
+
66
+ ## 4. Read / wait
67
+
68
+ ```cpp
69
+ Data* data = DataService::Server.WaitFor(player);
70
+ if (data == nullptr) {
71
+ return;
72
+ }
73
+
74
+ int money = data->Get(DataService::Server.Paths.Currencies.Money);
75
+ ```
76
+
77
+ - `WaitFor` — yield until the profile exists.
78
+ - `Get(player)` — already loaded or `nullptr`.
79
+ - `data->Get()` with no path — whole table.
80
+ - `GetPersisted` / `GetTransient` — saved vs session-only.
81
+
82
+ Client: `DataService::Client.WaitForData()` / `Get()`.
83
+
84
+ ## 5. Write and listen
85
+
86
+ ```cpp
87
+ data->Set(DataService::Server.Paths.Currencies.Money, 10);
88
+
89
+ void OnCurrenciesChanged() {
90
+ return;
91
+ }
92
+
93
+ data->GetChangedSignal(DataService::Server.Paths.Currencies).Connect(OnCurrenciesChanged);
94
+ ```
95
+
96
+ `GetChangedSignal` fires when that path (or a child) changes. Cluaupp has no lambdas — use a named function.
97
+
98
+ ## Rules
99
+
100
+ - Init on **server and client**. Writes that must persist belong on the server.
101
+ - Paths are not a C++ enum in the library. They exist because your Template was passed to `Init`.
102
+ - Keep boot (`Init`) in one file. Keep HUD / leaderstats in another and `WaitFor` there.
103
+
104
+ Full copies: [Data boot](../examples/data-boot.md), [Leaderstats](../examples/leaderstats.md), [HUD](../examples/hud.md).
@@ -0,0 +1,22 @@
1
+ ---
2
+ title: Using libraries
3
+ sidebar_position: 1
4
+ ---
5
+
6
+ # Libraries
7
+
8
+ `#include <cluaupp/libs/...hpp>` is IntelliSense. `cluaupp build` copies the real Luau into `libs/` (`ReplicatedStorage.CluauppLibs`). You do not `require` by hand in C++.
9
+
10
+ | Guide | Use it when |
11
+ | --- | --- |
12
+ | [DataService](dataservice.md) | Player save data, `Init`, `WaitFor`, `GetChangedSignal` |
13
+ | [Janitor](janitor.md) | Connections, instances, cleanup on leave |
14
+ | [Promise](promise.md) | Delay, Then / Catch / Await |
15
+ | [Net](net.md) | RemoteEvent / RemoteFunction without making remotes |
16
+ | [More libraries](more.md) | FormatNumber, Fusion, Cmdr, Twinkle, MathUtils, … |
17
+
18
+ Sources: [runtime/SOURCES.md](https://github.com/KartzRbx/Cluaupp/blob/main/runtime/SOURCES.md). Re-vendor: `node scripts/vendor-libs.js`.
19
+
20
+ Wally is optional. Do not install a second Janitor from Wally — CluauppLibs already has howmanysmall/Janitor.
21
+
22
+ Copy-paste systems: [Examples](../examples/index.md).
@@ -0,0 +1,65 @@
1
+ ---
2
+ title: Janitor
3
+ sidebar_position: 3
4
+ ---
5
+
6
+ # Janitor
7
+
8
+ Cleans connections, instances, promises, and threads. Header: `#include <cluaupp/libs/janitor.hpp>`. Runtime is [howmanysmall/Janitor](https://github.com/howmanysmall/Janitor). Types come from `_impl` — there is no fake `Has` method.
9
+
10
+ ## Create and add
11
+
12
+ ```cpp
13
+ auto* janitor = new Janitor();
14
+
15
+ janitor->Add(Players->PlayerAdded.Connect(OnPlayer));
16
+ janitor->Add(part, "Destroy");
17
+ janitor->Add(print, true);
18
+ ```
19
+
20
+ | Call | Cleanup |
21
+ | --- | --- |
22
+ | `Add(connection)` | `Disconnect` |
23
+ | `Add(instance)` / `Add(instance, "Destroy")` | `Destroy` |
24
+ | `Add(fn, true)` | call the function |
25
+ | `Add(thread, true)` | `task.cancel` |
26
+
27
+ ## Indexed slots (per player)
28
+
29
+ The third argument is a namespace. Adding again with the same index removes the previous task first.
30
+
31
+ ```cpp
32
+ janitor->Add(sectionJanitor, "Destroy", player->Name);
33
+
34
+ if (janitor->Get(player->Name)) {
35
+ janitor->Remove(player->Name);
36
+ }
37
+ ```
38
+
39
+ `Remove` on a missing index is a no-op. There is **no** `Has` — use `Get`.
40
+
41
+ ## Lifetime
42
+
43
+ ```cpp
44
+ janitor->Cleanup();
45
+ janitor->Destroy();
46
+ janitor->LinkToInstance(player);
47
+ ```
48
+
49
+ - `Cleanup` — run every task, janitor stays usable.
50
+ - `Destroy` — cleanup and freeze the object.
51
+ - `LinkToInstance` — cleanup when that Instance is destroyed.
52
+
53
+ ## Promises
54
+
55
+ ```cpp
56
+ #include <cluaupp/libs/promise.hpp>
57
+
58
+ janitor->AddPromise(Promise::delay(1));
59
+ ```
60
+
61
+ Cleanup cancels or rejects the promise when the janitor runs.
62
+
63
+ ## Pattern
64
+
65
+ One janitor for the script. A **child** janitor per player, stored under `player->Name`, removed on `PlayerRemoving`. Copy: [Leaderstats](../examples/leaderstats.md).
@@ -0,0 +1,79 @@
1
+ ---
2
+ title: More libraries
3
+ sidebar_position: 6
4
+ ---
5
+
6
+ # More libraries
7
+
8
+ Same rule: include the header, call the API. `cluaupp build` already put the Luau in `CluauppLibs`.
9
+
10
+ ## FormatNumber
11
+
12
+ ```cpp
13
+ #include <cluaupp/libs/formatnumber.hpp>
14
+
15
+ print(FormatNumber::Abbreviate(1500));
16
+ print(FormatNumber::Comma(1500));
17
+ ```
18
+
19
+ ## MathUtils
20
+
21
+ ```cpp
22
+ #include <cluaupp/libs/math.hpp>
23
+
24
+ double t = MathUtils::Lerp(0, 1, 0.5);
25
+ ```
26
+
27
+ ## Twinkle (UI)
28
+
29
+ ```cpp
30
+ #include <cluaupp/libs/twinkle.hpp>
31
+
32
+ Twinkle::Fade(frame, true);
33
+ ```
34
+
35
+ ## Module3D
36
+
37
+ ```cpp
38
+ #include <cluaupp/libs/module3d.hpp>
39
+
40
+ Module3D::Attach3D(viewport, model);
41
+ ```
42
+
43
+ ## VfxUtil
44
+
45
+ ```cpp
46
+ #include <cluaupp/libs/vfx.hpp>
47
+
48
+ VfxUtil::Emit(root);
49
+ VfxUtil::Play(emitter);
50
+ ```
51
+
52
+ ## StickyBillboard
53
+
54
+ ```cpp
55
+ #include <cluaupp/libs/stickybillboard.hpp>
56
+
57
+ auto* billboard = StickyBillboard::new_(adornee, gui);
58
+ billboard->SetText("Hello");
59
+ ```
60
+
61
+ ## Fusion / Iris / Cmdr / TopbarPlus / Chrono / EzVisualz / StateMachine / Spring / Display
62
+
63
+ These are the GitHub systems behind typed `init.luau` borders. Include the matching `<cluaupp/libs/*.hpp>` and call the same names as the upstream README. Cluaupp does not re-document every Fusion `Value` / Cmdr command — use:
64
+
65
+ | Lib | Header | Upstream |
66
+ | --- | --- | --- |
67
+ | Fusion | `fusion.hpp` | [dphfox/Fusion](https://github.com/dphfox/Fusion) |
68
+ | Iris | `iris.hpp` | [SirMallard/Iris](https://github.com/SirMallard/Iris) |
69
+ | Cmdr | `cmdr.hpp` | [evaera/Cmdr](https://github.com/evaera/Cmdr) |
70
+ | TopbarPlus | `topbarplus.hpp` | [1ForeverHD/TopbarPlus](https://github.com/1ForeverHD/TopbarPlus) |
71
+ | Chrono | `chrono.hpp` | [Parihsz/Chrono](https://github.com/Parihsz/Chrono) |
72
+ | EzVisualz | `ezvisual.hpp` | [arxkdev/ezVisualz](https://github.com/arxkdev/ezVisualz) |
73
+ | StateMachine | `statemachine.hpp` | [Prooheckcp/RobloxStateMachine](https://github.com/Prooheckcp/RobloxStateMachine) |
74
+ | Spring | `spring.hpp` | [nightcycle/spring](https://github.com/nightcycle/spring) |
75
+ | Display | `display.hpp` | [nightcycle/display](https://github.com/nightcycle/display) |
76
+
77
+ ## Types-only
78
+
79
+ `ArrayIndexer` (`Table<Manifest, "Name">`) and `Occlude` (`Keys<Data, "field">`) are used by generated `{Service}Types.luau`. You rarely include them from game C++.
@@ -0,0 +1,35 @@
1
+ ---
2
+ title: Net
3
+ sidebar_position: 5
4
+ ---
5
+
6
+ # Net
7
+
8
+ Buffer-packed remotes. Header: `#include <cluaupp/libs/net.hpp>`. Same name on server and client — you do not create `RemoteEvent` instances yourself.
9
+
10
+ ```cpp
11
+ #include <cluaupp/roblox.hpp>
12
+ #include <cluaupp/libs/net.hpp>
13
+ #include <cluaupp/libs/janitor.hpp>
14
+
15
+ void OnCoins(Player* player, int amount) {
16
+ return;
17
+ }
18
+
19
+ void init() {
20
+ auto* janitor = new Janitor();
21
+ auto* coins = Net::Event("Coins");
22
+ coins->On(OnCoins);
23
+ janitor->Add(coins);
24
+ }
25
+ ```
26
+
27
+ | Side | API |
28
+ | --- | --- |
29
+ | Server | `Fire(player, ...)`, `FireAll(...)` |
30
+ | Client | `FireServer(...)` |
31
+ | Both | `On(callback)` |
32
+
33
+ Functions: `Net::Function("Shop")`, then `Invoke` / `InvokeServer`.
34
+
35
+ Validate everything the client fires. See [safety](../cpp-safety.md). Full copies: [Combat](../examples/combat.md), [Shop](../examples/shop.md).
@@ -0,0 +1,27 @@
1
+ ---
2
+ title: Promise
3
+ sidebar_position: 4
4
+ ---
5
+
6
+ # Promise
7
+
8
+ [evaera/roblox-lua-promise](https://github.com/evaera/roblox-lua-promise). Header: `#include <cluaupp/libs/promise.hpp>`.
9
+
10
+ C++ names `Then` / `Catch` / `Await` / `Cancel` are aliases of `andThen` / `catch` / `await` / `cancel`.
11
+
12
+ ```cpp
13
+ #include <cluaupp/roblox.hpp>
14
+ #include <cluaupp/libs/promise.hpp>
15
+
16
+ void AfterWait() {
17
+ print("1s later");
18
+ }
19
+
20
+ void init() {
21
+ Promise::delay(1);
22
+ }
23
+ ```
24
+
25
+ Useful statics: `Promise::delay(seconds)`, `Promise::resolve(value)`, `Promise::reject("err")`.
26
+
27
+ Cluaupp cannot nest lambdas. Prefer `delay` plus a named callback you already have, or keep promise chains in Luau libraries you call from C++.
@@ -0,0 +1,5 @@
1
+ {
2
+ "label": "OOP structure",
3
+ "position": 16,
4
+ "collapsed": false
5
+ }
@@ -0,0 +1,49 @@
1
+ ---
2
+ title: File tags
3
+ sidebar_position: 2
4
+ ---
5
+
6
+ # File tags
7
+
8
+ The **filename** decides the Roblox instance. This is the same key idea as roblox-ts (`*.server.ts` → Script).
9
+
10
+ | Source | Instance | RunContext |
11
+ | --- | --- | --- |
12
+ | `LeaderstatsServer.server.cpp` | **Script** | **Server** |
13
+ | `DataBoot.client.cpp` | **LocalScript** | Client |
14
+ | `Tools.plugin.cpp` | **Script** | Plugin |
15
+ | `Boot.legacy.cpp` | Script | **Legacy** |
16
+ | `Boot.legacy.server.cpp` | Script | **Legacy** |
17
+ | `Boot.legacy.client.cpp` | LocalScript | Legacy |
18
+ | `Damage.cpp` (no tag) | **ModuleScript** | — |
19
+
20
+ ## Server is not Legacy
21
+
22
+ `.server.cpp` is **not** `init.server.luau`. Rojo maps `*.server.luau` to a Script with RunContext **Legacy**. Cluaupp emits:
23
+
24
+ ```
25
+ out/server/LeaderstatsServer/
26
+ init.luau
27
+ init.meta.json -- className Script, RunContext Server
28
+ ```
29
+
30
+ `.legacy.server.cpp` is the 1:1 dump that *does* become Legacy.
31
+
32
+ `.client.cpp` stays `init.client.luau` (LocalScript). Do not add `init.meta.json` on the client boot.
33
+
34
+ ## Where files live
35
+
36
+ ```
37
+ src/server/.../*.server.cpp → out/server (ServerScriptService)
38
+ src/client/.../*.client.cpp → out/client (StarterPlayerScripts)
39
+ src/shared/.../*.cpp → out/shared (ReplicatedStorage)
40
+ ```
41
+
42
+ Name the **system**, not `init.server.cpp`. `LeaderstatsServer.server.cpp` and `DataBoot.server.cpp` are two services.
43
+
44
+ ## Skip the planner
45
+
46
+ - `"architecture": false` in `cluaupp.config.json`, or
47
+ - `.legacy.server.cpp` / `.legacy.client.cpp` per file
48
+
49
+ Use legacy only when you want one Luau file with no Main / Controller split.
@@ -0,0 +1,22 @@
1
+ ---
2
+ title: OOP structure
3
+ sidebar_position: 1
4
+ ---
5
+
6
+ # OOP structure
7
+
8
+ The **live site** (not just these markdown files) is GitHub Pages:
9
+
10
+ - [Learn → OOP structure](https://kartzrbx.github.io/Cluaupp/learn/index.html)
11
+ - [Guide → OOP](https://kartzrbx.github.io/Cluaupp/guide/oop.html)
12
+ - [Examples](https://kartzrbx.github.io/Cluaupp/guide/examples.html)
13
+
14
+ Cluaupp does **not** compile custom C++ `class` types yet. “OOP” here is how you **lay out systems** so Studio gets services, modules, and lifecycle — the same idea as roblox-ts filename keys + Flamework `@Service`, without decorators.
15
+
16
+ | Page | What you learn |
17
+ | --- | --- |
18
+ | [File tags](file-tags.md) | `.server` / `.client` / `.plugin` / `.legacy` / untagged |
19
+ | [Services](services.md) | One `.server.cpp` → Main, Controller, Start / Stop |
20
+ | [Modules](modules.md) | Untagged files, structs, named functions as methods |
21
+
22
+ Planner details: [Architecture](../architecture.md). Folder layout: [Organization](../cpp-organization.md). Full services: [Examples](../examples/index.md).
@@ -0,0 +1,86 @@
1
+ ---
2
+ title: Modules and structs
3
+ sidebar_position: 4
4
+ ---
5
+
6
+ # Modules and structs
7
+
8
+ Use this when you want a **reusable object** without a tagged Script.
9
+
10
+ ## Untagged `.cpp` = ModuleScript
11
+
12
+ ```
13
+ src/shared/util/Coins.cpp
14
+ ```
15
+
16
+ ```cpp
17
+ #include <cluaupp/roblox.hpp>
18
+
19
+ int DoubleCoins(int coins) {
20
+ return coins;
21
+ }
22
+ ```
23
+
24
+ ```
25
+ out/shared/util/Coins.luau -- ModuleScript, exported functions
26
+ ```
27
+
28
+ Shared **data and constants** belong in headers (`#include "TemplateData.hpp"`) — they are inlined. Untagged `.cpp` is for a ModuleScript other Luau can `require`. Prefer headers for data and tagged `.server.cpp` / `.client.cpp` for behavior.
29
+
30
+ ## Structs = data, not classes
31
+
32
+ The subset allows structs for IntelliSense and designated initializers (tables):
33
+
34
+ ```cpp
35
+ struct TemplateData {
36
+ struct Currencies {
37
+ int Money = 0;
38
+ int Level = 1;
39
+ } Currencies;
40
+ };
41
+
42
+ TemplateData playerData = TemplateData {};
43
+ ```
44
+
45
+ That is your “class fields”. Methods are **named functions** next to them (`EnsureStat`, `ApplyCurrencies`). Cluaupp does not emit `function TemplateData:GetMoney()`.
46
+
47
+ ## Methods as functions
48
+
49
+ ```cpp
50
+ void EnsureStat(Folder* folder, string name, int value) {
51
+ auto* stat = folder->FindFirstChild(name);
52
+ if (stat == nullptr) {
53
+ cout::print << "EnsureStat: " << name << " not found" << endl;
54
+ return;
55
+ }
56
+ }
57
+ ```
58
+
59
+ Pass the instance in (`folder`, `player`). That is composition: the Folder is the object, the function is the method.
60
+
61
+ ## Janitor as the destructor
62
+
63
+ There is no C++ destructor. Pair “construct” with a Janitor:
64
+
65
+ ```cpp
66
+ auto* janitor = new Janitor();
67
+ janitor->LinkToInstance(player);
68
+ janitor->Add(player->AncestryChanged.Connect(OnAncestry));
69
+ ```
70
+
71
+ When the player leaves, `LinkToInstance` runs `Cleanup`. That is RAII for Roblox.
72
+
73
+ ## Start / Stop
74
+
75
+ On generated services, `Main.Start` / `Main.Stop` are the constructor / destructor of the **system**. Put connections in Start (or in `init()` / `PlayersManager`). Do not leak `Connect` without a Janitor.
76
+
77
+ ## What not to write
78
+
79
+ ```cpp
80
+ class Shop {
81
+ int price;
82
+ void Buy(Player* player);
83
+ };
84
+ ```
85
+
86
+ Custom classes are **not supported**. Split into a struct (fields) + functions (methods) + a `.server.cpp` or untagged module (lifetime).
@@ -0,0 +1,83 @@
1
+ ---
2
+ title: Services
3
+ sidebar_position: 3
4
+ ---
5
+
6
+ # Services
7
+
8
+ Write **one tagged file per system**. Cluaupp reads the AST and emits a PascalCase folder: `Main`, managers, controllers, `*Types`.
9
+
10
+ ## Example: leaderstats
11
+
12
+ ```cpp
13
+ #include <cluaupp/roblox.hpp>
14
+ #include <cluaupp/libs/janitor.hpp>
15
+ #include <cluaupp/libs/dataservice.hpp>
16
+
17
+ void EnsureStat(Folder* folder, string name, int value) {
18
+ return;
19
+ }
20
+
21
+ void ApplyCurrencies(Player* player, Data* data) {
22
+ return;
23
+ }
24
+
25
+ void OnCurrenciesChanged() {
26
+ return;
27
+ }
28
+
29
+ void SetupPlayerManager(Player* player) {
30
+ Data* data = DataService::Server.WaitFor(player);
31
+ if (data == nullptr) {
32
+ return;
33
+ }
34
+ ApplyCurrencies(player, data);
35
+ data->GetChangedSignal(DataService::Server.Paths.Currencies).Connect(OnCurrenciesChanged);
36
+ }
37
+
38
+ void init() {
39
+ auto* players = GetService<Players>();
40
+ players->PlayerAdded.Connect(SetupPlayerManager);
41
+ }
42
+ ```
43
+
44
+ Typical output:
45
+
46
+ ```
47
+ out/server/LeaderstatsServer/
48
+ init.luau -- Script, RunContext Server
49
+ init.meta.json
50
+ Main.luau -- Start / Stop
51
+ PlayersManager.luau -- PlayerAdded + Janitor
52
+ DataController.luau -- your WaitFor / GetChangedSignal
53
+ CacheController.luau -- if you create IntValue folders
54
+ LeaderstatsServerTypes.luau
55
+ ```
56
+
57
+ Your functions stay in the matching controller. The planner does not drop `SetupPlayerManager` because `init` still calls it.
58
+
59
+ ## Lifecycle
60
+
61
+ Generated `Main` exposes `Start` / `Stop`. Managers own a Janitor. `Stop` cleans connections.
62
+
63
+ You do not write `class LeaderstatsServer` — name the **file** `LeaderstatsServer.server.cpp` and keep **functions** as the public API.
64
+
65
+ ## Boot vs gameplay
66
+
67
+ Keep DataService `Init` in `DataBoot.server.cpp` / `DataBoot.client.cpp`. Keep leaderstats / combat in their own `.server.cpp`. Services `WaitFor` after boot has run.
68
+
69
+ ## What becomes which role
70
+
71
+ | Your C++ | Generated role |
72
+ | --- | --- |
73
+ | `PlayerAdded`, `GetPlayers` | `PlayersManager` |
74
+ | `Folder` + `IntValue` | `CacheController` |
75
+ | `DataService`, `GetChangedSignal` | `DataController` |
76
+ | `TakeDamage`, `Humanoid` | `CombatController` |
77
+ | `Net::Event` | `NetController` |
78
+ | `UserInputService` | `InputController` |
79
+ | wiring | `Main.Start` / `Main.Stop` |
80
+
81
+ A tiny `print` in `init.client.cpp` stays a single LocalScript. The planner does not invent Managers for hello-world.
82
+
83
+ Copy-paste: [Examples](../examples/index.md).
@@ -0,0 +1,91 @@
1
+ ---
2
+ title: print and cout
3
+ sidebar_position: 8
4
+ ---
5
+
6
+ # print and cout
7
+
8
+ Cluaupp has no `<iostream>`. Logging is Roblox `print` / `warn` / `error`. You can still write C++-style `cout` / `cerr` / `endl` — the compiler flattens them.
9
+
10
+ ## Globals
11
+
12
+ ```cpp
13
+ print("ok");
14
+ warn("careful");
15
+ error("fail");
16
+ ```
17
+
18
+ ```luau
19
+ print("ok")
20
+ warn("careful")
21
+ error("fail")
22
+ ```
23
+
24
+ `print` in C++ takes a string (and chained `cout` pieces). Do not expect full C++ `std::cout` overloads.
25
+
26
+ ## Stream: `cout << … << endl`
27
+
28
+ Each `<<` is another argument. `endl` ends the statement (no extra `"\n"`).
29
+
30
+ ```cpp
31
+ cout << "EnsureStat: " << name << " not found" << endl;
32
+ ```
33
+
34
+ ```luau
35
+ print("EnsureStat: ", name, " not found")
36
+ ```
37
+
38
+ `cerr` is the same chain, emitted as `warn`:
39
+
40
+ ```cpp
41
+ cerr << "failed to load " << player->Name << endl;
42
+ ```
43
+
44
+ ```luau
45
+ warn("failed to load ", player.Name)
46
+ ```
47
+
48
+ ## Levels: `cout::print` / `warn` / `error` / `ping`
49
+
50
+ Use `::` to pick the Roblox function. The stream still concatenates with `<<`.
51
+
52
+ ```cpp
53
+ cout::print << "EnsureStat test: " << name << " not found" << endl;
54
+ cout::warn << "missing folder for " << player->Name << endl;
55
+ cout::error << "profile failed" << endl;
56
+ cout::ping << "here" << endl;
57
+ ```
58
+
59
+ ```luau
60
+ print("EnsureStat test: ", name, " not found")
61
+ warn("missing folder for ", player.Name)
62
+ error("profile failed")
63
+ print("here")
64
+ ```
65
+
66
+ | C++ | Luau |
67
+ | --- | --- |
68
+ | `cout << … << endl` | `print(…)` |
69
+ | `cout::print << … << endl` | `print(…)` |
70
+ | `cout::warn << … << endl` | `warn(…)` |
71
+ | `cout::error << … << endl` | `error(…)` |
72
+ | `cout::ping << … << endl` | `print(…)` |
73
+ | `cerr << … << endl` | `warn(…)` |
74
+
75
+ `cout::ping` is a debug alias of `print` (no extra Studio highlight).
76
+
77
+ ## Call form (no `<<`)
78
+
79
+ ```cpp
80
+ cout::print("ok");
81
+ cout::warn("careful");
82
+ cout::error("fail");
83
+ cout::ping("here");
84
+ cout::endl();
85
+ ```
86
+
87
+ `cout::endl()` is a no-op in Luau (flush is already the statement). Prefer `<< endl` on a stream.
88
+
89
+ ## IntelliSense
90
+
91
+ `struct cout` / `cerr` and `endl` live in `<cluaupp/roblox.hpp>`. Include that header so clangd knows `cout::print` and `operator<<`.