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,96 @@
1
+ ---
2
+ title: HUD
3
+ sidebar_position: 6
4
+ ---
5
+
6
+ # HUD
7
+
8
+ Client-only: wait for the replicated profile, write labels, listen for currency changes. No `Set` on persisted paths from the client.
9
+
10
+ Put a ScreenGui named `Hud` in StarterGui with `MoneyLabel` and `LevelLabel` (`TextLabel`).
11
+
12
+ Define callbacks **above** `init()` so clangd and the subset both see them (no lambdas).
13
+
14
+ ## `HudClient.client.cpp`
15
+
16
+ ```cpp
17
+ #include <cluaupp/roblox.hpp>
18
+ #include <cluaupp/libs/janitor.hpp>
19
+ #include <cluaupp/libs/dataservice.hpp>
20
+ #include <cluaupp/libs/formatnumber.hpp>
21
+ #include <cluaupp/libs/twinkle.hpp>
22
+
23
+ Players* Players = GetService<Players>();
24
+ Janitor* janitor = new Janitor();
25
+
26
+ TextLabel* FindLabel(PlayerGui* playerGui, string name) {
27
+ if (playerGui == nullptr) {
28
+ return nullptr;
29
+ }
30
+ Instance* gui = playerGui->FindFirstChild("Hud");
31
+ if (gui == nullptr) {
32
+ return nullptr;
33
+ }
34
+ return gui->FindFirstChild(name);
35
+ }
36
+
37
+ void Render(Data* data, TextLabel* moneyLabel, TextLabel* levelLabel) {
38
+ if (data == nullptr) {
39
+ return;
40
+ }
41
+ int money = data->Get(DataService::Client.Paths.Currencies.Money);
42
+ int level = data->Get(DataService::Client.Paths.Currencies.Level);
43
+ if (moneyLabel != nullptr) {
44
+ moneyLabel->Text = FormatNumber::Abbreviate(money);
45
+ }
46
+ if (levelLabel != nullptr) {
47
+ levelLabel->Text = FormatNumber::Comma(level);
48
+ }
49
+ }
50
+
51
+ void OnCurrenciesChanged() {
52
+ Player* player = Players->LocalPlayer;
53
+ if (player == nullptr) {
54
+ return;
55
+ }
56
+ PlayerGui* playerGui = player->FindFirstChildOfClass("PlayerGui");
57
+ TextLabel* moneyLabel = FindLabel(playerGui, "MoneyLabel");
58
+ TextLabel* levelLabel = FindLabel(playerGui, "LevelLabel");
59
+ Render(DataService::Client.Get(), moneyLabel, levelLabel);
60
+ }
61
+
62
+ void init() {
63
+ Player* player = Players->LocalPlayer;
64
+ if (player == nullptr) {
65
+ return;
66
+ }
67
+ PlayerGui* playerGui = player->FindFirstChildOfClass("PlayerGui");
68
+ if (playerGui == nullptr) {
69
+ return;
70
+ }
71
+
72
+ Data* data = DataService::Client.WaitForData();
73
+ if (data == nullptr) {
74
+ cout::warn << "HUD: profile missing" << endl;
75
+ return;
76
+ }
77
+
78
+ TextLabel* moneyLabel = FindLabel(playerGui, "MoneyLabel");
79
+ TextLabel* levelLabel = FindLabel(playerGui, "LevelLabel");
80
+ Render(data, moneyLabel, levelLabel);
81
+
82
+ if (moneyLabel != nullptr) {
83
+ Twinkle::Fade(moneyLabel, true);
84
+ }
85
+
86
+ janitor->Add(data->GetChangedSignal(DataService::Client.Paths.Currencies).Connect(OnCurrenciesChanged));
87
+ }
88
+ ```
89
+
90
+ ## Rules
91
+
92
+ - Call `DataService::Client.Init()` in [Data boot](data-boot.md) first. Script order in StarterPlayerScripts is not a contract — `WaitForData` yields until the profile exists.
93
+ - Do not `data->Set` Money from the HUD. Display only.
94
+ - `Twinkle::Fade` is optional. See [more libraries](../libraries/more.md).
95
+
96
+ `HudClient.client.cpp` is a LocalScript (`init.client.luau`).
@@ -0,0 +1,43 @@
1
+ ---
2
+ title: Examples
3
+ sidebar_position: 1
4
+ ---
5
+
6
+ # Examples
7
+
8
+ High-quality Cluaupp systems you can copy. Each page is a full service: C++ that the subset actually compiles, server authority, Janitor cleanup, and the file names Studio expects.
9
+
10
+ These are **not** dumps of `int main()`. Entry is `void init()`. There are no lambdas and no custom C++ classes — named functions + structs.
11
+
12
+ ## Suggested layout
13
+
14
+ ```
15
+ src/
16
+ shared/constants/TemplateData.hpp
17
+ shared/constants/CombatConfig.hpp
18
+ shared/constants/ShopCatalog.hpp
19
+ server/boot/DataBoot.server.cpp
20
+ client/boot/DataBoot.client.cpp
21
+ server/services/leaderstats/LeaderstatsServer.server.cpp
22
+ server/services/combat/CombatServer.server.cpp
23
+ client/controllers/combat/CombatClient.client.cpp
24
+ server/services/shop/ShopServer.server.cpp
25
+ client/controllers/shop/ShopClient.client.cpp
26
+ client/controllers/hud/HudClient.client.cpp
27
+ server/services/weapons/SwordServer.server.cpp
28
+ ```
29
+
30
+ Boot DataService **once**. Other services `WaitFor` after that.
31
+
32
+ ## Catalog
33
+
34
+ | Example | Teaches |
35
+ | --- | --- |
36
+ | [Data boot](data-boot.md) | `Server.Init` / `Client.Init`, your Template, not `main()` |
37
+ | [Leaderstats](leaderstats.md) | Folder `leaderstats`, FormatNumber, `GetChangedSignal`, per-player Janitor |
38
+ | [Combat (server validation)](combat.md) | Client sends **intent**; server checks range, cooldown, health, then `TakeDamage` |
39
+ | [Shop](shop.md) | `Net` buy remote, prices on the server, DataService debit |
40
+ | [HUD](hud.md) | Client `WaitForData`, labels, Twinkle |
41
+ | [Sword / Touched](sword.md) | Hitbox on the **server**, debounce, no client damage |
42
+
43
+ Read with [Libraries](../libraries/index.md), [OOP structure](../oop/index.md), and [Safety](../cpp-safety.md).
@@ -0,0 +1,140 @@
1
+ ---
2
+ title: Leaderstats
3
+ sidebar_position: 3
4
+ ---
5
+
6
+ # Leaderstats
7
+
8
+ Roblox shows the player list from a Folder named exactly `leaderstats` under the Player, with `IntValue` / `StringValue` children. This service **creates** those values, then mirrors DataService currencies into them.
9
+
10
+ It does **not** call `DataService.Init`. Boot that in [Data boot](data-boot.md).
11
+
12
+ ## Why this shape
13
+
14
+ | Rule | Why |
15
+ | --- | --- |
16
+ | Create the Folder if missing | `FindFirstChild` is not a constructor |
17
+ | Create the stat if missing | Returning early when it is absent never shows Money |
18
+ | `StringValue` + `FormatNumber::Abbreviate` | Player list wants a string like `1.5K` |
19
+ | Janitor per player, keyed by `player->Name` | Leaving the game must `Destroy` the section janitor |
20
+ | `GetChangedSignal(Paths.Currencies)` | HUD / list update without polling |
21
+ | Named function, not a lambda | Cluaupp has no lambdas. The shared callback refreshes **all** players (currency writes are rare) |
22
+
23
+ ## `LeaderstatsServer.server.cpp`
24
+
25
+ ```cpp
26
+ #include <cluaupp/roblox.hpp>
27
+ #include <cluaupp/libs/janitor.hpp>
28
+ #include <cluaupp/libs/dataservice.hpp>
29
+ #include <cluaupp/libs/formatnumber.hpp>
30
+
31
+ Players* Players = GetService<Players>();
32
+ Janitor* janitor = new Janitor();
33
+
34
+ void EnsureStat(Folder* leaderstats, string name) {
35
+ Instance* existing = leaderstats->FindFirstChild(name);
36
+ if (existing != nullptr) {
37
+ return;
38
+ }
39
+ StringValue* stat = new StringValue(leaderstats);
40
+ stat->Name = name;
41
+ stat->Value = "0";
42
+ }
43
+
44
+ void SetStat(Folder* leaderstats, string name, int value) {
45
+ StringValue* stat = leaderstats->FindFirstChild(name);
46
+ if (stat == nullptr) {
47
+ cout::warn << "leaderstats missing " << name << endl;
48
+ return;
49
+ }
50
+ stat->Value = FormatNumber::Abbreviate(value);
51
+ }
52
+
53
+ Folder* EnsureLeaderstats(Player* player) {
54
+ Folder* leaderstats = player->FindFirstChild("leaderstats");
55
+ if (leaderstats != nullptr) {
56
+ return leaderstats;
57
+ }
58
+ leaderstats = new Folder(player);
59
+ leaderstats->Name = "leaderstats";
60
+ return leaderstats;
61
+ }
62
+
63
+ void ApplyCurrencies(Player* player) {
64
+ Data* data = DataService::Server.Get(player);
65
+ if (data == nullptr) {
66
+ return;
67
+ }
68
+ Folder* leaderstats = player->FindFirstChild("leaderstats");
69
+ if (leaderstats == nullptr) {
70
+ return;
71
+ }
72
+ int money = data->Get(DataService::Server.Paths.Currencies.Money);
73
+ int level = data->Get(DataService::Server.Paths.Currencies.Level);
74
+ SetStat(leaderstats, "Money", money);
75
+ SetStat(leaderstats, "Level", level);
76
+ }
77
+
78
+ void OnCurrenciesChanged() {
79
+ for (Player* player : Players->GetPlayers()) {
80
+ ApplyCurrencies(player);
81
+ }
82
+ }
83
+
84
+ void SetupPlayer(Player* player) {
85
+ Data* data = DataService::Server.WaitFor(player);
86
+ if (data == nullptr) {
87
+ cout::warn << "no profile for " << player->Name << endl;
88
+ return;
89
+ }
90
+
91
+ Folder* leaderstats = EnsureLeaderstats(player);
92
+ EnsureStat(leaderstats, "Money");
93
+ EnsureStat(leaderstats, "Level");
94
+ ApplyCurrencies(player);
95
+
96
+ Janitor* section = new Janitor();
97
+ section->Add(data->GetChangedSignal(DataService::Server.Paths.Currencies).Connect(OnCurrenciesChanged));
98
+ section->Add(leaderstats, "Destroy");
99
+ janitor->Add(section, "Destroy", player->Name);
100
+ }
101
+
102
+ void OnPlayerRemoving(Player* player) {
103
+ if (janitor->Get(player->Name)) {
104
+ janitor->Remove(player->Name);
105
+ }
106
+ }
107
+
108
+ void OnClose() {
109
+ janitor->Destroy();
110
+ }
111
+
112
+ void init() {
113
+ for (Player* player : Players->GetPlayers()) {
114
+ SetupPlayer(player);
115
+ }
116
+ janitor->Add(Players->PlayerAdded.Connect(SetupPlayer));
117
+ janitor->Add(Players->PlayerRemoving.Connect(OnPlayerRemoving));
118
+ game->BindToClose(OnClose);
119
+ }
120
+ ```
121
+
122
+ ## Output
123
+
124
+ `LeaderstatsServer.server.cpp` becomes a service folder: `init.luau` (Script, RunContext **Server**), `Main`, `PlayersManager`, `DataController` / `CacheController`, Types. Your functions stay in the domain controller. See [services](../oop/services.md).
125
+
126
+ ## Bugs this example avoids
127
+
128
+ | Broken | Correct |
129
+ | --- | --- |
130
+ | `if (!existing) { return; }` then never create the value | `EnsureStat` **creates** when missing |
131
+ | `if (!money) { money->Value = ... }` | That writes only when the child is **nil** (crash / no-op). Set when the child **exists** |
132
+ | `DataService.Get` before `WaitFor` on join | `WaitFor` in `SetupPlayer`, `Get` in the refresh path |
133
+ | `int main()` | `void init()` |
134
+ | One global connection, never removed | Section janitor destroyed on `PlayerRemoving` |
135
+
136
+ `Paths.Currencies` is valid **after** your Template was passed to `Init`. The library header does not define those fields.
137
+
138
+ ## IntValue vs StringValue
139
+
140
+ Use `IntValue` if you want the default numeric sort and no abbreviation. Use `StringValue` + `FormatNumber::Abbreviate` for `1.5K`. Do not mix both names (`Money` twice).
@@ -0,0 +1,122 @@
1
+ ---
2
+ title: Shop
3
+ sidebar_position: 5
4
+ ---
5
+
6
+ # Shop
7
+
8
+ The client sends **which product**. The server looks up the price, checks Money, then `Set`. Never let the client send the new balance or the price.
9
+
10
+ ## Catalog header
11
+
12
+ `src/shared/constants/ShopCatalog.hpp`
13
+
14
+ Cluaupp has no maps. A function with early returns is the catalog.
15
+
16
+ ```cpp
17
+ #pragma once
18
+
19
+ const int PRODUCT_HEALTH_PACK = 1;
20
+ const int PRODUCT_SPEED_TOME = 2;
21
+
22
+ int PriceOf(int productId) {
23
+ if (productId == PRODUCT_HEALTH_PACK) {
24
+ return 50;
25
+ }
26
+ if (productId == PRODUCT_SPEED_TOME) {
27
+ return 200;
28
+ }
29
+ return 0;
30
+ }
31
+ ```
32
+
33
+ `PriceOf` lives in a header so the **server** is the copy that matters. The client may include it for UI labels, but a patched client cannot change what the server charges.
34
+
35
+ ## Server — `ShopServer.server.cpp`
36
+
37
+ ```cpp
38
+ #include <cluaupp/roblox.hpp>
39
+ #include <cluaupp/libs/janitor.hpp>
40
+ #include <cluaupp/libs/net.hpp>
41
+ #include <cluaupp/libs/dataservice.hpp>
42
+ #include "../../../shared/constants/ShopCatalog.hpp"
43
+
44
+ Janitor* janitor = new Janitor();
45
+ NetEvent* Buy = Net::Event("Buy");
46
+
47
+ void ApplyProduct(Player* player, int productId) {
48
+ Model* character = player->Character;
49
+ if (character == nullptr) {
50
+ return;
51
+ }
52
+ Humanoid* humanoid = character->FindFirstChildOfClass("Humanoid");
53
+ if (humanoid == nullptr) {
54
+ return;
55
+ }
56
+ if (productId == PRODUCT_HEALTH_PACK) {
57
+ humanoid->Health = humanoid->MaxHealth;
58
+ return;
59
+ }
60
+ if (productId == PRODUCT_SPEED_TOME) {
61
+ humanoid->WalkSpeed = 24;
62
+ return;
63
+ }
64
+ }
65
+
66
+ void OnBuy(Player* player, int productId) {
67
+ int price = PriceOf(productId);
68
+ if (price < 1) {
69
+ cout::warn << "unknown product " << productId << endl;
70
+ return;
71
+ }
72
+ Data* data = DataService::Server.WaitFor(player);
73
+ if (data == nullptr) {
74
+ return;
75
+ }
76
+ int money = data->Get(DataService::Server.Paths.Currencies.Money);
77
+ if (money < price) {
78
+ cout::ping << player->Name << " cannot afford " << productId << endl;
79
+ return;
80
+ }
81
+ data->Set(DataService::Server.Paths.Currencies.Money, money - price);
82
+ ApplyProduct(player, productId);
83
+ }
84
+
85
+ void init() {
86
+ Buy->On(OnBuy);
87
+ janitor->Add(Buy);
88
+ }
89
+ ```
90
+
91
+ Debit **before** (or atomically with) the effect. If you grant the item first and the `Set` fails, the player dupes.
92
+
93
+ ## Client — `ShopClient.client.cpp`
94
+
95
+ ```cpp
96
+ #include <cluaupp/roblox.hpp>
97
+ #include <cluaupp/libs/janitor.hpp>
98
+ #include <cluaupp/libs/net.hpp>
99
+ #include "../../../shared/constants/ShopCatalog.hpp"
100
+
101
+ Janitor* janitor = new Janitor();
102
+ NetEvent* Buy = Net::Event("Buy");
103
+
104
+ void OnHealthPackClicked() {
105
+ Buy->FireServer(PRODUCT_HEALTH_PACK);
106
+ }
107
+
108
+ void init() {
109
+ return;
110
+ }
111
+ ```
112
+
113
+ Wire `OnHealthPackClicked` to a `TextButton->MouseButton1Click.Connect(OnHealthPackClicked)` when you have the GUI. The remote payload is only the product id.
114
+
115
+ ## Rules
116
+
117
+ - `productId < 1` / unknown id → `PriceOf` returns `0` → reject.
118
+ - Do not `FireServer(price)` or `FireServer(newMoney)`.
119
+ - [Leaderstats](leaderstats.md) updates from `GetChangedSignal` after `Set`.
120
+ - Same Net name `"Buy"` on both sides.
121
+
122
+ See [Safety](../cpp-safety.md).
@@ -0,0 +1,108 @@
1
+ ---
2
+ title: Sword (Touched)
3
+ sidebar_position: 7
4
+ ---
5
+
6
+ # Sword (Touched)
7
+
8
+ Melee that uses a **server** `Touched` on the tool handle. The client can play animations; it must not tell the server how much damage to apply.
9
+
10
+ Same authority as [combat](combat.md), without a Net remote: while the Tool is equipped, `Touched` runs on the server.
11
+
12
+ ## Config
13
+
14
+ `src/shared/constants/SwordConfig.hpp`
15
+
16
+ ```cpp
17
+ #pragma once
18
+
19
+ const double SWORD_COOLDOWN = 0.35;
20
+ const int SWORD_DAMAGE = 18;
21
+ ```
22
+
23
+ ## Script inside the Tool
24
+
25
+ Put the source next to the Tool so `script->Parent` is the `Tool`. Use **`.legacy.server.cpp`** so Cluaupp emits a 1:1 Script (no Main/Controller split) that Rojo can parent under `ClassicSword`.
26
+
27
+ `src/server/tools/Sword.legacy.server.cpp`
28
+
29
+ ```cpp
30
+ #include <cluaupp/roblox.hpp>
31
+ #include "../../shared/constants/SwordConfig.hpp"
32
+
33
+ Players* Players = GetService<Players>();
34
+ Tool* tool = script->Parent;
35
+
36
+ void OnTouched(Instance* hit) {
37
+ if (hit == nullptr) {
38
+ return;
39
+ }
40
+
41
+ Player* attacker = Players->GetPlayerFromCharacter(tool->Parent);
42
+ if (attacker == nullptr) {
43
+ return;
44
+ }
45
+
46
+ NumberValue* last = attacker->FindFirstChild("LastSwordAt");
47
+ if (last != nullptr) {
48
+ if (tick() - last->Value < SWORD_COOLDOWN) {
49
+ return;
50
+ }
51
+ }
52
+
53
+ Instance* model = hit->FindFirstAncestorOfClass("Model");
54
+ if (model == nullptr) {
55
+ return;
56
+ }
57
+ Player* victim = Players->GetPlayerFromCharacter(model);
58
+ if (victim == nullptr) {
59
+ return;
60
+ }
61
+ if (victim == attacker) {
62
+ return;
63
+ }
64
+
65
+ Humanoid* humanoid = model->FindFirstChildOfClass("Humanoid");
66
+ if (humanoid == nullptr) {
67
+ return;
68
+ }
69
+ if (humanoid->Health <= 0) {
70
+ return;
71
+ }
72
+
73
+ if (last == nullptr) {
74
+ last = new NumberValue(attacker);
75
+ last->Name = "LastSwordAt";
76
+ }
77
+ last->Value = tick();
78
+ humanoid->TakeDamage(SWORD_DAMAGE);
79
+ }
80
+
81
+ void init() {
82
+ BasePart* handle = tool->FindFirstChild("Handle");
83
+ if (handle == nullptr) {
84
+ cout::warn << "ClassicSword missing Handle" << endl;
85
+ return;
86
+ }
87
+ handle->Touched.Connect(OnTouched);
88
+ }
89
+ ```
90
+
91
+ `tool->Parent` is the Character while equipped, `nil` in the backpack — `GetPlayerFromCharacter` then fails and the swing is ignored. That is what you want.
92
+
93
+ ## Why `.legacy`
94
+
95
+ A tagged `Sword.server.cpp` in `src/server/` becomes a service folder in ServerScriptService. A Tool Script must live **under the Tool**. Legacy keeps one file you can Rojo-map into `ClassicSword`.
96
+
97
+ ## Rules
98
+
99
+ | Do | Do not |
100
+ | --- | --- |
101
+ | `TakeDamage(SWORD_DAMAGE)` on the server | `FireServer(damage)` from a LocalScript |
102
+ | Skip `victim == attacker` | Damage the owner when the handle clips their own parts |
103
+ | Cooldown on the attacker | Trust `Touched` rate (it fires every physics step) |
104
+ | `GetPlayerFromCharacter` | Treat every Humanoid as a player if you only want PvP |
105
+
106
+ Client: a LocalScript in the same Tool can play an AnimationTrack on `Activated`. It still must not replicate a damage number.
107
+
108
+ For ranged hits (mouse target → remote), use [combat](combat.md).
@@ -122,13 +122,15 @@ If a file defines `void init()`, Cluaupp calls `init()` at the end of the `.luau
122
122
 
123
123
  ## IntelliSense
124
124
 
125
- `cluaupp init` copies `include/cluaupp/roblox.hpp`, `.vscode/c_cpp_properties.json`, and `compile_flags.txt` (clangd). `cluaupp build` refreshes `include/cluaupp/` so completions stay in sync with the compiler.
125
+ `cluaupp init` writes the C++ IntelliSense config and installs Microsoft `ms-vscode.cpptools` (via VSIX on Cursor). Then reload the window.
126
126
 
127
- In this repo the include path is `cluau/include` (and `game/include`). After opening a `.cpp` file, complete `Player`, `FindFirstChild`, `GetPlayers`, and `Vector3`.
127
+ ```bash
128
+ cluaupp intellisense
129
+ ```
128
130
 
129
- The header is **not** compiled to Luau; it only feeds the C++ language server. Use `#include <cluaupp/roblox.hpp>` at the top of each source file.
131
+ `#include <cluaupp/roblox.hpp>` at the top of each source file. The header is **not** compiled to Luau.
130
132
 
