@amxts/core 0.2.3 → 0.3.0

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.
Files changed (71) hide show
  1. package/README.md +5 -5
  2. package/README.ru.md +5 -5
  3. package/as/amxts.d.ts +13 -3
  4. package/as/entities.ts +571 -501
  5. package/as/events.ts +3323 -557
  6. package/as/facade.ts +3079 -718
  7. package/as/fetch.ts +2 -2
  8. package/as/fs.ts +352 -59
  9. package/as/hlds.ts +121 -120
  10. package/as/hooks.ts +10585 -5177
  11. package/as/lib/check.ts +3 -2
  12. package/as/natives.ts +856 -1272
  13. package/as/promise.ts +37 -27
  14. package/as/vector.ts +94 -14
  15. package/includes/order.txt +17 -19
  16. package/node_modules/assemblyscript/std/assembly/index.d.ts +1 -1
  17. package/package.json +9 -6
  18. package/runtime/deps/assemblyscript/dist/asc.js +136 -42
  19. package/runtime/deps/assemblyscript/dist/assemblyscript.js +141 -141
  20. package/runtime/licenses/README.md +9 -7
  21. package/runtime/licenses/ReGameDLL-LICENSE +21 -0
  22. package/runtime/licenses/ReHLDS-LICENSE +21 -0
  23. package/runtime/licenses/zlib-LICENSE +22 -0
  24. package/runtime/natives.txt +618 -523
  25. package/runtime/patches/README.md +87 -1
  26. package/runtime/patches/assemblyscript-0.28.20-amxts.patch +4838 -416
  27. package/runtime/patches/wamr-2.4.5-amxts.patch +417 -5
  28. package/scripts/auto-imports.ts +1 -1
  29. package/scripts/build-identity.ts +102 -65
  30. package/scripts/build-wasm.ts +17 -13
  31. package/scripts/check.ts +4 -1
  32. package/scripts/client-messages.ts +73 -1
  33. package/scripts/compile-cache.ts +11 -8
  34. package/scripts/compile-pool.ts +6 -3
  35. package/scripts/compile-worker.ts +23 -23
  36. package/scripts/compile.ts +142 -19
  37. package/scripts/docs/as/amxts.ts +10 -4
  38. package/scripts/docs/as/facade.ts +1779 -275
  39. package/scripts/docs/as/fetch.ts +2 -2
  40. package/scripts/docs/as/fs.ts +190 -6
  41. package/scripts/docs/as/vector.ts +81 -0
  42. package/scripts/docs/entities.ts +12 -0
  43. package/scripts/docs/events.ts +49 -13
  44. package/scripts/docs/game.ts +6 -0
  45. package/scripts/docs/messages.ts +10 -1
  46. package/scripts/hlds-events.ts +11 -11
  47. package/scripts/includes.ts +3 -4
  48. package/scripts/leaf-natives.ts +84 -0
  49. package/scripts/player-fields.ts +1 -1
  50. package/scripts/plugin-natives.ts +1 -1
  51. package/scripts/prebuilt.ts +42 -11
  52. package/scripts/project.ts +63 -4
  53. package/scripts/reapi-events.ts +9 -9
  54. package/scripts/server-env.ts +145 -0
  55. package/scripts/shared-modules.ts +23 -7
  56. package/scripts/system.ts +1 -1
  57. package/scripts/tracked-fs.ts +11 -2
  58. package/scripts/typed-commands.ts +153 -15
  59. package/scripts/upgrade-calls.ts +225 -0
  60. package/scripts/upgrade-names.ts +1 -4
  61. package/scripts/upgrade.ts +47 -34
  62. package/src/cli-api.mjs +3 -2
  63. package/src/system.mjs +21 -11
  64. package/src/testing/compile.ts +31 -8
  65. package/src/testing/coroutines.ts +0 -10
  66. package/src/testing/memory.ts +89 -2
  67. package/src/testing/natives.ts +67 -30
  68. package/src/testing/server.ts +1094 -155
  69. package/src/testing/tables.ts +23 -6
  70. package/src/typecheck.mjs +110 -0
  71. package/runtime/licenses/Bun-LICENSE.md +0 -84
