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
package/src/parse.js
CHANGED
|
@@ -199,8 +199,18 @@ function parse(source, fileName) {
|
|
|
199
199
|
return left;
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
function parseShift() {
|
|
203
|
+
let left = parseBinary();
|
|
204
|
+
while (at("op", "<<")) {
|
|
205
|
+
eat("op", "<<");
|
|
206
|
+
const right = parseBinary();
|
|
207
|
+
left = { type: "binary", op: "<<", left, right };
|
|
208
|
+
}
|
|
209
|
+
return left;
|
|
210
|
+
}
|
|
211
|
+
|
|
202
212
|
function parseExpr() {
|
|
203
|
-
const left =
|
|
213
|
+
const left = parseShift();
|
|
204
214
|
if (at("op", "=")) {
|
|
205
215
|
i += 1;
|
|
206
216
|
return { type: "assign", left, right: parseExpr() };
|
|
@@ -223,6 +233,58 @@ function parse(source, fileName) {
|
|
|
223
233
|
return body;
|
|
224
234
|
}
|
|
225
235
|
|
|
236
|
+
function parseSwitch() {
|
|
237
|
+
eat("kw", "switch");
|
|
238
|
+
eat("op", "(");
|
|
239
|
+
const discriminant = parseExpr();
|
|
240
|
+
eat("op", ")");
|
|
241
|
+
eat("op", "{");
|
|
242
|
+
const cases = [];
|
|
243
|
+
let current = null;
|
|
244
|
+
const flush = () => {
|
|
245
|
+
if (current) {
|
|
246
|
+
cases.push(current);
|
|
247
|
+
current = null;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
while (!at("eof") && !at("op", "}")) {
|
|
251
|
+
skipSemicolons();
|
|
252
|
+
if (at("op", "}")) {
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
if (at("kw", "case")) {
|
|
256
|
+
i += 1;
|
|
257
|
+
const value = parseExpr();
|
|
258
|
+
eat("op", ":");
|
|
259
|
+
if (current && current.body.length === 0 && !current.isDefault) {
|
|
260
|
+
current.values.push(value);
|
|
261
|
+
} else {
|
|
262
|
+
flush();
|
|
263
|
+
current = { values: [value], body: [], isDefault: false };
|
|
264
|
+
}
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (at("kw", "default")) {
|
|
268
|
+
i += 1;
|
|
269
|
+
eat("op", ":");
|
|
270
|
+
flush();
|
|
271
|
+
current = { values: [], body: [], isDefault: true };
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (!current) {
|
|
275
|
+
throw error("switch body needs case or default before statements");
|
|
276
|
+
}
|
|
277
|
+
if (at("op", "{")) {
|
|
278
|
+
current.body.push(...parseBlock());
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
current.body.push(parseStmt());
|
|
282
|
+
}
|
|
283
|
+
flush();
|
|
284
|
+
eat("op", "}");
|
|
285
|
+
return { type: "switch", discriminant, cases };
|
|
286
|
+
}
|
|
287
|
+
|
|
226
288
|
function parseIf() {
|
|
227
289
|
eat("kw", "if");
|
|
228
290
|
eat("op", "(");
|
|
@@ -285,6 +347,9 @@ function parse(source, fileName) {
|
|
|
285
347
|
if (at("kw", "if")) {
|
|
286
348
|
return parseIf();
|
|
287
349
|
}
|
|
350
|
+
if (at("kw", "switch")) {
|
|
351
|
+
return parseSwitch();
|
|
352
|
+
}
|
|
288
353
|
if (at("kw", "for")) {
|
|
289
354
|
return parseFor();
|
|
290
355
|
}
|
|
@@ -293,6 +358,11 @@ function parse(source, fileName) {
|
|
|
293
358
|
const value = at("op", ";") || at("op", "}") ? null : parseExpr();
|
|
294
359
|
return { type: "return", value };
|
|
295
360
|
}
|
|
361
|
+
if (at("kw", "break")) {
|
|
362
|
+
i += 1;
|
|
363
|
+
skipSemicolons();
|
|
364
|
+
return { type: "break" };
|
|
365
|
+
}
|
|
296
366
|
if (at("kw", "while")) {
|
|
297
367
|
i += 1;
|
|
298
368
|
eat("op", "(");
|
|
@@ -312,12 +382,25 @@ function parse(source, fileName) {
|
|
|
312
382
|
return { name: "arg", valueType };
|
|
313
383
|
}
|
|
314
384
|
|
|
385
|
+
function parseQualifiedName() {
|
|
386
|
+
const parts = [eat("ident").value];
|
|
387
|
+
while (at("op", "::")) {
|
|
388
|
+
i += 1;
|
|
389
|
+
parts.push(eat("ident").value);
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
parts,
|
|
393
|
+
name: parts[parts.length - 1],
|
|
394
|
+
owner: parts.length > 1 ? parts.slice(0, -1).join("::") : null,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
315
398
|
function parseFunction() {
|
|
316
399
|
const returnType = parseType();
|
|
317
400
|
if (!returnType || !at("ident")) {
|
|
318
401
|
throw error("invalid function declaration");
|
|
319
402
|
}
|
|
320
|
-
const
|
|
403
|
+
const qualified = parseQualifiedName();
|
|
321
404
|
eat("op", "(");
|
|
322
405
|
const params = [];
|
|
323
406
|
if (!at("op", ")")) {
|
|
@@ -330,10 +413,96 @@ function parse(source, fileName) {
|
|
|
330
413
|
eat("op", ")");
|
|
331
414
|
if (at("op", ";")) {
|
|
332
415
|
i += 1;
|
|
333
|
-
return { type: "proto", name, returnType, params };
|
|
416
|
+
return { type: "proto", name: qualified.name, owner: qualified.owner, returnType, params };
|
|
334
417
|
}
|
|
335
418
|
const body = parseBlock();
|
|
336
|
-
return { type: "function", name, returnType, params, body };
|
|
419
|
+
return { type: "function", name: qualified.name, owner: qualified.owner, returnType, params, body };
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function parseStruct() {
|
|
423
|
+
eat("kw");
|
|
424
|
+
const name = at("ident") ? eat("ident").value : "_anon";
|
|
425
|
+
if (at("op", ";")) {
|
|
426
|
+
i += 1;
|
|
427
|
+
return { type: "struct", name, fields: [], methods: [] };
|
|
428
|
+
}
|
|
429
|
+
eat("op", "{");
|
|
430
|
+
const fields = [];
|
|
431
|
+
const methods = [];
|
|
432
|
+
while (!at("eof") && !at("op", "}")) {
|
|
433
|
+
skipSemicolons();
|
|
434
|
+
if (at("op", "}")) {
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
if (at("kw", "public") || at("kw", "private") || at("kw", "protected")) {
|
|
438
|
+
i += 1;
|
|
439
|
+
if (at("op", ":")) {
|
|
440
|
+
i += 1;
|
|
441
|
+
}
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
if (at("kw", "struct") || at("kw", "class")) {
|
|
445
|
+
const nested = parseStruct();
|
|
446
|
+
if (nested.instance) {
|
|
447
|
+
fields.push({
|
|
448
|
+
type: "decl",
|
|
449
|
+
name: nested.instance.name,
|
|
450
|
+
valueType: nested.name,
|
|
451
|
+
value:
|
|
452
|
+
nested.instance.value || {
|
|
453
|
+
type: "initlist",
|
|
454
|
+
fields: nested.fields.map((field) => ({
|
|
455
|
+
name: field.name,
|
|
456
|
+
value: field.value || { type: "null" },
|
|
457
|
+
})),
|
|
458
|
+
},
|
|
459
|
+
isConst: false,
|
|
460
|
+
owner: name,
|
|
461
|
+
});
|
|
462
|
+
} else {
|
|
463
|
+
fields.push(...nested.fields);
|
|
464
|
+
methods.push(...nested.methods);
|
|
465
|
+
}
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
const saved = i;
|
|
469
|
+
const isConst = at("kw", "const");
|
|
470
|
+
if (isConst) {
|
|
471
|
+
i += 1;
|
|
472
|
+
}
|
|
473
|
+
const valueType = parseType();
|
|
474
|
+
if (valueType && at("ident")) {
|
|
475
|
+
if (peek(1).value === "(") {
|
|
476
|
+
i = saved;
|
|
477
|
+
const method = parseFunction();
|
|
478
|
+
method.owner = method.owner || name;
|
|
479
|
+
methods.push(method);
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const fieldName = eat("ident").value;
|
|
483
|
+
let value = null;
|
|
484
|
+
if (at("op", "=")) {
|
|
485
|
+
i += 1;
|
|
486
|
+
value = parseExpr();
|
|
487
|
+
}
|
|
488
|
+
skipSemicolons();
|
|
489
|
+
fields.push({ type: "decl", name: fieldName, valueType, value, isConst, owner: name });
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
i = saved;
|
|
493
|
+
skipBalanced();
|
|
494
|
+
}
|
|
495
|
+
eat("op", "}");
|
|
496
|
+
let instance = null;
|
|
497
|
+
if (at("ident")) {
|
|
498
|
+
instance = { name: eat("ident").value, value: null };
|
|
499
|
+
if (at("op", "=")) {
|
|
500
|
+
i += 1;
|
|
501
|
+
instance.value = parseExpr();
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
skipSemicolons();
|
|
505
|
+
return { type: "struct", name, fields, methods, instance };
|
|
337
506
|
}
|
|
338
507
|
|
|
339
508
|
function parseTopLevelDecl(isConst, valueType) {
|
|
@@ -414,7 +583,13 @@ function parse(source, fileName) {
|
|
|
414
583
|
skipSemicolons();
|
|
415
584
|
continue;
|
|
416
585
|
}
|
|
417
|
-
if (at("kw", "struct") || at("kw", "class")
|
|
586
|
+
if (at("kw", "struct") || at("kw", "class")) {
|
|
587
|
+
const parsed = parseStruct();
|
|
588
|
+
decls.push(...parsed.fields);
|
|
589
|
+
decls.push(...parsed.methods);
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
if (at("kw", "enum") || at("kw", "template") || at("kw", "typedef") || at("kw", "extern")) {
|
|
418
593
|
skipTypeDecl();
|
|
419
594
|
continue;
|
|
420
595
|
}
|
|
@@ -426,7 +601,7 @@ function parse(source, fileName) {
|
|
|
426
601
|
const maybeType = parseType();
|
|
427
602
|
if (maybeType && at("ident")) {
|
|
428
603
|
const next = peek(1);
|
|
429
|
-
if (next.value === "(") {
|
|
604
|
+
if (next.value === "(" || next.value === "::") {
|
|
430
605
|
i = saved;
|
|
431
606
|
decls.push(parseFunction());
|
|
432
607
|
skipSemicolons();
|
|
@@ -438,6 +613,12 @@ function parse(source, fileName) {
|
|
|
438
613
|
}
|
|
439
614
|
}
|
|
440
615
|
i = saved;
|
|
616
|
+
if (at("ident") && (peek(1).value === "::" || peek(1).value === "(" || peek(1).value === "." || peek(1).value === "->")) {
|
|
617
|
+
const expr = parseExpr();
|
|
618
|
+
skipSemicolons();
|
|
619
|
+
decls.push({ type: "expr", expr });
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
441
622
|
decls.push(parseFunction());
|
|
442
623
|
skipSemicolons();
|
|
443
624
|
}
|
package/src/preprocess.js
CHANGED
|
@@ -27,18 +27,120 @@ function isEngineStub(filePath) {
|
|
|
27
27
|
);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function includeVariants(name) {
|
|
31
|
+
const variants = [name];
|
|
32
|
+
if (/\.h$/i.test(name)) {
|
|
33
|
+
variants.push(name.replace(/\.h$/i, ".hpp"), name.replace(/\.h$/i, ".hh"));
|
|
34
|
+
} else if (/\.hpp$/i.test(name)) {
|
|
35
|
+
variants.push(name.replace(/\.hpp$/i, ".h"), name.replace(/\.hpp$/i, ".hh"));
|
|
36
|
+
} else if (/\.hh$/i.test(name)) {
|
|
37
|
+
variants.push(name.replace(/\.hh$/i, ".h"), name.replace(/\.hh$/i, ".hpp"));
|
|
38
|
+
}
|
|
39
|
+
return [...new Set(variants)];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function siblingImplementation(headerPath) {
|
|
43
|
+
if (!headerPath || !isHeaderFile(headerPath)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const base = headerPath.replace(/\.(h|hpp|hh)$/i, "");
|
|
47
|
+
for (const ext of [".cpp", ".cc", ".cxx", ".c"]) {
|
|
48
|
+
if (fs.existsSync(base + ext)) {
|
|
49
|
+
return base + ext;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function siblingHeader(implPath) {
|
|
56
|
+
if (!implPath || isHeaderFile(implPath)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const base = String(implPath).replace(/\.(cpp|cc|cxx|c)$/i, "");
|
|
60
|
+
if (base === String(implPath)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
for (const ext of [".h", ".hpp", ".hh"]) {
|
|
64
|
+
if (fs.existsSync(base + ext)) {
|
|
65
|
+
return base + ext;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function implOutName(rel) {
|
|
72
|
+
return String(rel)
|
|
73
|
+
.replace(/\\/g, "/")
|
|
74
|
+
.replace(/\.(cpp|cc|cxx|c)$/i, "Impl.luau");
|
|
75
|
+
}
|
|
76
|
+
|
|
30
77
|
function resolveInclude(name, fromFile, includeDirs) {
|
|
31
78
|
const bases = [path.dirname(fromFile), ...(includeDirs || [])];
|
|
32
79
|
for (const base of bases) {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
80
|
+
for (const variant of includeVariants(name)) {
|
|
81
|
+
const candidate = path.resolve(base, variant);
|
|
82
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
83
|
+
return candidate;
|
|
84
|
+
}
|
|
36
85
|
}
|
|
37
86
|
}
|
|
38
87
|
return null;
|
|
39
88
|
}
|
|
40
89
|
|
|
90
|
+
function scanHeaderExports(source) {
|
|
91
|
+
const consts = [];
|
|
92
|
+
const structs = [];
|
|
93
|
+
for (const match of String(source).matchAll(/\bconst\s+(?:int|bool|float|double|auto|string)\s+(\w+)/g)) {
|
|
94
|
+
if (!consts.includes(match[1])) {
|
|
95
|
+
consts.push(match[1]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
for (const match of String(source).matchAll(/\bstruct\s+(\w+)/g)) {
|
|
99
|
+
if (!structs.includes(match[1])) {
|
|
100
|
+
structs.push(match[1]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const hasProtos = /\b(?:void|int|bool|float|double|auto|string|[\w:]+)\s+\w+\s*\([^;]*\)\s*;/.test(String(source));
|
|
104
|
+
return { consts, structs, hasProtos };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function pushModuleInclude(options, resolved, impl) {
|
|
108
|
+
options.moduleIncludes = options.moduleIncludes || [];
|
|
109
|
+
const srcDir = options.srcDir || null;
|
|
110
|
+
const moduleFile = impl || resolved;
|
|
111
|
+
const rel = srcDir ? path.relative(srcDir, moduleFile).replace(/\\/g, "/") : path.basename(moduleFile);
|
|
112
|
+
const headerText = fs.readFileSync(resolved, "utf8");
|
|
113
|
+
options.moduleIncludes.push({
|
|
114
|
+
name: path.basename(resolved).replace(/\.(h|hpp|hh)$/i, ""),
|
|
115
|
+
outRel: rel.replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau"),
|
|
116
|
+
header: resolved,
|
|
117
|
+
impl: impl || null,
|
|
118
|
+
exports: scanHeaderExports(headerText),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function applyPragmas(source, options) {
|
|
123
|
+
let nstrict = false;
|
|
124
|
+
let strict = false;
|
|
125
|
+
for (const line of String(source).split(/\r?\n/)) {
|
|
126
|
+
if (/^\s*#\s*pragma\s+nstrict\b/i.test(line)) {
|
|
127
|
+
nstrict = true;
|
|
128
|
+
} else if (/^\s*#\s*pragma\s+strict\b/i.test(line)) {
|
|
129
|
+
strict = true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (nstrict) {
|
|
133
|
+
options.strict = false;
|
|
134
|
+
} else if (strict) {
|
|
135
|
+
options.strict = true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
41
139
|
function preprocess(source, filePath, options = {}) {
|
|
140
|
+
if (!options.pragmaResolved) {
|
|
141
|
+
options.pragmaResolved = true;
|
|
142
|
+
applyPragmas(source, options);
|
|
143
|
+
}
|
|
42
144
|
const seen = options.seen || new Set();
|
|
43
145
|
const includeDirs = options.includeDirs || [];
|
|
44
146
|
const resolvedSelf = filePath ? path.resolve(filePath) : null;
|
|
@@ -56,6 +158,13 @@ function preprocess(source, filePath, options = {}) {
|
|
|
56
158
|
if (!resolved || isEngineStub(resolved) || seen.has(resolved)) {
|
|
57
159
|
continue;
|
|
58
160
|
}
|
|
161
|
+
const impl = siblingImplementation(resolved);
|
|
162
|
+
const including = filePath ? path.resolve(filePath) : null;
|
|
163
|
+
const ownHeader = Boolean(impl && including && path.resolve(impl) === including);
|
|
164
|
+
if (!ownHeader) {
|
|
165
|
+
pushModuleInclude(options, resolved, impl);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
59
168
|
seen.add(resolved);
|
|
60
169
|
const inner = fs.readFileSync(resolved, "utf8");
|
|
61
170
|
out.push(preprocess(inner, resolved, { ...options, seen }));
|
|
@@ -75,6 +184,12 @@ module.exports = {
|
|
|
75
184
|
isSourceFile,
|
|
76
185
|
isHeaderFile,
|
|
77
186
|
toLuauPath,
|
|
187
|
+
applyPragmas,
|
|
78
188
|
preprocess,
|
|
189
|
+
scanHeaderExports,
|
|
79
190
|
isEngineStub,
|
|
191
|
+
siblingImplementation,
|
|
192
|
+
siblingHeader,
|
|
193
|
+
implOutName,
|
|
194
|
+
resolveInclude,
|
|
80
195
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#include <cluaupp/roblox.hpp>
|
|
2
|
+
|
|
3
|
+
void CreateLeaderstats(Player* player) {
|
|
4
|
+
if (player->FindFirstChild("leaderstats") != nullptr) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
auto* leaderstats = new Folder(player);
|
|
9
|
+
leaderstats->Name = "leaderstats";
|
|
10
|
+
|
|
11
|
+
auto* coins = new IntValue(leaderstats);
|
|
12
|
+
coins->Name = "Coins";
|
|
13
|
+
coins->Value = 0;
|
|
14
|
+
|
|
15
|
+
auto* level = new IntValue(leaderstats);
|
|
16
|
+
level->Name = "Level";
|
|
17
|
+
level->Value = 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
void init() {
|
|
21
|
+
auto* players = GetService<Players>();
|
|
22
|
+
|
|
23
|
+
for (auto* player : players->GetPlayers()) {
|
|
24
|
+
CreateLeaderstats(player);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
players->PlayerAdded.Connect(CreateLeaderstats);
|
|
28
|
+
}
|
package/src/understand.js
CHANGED
|
@@ -94,7 +94,18 @@ const INTENTS = {
|
|
|
94
94
|
data: {
|
|
95
95
|
role: "controller",
|
|
96
96
|
module: "DataController",
|
|
97
|
-
tokens: [
|
|
97
|
+
tokens: [
|
|
98
|
+
"DataStore",
|
|
99
|
+
"DataStoreService",
|
|
100
|
+
"DataService",
|
|
101
|
+
"SetAsync",
|
|
102
|
+
"GetAsync",
|
|
103
|
+
"UpdateAsync",
|
|
104
|
+
"ProfileStore",
|
|
105
|
+
"GetChangedSignal",
|
|
106
|
+
"WaitFor",
|
|
107
|
+
"Paths",
|
|
108
|
+
],
|
|
98
109
|
},
|
|
99
110
|
character: {
|
|
100
111
|
role: "controller",
|
|
@@ -187,19 +198,28 @@ function toPascalServiceName(fileName) {
|
|
|
187
198
|
|
|
188
199
|
function parseFileTag(fileName) {
|
|
189
200
|
const base = path.basename(fileName);
|
|
201
|
+
if (new RegExp(`\\.legacy\\.plugin\\.${EXT}$`, "i").test(base)) {
|
|
202
|
+
return { key: "legacy.plugin", emit: "legacy", runtime: "plugin", rojo: "Script", runContext: "Plugin" };
|
|
203
|
+
}
|
|
190
204
|
if (new RegExp(`\\.legacy\\.server\\.${EXT}$`, "i").test(base)) {
|
|
191
|
-
return { key: "legacy.server", emit: "legacy", runtime: "server", rojo: "Script" };
|
|
205
|
+
return { key: "legacy.server", emit: "legacy", runtime: "server", rojo: "Script", runContext: null };
|
|
192
206
|
}
|
|
193
207
|
if (new RegExp(`\\.legacy\\.client\\.${EXT}$`, "i").test(base)) {
|
|
194
|
-
return { key: "legacy.client", emit: "legacy", runtime: "client", rojo: "LocalScript" };
|
|
208
|
+
return { key: "legacy.client", emit: "legacy", runtime: "client", rojo: "LocalScript", runContext: null };
|
|
209
|
+
}
|
|
210
|
+
if (new RegExp(`\\.legacy\\.${EXT}$`, "i").test(base)) {
|
|
211
|
+
return { key: "legacy", emit: "legacy", runtime: "server", rojo: "Script", runContext: null };
|
|
212
|
+
}
|
|
213
|
+
if (new RegExp(`\\.plugin\\.${EXT}$`, "i").test(base)) {
|
|
214
|
+
return { key: "plugin", emit: "service", runtime: "plugin", rojo: "Script", runContext: "Plugin" };
|
|
195
215
|
}
|
|
196
216
|
if (new RegExp(`\\.server\\.${EXT}$`, "i").test(base)) {
|
|
197
|
-
return { key: "server", emit: "service", runtime: "server", rojo: "Script" };
|
|
217
|
+
return { key: "server", emit: "service", runtime: "server", rojo: "Script", runContext: "Server" };
|
|
198
218
|
}
|
|
199
219
|
if (new RegExp(`\\.client\\.${EXT}$`, "i").test(base)) {
|
|
200
|
-
return { key: "client", emit: "service", runtime: "client", rojo: "LocalScript" };
|
|
220
|
+
return { key: "client", emit: "service", runtime: "client", rojo: "LocalScript", runContext: "Client" };
|
|
201
221
|
}
|
|
202
|
-
return { key: "module", emit: "module", runtime: "shared", rojo: "ModuleScript" };
|
|
222
|
+
return { key: "module", emit: "module", runtime: "shared", rojo: "ModuleScript", runContext: null };
|
|
203
223
|
}
|
|
204
224
|
|
|
205
225
|
function scoreIntents(named) {
|
|
@@ -362,9 +382,37 @@ function classifyFunctions(ast, intents) {
|
|
|
362
382
|
absorb,
|
|
363
383
|
});
|
|
364
384
|
}
|
|
385
|
+
keepReferencedFunctions(ast, classified);
|
|
365
386
|
return classified;
|
|
366
387
|
}
|
|
367
388
|
|
|
389
|
+
function keepReferencedFunctions(ast, classified) {
|
|
390
|
+
const kept = new Set(["init"]);
|
|
391
|
+
for (const item of classified) {
|
|
392
|
+
if (item.absorb !== "cache") {
|
|
393
|
+
kept.add(item.name);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
let changed = true;
|
|
398
|
+
while (changed) {
|
|
399
|
+
changed = false;
|
|
400
|
+
for (const decl of ast.body || []) {
|
|
401
|
+
if (decl.type !== "function" || !kept.has(decl.name)) {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
const named = namesIn(decl);
|
|
405
|
+
for (const item of classified) {
|
|
406
|
+
if (item.absorb === "cache" && named.has(item.name)) {
|
|
407
|
+
item.absorb = "none";
|
|
408
|
+
kept.add(item.name);
|
|
409
|
+
changed = true;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
368
416
|
function analyze(ast, fileName) {
|
|
369
417
|
const tag = parseFileTag(fileName);
|
|
370
418
|
const named = namesIn(ast);
|
|
@@ -420,6 +468,7 @@ function analyze(ast, fileName) {
|
|
|
420
468
|
serviceName,
|
|
421
469
|
typesName: `${serviceName}Types`,
|
|
422
470
|
isClient: tag.runtime === "client",
|
|
471
|
+
runContext: tag.runContext || null,
|
|
423
472
|
intents,
|
|
424
473
|
primaryDomain,
|
|
425
474
|
roles,
|
|
@@ -432,10 +481,20 @@ function analyze(ast, fileName) {
|
|
|
432
481
|
};
|
|
433
482
|
}
|
|
434
483
|
|
|
484
|
+
function modernScriptOutName(relativeName) {
|
|
485
|
+
const rel = String(relativeName).replace(/\\/g, "/");
|
|
486
|
+
if (/\.plugin\.(cpp|cc|cxx|c|h|hpp|hh)$/i.test(rel)) {
|
|
487
|
+
return rel.replace(/\.plugin\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
|
|
488
|
+
}
|
|
489
|
+
return rel.replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
|
|
490
|
+
}
|
|
491
|
+
|
|
435
492
|
function legacyOutName(relativeName) {
|
|
436
493
|
return String(relativeName)
|
|
437
494
|
.replace(/\\/g, "/")
|
|
495
|
+
.replace(/\.legacy\.plugin\./i, ".")
|
|
438
496
|
.replace(/\.legacy\.(server|client)\./i, ".$1.")
|
|
497
|
+
.replace(/\.legacy\./i, ".server.")
|
|
439
498
|
.replace(new RegExp(`\\.${EXT}$`, "i"), ".luau");
|
|
440
499
|
}
|
|
441
500
|
|
|
@@ -449,5 +508,6 @@ module.exports = {
|
|
|
449
508
|
scoreIntents,
|
|
450
509
|
analyze,
|
|
451
510
|
legacyOutName,
|
|
511
|
+
modernScriptOutName,
|
|
452
512
|
looksLikeCacheSetup,
|
|
453
513
|
};
|
package/templates/game/.clangd
CHANGED
|
@@ -2,14 +2,18 @@
|
|
|
2
2
|
"configurations": [
|
|
3
3
|
{
|
|
4
4
|
"name": "Cluaupp",
|
|
5
|
+
"compilerPath": "C:/Program Files/LLVM/bin/clang++.exe",
|
|
5
6
|
"cStandard": "c17",
|
|
6
7
|
"cppStandard": "c++20",
|
|
7
|
-
"intelliSenseMode": "windows-
|
|
8
|
+
"intelliSenseMode": "windows-clang-x64",
|
|
8
9
|
"includePath": [
|
|
9
10
|
"${workspaceFolder}/include",
|
|
10
11
|
"${workspaceFolder}/src"
|
|
11
12
|
],
|
|
12
|
-
"
|
|
13
|
+
"forcedInclude": [
|
|
14
|
+
"${workspaceFolder}/include/cluaupp/roblox.hpp"
|
|
15
|
+
],
|
|
16
|
+
"compileCommands": "${workspaceFolder}/compile_commands.json",
|
|
13
17
|
"browse": {
|
|
14
18
|
"path": [
|
|
15
19
|
"${workspaceFolder}/include",
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
+
"C_Cpp.intelliSenseEngine": "default",
|
|
2
3
|
"C_Cpp.default.cppStandard": "c++20",
|
|
3
|
-
"C_Cpp.default.
|
|
4
|
+
"C_Cpp.default.cStandard": "c17",
|
|
5
|
+
"C_Cpp.default.compilerPath": "C:/Program Files/LLVM/bin/clang++.exe",
|
|
6
|
+
"C_Cpp.default.intelliSenseMode": "windows-clang-x64",
|
|
4
7
|
"C_Cpp.default.includePath": [
|
|
5
8
|
"${workspaceFolder}/include",
|
|
6
9
|
"${workspaceFolder}/src"
|
|
@@ -8,5 +11,16 @@
|
|
|
8
11
|
"C_Cpp.default.browse.path": [
|
|
9
12
|
"${workspaceFolder}/include",
|
|
10
13
|
"${workspaceFolder}/src"
|
|
11
|
-
]
|
|
14
|
+
],
|
|
15
|
+
"clangd.enable": false,
|
|
16
|
+
"files.associations": {
|
|
17
|
+
"*.hpp": "cpp",
|
|
18
|
+
"*.h": "cpp",
|
|
19
|
+
"*.server.cpp": "cpp",
|
|
20
|
+
"*.client.cpp": "cpp",
|
|
21
|
+
"*.plugin.cpp": "cpp",
|
|
22
|
+
"*.legacy.cpp": "cpp",
|
|
23
|
+
"*.legacy.server.cpp": "cpp",
|
|
24
|
+
"*.legacy.client.cpp": "cpp"
|
|
25
|
+
}
|
|
12
26
|
}
|