131
- Reload the window (Command Palette → **Developer: Reload Window**) if completions were missing before the include path existed.
133
+ See [IntelliSense](intellisense.md).
132
134
 
133
135
  ## Watch
134
136
 
@@ -153,17 +155,21 @@ void init() {
153
155
  }
154
156
  ```
155
157
 
156
- Wally is optional. Fusion, Cmdr, DataServiceV2, and the rest ship **inside** `libs/` (full GitHub source). See [Libraries](libraries.md).
158
+ Wally is optional. Fusion, Cmdr, DataServiceV2, and the rest ship **inside** `libs/` (full GitHub source). See [Libraries](libraries/index.md) — start with [DataService Init](libraries/dataservice.md) and [Janitor](libraries/janitor.md).
157
159
 
158
- See [Libraries](libraries.md), [types](cpp-types.md), [safety](cpp-safety.md).
160
+ See [Libraries](libraries/index.md), [OOP](oop/index.md), [types](cpp-types.md), [safety](cpp-safety.md).
159
161
 
160
162
  ## Next
161
163
 
162
164
  - [C++ → Luau syntax](syntax.md)
165
+ - [print and cout](print-cout.md)
163
166
  - [C++ types](cpp-types.md)
164
167
  - [const](cpp-const.md)
165
168
  - [Safety](cpp-safety.md)
166
169
  - [Organization](cpp-organization.md)
167
170
  - [Architecture](architecture.md)
171
+ - [OOP structure](oop/index.md)
172
+ - [Libraries](libraries/index.md)
173
+ - [Examples](examples/index.md)
168
174
  - [Roblox API](roblox-api.md)
169
175
  - [CLI](cli.md)
@@ -0,0 +1,31 @@
1
+ # IntelliSense
2
+
3
+ `cluaupp init` and `cluaupp intellisense` install the same setup that works in Cursor:
4
+
5
+ 1. Microsoft [C/C++](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) (`ms-vscode.cpptools`)
6
+ 2. `.vscode/c_cpp_properties.json` — LLVM `clang++`, C++20, `include/` + `src/`, `compile_commands.json`
7
+ 3. Forced include of `include/cluaupp/roblox.hpp` so `GetService`, `Player`, `Janitor` complete
8
+ 4. The Cluaupp completion engine (Roblox APIs + your `struct`s)
9
+
10
+ Cursor’s marketplace does not list `ms-vscode.cpptools`. Cluaupp downloads the official VSIX from [vscode-cpptools releases](https://github.com/microsoft/vscode-cpptools/releases) and runs `cursor --install-extension`.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ cluaupp intellisense
16
+ ```
17
+
18
+ Reload: Command Palette → **Developer: Reload Window**. Status bar `{} C++` should read **IntelliSense: Ready**.
19
+
20
+ `cluaupp build` / `watch` only refresh `compile_commands.json` (no download). They never keep deleted files in the compilation database.
21
+
22
+ ## Config that ships
23
+
24
+ | File | Role |
25
+ | --- | --- |
26
+ | `.vscode/c_cpp_properties.json` | compiler path, include path, `compileCommands` |
27
+ | `.vscode/settings.json` | `C_Cpp.intelliSenseEngine: default`, `clangd.enable: false` |
28
+ | `.vscode/extensions.json` | recommends `ms-vscode.cpptools` |
29
+ | `compile_commands.json` | one entry per `src/` file |
30
+
31
+ `#include <cluaupp/roblox.hpp>` at the top of each source file. The header is not compiled to Luau.
package/docs/intro.md CHANGED
@@ -7,7 +7,7 @@ sidebar_position: 1
7
7
 
