cluaupp 0.1.2 → 0.1.5
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.
- package/CHANGELOG.md +29 -1
- package/README.md +10 -13
- package/docs/README.md +13 -10
- package/docs/architecture.md +30 -73
- package/docs/cli.md +5 -6
- package/docs/comparison.md +3 -3
- package/docs/config.md +6 -6
- package/docs/cpp-advanced.md +10 -6
- package/docs/cpp-organization.md +4 -2
- package/docs/cpp-safety.md +11 -7
- package/docs/cpp-types.md +1 -1
- package/docs/examples/_category_.json +5 -0
- package/docs/examples/combat.md +239 -0
- package/docs/examples/data-boot.md +98 -0
- package/docs/examples/hud.md +96 -0
- package/docs/examples/index.md +43 -0
- package/docs/examples/leaderstats.md +140 -0
- package/docs/examples/shop.md +122 -0
- package/docs/examples/sword.md +108 -0
- package/docs/getting-started.md +14 -7
- package/docs/intellisense.md +31 -0
- package/docs/intro.md +3 -3
- package/docs/libraries/_category_.json +5 -0
- package/docs/libraries/dataservice.md +104 -0
- package/docs/libraries/index.md +22 -0
- package/docs/libraries/janitor.md +65 -0
- package/docs/libraries/more.md +79 -0
- package/docs/libraries/net.md +35 -0
- package/docs/libraries/promise.md +27 -0
- package/docs/oop/_category_.json +5 -0
- package/docs/oop/file-tags.md +48 -0
- package/docs/oop/index.md +22 -0
- package/docs/oop/modules.md +88 -0
- package/docs/oop/services.md +76 -0
- package/docs/print-cout.md +91 -0
- package/docs/syntax.md +63 -8
- package/editors/vscode/extension.js +146 -0
- package/editors/vscode/package.json +25 -0
- package/include/cluaupp/libs/janitor.hpp +7 -3
- package/include/cluaupp/roblox.hpp +19 -0
- package/package.json +66 -65
- package/runtime/Janitor/init.luau +4 -34
- package/src/architecture.js +174 -66
- package/src/cli.js +88 -21
- package/src/client/init.client.cpp +5 -0
- package/src/compile.js +77 -7
- package/src/editor-install.js +218 -0
- package/src/emit.js +321 -9
- package/src/headers.js +234 -0
- package/src/intellisense.js +1368 -0
- package/src/layout.js +129 -0
- package/src/lex.js +17 -9
- package/src/libs.js +165 -18
- package/src/lsp.js +227 -0
- package/src/parse.js +187 -6
- package/src/preprocess.js +118 -3
- package/src/server/leaderstats.server.cpp +28 -0
- package/src/shared/config.cpp +5 -0
- package/src/shared/config.h +3 -0
- package/src/understand.js +66 -6
- package/templates/game/.clangd +11 -0
- package/templates/game/.vscode/c_cpp_properties.json +6 -2
- package/templates/game/.vscode/extensions.json +6 -0
- package/templates/game/.vscode/settings.json +16 -2
- package/templates/game/cluaupp.config.json +2 -2
- package/templates/game/compile_flags.txt +2 -0
- package/templates/game/src/client/init.client.cpp +1 -0
- package/templates/game/src/server/leaderstats.server.cpp +1 -0
- package/docs/libraries.md +0 -80
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
| `LeaderstatsServer.h` | **ModuleScript** (types) | — |
|
|
20
|
+
| `LeaderstatsServer.cpp` (sibling of `.h`) | **ModuleScript** (`*Impl`) | — |
|
|
21
|
+
|
|
22
|
+
## One file in, one file out
|
|
23
|
+
|
|
24
|
+
Rojo infers the instance from the filename key: `*.server.luau` is a Script, `*.client.luau` is a LocalScript, untagged `.luau` is a ModuleScript.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
src/server/boot/DataBoot.server.cpp → out/server/boot/DataBoot.server.luau
|
|
28
|
+
src/client/boot/DataBoot.client.cpp → out/client/boot/DataBoot.client.luau
|
|
29
|
+
src/shared/damage.cpp → out/shared/Damage.luau
|
|
30
|
+
src/server/services/leaderstats/LeaderstatsServer.h → LeaderstatsServer.luau (export type)
|
|
31
|
+
src/server/services/leaderstats/LeaderstatsServer.cpp → LeaderstatsServerImpl.luau
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`.legacy.server.cpp` still emits a Legacy Script (`boot.server.luau`). `.client.cpp` stays a LocalScript. Do not invent `init.meta.json` on the default 1:1 path.
|
|
35
|
+
|
|
36
|
+
## Where files live
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
src/server/.../*.server.cpp → out/server (ServerScriptService.Cluaupp)
|
|
40
|
+
src/client/.../*.client.cpp → out/client (StarterPlayerScripts.Cluaupp)
|
|
41
|
+
src/shared/.../*.cpp → out/shared (ReplicatedStorage.Cluaupp)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Name the **system**, not `init.server.cpp`. `LeaderstatsServer.server.cpp` and `DataBoot.server.cpp` are two files.
|
|
45
|
+
|
|
46
|
+
## Opt-in planner
|
|
47
|
+
|
|
48
|
+
`"architecture": true` in `cluaupp.config.json` restores PascalCase service folders (`Main`, Controller, Types). Default is off.
|
|
@@ -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 Script / LocalScript / ModuleScript from the filename tag — the same idea as roblox-ts. Flamework-style Start/Stop folders are opt-in (`"architecture": true`).
|
|
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` → one `.server.luau` |
|
|
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,88 @@
|
|
|
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
|
+
Quoted `#include "TemplateData.hpp"` is not inlined when the header has a sibling `.cpp` or is a shared module: `cluaupp build` emits `require(ReplicatedStorage.Cluaupp.constants.TemplateData)` / `require(ServerScriptService.Cluaupp.configurations.PlayerDataVersion)` (Rojo roots from `default.project.json`), never `script.Parent.Parent.shared`.
|
|
29
|
+
|
|
30
|
+
`.h` / `.hpp` are **type modules**. A service header becomes `export type Name = { init: (self: Name) -> (), … }` and a typed table. The sibling `.cpp` is the construction (`NameImpl.luau`); the header `require`s that impl and binds the functions. Data-only structs still emit a constructor so `TemplateData()` works. Untagged `.cpp` without a header is a ModuleScript other Luau can `require`. Prefer headers for the public type, tagged `.server.cpp` / `.client.cpp` for scripts, and the sibling `.cpp` for method bodies.
|
|
31
|
+
|
|
32
|
+
## Structs = data, not classes
|
|
33
|
+
|
|
34
|
+
The subset allows structs for IntelliSense and designated initializers (tables):
|
|
35
|
+
|
|
36
|
+
```cpp
|
|
37
|
+
struct TemplateData {
|
|
38
|
+
struct Currencies {
|
|
39
|
+
int Money = 0;
|
|
40
|
+
int Level = 1;
|
|
41
|
+
} Currencies;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
TemplateData playerData = TemplateData {};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
That is your “class fields”. Methods are **named functions** next to them (`EnsureStat`, `ApplyCurrencies`). Cluaupp does not emit `function TemplateData:GetMoney()`.
|
|
48
|
+
|
|
49
|
+
## Methods as functions
|
|
50
|
+
|
|
51
|
+
```cpp
|
|
52
|
+
void EnsureStat(Folder* folder, string name, int value) {
|
|
53
|
+
auto* stat = folder->FindFirstChild(name);
|
|
54
|
+
if (stat == nullptr) {
|
|
55
|
+
cout::print << "EnsureStat: " << name << " not found" << endl;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Pass the instance in (`folder`, `player`). That is composition: the Folder is the object, the function is the method.
|
|
62
|
+
|
|
63
|
+
## Janitor as the destructor
|
|
64
|
+
|
|
65
|
+
There is no C++ destructor. Pair “construct” with a Janitor:
|
|
66
|
+
|
|
67
|
+
```cpp
|
|
68
|
+
auto* janitor = new Janitor();
|
|
69
|
+
janitor->LinkToInstance(player);
|
|
70
|
+
janitor->Add(player->AncestryChanged.Connect(OnAncestry));
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
When the player leaves, `LinkToInstance` runs `Cleanup`. That is RAII for Roblox.
|
|
74
|
+
|
|
75
|
+
## Start / Stop
|
|
76
|
+
|
|
77
|
+
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.
|
|
78
|
+
|
|
79
|
+
## What not to write
|
|
80
|
+
|
|
81
|
+
```cpp
|
|
82
|
+
class Shop {
|
|
83
|
+
int price;
|
|
84
|
+
void Buy(Player* player);
|
|
85
|
+
};
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Custom classes are **not supported**. Split into a struct (fields) + functions (methods) + a `.server.cpp` or untagged module (lifetime).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Services
|
|
3
|
+
sidebar_position: 3
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Services
|
|
7
|
+
|
|
8
|
+
Write **one tagged file per system**. Cluaupp emits **one** Luau instance: `LeaderstatsServer.server.luau` from `LeaderstatsServer.server.cpp`. It does not invent Main / Controller / Types unless `"architecture": true`.
|
|
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/services/leaderstats/LeaderstatsServer.server.luau
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Your functions stay in that file. `init()` runs at the end. There is no fake `Main` that only forwards to a Controller.
|
|
51
|
+
|
|
52
|
+
## Lifecycle
|
|
53
|
+
|
|
54
|
+
You own `init` and Janitor cleanup in the same file. You do not write `class LeaderstatsServer` — name the **file** `LeaderstatsServer.server.cpp` and keep **functions** as the public API.
|
|
55
|
+
|
|
56
|
+
`"architecture": true` is the old ForeverHD split (`Main.Start` / `Main.Stop`, Managers, Types). Leave it off unless you want that.
|
|
57
|
+
|
|
58
|
+
## Boot vs gameplay
|
|
59
|
+
|
|
60
|
+
Keep DataService `Init` in `DataBoot.server.cpp` / `DataBoot.client.cpp`. Keep leaderstats / combat in their own `.server.cpp`. Services `WaitFor` after boot has run.
|
|
61
|
+
|
|
62
|
+
## Opt-in roles (`"architecture": true`)
|
|
63
|
+
|
|
64
|
+
| Your C++ | Generated role |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `PlayerAdded`, `GetPlayers` | `PlayersManager` |
|
|
67
|
+
| `Folder` + `IntValue` | `CacheController` |
|
|
68
|
+
| `DataService`, `GetChangedSignal` | `DataController` |
|
|
69
|
+
| `TakeDamage`, `Humanoid` | `CombatController` |
|
|
70
|
+
| `Net::Event` | `NetController` |
|
|
71
|
+
| `UserInputService` | `InputController` |
|
|
72
|
+
| wiring | `Main.Start` / `Main.Stop` |
|
|
73
|
+
|
|
74
|
+
A tiny `print` in `init.client.cpp` stays a single LocalScript.
|
|
75
|
+
|
|
76
|
+
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<<`.
|
package/docs/syntax.md
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Cluaupp is not a full C++ compiler. It is a **subset** aimed at Roblox scripts, in the same spirit as roblox-ts (restricted TypeScript → Luau).
|
|
4
4
|
|
|
5
|
-
Generated Luau follows the current language: [
|
|
5
|
+
Generated Luau follows the current language: [`local`](https://luau.org/getting-started), `const`, and `const function`. Injected `require` / `GetService` lines are `const`. `--!strict` is emitted only when the file has `#pragma strict` (or `"strict": true` in config).
|
|
6
6
|
|
|
7
7
|
## Files
|
|
8
8
|
|
|
9
9
|
- Extensions: `.cpp`, `.h`, `.hpp` (also `.cc`, `.hh`)
|
|
10
|
-
- Quoted `#include "file.h"` is inlined
|
|
10
|
+
- Quoted `#include "file.h"` becomes a Rojo `require` (or is inlined for a header that belongs to the same `.cpp`)
|
|
11
11
|
- `#include <cluaupp/roblox.hpp>` and `<cluaupp/libs/*.hpp>` are IntelliSense only; library headers also inject `require(CluauppLibs.*)`
|
|
12
|
-
-
|
|
13
|
-
-
|
|
12
|
+
- `#pragma once` is ignored
|
|
13
|
+
- `#pragma strict` / `#pragma nstrict` control `--!strict` for that compilation unit
|
|
14
|
+
- Function prototypes in a `.h` / `.hpp` become `export type` fields (`init: (self: Name) -> ()`). Only `.cpp` files emit function bodies.
|
|
14
15
|
|
|
15
16
|
## Functions
|
|
16
17
|
|
|
@@ -25,11 +26,11 @@ int doubleCoins(int coins) {
|
|
|
25
26
|
```
|
|
26
27
|
|
|
27
28
|
```luau
|
|
28
|
-
|
|
29
|
+
const function CreateLeaderstats(player: Player)
|
|
29
30
|
return
|
|
30
31
|
end
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
const function doubleCoins(coins: number): number
|
|
33
34
|
return coins
|
|
34
35
|
end
|
|
35
36
|
```
|
|
@@ -73,6 +74,22 @@ while (true) {
|
|
|
73
74
|
for (auto* player : players->GetPlayers()) {
|
|
74
75
|
CreateLeaderstats(player);
|
|
75
76
|
}
|
|
77
|
+
|
|
78
|
+
switch (action) {
|
|
79
|
+
case "buy":
|
|
80
|
+
case "purchase":
|
|
81
|
+
Grant(player);
|
|
82
|
+
break;
|
|
83
|
+
case "sell":
|
|
84
|
+
if (amount <= 0) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
Take(player);
|
|
88
|
+
break;
|
|
89
|
+
default:
|
|
90
|
+
warn("unknown");
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
76
93
|
```
|
|
77
94
|
|
|
78
95
|
```luau
|
|
@@ -89,9 +106,22 @@ end
|
|
|
89
106
|
for _, player in players:GetPlayers() do
|
|
90
107
|
CreateLeaderstats(player)
|
|
91
108
|
end
|
|
109
|
+
|
|
110
|
+
repeat
|
|
111
|
+
if action == "buy" or action == "purchase" then
|
|
112
|
+
Grant(player)
|
|
113
|
+
elseif action == "sell" then
|
|
114
|
+
if amount <= 0 then
|
|
115
|
+
break
|
|
116
|
+
end
|
|
117
|
+
Take(player)
|
|
118
|
+
else
|
|
119
|
+
warn("unknown")
|
|
120
|
+
end
|
|
121
|
+
until true
|
|
92
122
|
```
|
|
93
123
|
|
|
94
|
-
|
|
124
|
+
`switch` evaluates the discriminant **once**, then becomes `if` / `elseif` / `else` inside `repeat … until true` so `break` still exits the switch (even from inside an `if`). Stacked `case` labels share a body (`case "buy": case "purchase":`). There is no C-style fall-through into the next case’s statements. C-style `for (int i = 0; i < n; i++)` is not supported yet.
|
|
95
125
|
|
|
96
126
|
## Expressions
|
|
97
127
|
|
|
@@ -143,8 +173,33 @@ local players: Players = game:GetService("Players")
|
|
|
143
173
|
|
|
144
174
|
The first argument of `new Class(parent)` becomes `.Parent`. With no argument: `Instance.new("Class")` only.
|
|
145
175
|
|
|
176
|
+
## print / cout
|
|
177
|
+
|
|
178
|
+
`print`, `warn`, and `error` work as in Luau. `cout << … << endl` is the C++ spelling — each `<<` is another argument, `endl` ends the line. `cout::warn` / `cout::error` / `cout::ping` pick the Roblox function.
|
|
179
|
+
|
|
180
|
+
```cpp
|
|
181
|
+
cout << "EnsureStat: " << name << " not found" << endl;
|
|
182
|
+
cout::print << "EnsureStat: " << name << " not found" << endl;
|
|
183
|
+
cerr << "failed";
|
|
184
|
+
cout::print("ok");
|
|
185
|
+
cout::warn("careful");
|
|
186
|
+
cout::error("fail");
|
|
187
|
+
cout::ping("here");
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
```luau
|
|
191
|
+
print("EnsureStat: ", name, " not found")
|
|
192
|
+
warn("failed")
|
|
193
|
+
print("ok")
|
|
194
|
+
warn("careful")
|
|
195
|
+
error("fail")
|
|
196
|
+
print("here")
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Full table and IntelliSense notes: [print and cout](print-cout.md).
|
|
200
|
+
|
|
146
201
|
## Not supported yet
|
|
147
202
|
|
|
148
|
-
Custom C++ classes, generic templates besides `GetService<T>`, pointer arithmetic, `
|
|
203
|
+
Custom C++ classes, generic templates besides `GetService<T>`, pointer arithmetic, `std::`, overloading, macros (except `#pragma strict` / `#pragma nstrict` / `#pragma once`).
|
|
149
204
|
|
|
150
205
|
If you need one of those patterns, open an issue with the C++ and the expected Luau.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const vscode = require("vscode");
|
|
6
|
+
|
|
7
|
+
function loadEngine(context) {
|
|
8
|
+
const folders = vscode.workspace.workspaceFolders || [];
|
|
9
|
+
const workspaceFolder = folders[0] ? folders[0].uri.fsPath : null;
|
|
10
|
+
const rootFile = path.join(context.extensionPath, "cluaupp.root");
|
|
11
|
+
const roots = [];
|
|
12
|
+
if (fs.existsSync(rootFile)) {
|
|
13
|
+
roots.push(fs.readFileSync(rootFile, "utf8").trim());
|
|
14
|
+
}
|
|
15
|
+
if (workspaceFolder) {
|
|
16
|
+
roots.push(path.join(workspaceFolder, "..", "Cluaupp"));
|
|
17
|
+
roots.push(path.join(workspaceFolder, "node_modules", "cluaupp"));
|
|
18
|
+
}
|
|
19
|
+
roots.push(path.join(context.extensionPath, "..", ".."));
|
|
20
|
+
for (const root of roots) {
|
|
21
|
+
const engine = path.join(root, "src", "intellisense.js");
|
|
22
|
+
if (fs.existsSync(engine)) {
|
|
23
|
+
return { engine: require(engine), root, workspaceFolder };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
throw new Error("Cluaupp IntelliSense engine not found. Run `cluaupp intellisense` in the game folder.");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function kindOf(kind) {
|
|
30
|
+
const map = {
|
|
31
|
+
method: vscode.CompletionItemKind.Method,
|
|
32
|
+
function: vscode.CompletionItemKind.Function,
|
|
33
|
+
constructor: vscode.CompletionItemKind.Constructor,
|
|
34
|
+
property: vscode.CompletionItemKind.Property,
|
|
35
|
+
variable: vscode.CompletionItemKind.Variable,
|
|
36
|
+
class: vscode.CompletionItemKind.Class,
|
|
37
|
+
enum: vscode.CompletionItemKind.Enum,
|
|
38
|
+
keyword: vscode.CompletionItemKind.Keyword,
|
|
39
|
+
event: vscode.CompletionItemKind.Event,
|
|
40
|
+
file: vscode.CompletionItemKind.File,
|
|
41
|
+
};
|
|
42
|
+
return map[kind] || vscode.CompletionItemKind.Text;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function activate(context) {
|
|
46
|
+
let loaded;
|
|
47
|
+
try {
|
|
48
|
+
loaded = loadEngine(context);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
vscode.window.showWarningMessage(String(err.message || err));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const selector = { language: "cpp", scheme: "file" };
|
|
55
|
+
const diagnostics = vscode.languages.createDiagnosticCollection("cluaupp");
|
|
56
|
+
context.subscriptions.push(diagnostics);
|
|
57
|
+
|
|
58
|
+
const optionsFor = (document) => ({
|
|
59
|
+
projectRoot: loaded.workspaceFolder || path.dirname(document.uri.fsPath),
|
|
60
|
+
file: document.uri.fsPath,
|
|
61
|
+
filePath: document.uri.fsPath,
|
|
62
|
+
includeDirs: [
|
|
63
|
+
path.dirname(document.uri.fsPath),
|
|
64
|
+
path.join(loaded.workspaceFolder || "", "src"),
|
|
65
|
+
path.join(loaded.workspaceFolder || "", "include"),
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const refreshDiagnostics = (document) => {
|
|
70
|
+
if (!document || document.languageId !== "cpp") {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const items = loaded.engine.diagnosticsFor(document.getText(), document.uri.fsPath, optionsFor(document));
|
|
74
|
+
diagnostics.set(
|
|
75
|
+
document.uri,
|
|
76
|
+
items.map((item) => {
|
|
77
|
+
const line = Math.max(0, (item.line || 1) - 1);
|
|
78
|
+
const col = Math.max(0, (item.col || 1) - 1);
|
|
79
|
+
const range = new vscode.Range(line, col, line, col + 1);
|
|
80
|
+
return new vscode.Diagnostic(range, item.message, vscode.DiagnosticSeverity.Error);
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
context.subscriptions.push(
|
|
86
|
+
vscode.languages.registerCompletionItemProvider(
|
|
87
|
+
selector,
|
|
88
|
+
{
|
|
89
|
+
provideCompletionItems(document, position) {
|
|
90
|
+
const offset = document.offsetAt(position);
|
|
91
|
+
const items = loaded.engine.completeAt(document.getText(), offset, optionsFor(document));
|
|
92
|
+
return items.map((item) => {
|
|
93
|
+
const completion = new vscode.CompletionItem(item.name, kindOf(item.kind));
|
|
94
|
+
completion.detail = item.detail || item.type || "";
|
|
95
|
+
completion.sortText = `${item.kind === "keyword" ? "2" : "0"}_${item.name}`;
|
|
96
|
+
completion.insertText = item.name;
|
|
97
|
+
return completion;
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
".",
|
|
102
|
+
":",
|
|
103
|
+
">",
|
|
104
|
+
"<",
|
|
105
|
+
'"',
|
|
106
|
+
"/",
|
|
107
|
+
),
|
|
108
|
+
vscode.languages.registerHoverProvider(selector, {
|
|
109
|
+
provideHover(document, position) {
|
|
110
|
+
const hover = loaded.engine.hoverAt(document.getText(), document.offsetAt(position), optionsFor(document));
|
|
111
|
+
if (!hover) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
return new vscode.Hover(new vscode.MarkdownString(`**${hover.name}**\n\n\`${hover.detail || hover.type || ""}\``));
|
|
115
|
+
},
|
|
116
|
+
}),
|
|
117
|
+
vscode.languages.registerDefinitionProvider(selector, {
|
|
118
|
+
provideDefinition(document, position) {
|
|
119
|
+
const def = loaded.engine.definitionAt(document.getText(), document.offsetAt(position), optionsFor(document));
|
|
120
|
+
if (!def || !def.file) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return new vscode.Location(vscode.Uri.file(def.file), new vscode.Position(Math.max(0, (def.line || 1) - 1), 0));
|
|
124
|
+
},
|
|
125
|
+
}),
|
|
126
|
+
vscode.workspace.onDidChangeTextDocument((event) => refreshDiagnostics(event.document)),
|
|
127
|
+
vscode.workspace.onDidOpenTextDocument(refreshDiagnostics),
|
|
128
|
+
vscode.commands.registerCommand("cluaupp.restartIntelliSense", () => {
|
|
129
|
+
try {
|
|
130
|
+
delete require.cache[require.resolve(path.join(loaded.root, "src", "intellisense.js"))];
|
|
131
|
+
loaded = loadEngine(context);
|
|
132
|
+
vscode.window.showInformationMessage("Cluaupp IntelliSense reloaded.");
|
|
133
|
+
} catch (err) {
|
|
134
|
+
vscode.window.showErrorMessage(String(err.message || err));
|
|
135
|
+
}
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
for (const document of vscode.workspace.textDocuments) {
|
|
140
|
+
refreshDiagnostics(document);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function deactivate() {}
|
|
145
|
+
|
|
146
|
+
module.exports = { activate, deactivate };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cluaupp-intellisense",
|
|
3
|
+
"displayName": "Cluaupp IntelliSense",
|
|
4
|
+
"description": "Completions, hover, and go-to-definition for Cluaupp C++ (Roblox APIs, DataService paths, project structs).",
|
|
5
|
+
"version": "0.1.4",
|
|
6
|
+
"publisher": "kartzdev",
|
|
7
|
+
"engines": {
|
|
8
|
+
"vscode": "^1.74.0"
|
|
9
|
+
},
|
|
10
|
+
"categories": [
|
|
11
|
+
"Programming Languages"
|
|
12
|
+
],
|
|
13
|
+
"activationEvents": [
|
|
14
|
+
"onLanguage:cpp"
|
|
15
|
+
],
|
|
16
|
+
"main": "./extension.js",
|
|
17
|
+
"contributes": {
|
|
18
|
+
"commands": [
|
|
19
|
+
{
|
|
20
|
+
"command": "cluaupp.restartIntelliSense",
|
|
21
|
+
"title": "Cluaupp: Restart IntelliSense"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -10,6 +10,8 @@ public:
|
|
|
10
10
|
template <typename T>
|
|
11
11
|
T Add(T object);
|
|
12
12
|
template <typename T>
|
|
13
|
+
T Add(T object, bool callDirectly);
|
|
14
|
+
template <typename T>
|
|
13
15
|
T Add(T object, string methodName);
|
|
14
16
|
template <typename T>
|
|
15
17
|
T Add(T object, string methodName, string index);
|
|
@@ -17,14 +19,15 @@ public:
|
|
|
17
19
|
T AddObject(T constructor);
|
|
18
20
|
template <typename P>
|
|
19
21
|
P AddPromise(P promise);
|
|
22
|
+
template <typename P>
|
|
23
|
+
P AddPromise(P promise, string index);
|
|
20
24
|
Janitor* Remove(string index);
|
|
21
25
|
Janitor* RemoveNoClean(string index);
|
|
22
26
|
Janitor* RemoveList(string index);
|
|
27
|
+
Janitor* RemoveListNoClean(string index);
|
|
23
28
|
template <typename T>
|
|
24
29
|
T Get(string index);
|
|
25
|
-
|
|
26
|
-
T GetAll();
|
|
27
|
-
bool Has(string index);
|
|
30
|
+
void GetAll();
|
|
28
31
|
void Cleanup();
|
|
29
32
|
void Destroy();
|
|
30
33
|
RBXScriptConnection LinkToInstance(Instance* object);
|
|
@@ -32,4 +35,5 @@ public:
|
|
|
32
35
|
Janitor* LinkToInstances(Instance* object);
|
|
33
36
|
static bool Is(Instance* value);
|
|
34
37
|
static bool Is(string value);
|
|
38
|
+
static bool instanceof(Instance* value);
|
|
35
39
|
};
|
|
@@ -21,6 +21,25 @@ extern LuaSourceContainer* script;
|
|
|
21
21
|
void print(string message);
|
|
22
22
|
void warn(string message);
|
|
23
23
|
void error(string message);
|
|
24
|
+
|
|
25
|
+
struct cout {
|
|
26
|
+
static void print(string message);
|
|
27
|
+
static void warn(string message);
|
|
28
|
+
static void error(string message);
|
|
29
|
+
static void ping(string message);
|
|
30
|
+
static void endl();
|
|
31
|
+
cout& operator<<(string value);
|
|
32
|
+
cout& operator<<(int value);
|
|
33
|
+
cout& operator<<(double value);
|
|
34
|
+
cout& operator<<(bool value);
|
|
35
|
+
} cout;
|
|
36
|
+
|
|
37
|
+
struct cerr {
|
|
38
|
+
cerr& operator<<(string value);
|
|
39
|
+
cerr& operator<<(int value);
|
|
40
|
+
} cerr;
|
|
41
|
+
|
|
42
|
+
const int endl = 0;
|
|
24
43
|
double tick();
|
|
25
44
|
double time();
|
|
26
45
|
void wait(double seconds = 0);
|