@tilua-types/roblox 10.0.2 → 10.1.1
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/README.md +3 -3
- package/index.d.tilua +1 -1
- package/lowering.mjs +102 -102
- package/package.json +2 -2
- package/runtime/roblox.luau +139 -139
package/README.md
CHANGED
|
@@ -158,11 +158,11 @@ luau-lsp is MIT licensed. Its notice is in
|
|
|
158
158
|
<!-- compatibility:start -->
|
|
159
159
|
## Compatibility
|
|
160
160
|
|
|
161
|
-
`@tilua-types/roblox@10.
|
|
161
|
+
`@tilua-types/roblox@10.1.1` works with these tilua packages. *Released with* is the
|
|
162
162
|
version it was built and tested against.
|
|
163
163
|
|
|
164
164
|
| Package | Range | Released with | |
|
|
165
165
|
| --- | --- | --- | --- |
|
|
166
|
-
| `@tilua-types/luau` | `^9.
|
|
167
|
-
| `@tilua/parser` | `^9.0.0` | 9.
|
|
166
|
+
| `@tilua-types/luau` | `^9.1.1` | 9.1.1 | dependency |
|
|
167
|
+
| `@tilua/parser` | `^9.0.0` | 9.1.1 | peer |
|
|
168
168
|
<!-- compatibility:end -->
|
package/index.d.tilua
CHANGED
|
@@ -8465,7 +8465,7 @@ declare class SharedTable {
|
|
|
8465
8465
|
[string | number]: any,
|
|
8466
8466
|
__iter: (self: SharedTable) => ((arg1: any, arg2: number) => [number, any]),
|
|
8467
8467
|
}
|
|
8468
|
-
type RBXScriptSignal<T extends unknown[] = any[]> = { Wait: (self: RBXScriptSignal<T>) => T, Connect: (self: RBXScriptSignal<T>, callback: (...args: T) => void) => RBXScriptConnection, ConnectParallel: (self: RBXScriptSignal<T>, callback: (...args: T) => void) => RBXScriptConnection, Once: (self: RBXScriptSignal<T>, callback: (...args: T) => void) => RBXScriptConnection }
|
|
8468
|
+
type RBXScriptSignal<T extends unknown[] = any[]> = { Wait: (self: RBXScriptSignal<T>) => T extends [] ? void : T extends [infer V] ? V : any[] extends T ? any : T, Connect: (self: RBXScriptSignal<T>, callback: (...args: T) => void) => RBXScriptConnection, ConnectParallel: (self: RBXScriptSignal<T>, callback: (...args: T) => void) => RBXScriptConnection, Once: (self: RBXScriptSignal<T>, callback: (...args: T) => void) => RBXScriptConnection }
|
|
8469
8469
|
type HttpRequestOptions = { Url: string, Method: "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "CONNECT" | "OPTIONS" | "TRACE" | "PATCH" | nil, Headers?: { [string]: string | Secret }, Body?: string, Compress?: Enum.HttpCompression }
|
|
8470
8470
|
type HttpResponseData = { Success: boolean, StatusCode: number, StatusMessage: string, Headers: { [string]: string }, Body?: string }
|
|
8471
8471
|
type HumanoidDescriptionAccessory = { AssetId: number, AccessoryType: Enum.AccessoryType, IsLayered: boolean, Order?: number, Puffiness?: number }
|
package/lowering.mjs
CHANGED
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
/**
|
|
3
|
-
* What tilua lowers for Roblox: callbacks that Roblox runs later, on a thread
|
|
4
|
-
* of their own.
|
|
5
|
-
*
|
|
6
|
-
* The entry of a bundle runs under `xpcall`, which is what reports an error
|
|
7
|
-
* in the project's files instead of in the bundle's lines. A callback given
|
|
8
|
-
* to `task.spawn` or to `signal:Connect` is not under it when it runs, so its
|
|
9
|
-
* callback is wrapped in the same handler first (see runtime/roblox.luau):
|
|
10
|
-
*
|
|
11
|
-
* part.Touched:Connect(onTouched) -> tilua_roblox.Connect(part.Touched, onTouched)
|
|
12
|
-
* task.spawn(work, 1) -> tilua_roblox.task.spawn(work, 1)
|
|
13
|
-
*
|
|
14
|
-
* Only where Roblox starts a thread. A callback run synchronously — a
|
|
15
|
-
* `table.sort` comparator, `names:map(f)` — has its caller's `xpcall` already.
|
|
16
|
-
*
|
|
17
|
-
* `HttpService:JSONEncode(value)` is the language's JSON (the runtime's
|
|
18
|
-
* `JSONEncode`), which knows `[]` from `{}` where Roblox's sees two empty
|
|
19
|
-
* tables; `JSONDecode` reads back the same.
|
|
20
|
-
*
|
|
21
|
-
* And where Luau answers several values, tilua has one — an array. A method
|
|
22
|
-
* whose type returns a tuple (`workspace:FindPartOnRay(ray)`, `
|
|
23
|
-
* is called through the runtime's `values`, which gathers them, and so are the
|
|
24
|
-
* Luau globals that answer several (`string.unpack`, `utf8.codepoint`,
|
|
25
|
-
* `coroutine.close`).
|
|
26
|
-
*
|
|
27
|
-
* `print`, `error` and the console are the compiler's own: Lua's standard
|
|
28
|
-
* library is built into tilua.
|
|
29
|
-
*
|
|
30
|
-
* With `"output": "files"`, where each file ends up in the game is read off
|
|
31
|
-
* the project's Rojo project (`rojo`, `default.project.json` unless set), and
|
|
32
|
-
* the files require each other by the paths the game knows them by (see
|
|
33
|
-
* rojo.mjs).
|
|
34
|
-
*/
|
|
35
|
-
import { readFileSync } from "node:fs"
|
|
36
|
-
import { dirname, join } from "node:path"
|
|
37
|
-
import { fileURLToPath } from "node:url"
|
|
38
|
-
import { hasMembers, isInstanceOf, luauString, returnsTuple } from "@tilua/parser"
|
|
39
|
-
import { rojoLayout } from "./rojo.mjs"
|
|
40
|
-
|
|
41
|
-
/** @typedef {import("@tilua/parser").LoweringPlugin} LoweringPlugin */
|
|
42
|
-
|
|
43
|
-
const here = dirname(fileURLToPath(import.meta.url))
|
|
44
|
-
const roblox = readFileSync(join(here, "runtime", "roblox.luau"), "utf8")
|
|
45
|
-
|
|
46
|
-
const SIGNAL_METHODS = new Set(["Connect", "Once", "ConnectParallel"])
|
|
47
|
-
|
|
48
|
-
/** A service's methods that keep a callback, by the class that has them. */
|
|
49
|
-
const SERVICE_METHODS = new Map([
|
|
50
|
-
["BindToRenderStep", "RunService"],
|
|
51
|
-
["BindToClose", "DataModel"],
|
|
52
|
-
["BindAction", "ContextActionService"],
|
|
53
|
-
["BindActionAtPriority", "ContextActionService"],
|
|
54
|
-
])
|
|
55
|
-
|
|
56
|
-
/** HttpService's JSON, which the runtime writes and reads as tilua's values
|
|
57
|
-
* are: `[]` an array and `{}` an object, empty or not. */
|
|
58
|
-
const JSON_METHODS = new Set(["JSONEncode", "JSONDecode", "JSONEncodeAsync", "JSONDecodeAsync"])
|
|
59
|
-
|
|
60
|
-
/** `RBXScriptSignal<T...>` is structural once it is resolved: an object that
|
|
61
|
-
* can be connected to and waited on. */
|
|
62
|
-
const SIGNAL_MEMBERS = ["Connect", "Once", "Wait"]
|
|
63
|
-
|
|
64
|
-
/** Luau globals answering several values, which the runtime gathers. */
|
|
65
|
-
const GATHERED_GLOBALS = new Set(["string.unpack", "coroutine.close", "utf8.codepoint", "utf8.codes", "utf8.graphemes"])
|
|
66
|
-
|
|
67
|
-
/** The Rojo project a config that names none has. */
|
|
68
|
-
const DEFAULT_PROJECT = "default.project.json"
|
|
69
|
-
|
|
70
|
-
/** @type {LoweringPlugin} */
|
|
71
|
-
const plugin = {
|
|
72
|
-
runtime: { roblox },
|
|
73
|
-
|
|
74
|
-
methodCall({ method, receiver, use }) {
|
|
75
|
-
if (SIGNAL_METHODS.has(method) && hasMembers(receiver, SIGNAL_MEMBERS)) return { callee: `${use("roblox")}.${method}` }
|
|
76
|
-
if (JSON_METHODS.has(method) && isInstanceOf(receiver, "HttpService")) return { callee: `${use("roblox")}.${method}` }
|
|
77
|
-
const service = SERVICE_METHODS.get(method)
|
|
78
|
-
if (service !== undefined && isInstanceOf(receiver, service)) return { callee: `${use("roblox")}.${method}` }
|
|
79
|
-
if (returnsTuple(receiver, method)) {
|
|
80
|
-
return { callee: `${use("roblox")}.values`, prepend: [luauString(method)] }
|
|
81
|
-
}
|
|
82
|
-
return undefined
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
globalCall({ name, use }) {
|
|
86
|
-
if (GATHERED_GLOBALS.has(name)) return { callee: `${use("roblox")}.globals.${name.replace(".", "_")}` }
|
|
87
|
-
return undefined
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
globalValue({ name, use }) {
|
|
91
|
-
if (name === "task") return `${use("roblox")}.task`
|
|
92
|
-
if (GATHERED_GLOBALS.has(name)) return `${use("roblox")}.globals.${name.replace(".", "_")}`
|
|
93
|
-
return undefined
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
files(layout) {
|
|
97
|
-
const project = typeof layout.options.rojo === "string" ? layout.options.rojo : DEFAULT_PROJECT
|
|
98
|
-
return rojoLayout(layout, project, project !== DEFAULT_PROJECT)
|
|
99
|
-
},
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export default plugin
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* What tilua lowers for Roblox: callbacks that Roblox runs later, on a thread
|
|
4
|
+
* of their own.
|
|
5
|
+
*
|
|
6
|
+
* The entry of a bundle runs under `xpcall`, which is what reports an error
|
|
7
|
+
* in the project's files instead of in the bundle's lines. A callback given
|
|
8
|
+
* to `task.spawn` or to `signal:Connect` is not under it when it runs, so its
|
|
9
|
+
* callback is wrapped in the same handler first (see runtime/roblox.luau):
|
|
10
|
+
*
|
|
11
|
+
* part.Touched:Connect(onTouched) -> tilua_roblox.Connect(part.Touched, onTouched)
|
|
12
|
+
* task.spawn(work, 1) -> tilua_roblox.task.spawn(work, 1)
|
|
13
|
+
*
|
|
14
|
+
* Only where Roblox starts a thread. A callback run synchronously — a
|
|
15
|
+
* `table.sort` comparator, `names:map(f)` — has its caller's `xpcall` already.
|
|
16
|
+
*
|
|
17
|
+
* `HttpService:JSONEncode(value)` is the language's JSON (the runtime's
|
|
18
|
+
* `JSONEncode`), which knows `[]` from `{}` where Roblox's sees two empty
|
|
19
|
+
* tables; `JSONDecode` reads back the same.
|
|
20
|
+
*
|
|
21
|
+
* And where Luau answers several values, tilua has one — an array. A method
|
|
22
|
+
* whose type returns a tuple (`workspace:FindPartOnRay(ray)`, `cf:ToOrientation()`)
|
|
23
|
+
* is called through the runtime's `values`, which gathers them, and so are the
|
|
24
|
+
* Luau globals that answer several (`string.unpack`, `utf8.codepoint`,
|
|
25
|
+
* `coroutine.close`).
|
|
26
|
+
*
|
|
27
|
+
* `print`, `error` and the console are the compiler's own: Lua's standard
|
|
28
|
+
* library is built into tilua.
|
|
29
|
+
*
|
|
30
|
+
* With `"output": "files"`, where each file ends up in the game is read off
|
|
31
|
+
* the project's Rojo project (`rojo`, `default.project.json` unless set), and
|
|
32
|
+
* the files require each other by the paths the game knows them by (see
|
|
33
|
+
* rojo.mjs).
|
|
34
|
+
*/
|
|
35
|
+
import { readFileSync } from "node:fs"
|
|
36
|
+
import { dirname, join } from "node:path"
|
|
37
|
+
import { fileURLToPath } from "node:url"
|
|
38
|
+
import { hasMembers, isInstanceOf, luauString, returnsTuple } from "@tilua/parser"
|
|
39
|
+
import { rojoLayout } from "./rojo.mjs"
|
|
40
|
+
|
|
41
|
+
/** @typedef {import("@tilua/parser").LoweringPlugin} LoweringPlugin */
|
|
42
|
+
|
|
43
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
44
|
+
const roblox = readFileSync(join(here, "runtime", "roblox.luau"), "utf8")
|
|
45
|
+
|
|
46
|
+
const SIGNAL_METHODS = new Set(["Connect", "Once", "ConnectParallel"])
|
|
47
|
+
|
|
48
|
+
/** A service's methods that keep a callback, by the class that has them. */
|
|
49
|
+
const SERVICE_METHODS = new Map([
|
|
50
|
+
["BindToRenderStep", "RunService"],
|
|
51
|
+
["BindToClose", "DataModel"],
|
|
52
|
+
["BindAction", "ContextActionService"],
|
|
53
|
+
["BindActionAtPriority", "ContextActionService"],
|
|
54
|
+
])
|
|
55
|
+
|
|
56
|
+
/** HttpService's JSON, which the runtime writes and reads as tilua's values
|
|
57
|
+
* are: `[]` an array and `{}` an object, empty or not. */
|
|
58
|
+
const JSON_METHODS = new Set(["JSONEncode", "JSONDecode", "JSONEncodeAsync", "JSONDecodeAsync"])
|
|
59
|
+
|
|
60
|
+
/** `RBXScriptSignal<T...>` is structural once it is resolved: an object that
|
|
61
|
+
* can be connected to and waited on. */
|
|
62
|
+
const SIGNAL_MEMBERS = ["Connect", "Once", "Wait"]
|
|
63
|
+
|
|
64
|
+
/** Luau globals answering several values, which the runtime gathers. */
|
|
65
|
+
const GATHERED_GLOBALS = new Set(["string.unpack", "coroutine.close", "utf8.codepoint", "utf8.codes", "utf8.graphemes"])
|
|
66
|
+
|
|
67
|
+
/** The Rojo project a config that names none has. */
|
|
68
|
+
const DEFAULT_PROJECT = "default.project.json"
|
|
69
|
+
|
|
70
|
+
/** @type {LoweringPlugin} */
|
|
71
|
+
const plugin = {
|
|
72
|
+
runtime: { roblox },
|
|
73
|
+
|
|
74
|
+
methodCall({ method, receiver, use }) {
|
|
75
|
+
if (SIGNAL_METHODS.has(method) && hasMembers(receiver, SIGNAL_MEMBERS)) return { callee: `${use("roblox")}.${method}` }
|
|
76
|
+
if (JSON_METHODS.has(method) && isInstanceOf(receiver, "HttpService")) return { callee: `${use("roblox")}.${method}` }
|
|
77
|
+
const service = SERVICE_METHODS.get(method)
|
|
78
|
+
if (service !== undefined && isInstanceOf(receiver, service)) return { callee: `${use("roblox")}.${method}` }
|
|
79
|
+
if (returnsTuple(receiver, method)) {
|
|
80
|
+
return { callee: `${use("roblox")}.values`, prepend: [luauString(method)] }
|
|
81
|
+
}
|
|
82
|
+
return undefined
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
globalCall({ name, use }) {
|
|
86
|
+
if (GATHERED_GLOBALS.has(name)) return { callee: `${use("roblox")}.globals.${name.replace(".", "_")}` }
|
|
87
|
+
return undefined
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
globalValue({ name, use }) {
|
|
91
|
+
if (name === "task") return `${use("roblox")}.task`
|
|
92
|
+
if (GATHERED_GLOBALS.has(name)) return `${use("roblox")}.globals.${name.replace(".", "_")}`
|
|
93
|
+
return undefined
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
files(layout) {
|
|
97
|
+
const project = typeof layout.options.rojo === "string" ? layout.options.rojo : DEFAULT_PROJECT
|
|
98
|
+
return rojoLayout(layout, project, project !== DEFAULT_PROJECT)
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export default plugin
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tilua-types/roblox",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.1.1",
|
|
4
4
|
"description": "Luau and the whole Roblox API as tilua types: every class, enum, data type, service and global",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"files": [
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@tilua-types/luau": "^9.
|
|
26
|
+
"@tilua-types/luau": "^9.1.1"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"@tilua/parser": "^9.0.0"
|
package/runtime/roblox.luau
CHANGED
|
@@ -1,139 +1,139 @@
|
|
|
1
|
-
-- Callbacks Roblox runs on a thread of their own, reported in the project's
|
|
2
|
-
-- files.
|
|
3
|
-
--
|
|
4
|
-
-- A bundle's entry runs under `xpcall`, so an error nothing caught names the
|
|
5
|
-
-- file and line the author wrote. A callback handed to `task.spawn` or to
|
|
6
|
-
-- `signal:Connect` runs later, on a new thread, where that `xpcall` is not on
|
|
7
|
-
-- the stack: its errors would name lines of the bundle. `guard` gives such a
|
|
8
|
-
-- callback the same handler — `__FAIL__`, the bundle's own — and raises what
|
|
9
|
-
-- it returns on the callback's thread, where Roblox reports it as before.
|
|
10
|
-
--
|
|
11
|
-
-- The error is raised again, never swallowed: code that relied on it
|
|
12
|
-
-- stopping the callback still sees it stop. Outside a bundle there is nothing
|
|
13
|
-
-- to map, and callbacks are passed through untouched.
|
|
14
|
-
|
|
15
|
-
local __NAME__ = {}
|
|
16
|
-
local fail = __FAIL__
|
|
17
|
-
|
|
18
|
-
local function guard(callback)
|
|
19
|
-
if type(callback) ~= "function" or fail == nil then
|
|
20
|
-
return callback
|
|
21
|
-
end
|
|
22
|
-
return function(...)
|
|
23
|
-
local results = table.pack(xpcall(callback, fail, ...))
|
|
24
|
-
if not results[1] then
|
|
25
|
-
error(results[2], 0)
|
|
26
|
-
end
|
|
27
|
-
return table.unpack(results, 2, results.n)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
__NAME__.guard = guard
|
|
32
|
-
|
|
33
|
-
-- `signal:Connect(f)` and its siblings.
|
|
34
|
-
function __NAME__.Connect(signal, callback)
|
|
35
|
-
return signal:Connect(guard(callback))
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
function __NAME__.Once(signal, callback)
|
|
39
|
-
return signal:Once(guard(callback))
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
function __NAME__.ConnectParallel(signal, callback)
|
|
43
|
-
return signal:ConnectParallel(guard(callback))
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
-- Services that keep a callback and run it later.
|
|
47
|
-
function __NAME__.BindToRenderStep(service, name, priority, callback)
|
|
48
|
-
return service:BindToRenderStep(name, priority, guard(callback))
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
function __NAME__.BindToClose(game, callback)
|
|
52
|
-
return game:BindToClose(guard(callback))
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
function __NAME__.BindAction(service, name, callback, ...)
|
|
56
|
-
return service:BindAction(name, guard(callback), ...)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
function __NAME__.BindActionAtPriority(service, name, callback, ...)
|
|
60
|
-
return service:BindActionAtPriority(name, guard(callback), ...)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
-- `task`, with the functions that start a thread guarding what they start.
|
|
64
|
-
-- A thread passed instead of a function is left as it is. Everything else is
|
|
65
|
-
-- `task`'s own. Standing in for the global, rather than rewriting calls,
|
|
66
|
-
-- covers `local spawn = task.spawn` too.
|
|
67
|
-
__NAME__.task = setmetatable({
|
|
68
|
-
spawn = function(target, ...)
|
|
69
|
-
return task.spawn(guard(target), ...)
|
|
70
|
-
end,
|
|
71
|
-
defer = function(target, ...)
|
|
72
|
-
return task.defer(guard(target), ...)
|
|
73
|
-
end,
|
|
74
|
-
delay = function(duration, target, ...)
|
|
75
|
-
return task.delay(duration, guard(target), ...)
|
|
76
|
-
end,
|
|
77
|
-
}, { __index = task })
|
|
78
|
-
|
|
79
|
-
-- HttpService's JSON, as tilua's values have it: `[]` is an array and `{}`
|
|
80
|
-
-- an object, empty or not — `JSONEncode` alone writes every empty table as
|
|
81
|
-
-- `[]` — and what `JSONDecode` reads back is the same. `__JSON__` is the
|
|
82
|
-
-- language's; the Async ones yield nothing here, the work being the same.
|
|
83
|
-
function __NAME__.JSONEncode(_, value)
|
|
84
|
-
return __JSON__.encode(value)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
function __NAME__.JSONDecode(_, text)
|
|
88
|
-
return __JSON__.decode(text)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
__NAME__.JSONEncodeAsync = __NAME__.JSONEncode
|
|
92
|
-
__NAME__.JSONDecodeAsync = __NAME__.JSONDecode
|
|
93
|
-
|
|
94
|
-
-- Where Luau answers several values, tilua has one: an array. A Roblox method
|
|
95
|
-
-- whose type says it returns several — `workspace:FindPartOnRay(ray)`,
|
|
96
|
-
-- `
|
|
97
|
-
-- hands its values back as that array.
|
|
98
|
-
function __NAME__.values(name, receiver, ...)
|
|
99
|
-
return { receiver[name](receiver, ...) }
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
-- The same for Luau's own globals that answer several values.
|
|
103
|
-
__NAME__.globals = {
|
|
104
|
-
string_unpack = function(...)
|
|
105
|
-
return { string.unpack(...) }
|
|
106
|
-
end,
|
|
107
|
-
coroutine_close = function(co)
|
|
108
|
-
return { coroutine.close(co) }
|
|
109
|
-
end,
|
|
110
|
-
utf8_codepoint = function(...)
|
|
111
|
-
return { utf8.codepoint(...) }
|
|
112
|
-
end,
|
|
113
|
-
-- An iteration — `{ step, state, first }` — whose steps answer one
|
|
114
|
-
-- `{ position, code }` each, as a loop walks them.
|
|
115
|
-
utf8_codes = function(s)
|
|
116
|
-
local step, state, first = utf8.codes(s)
|
|
117
|
-
return {
|
|
118
|
-
function(_, previous)
|
|
119
|
-
local position, code = step(state, previous ~= nil and previous[1] or first)
|
|
120
|
-
if position == nil then
|
|
121
|
-
return nil
|
|
122
|
-
end
|
|
123
|
-
return { position, code }
|
|
124
|
-
end,
|
|
125
|
-
s,
|
|
126
|
-
nil,
|
|
127
|
-
}
|
|
128
|
-
end,
|
|
129
|
-
utf8_graphemes = function(s, i, j)
|
|
130
|
-
local step = utf8.graphemes(s, i, j)
|
|
131
|
-
return function()
|
|
132
|
-
local first, last = step()
|
|
133
|
-
if first == nil then
|
|
134
|
-
return nil
|
|
135
|
-
end
|
|
136
|
-
return { first, last }
|
|
137
|
-
end
|
|
138
|
-
end,
|
|
139
|
-
}
|
|
1
|
+
-- Callbacks Roblox runs on a thread of their own, reported in the project's
|
|
2
|
+
-- files.
|
|
3
|
+
--
|
|
4
|
+
-- A bundle's entry runs under `xpcall`, so an error nothing caught names the
|
|
5
|
+
-- file and line the author wrote. A callback handed to `task.spawn` or to
|
|
6
|
+
-- `signal:Connect` runs later, on a new thread, where that `xpcall` is not on
|
|
7
|
+
-- the stack: its errors would name lines of the bundle. `guard` gives such a
|
|
8
|
+
-- callback the same handler — `__FAIL__`, the bundle's own — and raises what
|
|
9
|
+
-- it returns on the callback's thread, where Roblox reports it as before.
|
|
10
|
+
--
|
|
11
|
+
-- The error is raised again, never swallowed: code that relied on it
|
|
12
|
+
-- stopping the callback still sees it stop. Outside a bundle there is nothing
|
|
13
|
+
-- to map, and callbacks are passed through untouched.
|
|
14
|
+
|
|
15
|
+
local __NAME__ = {}
|
|
16
|
+
local fail = __FAIL__
|
|
17
|
+
|
|
18
|
+
local function guard(callback)
|
|
19
|
+
if type(callback) ~= "function" or fail == nil then
|
|
20
|
+
return callback
|
|
21
|
+
end
|
|
22
|
+
return function(...)
|
|
23
|
+
local results = table.pack(xpcall(callback, fail, ...))
|
|
24
|
+
if not results[1] then
|
|
25
|
+
error(results[2], 0)
|
|
26
|
+
end
|
|
27
|
+
return table.unpack(results, 2, results.n)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
__NAME__.guard = guard
|
|
32
|
+
|
|
33
|
+
-- `signal:Connect(f)` and its siblings.
|
|
34
|
+
function __NAME__.Connect(signal, callback)
|
|
35
|
+
return signal:Connect(guard(callback))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
function __NAME__.Once(signal, callback)
|
|
39
|
+
return signal:Once(guard(callback))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
function __NAME__.ConnectParallel(signal, callback)
|
|
43
|
+
return signal:ConnectParallel(guard(callback))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
-- Services that keep a callback and run it later.
|
|
47
|
+
function __NAME__.BindToRenderStep(service, name, priority, callback)
|
|
48
|
+
return service:BindToRenderStep(name, priority, guard(callback))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
function __NAME__.BindToClose(game, callback)
|
|
52
|
+
return game:BindToClose(guard(callback))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
function __NAME__.BindAction(service, name, callback, ...)
|
|
56
|
+
return service:BindAction(name, guard(callback), ...)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
function __NAME__.BindActionAtPriority(service, name, callback, ...)
|
|
60
|
+
return service:BindActionAtPriority(name, guard(callback), ...)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
-- `task`, with the functions that start a thread guarding what they start.
|
|
64
|
+
-- A thread passed instead of a function is left as it is. Everything else is
|
|
65
|
+
-- `task`'s own. Standing in for the global, rather than rewriting calls,
|
|
66
|
+
-- covers `local spawn = task.spawn` too.
|
|
67
|
+
__NAME__.task = setmetatable({
|
|
68
|
+
spawn = function(target, ...)
|
|
69
|
+
return task.spawn(guard(target), ...)
|
|
70
|
+
end,
|
|
71
|
+
defer = function(target, ...)
|
|
72
|
+
return task.defer(guard(target), ...)
|
|
73
|
+
end,
|
|
74
|
+
delay = function(duration, target, ...)
|
|
75
|
+
return task.delay(duration, guard(target), ...)
|
|
76
|
+
end,
|
|
77
|
+
}, { __index = task })
|
|
78
|
+
|
|
79
|
+
-- HttpService's JSON, as tilua's values have it: `[]` is an array and `{}`
|
|
80
|
+
-- an object, empty or not — `JSONEncode` alone writes every empty table as
|
|
81
|
+
-- `[]` — and what `JSONDecode` reads back is the same. `__JSON__` is the
|
|
82
|
+
-- language's; the Async ones yield nothing here, the work being the same.
|
|
83
|
+
function __NAME__.JSONEncode(_, value)
|
|
84
|
+
return __JSON__.encode(value)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
function __NAME__.JSONDecode(_, text)
|
|
88
|
+
return __JSON__.decode(text)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
__NAME__.JSONEncodeAsync = __NAME__.JSONEncode
|
|
92
|
+
__NAME__.JSONDecodeAsync = __NAME__.JSONDecode
|
|
93
|
+
|
|
94
|
+
-- Where Luau answers several values, tilua has one: an array. A Roblox method
|
|
95
|
+
-- whose type says it returns several — `workspace:FindPartOnRay(ray)`,
|
|
96
|
+
-- `cf:ToOrientation()`, a signal with several arguments — is called through
|
|
97
|
+
-- this, which hands its values back as that array.
|
|
98
|
+
function __NAME__.values(name, receiver, ...)
|
|
99
|
+
return { receiver[name](receiver, ...) }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
-- The same for Luau's own globals that answer several values.
|
|
103
|
+
__NAME__.globals = {
|
|
104
|
+
string_unpack = function(...)
|
|
105
|
+
return { string.unpack(...) }
|
|
106
|
+
end,
|
|
107
|
+
coroutine_close = function(co)
|
|
108
|
+
return { coroutine.close(co) }
|
|
109
|
+
end,
|
|
110
|
+
utf8_codepoint = function(...)
|
|
111
|
+
return { utf8.codepoint(...) }
|
|
112
|
+
end,
|
|
113
|
+
-- An iteration — `{ step, state, first }` — whose steps answer one
|
|
114
|
+
-- `{ position, code }` each, as a loop walks them.
|
|
115
|
+
utf8_codes = function(s)
|
|
116
|
+
local step, state, first = utf8.codes(s)
|
|
117
|
+
return {
|
|
118
|
+
function(_, previous)
|
|
119
|
+
local position, code = step(state, previous ~= nil and previous[1] or first)
|
|
120
|
+
if position == nil then
|
|
121
|
+
return nil
|
|
122
|
+
end
|
|
123
|
+
return { position, code }
|
|
124
|
+
end,
|
|
125
|
+
s,
|
|
126
|
+
nil,
|
|
127
|
+
}
|
|
128
|
+
end,
|
|
129
|
+
utf8_graphemes = function(s, i, j)
|
|
130
|
+
local step = utf8.graphemes(s, i, j)
|
|
131
|
+
return function()
|
|
132
|
+
local first, last = step()
|
|
133
|
+
if first == nil then
|
|
134
|
+
return nil
|
|
135
|
+
end
|
|
136
|
+
return { first, last }
|
|
137
|
+
end
|
|
138
|
+
end,
|
|
139
|
+
}
|