@amxts/core 0.1.0 → 0.2.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 +21 -25
- package/README.ru.md +22 -24
- package/as/amxts.d.ts +63 -3
- package/as/constants.ts +684 -116
- package/as/entities.ts +1905 -1364
- package/as/events.ts +1696 -907
- package/as/facade.ts +1240 -220
- package/as/fetch.ts +1519 -0
- package/as/flags.ts +159 -145
- package/as/hlds.ts +1071 -0
- package/as/hooks.ts +5226 -5379
- package/as/kit.ts +15 -5
- package/as/lib/check.ts +1 -1
- package/as/natives.ts +2658 -2499
- package/as/remote.ts +39 -15
- package/as/tsconfig.json +7 -2
- package/as/vector.ts +12 -9
- package/includes/order.txt +22 -29
- package/includes/sources.json +0 -8
- package/node_modules/assemblyscript/std/assembly/index.d.ts +20 -11
- package/package.json +29 -7
- package/runtime/deps/assemblyscript/dist/asc.js +86 -16
- package/runtime/deps/assemblyscript/dist/assemblyscript.js +29 -29
- package/runtime/licenses/MPL-2.0.txt +373 -0
- package/runtime/licenses/README.md +5 -1
- package/runtime/licenses/curl-COPYING +22 -0
- package/runtime/licenses/libssh2-COPYING +43 -0
- package/runtime/licenses/mbedTLS-LICENSE +553 -0
- package/runtime/natives.txt +108 -105
- package/runtime/patches/assemblyscript-0.28.20-amxts.patch +752 -296
- package/runtime/patches/wamr-2.4.5-amxts.patch +148 -0
- package/scripts/apply-docs.ts +2 -2
- package/scripts/auto-imports.ts +2 -2
- package/scripts/build-identity.ts +115 -0
- package/scripts/build-wasm.ts +101 -50
- package/scripts/check.ts +17 -3
- package/scripts/client-messages.ts +246 -0
- package/scripts/compile-cache.ts +4 -2
- package/scripts/compile-pool.ts +128 -0
- package/scripts/compile-worker.ts +23 -0
- package/scripts/compile.ts +156 -59
- package/scripts/docker-server.ts +13 -3
- package/scripts/docs/as/amxts.ts +86 -0
- package/scripts/docs/as/facade.ts +688 -188
- package/scripts/docs/as/fetch.ts +805 -0
- package/scripts/docs/entities.ts +43 -144
- package/scripts/docs/events.ts +17 -28
- package/scripts/docs/game.ts +68 -22
- package/scripts/docs/messages.ts +330 -16
- package/scripts/editor-api.ts +101 -0
- package/scripts/hlds-events.ts +370 -0
- package/scripts/includes.ts +12 -10
- package/scripts/player-fields.ts +16 -15
- package/scripts/prebuilt.ts +65 -23
- package/scripts/prepare.ts +42 -28
- package/scripts/project.ts +120 -20
- package/scripts/reapi-events.ts +98 -0
- package/scripts/shared-modules.ts +85 -23
- package/scripts/source-map.ts +242 -0
- package/scripts/system.ts +1 -1
- package/scripts/typed-commands.ts +263 -0
- package/scripts/typed-configs.ts +23 -12
- package/scripts/ui.ts +3 -1
- package/scripts/upgrade-menus.ts +274 -0
- package/scripts/upgrade-names.ts +384 -0
- package/scripts/upgrade.ts +857 -0
- package/src/cli-api.mjs +37 -4
- package/src/system.mjs +74 -3
- package/src/testing/compile.ts +8 -5
- package/src/testing/index.ts +2 -1
- package/src/testing/natives.ts +199 -5
- package/src/testing/network.ts +531 -0
- package/src/testing/server.ts +572 -48
- package/src/testing/setup.ts +1 -1
- package/src/testing/tables.ts +25 -0
- package/as/modules/http.ts +0 -184
- package/scripts/docs/as/modules/http.ts +0 -66
package/scripts/compile.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { PluginNative } from './plugin-natives';
|
|
2
|
+
import type { Sources } from './project';
|
|
3
|
+
import type { PluginMap } from './source-map';
|
|
2
4
|
import type { System } from './system';
|
|
3
5
|
import { spawnSync } from 'node:child_process';
|
|
4
6
|
|
|
@@ -17,10 +19,13 @@ import * as assemblyscript from '../runtime/deps/assemblyscript/dist/assemblyscr
|
|
|
17
19
|
// @ts-ignore - its types are beside it, under a path tsconfig does not map
|
|
18
20
|
import binaryen from '../runtime/deps/assemblyscript/node_modules/binaryen/index.js';
|
|
19
21
|
import { ascMain } from './asc';
|
|
22
|
+
import { ABI_SECTION, abiIdentity } from './build-identity';
|
|
20
23
|
import { playerFieldsBuild } from './player-fields';
|
|
21
24
|
import { includeName, nativeContract, nativesTransform, pawnInclude } from './plugin-natives';
|
|
22
25
|
import { ascPath, sourcesFor } from './project';
|
|
26
|
+
import { targetTransforms } from './reapi-events';
|
|
23
27
|
import { sharedModulesBuild } from './shared-modules';
|
|
28
|
+
import { displayName, MAP_SECTION, mapText, pluginMap, withSection } from './source-map';
|
|
24
29
|
import { HOST_SYSTEM, TARGET_ABI } from './system';
|
|
25
30
|
|
|
26
31
|
export interface Plugin {
|
|
@@ -96,6 +101,35 @@ export class HoistImports {
|
|
|
96
101
|
/** The import a coroutine parks in - the one Asyncify unwinds from. */
|
|
97
102
|
export const SUSPEND_IMPORT = 'env.co_suspend';
|
|
98
103
|
|
|
104
|
+
/** The import of a plugin that makes a promise: it is compiled again with ASYNC_EXPORTS. */
|
|
105
|
+
const WAKE_IMPORT = 'env.co_wake';
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* What wamrc keeps for a failed call's stack (scripts/source-map.ts): each
|
|
109
|
+
* function's frame with its index and the offset of the call it is in, or of
|
|
110
|
+
* the trap - a store at each call, a push and a pop at each function. No
|
|
111
|
+
* values: with them WAMR's frames are no longer the small ones. A dev build's
|
|
112
|
+
* alone (`amxts dev`, a quick build): measured, the frames took a fifth to a
|
|
113
|
+
* third of a call-heavy plugin's time, so a full build keeps none, and an
|
|
114
|
+
* error there says its message - and a throw or an abort its place.
|
|
115
|
+
*/
|
|
116
|
+
const STACK_FLAGS = ['--enable-dump-call-stack', '--call-stack-features=bounds-checks,ip,func-idx,trap-ip'];
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The map goes into the .aot either way, and so does the ABI the plugin is
|
|
120
|
+
* compiled against, which the module checks before it loads it
|
|
121
|
+
* (scripts/build-identity.ts).
|
|
122
|
+
*/
|
|
123
|
+
const SECTIONS = `--emit-custom-sections=${MAP_SECTION},${ABI_SECTION}`;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The CPU the machine code is for: a Pentium 4's, SSE2. LLVM's i386 left
|
|
127
|
+
* alone does fractions on the x87, where a number made whole - every `%`,
|
|
128
|
+
* every number given where a cell goes - costs several times as much. Every
|
|
129
|
+
* CPU a game server runs on has SSE2.
|
|
130
|
+
*/
|
|
131
|
+
const CPU = '--cpu=pentium4';
|
|
132
|
+
|
|
99
133
|
/**
|
|
100
134
|
* Where compilePlugin writes the Pawn include for a plugin's natives: beside
|
|
101
135
|
* the .aot, named after the plugin - or, for a plugin that implements an
|
|
@@ -134,6 +168,9 @@ export function compileToMachineCode(plugin: Plugin, wasm: string, natives: Plug
|
|
|
134
168
|
'--target=i386',
|
|
135
169
|
`--target-abi=${TARGET_ABI[plugin.system ?? HOST_SYSTEM]}`,
|
|
136
170
|
`--native-signatures=${plugin.signatures}`,
|
|
171
|
+
...(plugin.quick ? STACK_FLAGS : []),
|
|
172
|
+
SECTIONS,
|
|
173
|
+
CPU,
|
|
137
174
|
...level,
|
|
138
175
|
'-o',
|
|
139
176
|
part,
|
|
@@ -150,12 +187,13 @@ export function compileToMachineCode(plugin: Plugin, wasm: string, natives: Plug
|
|
|
150
187
|
|
|
151
188
|
if (compile.status !== 0) {
|
|
152
189
|
rmSync(part, { force: true });
|
|
153
|
-
|
|
190
|
+
// A crash says nothing: still a failure, not an empty problem
|
|
191
|
+
return out.trim() || `wamrc stopped (${compile.status ?? compile.signal}) without a word`;
|
|
154
192
|
}
|
|
155
193
|
|
|
156
194
|
// A signature that disagrees with its import is not an error to wamrc: it
|
|
157
|
-
// drops that native to the generic path,
|
|
158
|
-
// says so only in this warning. That means the API and the module have
|
|
195
|
+
// drops that native to the generic path, many times slower than the direct
|
|
196
|
+
// call, and says so only in this warning. That means the API and the module have
|
|
159
197
|
// drifted apart, which is worth refusing rather than running slowly.
|
|
160
198
|
if (/failed to check signature/.test(out)) {
|
|
161
199
|
rmSync(part, { force: true });
|
|
@@ -182,9 +220,10 @@ export function writeInclude(output: string, natives: PluginNative[]): void {
|
|
|
182
220
|
|
|
183
221
|
/**
|
|
184
222
|
* The first half of compilePlugin: the plugin's .ts to `wasm`, ready for
|
|
185
|
-
* wamrc. Tests run the result as it is;
|
|
186
|
-
* names, so a test can see what Asyncify
|
|
187
|
-
* plugin's `export function`s become
|
|
223
|
+
* wamrc, with its map (scripts/source-map.ts). Tests run the result as it is;
|
|
224
|
+
* with `names` it keeps its function names, so a test can see what Asyncify
|
|
225
|
+
* instrumented. With `natives`, the plugin's `export function`s become
|
|
226
|
+
* natives, listed there.
|
|
188
227
|
*/
|
|
189
228
|
export async function compileToWasm(
|
|
190
229
|
plugin: Pick<Plugin, 'source' | 'root' | 'quick'>,
|
|
@@ -193,53 +232,93 @@ export async function compileToWasm(
|
|
|
193
232
|
natives?: PluginNative[],
|
|
194
233
|
): Promise<string | null> {
|
|
195
234
|
// A quick build leaves the optimising to one Binaryen pass afterwards.
|
|
196
|
-
const flags = [...(plugin.quick ? [] : ['--optimize']), ...(names ? ['--debug'] : [])];
|
|
197
|
-
const level = plugin.quick ? OPTIMIZE.quick :
|
|
198
|
-
let
|
|
199
|
-
if (problem) return problem;
|
|
235
|
+
const flags = [...(plugin.quick ? [] : ['--optimize']), ...(names ? ['--debug'] : []), '--sourceMap'];
|
|
236
|
+
const level = plugin.quick ? OPTIMIZE.quick : null;
|
|
237
|
+
let made = await compileWasm(plugin, BASE_EXPORTS, flags, level, natives);
|
|
200
238
|
|
|
201
239
|
// A plugin that makes a promise - an async function, fetch, sleep - has
|
|
202
240
|
// the host drive its jobs, so it is compiled again with the scheduler's
|
|
203
|
-
// exports.
|
|
204
|
-
|
|
205
|
-
if (importsOf(readFileSync(wasm)).has('env.co_wake')) {
|
|
241
|
+
// exports.
|
|
242
|
+
if (typeof made !== 'string' && importsOf(made.binary).has(WAKE_IMPORT)) {
|
|
206
243
|
if (natives) natives.length = 0;
|
|
207
|
-
|
|
208
|
-
if (problem) return problem;
|
|
244
|
+
made = await compileWasm(plugin, ASYNC_EXPORTS, flags, level, natives);
|
|
209
245
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
246
|
+
if (typeof made === 'string') return made;
|
|
247
|
+
mkdirSync(dirname(resolve(wasm)), { recursive: true });
|
|
248
|
+
writeFileSync(wasm, withSection(withSection(made.binary, MAP_SECTION, mapText(made.map)), ABI_SECTION, abiIdentity()));
|
|
213
249
|
return null;
|
|
214
250
|
}
|
|
215
251
|
|
|
252
|
+
/**
|
|
253
|
+
* The last transform of a compile: Binaryen's work after asc's own, on the
|
|
254
|
+
* module asc is about to write - a quick build's optimisation at `level`,
|
|
255
|
+
* and Asyncify for a plugin whose coroutines can park. It runs there rather
|
|
256
|
+
* than on the written binary so that the source map asc writes beside it is
|
|
257
|
+
* of the code that runs: Binaryen reads a binary without its map. asc has no
|
|
258
|
+
* hook after its optimiser, so this wraps the module's emitBinary, which asc
|
|
259
|
+
* calls once to write the module. `done` gets the functions' names, by wasm
|
|
260
|
+
* index, as they are written.
|
|
261
|
+
*
|
|
262
|
+
* A plugin that makes a promise but was compiled without the scheduler's
|
|
263
|
+
* exports is compiled again (compileToWasm), so nothing is done for it.
|
|
264
|
+
*/
|
|
265
|
+
export function finishing(hoodExports: string, level: number | null, done: (first: number, names: string[]) => void = () => {}) {
|
|
266
|
+
return {
|
|
267
|
+
afterCompile(module: any) {
|
|
268
|
+
const emit = module.emitBinary.bind(module);
|
|
269
|
+
module.emitBinary = (url?: string) => {
|
|
270
|
+
const imports = new Set(functionsOf(module).map(each => each.imported));
|
|
271
|
+
if (hoodExports === ASYNC_EXPORTS || !imports.has(WAKE_IMPORT)) {
|
|
272
|
+
if (imports.has(SUSPEND_IMPORT)) asyncify(module, level ?? OPTIMIZE.full);
|
|
273
|
+
else if (level !== null) optimize(module, level);
|
|
274
|
+
}
|
|
275
|
+
const functions = functionsOf(module);
|
|
276
|
+
const first = functions.filter(each => each.imported).length;
|
|
277
|
+
done(first, functions.slice(first).map(each => each.name));
|
|
278
|
+
return emit(url);
|
|
279
|
+
};
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* The module's functions in the order of their wasm indexes - Binaryen
|
|
286
|
+
* writes the imported ones first, then the rest, each in its own order -
|
|
287
|
+
* with each one's name, and `module.name` when it is imported.
|
|
288
|
+
*/
|
|
289
|
+
function functionsOf(module: any): { name: string; imported: string | null }[] {
|
|
290
|
+
const functions = Array.from({ length: module.getNumFunctions() }, (_, i) => binaryen.getFunctionInfo(module.getFunctionByIndex(i)));
|
|
291
|
+
const ordered = [...functions.filter(info => info.module), ...functions.filter(info => !info.module)];
|
|
292
|
+
return ordered.map(info => ({ name: info.name, imported: info.module ? `${info.module}.${info.base}` : null }));
|
|
293
|
+
}
|
|
294
|
+
|
|
216
295
|
/** Binaryen's optimisation at `level`: what a quick build runs in place of asc's. */
|
|
217
|
-
function
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
296
|
+
function optimize(module: any, level: number) {
|
|
297
|
+
binaryen.setOptimizeLevel(level);
|
|
298
|
+
binaryen.setShrinkLevel(0);
|
|
299
|
+
module.optimize();
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** What asc made of a plugin: the binary, and its map. */
|
|
303
|
+
interface Compiled {
|
|
304
|
+
binary: Uint8Array;
|
|
305
|
+
map: PluginMap;
|
|
228
306
|
}
|
|
229
307
|
|
|
230
308
|
/**
|
|
231
|
-
* asc, from the plugin's .ts to
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
309
|
+
* asc, from the plugin's .ts to a binary and its map, with `hoodExports` as
|
|
310
|
+
* the second entry and `level` a quick build's (finishing). With `natives`,
|
|
311
|
+
* the plugin's `export function`s become its natives and are listed there;
|
|
312
|
+
* without, they stay plain wasm exports - what a test host that calls a
|
|
313
|
+
* fixture's functions by name wants. What went wrong, as text.
|
|
235
314
|
*/
|
|
236
315
|
async function compileWasm(
|
|
237
316
|
plugin: Pick<Plugin, 'source' | 'root'>,
|
|
238
|
-
wasm: string,
|
|
239
317
|
hoodExports: string,
|
|
240
318
|
flags: string[],
|
|
319
|
+
level: number | null,
|
|
241
320
|
natives?: PluginNative[],
|
|
242
|
-
): Promise<
|
|
321
|
+
): Promise<Compiled | string> {
|
|
243
322
|
// --exportTable is not optional: a handler reaches the module as its index
|
|
244
323
|
// in the function table, and without this asc emits no table at all, so
|
|
245
324
|
// every index points at nothing.
|
|
@@ -260,6 +339,9 @@ async function compileWasm(
|
|
|
260
339
|
return String((problem as Error).message ?? problem);
|
|
261
340
|
}
|
|
262
341
|
|
|
342
|
+
let binary: Uint8Array | null = null;
|
|
343
|
+
let sourceMap = '';
|
|
344
|
+
let functions = { first: 0, names: [] as string[] };
|
|
263
345
|
const { error, stderr } = await ascMain(
|
|
264
346
|
[
|
|
265
347
|
entry,
|
|
@@ -268,7 +350,7 @@ async function compileWasm(
|
|
|
268
350
|
// entry file's exports reach the wasm).
|
|
269
351
|
HOOD_EXPORTS,
|
|
270
352
|
'--outFile',
|
|
271
|
-
wasm,
|
|
353
|
+
'plugin.wasm',
|
|
272
354
|
'--exportTable',
|
|
273
355
|
...flags,
|
|
274
356
|
],
|
|
@@ -282,21 +364,25 @@ async function compileWasm(
|
|
|
282
364
|
return text === null ? null : shared.read(path, playerFields.read(relative('.', (sources.real(path) ?? path)), text));
|
|
283
365
|
},
|
|
284
366
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
290
|
-
writeFileSync(path, contents);
|
|
367
|
+
// The binary and its source map, for compileToWasm to write as one.
|
|
368
|
+
writeFile(_: string, contents: string | Uint8Array): void {
|
|
369
|
+
if (typeof contents === 'string') sourceMap = contents;
|
|
370
|
+
else binary = contents;
|
|
291
371
|
},
|
|
292
372
|
|
|
293
373
|
listFiles(): string[] {
|
|
294
374
|
return [];
|
|
295
375
|
},
|
|
296
376
|
|
|
297
|
-
transforms:
|
|
298
|
-
|
|
299
|
-
|
|
377
|
+
transforms: [
|
|
378
|
+
HoistImports,
|
|
379
|
+
...(natives ? [nativesTransform(entry, natives, plugin.root)] : []),
|
|
380
|
+
playerFields.transform,
|
|
381
|
+
shared.transform,
|
|
382
|
+
// A project for plain HLDS listens for no event reapi alone delivers.
|
|
383
|
+
...targetTransforms(sources.project.config?.target),
|
|
384
|
+
finishing(hoodExports, level, (first, names) => (functions = { first, names })),
|
|
385
|
+
],
|
|
300
386
|
},
|
|
301
387
|
);
|
|
302
388
|
|
|
@@ -306,7 +392,26 @@ async function compileWasm(
|
|
|
306
392
|
|
|
307
393
|
// A transform's refusal - a native whose signature cannot cross - is an
|
|
308
394
|
// error with nothing written to stderr.
|
|
309
|
-
|
|
395
|
+
if (error || !binary) return stderr.toString() || String(error?.message ?? error);
|
|
396
|
+
// A quick build is a dev build (`amxts dev`): its map names the project's
|
|
397
|
+
// folder, where the module finds the sources to show a failed line. A
|
|
398
|
+
// build to ship names none - the machine's folders are not the server's.
|
|
399
|
+
const root = level === null ? '' : sources.project.dir;
|
|
400
|
+
return { binary, map: pluginMap(sourceMap, file => fileName(sources, plugin.root, file), functions.first, functions.names.map(displayName), root) };
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* A file of asc's source map as the plugin's map names it: as the project
|
|
405
|
+
* names it - `plugins/shop.ts`, `node_modules/@amxts/core/as/facade.ts` - or
|
|
406
|
+
* null for AssemblyScript's standard library, whose lines would double the
|
|
407
|
+
* map and mean nothing to an author; its functions keep their names.
|
|
408
|
+
*/
|
|
409
|
+
function fileName(sources: Sources, root: string, file: string): string | null {
|
|
410
|
+
// asc reads `~/` - the plugins folder - as a library of its own
|
|
411
|
+
const hood = /^~lib\/~\/(.*)$/.exec(file);
|
|
412
|
+
if (!hood && file.startsWith('~lib/')) return null;
|
|
413
|
+
const path = hood ? resolve(root, hood[1]) : ascPath(root, file, '');
|
|
414
|
+
return relative(sources.project.dir, sources.real(path) ?? path).replace(/\\/g, '/');
|
|
310
415
|
}
|
|
311
416
|
|
|
312
417
|
/**
|
|
@@ -319,28 +424,20 @@ async function compileWasm(
|
|
|
319
424
|
* prologue asks co_spawn to call it - so no `await` is ever reached through
|
|
320
425
|
* a function table, and a listener that is async returns to its caller at its
|
|
321
426
|
* first `await` like any other call. Nothing can be missing from the list,
|
|
322
|
-
* which is the failure that hangs rather than traps.
|
|
427
|
+
* which is the failure that hangs rather than traps. Then the optimiser at
|
|
428
|
+
* `level` - asc's own, or a quick build's lower one - so the rest of the
|
|
429
|
+
* module comes out as it went in.
|
|
323
430
|
*/
|
|
324
|
-
|
|
325
|
-
const module = binaryen.readBinary(wasm);
|
|
431
|
+
function asyncify(module: any, level: number) {
|
|
326
432
|
try {
|
|
327
|
-
module.setFeatures(binaryen.Features.All);
|
|
328
433
|
binaryen.setPassArgument('asyncify-imports', SUSPEND_IMPORT);
|
|
329
434
|
binaryen.setPassArgument('asyncify-ignore-indirect', '1');
|
|
330
|
-
// asc's own --optimize levels (a quick build's lower one), so the rest
|
|
331
|
-
// of the module comes out as it went in.
|
|
332
|
-
binaryen.setOptimizeLevel(level);
|
|
333
|
-
binaryen.setShrinkLevel(0);
|
|
334
|
-
binaryen.setDebugInfo(names);
|
|
335
435
|
module.runPasses(['asyncify']);
|
|
336
|
-
|
|
436
|
+
optimize(module, level);
|
|
337
437
|
if (!module.validate()) throw new Error('Asyncify produced an invalid module');
|
|
338
|
-
return module.emitBinary();
|
|
339
438
|
} finally {
|
|
340
439
|
binaryen.setPassArgument('asyncify-imports', null);
|
|
341
440
|
binaryen.setPassArgument('asyncify-ignore-indirect', null);
|
|
342
|
-
binaryen.setDebugInfo(false);
|
|
343
|
-
module.dispose();
|
|
344
441
|
}
|
|
345
442
|
}
|
|
346
443
|
|
package/scripts/docker-server.ts
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
|
-
// The Docker server a project runs on (docker/server,
|
|
2
|
-
// which running containers mount the project, and
|
|
1
|
+
// The Docker server a project runs on (docker/server, published as
|
|
2
|
+
// ghcr.io/amxts/server): which running containers mount the project, and
|
|
3
|
+
// their console.
|
|
3
4
|
//
|
|
4
5
|
// `amxts dev --docker` builds into dist/, which such a container reads where
|
|
5
6
|
// it is - its module reloads a plugin whose .aot changed - so nothing is
|
|
6
7
|
// deployed. What is left is showing the developer the server's answer, and
|
|
7
8
|
// that is the container's console: its amxts lines, as they come.
|
|
8
9
|
import { spawn, spawnSync } from 'node:child_process';
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
9
11
|
import { resolve } from 'node:path';
|
|
10
12
|
import process from 'node:process';
|
|
13
|
+
import { serverImage } from './system';
|
|
11
14
|
import { c } from './ui';
|
|
12
15
|
|
|
16
|
+
/**
|
|
17
|
+
* The server image of this core's version. Its module loads only the .aot of
|
|
18
|
+
* the same release's wamrc, so a project runs on the image of its own core;
|
|
19
|
+
* `bun run server:image` builds it from a checkout under the same name.
|
|
20
|
+
*/
|
|
21
|
+
export const SERVER_IMAGE = serverImage(JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version);
|
|
22
|
+
|
|
13
23
|
/** Where the image takes a project from. */
|
|
14
24
|
const PROJECT_MOUNT = '/project';
|
|
15
25
|
|
|
@@ -69,5 +79,5 @@ export function followConsoles(names: string[]): void {
|
|
|
69
79
|
|
|
70
80
|
/** The command that starts the server for this project. */
|
|
71
81
|
export function runCommand(projectDir: string): string {
|
|
72
|
-
return `docker run --rm -it -p 27015:27015/udp -v "${resolve(projectDir)}:${PROJECT_MOUNT}"
|
|
82
|
+
return `docker run --rm -it -p 27015:27015/udp -v "${resolve(projectDir)}:${PROJECT_MOUNT}" ${SERVER_IMAGE}`;
|
|
73
83
|
}
|
package/scripts/docs/as/amxts.ts
CHANGED
|
@@ -207,4 +207,90 @@ export default {
|
|
|
207
207
|
en: `Stops calling a listener added with \`addEventListener\` - the same function and the same field.`,
|
|
208
208
|
ru: `Перестаёт вызывать обработчик, добавленный через \`addEventListener\`, — ту же функцию с тем же полем.`,
|
|
209
209
|
},
|
|
210
|
+
'Server.addCommand': {
|
|
211
|
+
en: `
|
|
212
|
+
Adds a command players type, by its usage: the name, then the
|
|
213
|
+
arguments, \`<name>\` required and \`[name]\` optional. Their types are an
|
|
214
|
+
interface, the type argument; without it each is text.
|
|
215
|
+
|
|
216
|
+
\`\`\`ts
|
|
217
|
+
interface KickArgs {
|
|
218
|
+
target: Player;
|
|
219
|
+
reason?: string;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
server.addCommand<KickArgs>("/kick <target> [reason]", ({ player, target, reason }) => {
|
|
223
|
+
target.kick(reason ?? \`Kicked by \${player.name}\`);
|
|
224
|
+
}, { access: "kick" });
|
|
225
|
+
server.addCommand("/hp", ({ player }) => print(player, \`\${player.health} HP\`));
|
|
226
|
+
\`\`\`
|
|
227
|
+
|
|
228
|
+
A name with \`/\` is a chat command, one without a console command, and
|
|
229
|
+
\`"say <phrase>"\` a phrase written in chat. A \`number\` argument is
|
|
230
|
+
parsed, a \`Player\` found by \`#userid\`, the whole name or a part of it,
|
|
231
|
+
a \`string\` taken as it is - the last one takes the rest of the line. A
|
|
232
|
+
word that is not what the command takes answers the player with the
|
|
233
|
+
usage, and the handler does not run.
|
|
234
|
+
|
|
235
|
+
Pawn: \`register_clcmd\`
|
|
236
|
+
`,
|
|
237
|
+
ru: `
|
|
238
|
+
Добавляет команду, которую набирают игроки, по её использованию: имя,
|
|
239
|
+
затем аргументы, \`<name>\` — обязательный, \`[name]\` — необязательный.
|
|
240
|
+
Их типы — интерфейс, аргумент типа; без него каждый — текст.
|
|
241
|
+
|
|
242
|
+
\`\`\`ts
|
|
243
|
+
interface KickArgs {
|
|
244
|
+
target: Player;
|
|
245
|
+
reason?: string;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
server.addCommand<KickArgs>("/kick <target> [reason]", ({ player, target, reason }) => {
|
|
249
|
+
target.kick(reason ?? \`Kicked by \${player.name}\`);
|
|
250
|
+
}, { access: "kick" });
|
|
251
|
+
server.addCommand("/hp", ({ player }) => print(player, \`\${player.health} HP\`));
|
|
252
|
+
\`\`\`
|
|
253
|
+
|
|
254
|
+
Имя со \`/\` — команда чата, без него — команда консоли, а
|
|
255
|
+
\`"say <фраза>"\` — фраза, написанная в чат. Аргумент \`number\`
|
|
256
|
+
разбирается как число, \`Player\` находится по \`#userid\`, имени целиком
|
|
257
|
+
или его части, \`string\` берётся как есть — последний забирает остаток
|
|
258
|
+
строки. На слово, которое команда не принимает, игрок получает её
|
|
259
|
+
использование, и обработчик не запускается.
|
|
260
|
+
|
|
261
|
+
Pawn: \`register_clcmd\`
|
|
262
|
+
`,
|
|
263
|
+
},
|
|
264
|
+
'Server.addServerCommand': {
|
|
265
|
+
en: `
|
|
266
|
+
Adds a command of the server console - typed there, sent over rcon or
|
|
267
|
+
run by another plugin - by its usage, its arguments read as a player's
|
|
268
|
+
command's are. No player types it.
|
|
269
|
+
|
|
270
|
+
\`\`\`ts
|
|
271
|
+
interface ResetArgs {
|
|
272
|
+
what?: "scores" | "all";
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
server.addServerCommand<ResetArgs>("myplugin_reset [what]", ({ what }) => reset(what ?? "all"));
|
|
276
|
+
\`\`\`
|
|
277
|
+
|
|
278
|
+
Pawn: \`register_srvcmd\`
|
|
279
|
+
`,
|
|
280
|
+
ru: `
|
|
281
|
+
Добавляет команду консоли сервера — её набирают там, присылают по rcon
|
|
282
|
+
или запускает другой плагин — по её использованию; аргументы читаются
|
|
283
|
+
так же, как у команды игрока. Игрок её не набирает.
|
|
284
|
+
|
|
285
|
+
\`\`\`ts
|
|
286
|
+
interface ResetArgs {
|
|
287
|
+
what?: "scores" | "all";
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
server.addServerCommand<ResetArgs>("myplugin_reset [what]", ({ what }) => reset(what ?? "all"));
|
|
291
|
+
\`\`\`
|
|
292
|
+
|
|
293
|
+
Pawn: \`register_srvcmd\`
|
|
294
|
+
`,
|
|
295
|
+
},
|
|
210
296
|
};
|