@thigasdevelopment/luam 0.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/LICENSE +21 -0
- package/README.md +616 -0
- package/lua/async.lua +119 -0
- package/lua/class.lua +178 -0
- package/lua/development-logs-client.lua +13 -0
- package/lua/development-logs-server.lua +44 -0
- package/lua/dotenv.lua +160 -0
- package/lua/env.lua +18 -0
- package/lua/math.lua +24 -0
- package/lua/string.lua +82 -0
- package/lua/table.lua +63 -0
- package/lua/threads.lua +274 -0
- package/luam.mjs +9910 -0
- package/package.json +38 -0
- package/template/README.md +120 -0
- package/template/config.lua +14 -0
- package/template/env +11 -0
- package/template/framework/bootstrap-client.luam +9 -0
- package/template/framework/bootstrap-server.luam +9 -0
- package/template/framework/command.luam +54 -0
- package/template/framework/core.luam +58 -0
- package/template/framework/event.luam +39 -0
- package/template/framework/listener.luam +40 -0
- package/template/framework/loader.luam +73 -0
- package/template/framework/thread-pool.luam +27 -0
- package/template/gitignore +7 -0
- package/template/luam.json +11 -0
- package/template/src/client/commands/ping-command.luam +8 -0
- package/template/src/client/main.luam +5 -0
- package/template/src/server/commands/hello-command.luam +9 -0
- package/template/src/server/handlers/player-join-listener.luam +9 -0
- package/template/src/server/main.luam +11 -0
- package/template/src/shared/config.luam +5 -0
- package/template/src/shared/framework/command.luam +54 -0
- package/template/src/shared/framework/core.luam +58 -0
- package/template/src/shared/framework/event.luam +39 -0
- package/template/src/shared/framework/listener.luam +40 -0
- package/template/src/shared/framework/loader.luam +73 -0
- package/template/src/shared/framework/thread-pool.luam +27 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thigas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
<p align="center"><img src="assets/luam-mark.svg" alt="Luam" width="110"></p>
|
|
2
|
+
|
|
3
|
+
<h1 align="center">Luam</h1>
|
|
4
|
+
|
|
5
|
+
<p align="center">Typed Lua for Multi Theft Auto. Compiles to plain Lua 5.1.</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/@thigasdevelopment/luam"><img alt="npm" src="https://img.shields.io/npm/v/@thigasdevelopment/luam?color=cb3837&label=npm"></a>
|
|
9
|
+
<img alt="Target" src="https://img.shields.io/badge/target-Lua%205.1-000080">
|
|
10
|
+
<img alt="Platform" src="https://img.shields.io/badge/platform-Multi%20Theft%20Auto-3ddc97">
|
|
11
|
+
<img alt="Node" src="https://img.shields.io/badge/node-%3E%3D20-5fa04e">
|
|
12
|
+
<img alt="License" src="https://img.shields.io/badge/license-MIT-blue">
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
**Luam** is a typed language for [Multi Theft Auto](https://multitheftauto.com/).
|
|
16
|
+
You write `.luam` files with type annotations, classes, enums and template
|
|
17
|
+
strings. The compiler checks them and emits readable **Lua 5.1** plus a generated
|
|
18
|
+
`meta.xml` — a resource your server can start as-is.
|
|
19
|
+
|
|
20
|
+
It is *typed Lua*, not TypeScript. Blocks still end with `end` and inequality is
|
|
21
|
+
still `~=`, while comments use `#` and `#* ... *#` to avoid colliding with `--` decrement.
|
|
22
|
+
|
|
23
|
+
```lua
|
|
24
|
+
local health: number = 100
|
|
25
|
+
|
|
26
|
+
function heal(player: Player, amount: number): void
|
|
27
|
+
health += amount
|
|
28
|
+
|
|
29
|
+
outputChatBox(`${getPlayerName(player)} healed to ${health}`, player)
|
|
30
|
+
end
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Annotations are erased at build time. `dxDrawText` in a server file, a typo in an
|
|
34
|
+
MTA function name, a `string` passed where a `number` belongs — all build errors,
|
|
35
|
+
before the server ever starts. A build with any error writes nothing.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
You need [Node.js](https://nodejs.org/) 20 or newer and an
|
|
42
|
+
[MTA:SA](https://multitheftauto.com/) 1.5+ server. No Lua toolchain — the
|
|
43
|
+
compiler emits Lua text, it never runs it.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
node --version # must print v20.x or newer
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 1. Install the CLI
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install --global @thigasdevelopment/luam
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
That gives you one command, `luam`. Check it:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
luam --version
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
| Task | Command |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| Install | `npm install --global @thigasdevelopment/luam` |
|
|
64
|
+
| Update to the latest | `npm update --global @thigasdevelopment/luam` |
|
|
65
|
+
| Install a specific version | `npm install --global @thigasdevelopment/luam@0.1.1` |
|
|
66
|
+
| Uninstall | `npm uninstall --global @thigasdevelopment/luam` |
|
|
67
|
+
| Run once, without installing | `npx @thigasdevelopment/luam <command>` |
|
|
68
|
+
|
|
69
|
+
`npx @thigasdevelopment/luam build` works anywhere and caches the download, which is handy in CI or
|
|
70
|
+
on a machine you would rather not install into.
|
|
71
|
+
|
|
72
|
+
<details>
|
|
73
|
+
<summary><b><code>luam: command not found</code> after installing</b></summary>
|
|
74
|
+
|
|
75
|
+
npm put the binary in its global bin directory and that directory is not on your
|
|
76
|
+
`PATH`. Find it:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm config get prefix
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- **Windows** — add that folder itself to your user `PATH`
|
|
83
|
+
(*Settings → System → About → Advanced system settings → Environment
|
|
84
|
+
Variables*), then open a **new** terminal.
|
|
85
|
+
- **macOS / Linux** — add `<prefix>/bin` to your `PATH` in `~/.zshrc` or
|
|
86
|
+
`~/.bashrc`, then run `source ~/.zshrc`.
|
|
87
|
+
|
|
88
|
+
Nothing to configure if you would rather not: `npx @thigasdevelopment/luam <command>` needs no
|
|
89
|
+
`PATH` entry at all.
|
|
90
|
+
|
|
91
|
+
</details>
|
|
92
|
+
|
|
93
|
+
<details>
|
|
94
|
+
<summary><b>Install from source instead</b></summary>
|
|
95
|
+
|
|
96
|
+
For contributing, or to run a change that is not released yet. Needs
|
|
97
|
+
[pnpm](https://pnpm.io/) 9+.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
git clone https://github.com/ThigasDevelopment/luam.git
|
|
101
|
+
cd luam
|
|
102
|
+
pnpm install
|
|
103
|
+
pnpm install:cli
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`install:cli` bundles the compiler into one self-contained file, writes a
|
|
107
|
+
publishable manifest next to it, and runs `npm install --global` on the result.
|
|
108
|
+
It finishes by running `luam --version` and telling you what to fix if the npm
|
|
109
|
+
bin directory is not on your `PATH`.
|
|
110
|
+
|
|
111
|
+
</details>
|
|
112
|
+
|
|
113
|
+
### 2. Install the editor extension
|
|
114
|
+
|
|
115
|
+
The VS Code extension gives you types, completion and errors while you type —
|
|
116
|
+
from the same checker the CLI runs, so the editor and the build never disagree.
|
|
117
|
+
See [Editor support](#editor-support) below for how to install it.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Quick start
|
|
122
|
+
|
|
123
|
+
**1. Scaffold.** `init` writes exactly one file, `luam.json`. No framework, no
|
|
124
|
+
example tree, nothing to delete.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
mkdir my-resource && cd my-resource
|
|
128
|
+
luam init
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**2. Write some Luam.** Create the source tree yourself — **the folder decides
|
|
132
|
+
the environment**: `src/server` is server-side, `src/client` is client-side,
|
|
133
|
+
`src/shared` is both.
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
my-resource/
|
|
137
|
+
├── luam.json
|
|
138
|
+
└── src/
|
|
139
|
+
├── shared/config.luam
|
|
140
|
+
├── server/main.luam
|
|
141
|
+
└── client/hud.luam
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```lua
|
|
145
|
+
-- src/shared/config.luam
|
|
146
|
+
function formatPlayerName(name: string): string
|
|
147
|
+
return 'Player: ' .. name
|
|
148
|
+
end
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```lua
|
|
152
|
+
-- src/server/main.luam
|
|
153
|
+
addEventHandler('onPlayerJoin', root, function()
|
|
154
|
+
outputChatBox(formatPlayerName(getPlayerName(source)), root)
|
|
155
|
+
end)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```lua
|
|
159
|
+
-- src/client/hud.luam
|
|
160
|
+
local caption: string = `HUD ${RESOURCE_NAME:demo}`
|
|
161
|
+
|
|
162
|
+
addEventHandler('onClientRender', root, function()
|
|
163
|
+
dxDrawText(caption, 10, 10)
|
|
164
|
+
end)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The compiler already knows `formatPlayerName` is shared, so the server file may
|
|
168
|
+
call it — and that `dxDrawText` from `main.luam` would be an error.
|
|
169
|
+
|
|
170
|
+
**3. Build.**
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
luam check # diagnostics only, writes nothing
|
|
174
|
+
luam build # writes build/my-resource
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
An error names the file, the line and the rule:
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
src/client/hud.luam:4:5 error check-environment-api: API "outputChatBox" is server-only and is not available in a "client" file.
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**4. Run it.** Copy `build/my-resource` into
|
|
184
|
+
`<MTA Server>/mods/deathmatch/resources/`, then in the server console:
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
refresh
|
|
188
|
+
start my-resource
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**5. Iterate.** Point `luam.json` at your server and let `dev` build, sync,
|
|
192
|
+
restart, and stream resource logs on every save:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{ "name": "my-resource", "serverPath": "C:/MTA Server" }
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
luam dev
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## The CLI
|
|
205
|
+
|
|
206
|
+
Five commands. Every one of them reads `luam.json` from the current directory,
|
|
207
|
+
or from `--cwd`.
|
|
208
|
+
|
|
209
|
+
### `luam init`
|
|
210
|
+
|
|
211
|
+
Scaffolds `luam.json` and stops. The resource name comes from `--name`, or from
|
|
212
|
+
the directory you are in.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
luam init --name gamemode-race
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
An existing `luam.json` is kept and reported — pass `--force` to overwrite it.
|
|
219
|
+
|
|
220
|
+
### `luam check`
|
|
221
|
+
|
|
222
|
+
Compiles everything and prints diagnostics. **Writes nothing.** This is the
|
|
223
|
+
command to put in CI and in a pre-commit hook.
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
luam check
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
src/server/main.luam:11:23 error check-type-mismatch: Variable "total" expects "number" but received "string".
|
|
231
|
+
Build failed: 1 error, 0 warnings in 4 ms.
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### `luam build`
|
|
235
|
+
|
|
236
|
+
Compiles and writes the resource into `<outDir>/<name>`.
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
luam build
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
Discovery: done in 1 ms.
|
|
244
|
+
Compile: 3 files in 12 ms.
|
|
245
|
+
Assembly: done in 0 ms.
|
|
246
|
+
Manifest: done in 1 ms.
|
|
247
|
+
Write: 7 files in 2 ms.
|
|
248
|
+
Build passed: 3 files, 0 errors, 0 warnings in 16 ms.
|
|
249
|
+
Wrote 7 files to "build/my-resource".
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
A build that reports any error writes nothing, so a resource that worked is
|
|
253
|
+
never replaced with partial output.
|
|
254
|
+
|
|
255
|
+
### `luam dev`
|
|
256
|
+
|
|
257
|
+
Runs the `ensure` build, server sync, restart, and watch loop while following
|
|
258
|
+
`<serverPath>/mods/deathmatch/logs/server.log`. It starts at the end of the file,
|
|
259
|
+
so existing history is not printed.
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
luam dev
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Server records attributed to the active resource and relayed client
|
|
266
|
+
`outputDebugString` calls share one stable stream:
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
[14:22:07][server][info] Resource started
|
|
270
|
+
[14:22:09][client][warn] Missing vehicle model
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The client call still reaches the MTA debug console. `dev` adds a validated,
|
|
274
|
+
rate-limited MTA event relay only to the synchronized server resource. `build`
|
|
275
|
+
and `ensure` never include these development helpers. Engine output without a
|
|
276
|
+
resource identity may appear as plain server output; records attributed to
|
|
277
|
+
other resources are ignored.
|
|
278
|
+
|
|
279
|
+
### `luam ensure`
|
|
280
|
+
|
|
281
|
+
The loop you leave running while you work. It builds, mirrors the resource into
|
|
282
|
+
your MTA server, restarts it, and repeats all of that on every save.
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
luam ensure
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
How much it does depends on what `luam.json` gives it:
|
|
289
|
+
|
|
290
|
+
| Configured | What `ensure` does |
|
|
291
|
+
| --- | --- |
|
|
292
|
+
| nothing | Builds into `<outDir>/<name>` and watches |
|
|
293
|
+
| `serverPath` | Also mirrors the resource into the server. You restart it |
|
|
294
|
+
| `serverPath` + `transport` | Also refreshes and restarts the resource for you |
|
|
295
|
+
|
|
296
|
+
To get the restart, add an `http` transport pointing at a resource on your server
|
|
297
|
+
that exports `refreshResources` and `restartResource`:
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"name": "my-resource",
|
|
302
|
+
"serverPath": "C:/MTA Server",
|
|
303
|
+
"transport": {
|
|
304
|
+
"kind": "http",
|
|
305
|
+
"host": "127.0.0.1",
|
|
306
|
+
"port": 22005,
|
|
307
|
+
"resource": "luam-sync",
|
|
308
|
+
"username": "luam",
|
|
309
|
+
"passwordEnv": "LUAM_MTA_PASSWORD"
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
set LUAM_MTA_PASSWORD=... # Windows
|
|
316
|
+
export LUAM_MTA_PASSWORD=... # macOS / Linux
|
|
317
|
+
luam ensure
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Use `passwordEnv`, which names an environment variable, rather than an inline
|
|
321
|
+
`password` — no log line or diagnostic ever prints the value. MTA's HTTP
|
|
322
|
+
interface has no TLS, so keep `host` on `127.0.0.1` and tunnel over SSH instead
|
|
323
|
+
of exposing the port.
|
|
324
|
+
|
|
325
|
+
Each save rebuilds only the files whose source changed and writes only the files
|
|
326
|
+
whose content changed. If the build reports an error, nothing is synced and the
|
|
327
|
+
running server keeps the last version that compiled. `Ctrl+C` ends the watch;
|
|
328
|
+
`--no-watch` runs the whole cycle exactly once, which is what an editor task or
|
|
329
|
+
a deploy script wants.
|
|
330
|
+
|
|
331
|
+
### Options and exit codes
|
|
332
|
+
|
|
333
|
+
| Option | Meaning |
|
|
334
|
+
| --- | --- |
|
|
335
|
+
| `--cwd <path>` | Project directory holding `luam.json`. Defaults to the current directory |
|
|
336
|
+
| `--config <path>` | Load this file instead of `luam.json` |
|
|
337
|
+
| `--name <name>` | Resource name for `init` |
|
|
338
|
+
| `--force` | Let `init` overwrite a file that exists |
|
|
339
|
+
| `--watch` / `--no-watch` | Keep `ensure` or `dev` watching, or run it once. Both watch by default |
|
|
340
|
+
| `--offline` | Skip the `min_mta_version` lookup. `LUAM_OFFLINE` does the same |
|
|
341
|
+
| `--no-color` | Plain output, no colour or emoji. `NO_COLOR` does the same |
|
|
342
|
+
| `-h`, `--help` | Print the usage text |
|
|
343
|
+
| `-v`, `--version` | Print the CLI version |
|
|
344
|
+
|
|
345
|
+
| Exit code | Meaning |
|
|
346
|
+
| --- | --- |
|
|
347
|
+
| `0` | The command succeeded |
|
|
348
|
+
| `1` | The build reported errors |
|
|
349
|
+
| `2` | The command line or the configuration is invalid |
|
|
350
|
+
|
|
351
|
+
Progress is painted on stderr and the report goes to stdout, so redirecting
|
|
352
|
+
stdout captures the report alone. Output drops all escape sequences when the
|
|
353
|
+
stream is not a terminal — a CI log stays readable.
|
|
354
|
+
|
|
355
|
+
### `luam.json`
|
|
356
|
+
|
|
357
|
+
Only `name` is required.
|
|
358
|
+
|
|
359
|
+
| Field | Default | Meaning |
|
|
360
|
+
| --- | --- | --- |
|
|
361
|
+
| `name` | required | Names the output folder and the resource `ensure` restarts. MTA reads the resource name from the folder, so it never reaches `meta.xml` |
|
|
362
|
+
| `author`, `version`, `description` | unset | `meta.xml` info attributes |
|
|
363
|
+
| `sourceDirs` | `["src"]` | Scanned for `.luam` and `.d.luam` files |
|
|
364
|
+
| `assetDirs` | `["assets"]` | Copied verbatim and declared `<file>`, so clients download them |
|
|
365
|
+
| `outDir` | `"build"` | Receives `<outDir>/<name>` |
|
|
366
|
+
| `loadOrder` | `[]` | Source paths pinned ahead of their group in `meta.xml`. An entry matching no file fails the build |
|
|
367
|
+
| `oop` | `false` | Enables the MTA OOP API (`player:getName()`) and writes `<oop>true</oop>` |
|
|
368
|
+
| `helpers` | `[]` | Runtime helpers to copy even when no feature requires them |
|
|
369
|
+
| `serverPath` | unset | MTA server root, for `ensure` |
|
|
370
|
+
| `resourcesDir` | `"mods/deathmatch/resources"` | Resource directory relative to `serverPath` |
|
|
371
|
+
| `transport` | `{ "kind": "none" }` | How `ensure` restarts the resource |
|
|
372
|
+
| `development.logs` | disabled, safe limits | Client relay message length and rate limits used by `dev` |
|
|
373
|
+
|
|
374
|
+
Paths must stay inside their base directory — an absolute path or a `..` segment
|
|
375
|
+
is rejected.
|
|
376
|
+
|
|
377
|
+
Every field, the transport in detail, `.env` handling and declaration files:
|
|
378
|
+
**[`packages/cli/README.md`](packages/cli/README.md)**.
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## The language
|
|
383
|
+
|
|
384
|
+
```lua
|
|
385
|
+
local name: string = 'Thigas'
|
|
386
|
+
local player: Player? = nil # optional
|
|
387
|
+
local id: string | number = 1 # union
|
|
388
|
+
local names: string[] = {} # array
|
|
389
|
+
|
|
390
|
+
type PlayerId = number # alias
|
|
391
|
+
|
|
392
|
+
enum GameState { LOBBY, PLAYING } # GameState.LOBBY is 0
|
|
393
|
+
|
|
394
|
+
interface Command { # compile-only, never emitted
|
|
395
|
+
name: string
|
|
396
|
+
execute(player: Player): void
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
class VIPPlayer extends Player implements Command {
|
|
400
|
+
level: number = 1
|
|
401
|
+
|
|
402
|
+
constructor(name: string, level: number) {
|
|
403
|
+
self:super(name)
|
|
404
|
+
self.level = level
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
local vip = new VIPPlayer('Thigas', 2)
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
| Feature | Notes |
|
|
412
|
+
| --- | --- |
|
|
413
|
+
| Type annotations | Optionals, unions, arrays, aliases, generics, `fun(string): void` — all erased |
|
|
414
|
+
| Classes | `extends`, `implements`, `constructor`, `self:super(...)`, `new`, checked statically |
|
|
415
|
+
| Decorators | `@Getter` and `@Setter` generate typed Java-style accessors on fields or whole classes |
|
|
416
|
+
| Interfaces | Verified by the checker, never reach the generated Lua |
|
|
417
|
+
| Enums | Zero-based, checked members, erased when unused |
|
|
418
|
+
| Template strings | `` `Hi ${name:Guest}` `` — scope-checked, with defaults |
|
|
419
|
+
| Compound assignment | `+=`, `-=`, `*=`, `/=`, `..=` |
|
|
420
|
+
| Increment | `score++` and `score--` as statements, compiled to `score = score + 1` |
|
|
421
|
+
| Comments | `# line` and `#* block *#`; write length without a space as `#items` |
|
|
422
|
+
| Object extensions | `items.count` → `table.size(items)`, `name.trim`, `ratio.clamp(a, b)` |
|
|
423
|
+
| Multi-return | `local x, y, z = getElementPosition(el)` — typed from the MTA catalog |
|
|
424
|
+
| `export` | Erased from the Lua, written into `meta.xml` as `<export function="f" />` |
|
|
425
|
+
| Native libraries | `sleep` plus the `Threads`, `Async` and `Dotenv` classes, injected only when named |
|
|
426
|
+
| Native classes | `local tasks = new Async(100)` — same `new` as a project class |
|
|
427
|
+
| Deployment values | `.env` keys typed and reachable as `env.SERVER_NAME`, server-only |
|
|
428
|
+
| Strictness | `#!strict` (default), `#!nonstrict`, `#!nocheck` per file |
|
|
429
|
+
|
|
430
|
+
`class`, `interface`, `enum`, `new`, `fun` and `export` are contextual keywords —
|
|
431
|
+
existing Lua that uses them as identifiers keeps compiling. Reach for
|
|
432
|
+
`#!nocheck` when porting existing Lua: rename to `.luam` and the build passes
|
|
433
|
+
while you annotate module by module.
|
|
434
|
+
|
|
435
|
+
### Environments
|
|
436
|
+
|
|
437
|
+
Every file is `server`, `client` or `shared` — from its folder, or from a `#!`
|
|
438
|
+
directive. That decides which MTA APIs resolve.
|
|
439
|
+
|
|
440
|
+
```lua
|
|
441
|
+
#!client
|
|
442
|
+
|
|
443
|
+
dxDrawText('hud', 10, 10) # ok
|
|
444
|
+
outputChatBox('hi', player) # error: server API in a client file
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
`server` and `client` files may use `shared` declarations; `shared` may use only
|
|
448
|
+
`shared`; `server` and `client` never see each other. Events are scoped the same
|
|
449
|
+
way. A name the catalog does not know stays `any`, so a missing API never blocks
|
|
450
|
+
a build.
|
|
451
|
+
|
|
452
|
+
The catalog ships **1294 MTA declarations**, **203 events**, **57 element types**
|
|
453
|
+
and the Lua standard library, generated from the MTA wiki — plus the OOP surface
|
|
454
|
+
(**57 classes, 652 methods**) behind `"oop": true`.
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## What you get out
|
|
459
|
+
|
|
460
|
+
```
|
|
461
|
+
build/my-resource/
|
|
462
|
+
├── meta.xml generated: scripts typed by environment, helpers first
|
|
463
|
+
├── config.lua copied verbatim, yours to edit
|
|
464
|
+
├── .env written once, never overwritten
|
|
465
|
+
├── lib/shared/class.lua
|
|
466
|
+
└── src/
|
|
467
|
+
├── shared/config.lua
|
|
468
|
+
├── server/main.lua
|
|
469
|
+
└── client/hud.lua
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
The output mirrors the tree you authored, changing only the extension, so a path
|
|
473
|
+
in an MTA error maps straight back to a source file. Runtime helpers land in
|
|
474
|
+
`lib/<environment>` and are copied **only when the generated code uses the
|
|
475
|
+
feature** — a resource with no classes never carries `class.lua`.
|
|
476
|
+
|
|
477
|
+
`meta.xml` uses one wildcard per environment, so adding a module leaves it byte
|
|
478
|
+
identical. `min_mta_version` is resolved from the latest MTA release and cached;
|
|
479
|
+
with no network and no cache it is omitted with a warning, and the build still
|
|
480
|
+
succeeds.
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## Editor support
|
|
485
|
+
|
|
486
|
+
The **Luam** extension for VS Code starts a language server built on the same
|
|
487
|
+
frontend the CLI uses, so the editor and the build never disagree about a file.
|
|
488
|
+
|
|
489
|
+
| You get | Details |
|
|
490
|
+
| --- | --- |
|
|
491
|
+
| Syntax highlighting | `.luam` files, including type annotations and template strings |
|
|
492
|
+
| Diagnostics | On open and on every keystroke, cleared when you fix the file |
|
|
493
|
+
| Completion | Scope symbols, workspace globals, MTA APIs scoped to the file's environment, keywords |
|
|
494
|
+
| Member completion | `.` and `:` complete class fields and methods (inherited too), enum members, and native extensions |
|
|
495
|
+
| Hover | Declared or inferred type, function signature, and the environment of an MTA API |
|
|
496
|
+
| Navigation | Go to definition, find references, rename — across files for globals |
|
|
497
|
+
|
|
498
|
+
Completion is scoped exactly like the checker: `dxDrawText` never appears in a
|
|
499
|
+
server file, `kickPlayer` never appears in a client file.
|
|
500
|
+
|
|
501
|
+
### Installing it
|
|
502
|
+
|
|
503
|
+
The extension is not on the Marketplace yet, so install the `.vsix` by hand.
|
|
504
|
+
|
|
505
|
+
**From a release (easiest).** Download `luam-<version>.vsix` from the
|
|
506
|
+
[Releases page](https://github.com/ThigasDevelopment/luam/releases), then:
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
code --install-extension luam-0.1.1.vsix
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
Or in VS Code: **Extensions** → **⋯** menu → **Install from VSIX…** → pick the
|
|
513
|
+
file. Reload the window when it asks.
|
|
514
|
+
|
|
515
|
+
**From source.** Build the VSIX yourself:
|
|
516
|
+
|
|
517
|
+
```bash
|
|
518
|
+
git clone https://github.com/ThigasDevelopment/luam.git
|
|
519
|
+
cd luam
|
|
520
|
+
pnpm install
|
|
521
|
+
pnpm --filter luam bundle
|
|
522
|
+
npx --yes @vscode/vsce package --no-dependencies --skip-license --out luam.vsix
|
|
523
|
+
code --install-extension luam.vsix
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
**To hack on the extension**, skip packaging and launch a development host
|
|
527
|
+
instead — it reloads on rebuild:
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
pnpm --filter luam bundle
|
|
531
|
+
code --extensionDevelopmentPath=packages/vscode
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
The extension activates when the workspace holds a `luam.json` or any `.luam`
|
|
535
|
+
file, so open your resource folder as the workspace root.
|
|
536
|
+
|
|
537
|
+
### Commands and settings
|
|
538
|
+
|
|
539
|
+
| Command | Shortcut | What it does |
|
|
540
|
+
| --- | --- | --- |
|
|
541
|
+
| **Luam: Ensure Resource** | `Ctrl+Alt+E` (`Cmd+Alt+E`) | Runs `luam ensure` in a terminal for the current project |
|
|
542
|
+
| **Luam: Restart Language Server** | — | Restarts the server when it gets confused |
|
|
543
|
+
|
|
544
|
+
| Setting | Default | Meaning |
|
|
545
|
+
| --- | --- | --- |
|
|
546
|
+
| `luam.cliPath` | `"luam"` | Command used to run the CLI. Point it at a bundle to test an unreleased build |
|
|
547
|
+
| `luam.ensureWatch` | `true` | Pass `--watch` when the ensure command runs |
|
|
548
|
+
| `luam.trace.server` | `"off"` | Trace the LSP traffic. Set to `"verbose"` when reporting a bug |
|
|
549
|
+
|
|
550
|
+
### Other editors
|
|
551
|
+
|
|
552
|
+
The language server is editor-agnostic. Bundle it and launch it with `--stdio`
|
|
553
|
+
from any LSP client:
|
|
554
|
+
|
|
555
|
+
```bash
|
|
556
|
+
pnpm --filter @luam/lsp bundle # emits packages/lsp/dist/luam-lsp.mjs
|
|
557
|
+
node packages/lsp/dist/luam-lsp.mjs --stdio
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## Known limitations
|
|
563
|
+
|
|
564
|
+
- **No type narrowing.** `if value ~= nil then` does not refine `string?`.
|
|
565
|
+
- **Declaration order matters for classes within a file.** `extends` and `new`
|
|
566
|
+
resolve against classes declared earlier in the same file.
|
|
567
|
+
- **The MTA catalog can lag a release.** It comes from a pinned snapshot; a newer
|
|
568
|
+
function stays `any` rather than erroring.
|
|
569
|
+
- **No static members, declared metamethods, or generic classes.**
|
|
570
|
+
- **The editor does not re-check an open file when another one changes.**
|
|
571
|
+
Cross-module violations surface in `luam check`.
|
|
572
|
+
- **An export is named, never verified**, and cannot carry an attribute such as
|
|
573
|
+
`http="true"`.
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## Contributing
|
|
578
|
+
|
|
579
|
+
The repo is a pnpm workspace: `compiler`, `cli`, `lsp`, `vscode`, `runtime`,
|
|
580
|
+
`mta-types` and `template`.
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
pnpm install
|
|
584
|
+
pnpm typecheck # strict TypeScript across every package
|
|
585
|
+
pnpm test # 769 tests
|
|
586
|
+
pnpm build
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Branch from `develop`, add a fixture and a snapshot for new language behaviour,
|
|
590
|
+
and run `pnpm typecheck && pnpm test` before opening a pull request. The house
|
|
591
|
+
style is TypeScript only, strict, no `any`, no comments inside code, 4-space
|
|
592
|
+
indentation, single quotes, kebab-case file names, path aliases instead of `../`
|
|
593
|
+
imports, no barrel files, and everything written in English.
|
|
594
|
+
|
|
595
|
+
Each package documents itself: [`cli`](packages/cli/README.md),
|
|
596
|
+
[`lsp`](packages/lsp/README.md), [`mta-types`](packages/mta-types/README.md),
|
|
597
|
+
[`vscode`](packages/vscode/README.md) and
|
|
598
|
+
[`template`](packages/template/README.md). Released milestones and their changes
|
|
599
|
+
are in the [changelog](CHANGELOG.md).
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## Acknowledgments
|
|
604
|
+
|
|
605
|
+
[Multi Theft Auto](https://multitheftauto.com/) — the execution platform.
|
|
606
|
+
[Luau](https://luau-lang.org/) — the annotation syntax that keeps Lua looking
|
|
607
|
+
like Lua. **lua-class** and **mta-threads** — the runtimes behind `class.lua` and
|
|
608
|
+
`threads.lua`.
|
|
609
|
+
|
|
610
|
+
## License
|
|
611
|
+
|
|
612
|
+
[MIT](LICENSE) © Thigas
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
<p align="center"><sub>Write typed Lua. Ship plain Lua 5.1.</sub></p>
|