@@ -200,7 +200,7 @@ function cvarNumber(value: number): string {
200
200
  return Number.isInteger(value) ? String(value) : value.toFixed(6);
201
201
  }
202
202
 
203
- function giveWeapon(target: FakePlayer | undefined, name: string): number {
203
+ export function giveWeapon(target: FakePlayer | undefined, name: string): number {
204
204
  if (!target) return 0;
205
205
  if (name === 'item_kevlar' || name === 'item_assaultsuit') {
206
206
  target.armor = 100;
@@ -214,7 +214,15 @@ function weaponByName(target: FakePlayer | undefined, name: string) {
214
214
  return target?.items.find(w => w.kind === name);
215
215
  }
216
216
 
217
- function switchTo(target: FakePlayer | undefined, name: string): number {
217
+ /** A command run as the player's, as the game takes it: a weapon chosen, a side picked in the team menu. */
218
+ export function clientCommand(target: FakePlayer | undefined, line: string): void {
219
+ target?.commands.push(line);
220
+ if (line.startsWith('weapon_')) switchTo(target, line);
221
+ const side = { 'jointeam 1': 'TERRORIST', 'jointeam 2': 'CT', 'jointeam 6': 'SPECTATOR' }[line];
222
+ if (target && side) target.team = side as TeamName;
223
+ }
224
+
225
+ export function switchTo(target: FakePlayer | undefined, name: string): number {
218
226
  const weapon = weaponByName(target, name);
219
227
  if (!target || !weapon) return 0;
220
228
  target.set('m_pActiveItem', weapon.id);
@@ -273,17 +281,26 @@ function setHamReturn(call: NativeCall, value: Value): number {
273
281
  return 1;
274
282
  }
275
283
 
276
- // The engine's own message for a temporary effect: it is no user message, so
277
- // no name get_user_msgid handed out stands for it.
278
- const SVC_TEMPENTITY = 23;
284
+ // The engine's own messages, by number: below 64 no user message lives, so
285
+ // no name get_user_msgid handed out stands for them.
286
+ const SVC_NAMES = (
287
+ 'BAD NOP DISCONNECT EVENT VERSION SETVIEW SOUND TIME PRINT STUFFTEXT SETANGLE SERVERINFO LIGHTSTYLE UPDATEUSERINFO'
288
+ + ' DELTADESCRIPTION CLIENTDATA STOPSOUND PINGS PARTICLE DAMAGE SPAWNSTATIC EVENT_RELIABLE SPAWNBASELINE TEMPENTITY'
289
+ + ' SETPAUSE SIGNONNUM CENTERPRINT KILLEDMONSTER FOUNDSECRET SPAWNSTATICSOUND INTERMISSION FINALE CDTRACK RESTORE'
290
+ + ' CUTSCENE WEAPONANIM DECALNAME ROOMTYPE ADDANGLE NEWUSERMSG PACKETENTITIES DELTAPACKETENTITIES CHOKE RESOURCELIST'
291
+ + ' NEWMOVEVARS RESOURCEREQUEST CUSTOMIZATION CROSSHAIRANGLE SOUNDFADE FILETXFERFAILED HLTV DIRECTOR VOICEINIT'
292
+ + ' VOICEDATA SENDEXTRAINFO TIMESCALE RESOURCELOCATION SENDCVARVALUE SENDCVARVALUE2'
293
+ ).split(' ');
279
294
 
280
295
  /**
281
- * message_begin: a user message by its name, or a temporary effect - with
282
- * who it goes to (`dest`, MSG_*) and the point it is seen from.
296
+ * message_begin: a user message by its name, or the engine's own (a temporary
297
+ * effect, movevars, ...) as `SVC_<name>` - with who it goes to (`dest`,
298
+ * MSG_*) and the point it is seen from.
283
299
  */
284
300
  function beginMessage(call: NativeCall, dest: number, type: number, player: number, origin: number[]): number {
285
- if (type === SVC_TEMPENTITY) {
286
- call.server.writing = { name: 'SVC_TEMPENTITY', player, args: [], dest, origin };
301
+ const svc = SVC_NAMES[type];
302
+ if (svc) {
303
+ call.server.writing = { name: `SVC_${svc}`, player, args: [], dest, origin };
287
304
  return 1;
288
305
  }
289
306
  const name = [...call.server.messageIds].find(([, id]) => id === type)?.[0];
@@ -298,7 +315,7 @@ function sendText(call: NativeCall, id: number, variant: 'chat' | 'center' | 'co
298
315
  const PRINT_VARIANTS = ['', 'notify', 'console', 'chat', 'center'] as const;
299
316
 
300
317
  // Reunion's client_auth_type by its number, as the player's API names each.
301
- const AUTH_TYPES = ['unknown', 'dproto', 'steam', 'steamEmu', 'revEmu', 'oldRevEmu', 'hltv', 'sc2009', 'avsmp', 'sxei', 'revEmu2013', 'sse3'];
318
+ export const AUTH_TYPES = ['unknown', 'dproto', 'steam', 'steamEmu', 'revEmu', 'oldRevEmu', 'hltv', 'sc2009', 'avsmp', 'sxei', 'revEmu2013', 'sse3'];
302
319
 
303
320
  /** A Pawn string of UTF-8 bytes - a path, a line of a file - as text. */
304
321
  function bytesText(call: NativeCall, pointer: number): string {
@@ -387,6 +404,24 @@ export const NATIVES: Record<string, Native> = {
387
404
  return 1;
388
405
  },
389
406
  close_dir: (c, [handle]) => +c.server.handles.delete(handle),
407
+ delete_file: (c, [name]) => +c.server.files.delete(normalizePath(bytesText(c, name))),
408
+ rename_file: (c, [from, to]) => {
409
+ const source = normalizePath(bytesText(c, from));
410
+ const bytes = c.server.files.get(source);
411
+ if (!bytes) return 0;
412
+ c.server.files.delete(source);
413
+ c.server.files.set(normalizePath(bytesText(c, to)), bytes);
414
+ return 1;
415
+ },
416
+ // An empty folder only, as the real one.
417
+ rmdir: (c, [name]) => {
418
+ const folder = normalizePath(bytesText(c, name));
419
+ const inside = [...c.server.files.keys(), ...c.server.folders].some(path => path.startsWith(`${folder}/`));
420
+ return +(!inside && c.server.folders.delete(folder));
421
+ },
422
+ file_size: (c, [name]) => c.server.files.get(normalizePath(bytesText(c, name)))?.length ?? -1,
423
+ // Every file of the fake changed when the server was made.
424
+ GetFileTime: c => Math.floor(c.server.startedAt / 1000),
390
425
  // 0 when made; the parent has to be there, as for the real one.
391
426
  mkdir: (c, [name]) => {
392
427
  const folder = normalizePath(bytesText(c, name));
@@ -473,6 +508,10 @@ export const NATIVES: Record<string, Native> = {
473
508
  if (!target) throw new Error(`menu_display: player ${id} is not in game`);
474
509
  c.server.displayMenu(target, c.server.menu(menu), page);
475
510
  },
511
+ show_menu: (c, [id, keys, text]) => {
512
+ c.server.showMenu(id, keys, c.memory.text(text));
513
+ return 1;
514
+ },
476
515
  menu_destroy: (c, [menu]) => {
477
516
  c.server.destroyMenu(menu);
478
517
  return 1;
@@ -519,7 +558,7 @@ export const NATIVES: Record<string, Native> = {
519
558
  },
520
559
 
521
560
  set_hudmessage: (c, [r, g, b, x, y, _effects, _fxtime, hold, _fadein, _fadeout, channel]) => {
522
- c.server.hud = { color: [r, g, b], x: bitsFloat(x), y: bitsFloat(y), hold: bitsFloat(hold), channel };
561
+ c.server.hud = { color: [r, g, b], x: bitsFloat(x), y: bitsFloat(y), hold: bitsFloat(hold), channel, line: 0, large: false };
523
562
  },
524
563
  show_hudmessage: (c, [id, format, ...tail]) => sendText(c, id, 'hud', formatPawn(c, c.memory.text(format), tail)),
525
564
 
@@ -633,13 +672,7 @@ export const NATIVES: Record<string, Native> = {
633
672
  rg_switch_weapon: (c, [id, weapon]) => switchTo(player(c, id), entity(c, weapon)?.classname ?? ''),
634
673
  user_has_weapon: (c, [id, weapon]) => +!!weaponByName(player(c, id), WEAPON_NAMES[weapon] ?? ''),
635
674
  engclient_cmd: (c, [id, command, arg1, arg2]) => {
636
- const target = player(c, id);
637
- const line = [command, arg1, arg2].map(p => c.memory.text(p)).filter(Boolean).join(' ');
638
- target?.commands.push(line);
639
- if (line.startsWith('weapon_')) switchTo(target, line);
640
- // The team menu's slots, as the game handles `jointeam`.
641
- const side = { 'jointeam 1': 'TERRORIST', 'jointeam 2': 'CT', 'jointeam 6': 'SPECTATOR' }[line];
642
- if (target && side) target.team = side as TeamName;
675
+ clientCommand(player(c, id), [command, arg1, arg2].map(p => c.memory.text(p)).filter(Boolean).join(' '));
643
676
  },
644
677
  rg_reset_maxspeed: () => {},
645
678
 
@@ -745,23 +778,23 @@ export const NATIVES: Record<string, Native> = {
745
778
 
746
779
  nvault_open: (c, [name]) => {
747
780
  const vault = c.memory.text(name);
748
- c.server.vault(vault);
781
+ c.server.storage(vault);
749
782
  c.server.vaultHandles.push(vault);
750
783
  return c.server.vaultHandles.length - 1;
751
784
  },
752
785
  nvault_close: () => {},
753
786
  nvault_set: (c, [handle, key, value]) => {
754
- c.server.vault(c.server.vaultHandles[handle]).set(c.memory.text(key), c.memory.text(value));
787
+ c.server.storage(c.server.vaultHandles[handle]).set(c.memory.text(key), c.memory.text(value));
755
788
  },
756
789
  nvault_lookup: (c, [handle, key, value, length, stampAt]) => {
757
- const found = c.server.vault(c.server.vaultHandles[handle]).get(c.memory.text(key));
790
+ const found = c.server.storage(c.server.vaultHandles[handle]).get(c.memory.text(key));
758
791
  if (found === undefined) return 0;
759
792
  c.memory.setText(value, length, found);
760
793
  if (stampAt) c.memory.setCell(stampAt, 0);
761
794
  return 1;
762
795
  },
763
796
  nvault_remove: (c, [handle, key]) => {
764
- c.server.vault(c.server.vaultHandles[handle]).delete(c.memory.text(key));
797
+ c.server.storage(c.server.vaultHandles[handle]).delete(c.memory.text(key));
765
798
  },
766
799
 
767
800
  // ------------------------------------------------------------ cvars
@@ -812,6 +845,12 @@ export const NATIVES: Record<string, Native> = {
812
845
  // ------------------------------------------------------------ the game (reapi, engine, resemiclip)
813
846
 
814
847
  register_dictionary: (c, [file]) => (c.server.loadDictionary(c.memory.text(file)) ? 1 : 0),
848
+ get_langsnum: c => c.server.dictionary.size,
849
+ get_lang: (c, [id, name]) => {
850
+ const code = [...c.server.dictionary.keys()][id] ?? '';
851
+ [code.charCodeAt(0) || 0, code.charCodeAt(1) || 0, 0].forEach((cell, at) => c.memory.setCell(name + at * 4, cell));
852
+ return 1;
853
+ },
815
854
  GetLangTransKey: (c, [key]) => c.server.langKey(c.memory.text(key)),
816
855
  LookupLangKey: (c, [out, size, key, id]) => {
817
856
  const text = c.server.lookupLang(c.memory.text(key), c.memory.cell(id));
@@ -1004,7 +1043,7 @@ export const NATIVES: Record<string, Native> = {
1004
1043
  GetHamReturnString: (c, [out, size]) => c.memory.setText(out, size, String(chain(c).answer)),
1005
1044
  GetHamReturnVector: (c, [out]) => c.memory.setVector(out, [0, 0, 0]),
1006
1045
  set_dhudmessage: (c, [r, g, b, x, y, _effects, _fxtime, hold]) => {
1007
- c.server.hud = { color: [r, g, b], x: bitsFloat(x), y: bitsFloat(y), hold: bitsFloat(hold), channel: -1 };
1046
+ c.server.hud = { color: [r, g, b], x: bitsFloat(x), y: bitsFloat(y), hold: bitsFloat(hold), channel: -1, line: 0, large: true };
1008
1047
  },
1009
1048
  // A server without reapi hears ReGameDLL's events through these (as/hlds.ts).
1010
1049
  // register_logevent(function[], argsnum, ...filters): server.gameLog() calls it.
@@ -1056,13 +1095,7 @@ export const NATIVES: Record<string, Native> = {
1056
1095
  if (c.server.players.length >= c.server.maxPlayers) return 0;
1057
1096
  return c.server.join(text(0), { bot: true, team: 'UNASSIGNED', alive: false }).id;
1058
1097
  }
1059
- if (type === constant('EngFunc_RunPlayerMove')) {
1060
- const bot = player(c, cell(0));
1061
- const [, yaw] = c.memory.vector(tail[1]);
1062
- const [forward, msec] = [float(2), cell(7)];
1063
- call(`RunPlayerMove ${cell(0)} ${vector(1)} ${forward} ${float(3)} ${float(4)} ${cell(5)} ${cell(6)} ${msec}`);
1064
- if (bot) bot.origin = [bot.origin[0] + Math.cos(yaw * Math.PI / 180) * forward * msec / 1000, bot.origin[1] + Math.sin(yaw * Math.PI / 180) * forward * msec / 1000, bot.origin[2]];
1065
- }
1098
+ if (type === constant('EngFunc_RunPlayerMove')) c.server.runPlayerMove(cell(0), c.memory.vector(tail[1]), float(2), float(3), float(4), cell(5), cell(6), cell(7));
1066
1099
  return 0;
1067
1100
  },
1068
1101
  dllfunc: (c, [type, ...tail]) => {
@@ -1111,7 +1144,11 @@ const SAME_AS: Record<string, string> = {
1111
1144
  write_long: 'write_byte',
1112
1145
  // A message sent through the engine, which every plugin's message hooks hear (emessage_end).
1113
1146
  emessage_begin: 'message_begin',
1147
+ emessage_begin_f: 'message_begin_f',
1114
1148
  ewrite_byte: 'write_byte',
1149
+ ewrite_char: 'write_byte',
1150
+ ewrite_coord_f: 'write_coord_f',
1151
+ ewrite_angle_f: 'write_coord_f',
1115
1152
  ewrite_long: 'write_byte',
1116
1153
  ewrite_short: 'write_byte',
1117
1154
  ewrite_string: 'write_string',