8
8
  **The definitive merge of C++ and modern Luau.** You write a C++ subset. Cluaupp emits [Luau](https://luau.org/getting-started) with `--!strict`, `local`, and `const`, and calls the Roblox API the way Studio does.
9
9
 
10
- This site is built with [Moonwave](https://eryn.io/moonwave/) for local markdown docs (`npm run docs`). The public site is the cinematic engine dump at [GitHub Pages](https://kartzrbx.github.io/Cluaupp/). The language is in the same spirit as [roblox-ts](https://roblox-ts.com): a familiar syntax, a restricted subset, readable output.
10
+ This site is built with [Moonwave](https://eryn.io/moonwave/) for local markdown (`npm run docs`). The **public** site is generated into `site/` and deployed to [GitHub Pages](https://kartzrbx.github.io/Cluaupp/) (Learn tabs, OOP, Examples, API). The language is in the same spirit as [roblox-ts](https://roblox-ts.com): a familiar syntax, a restricted subset, readable output.
11
11
 
12
12
  ## What you get
13
13
 
@@ -34,4 +34,4 @@ void init() {
34
34
  }
35
35
  ```
36
36
 
37
- Read [Getting started](getting-started.md), then [C++ types](cpp-types.md) and [Libraries](libraries.md).
37
+ Read [Getting started](getting-started.md), then [Examples](examples/index.md), [Libraries](libraries/index.md), and [OOP structure](oop/index.md).
@@ -0,0 +1,5 @@
1
+ {
2
+ "label": "Libraries",
3
+ "position": 20,
4
+ "collapsed": false
5
+ }