@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.
Files changed (77) hide show
  1. package/README.md +21 -25
  2. package/README.ru.md +22 -24
  3. package/as/amxts.d.ts +63 -3
  4. package/as/constants.ts +684 -116
  5. package/as/entities.ts +1905 -1364
  6. package/as/events.ts +1696 -907
  7. package/as/facade.ts +1240 -220
  8. package/as/fetch.ts +1519 -0
  9. package/as/flags.ts +159 -145
  10. package/as/hlds.ts +1071 -0
  11. package/as/hooks.ts +5226 -5379
  12. package/as/kit.ts +15 -5
  13. package/as/lib/check.ts +1 -1
  14. package/as/natives.ts +2658 -2499
  15. package/as/remote.ts +39 -15
  16. package/as/tsconfig.json +7 -2
  17. package/as/vector.ts +12 -9
  18. package/includes/order.txt +22 -29
  19. package/includes/sources.json +0 -8
  20. package/node_modules/assemblyscript/std/assembly/index.d.ts +20 -11
  21. package/package.json +29 -7
  22. package/runtime/deps/assemblyscript/dist/asc.js +86 -16
  23. package/runtime/deps/assemblyscript/dist/assemblyscript.js +29 -29
  24. package/runtime/licenses/MPL-2.0.txt +373 -0
  25. package/runtime/licenses/README.md +5 -1
  26. package/runtime/licenses/curl-COPYING +22 -0
  27. package/runtime/licenses/libssh2-COPYING +43 -0
  28. package/runtime/licenses/mbedTLS-LICENSE +553 -0
  29. package/runtime/natives.txt +108 -105
  30. package/runtime/patches/assemblyscript-0.28.20-amxts.patch +752 -296
  31. package/runtime/patches/wamr-2.4.5-amxts.patch +148 -0
  32. package/scripts/apply-docs.ts +2 -2
  33. package/scripts/auto-imports.ts +2 -2
  34. package/scripts/build-identity.ts +115 -0
  35. package/scripts/build-wasm.ts +101 -50
  36. package/scripts/check.ts +17 -3
  37. package/scripts/client-messages.ts +246 -0
  38. package/scripts/compile-cache.ts +4 -2
  39. package/scripts/compile-pool.ts +128 -0
  40. package/scripts/compile-worker.ts +23 -0
  41. package/scripts/compile.ts +156 -59
  42. package/scripts/docker-server.ts +13 -3
  43. package/scripts/docs/as/amxts.ts +86 -0
  44. package/scripts/docs/as/facade.ts +688 -188
  45. package/scripts/docs/as/fetch.ts +805 -0
  46. package/scripts/docs/entities.ts +43 -144
  47. package/scripts/docs/events.ts +17 -28
  48. package/scripts/docs/game.ts +68 -22
  49. package/scripts/docs/messages.ts +330 -16
  50. package/scripts/editor-api.ts +101 -0
  51. package/scripts/hlds-events.ts +370 -0
  52. package/scripts/includes.ts +12 -10
  53. package/scripts/player-fields.ts +16 -15
  54. package/scripts/prebuilt.ts +65 -23
  55. package/scripts/prepare.ts +42 -28
  56. package/scripts/project.ts +120 -20
  57. package/scripts/reapi-events.ts +98 -0
  58. package/scripts/shared-modules.ts +85 -23
  59. package/scripts/source-map.ts +242 -0
  60. package/scripts/system.ts +1 -1
  61. package/scripts/typed-commands.ts +263 -0
  62. package/scripts/typed-configs.ts +23 -12
  63. package/scripts/ui.ts +3 -1
  64. package/scripts/upgrade-menus.ts +274 -0
  65. package/scripts/upgrade-names.ts +384 -0
  66. package/scripts/upgrade.ts +857 -0
  67. package/src/cli-api.mjs +37 -4
  68. package/src/system.mjs +74 -3
  69. package/src/testing/compile.ts +8 -5
  70. package/src/testing/index.ts +2 -1
  71. package/src/testing/natives.ts +199 -5
  72. package/src/testing/network.ts +531 -0
  73. package/src/testing/server.ts +572 -48
  74. package/src/testing/setup.ts +1 -1
  75. package/src/testing/tables.ts +25 -0
  76. package/as/modules/http.ts +0 -184
  77. package/scripts/docs/as/modules/http.ts +0 -66
@@ -0,0 +1,384 @@
1
+ // The names `amxts upgrade` brings up to date (scripts/upgrade.ts): the API's
2
+ // names that were the engine's words and are now the player's, and the ones
3
+ // left out of the API, which the natives still reach.
4
+
5
+ /** Game events by their old name; each event's class follows its name. */
6
+ export const EVENTS: Record<string, string> = {
7
+ addAccount: 'addMoney',
8
+ restartRound: 'newRound',
9
+ onRoundFreezeEnd: 'roundStart',
10
+ hasRestrictItem: 'itemRestricted',
11
+ cleanUpMap: 'mapReset',
12
+ flPlayerFallDamage: 'fallDamage',
13
+ fPlayerCanTakeDamage: 'canTakeDamage',
14
+ fPlayerCanRespawn: 'canRespawn',
15
+ fShouldSwitchWeapon: 'shouldSwitchWeapon',
16
+ buyWeaponByWeaponId: 'buyWeapon',
17
+ buyGunAmmo: 'buyAmmo',
18
+ giveNamedItem: 'giveItem',
19
+ giveC4: 'giveBomb',
20
+ makeBomber: 'becomeBomber',
21
+ makeVip: 'becomeVip',
22
+ goToIntermission: 'intermission',
23
+ precacheGenericI: 'precacheFile',
24
+ precacheModelI: 'precacheModel',
25
+ precacheSoundI: 'precacheSound',
26
+ entSelectSpawnPoint: 'selectSpawnPoint',
27
+ getPlayerSpawnSpot: 'spawnSpot',
28
+ executeServerStringCmd: 'serverCommand',
29
+ clientPrintf: 'consoleMessage',
30
+ sendSayMessage: 'chatMessage',
31
+ hintMessageEx: 'hintMessage',
32
+ dropClient: 'disconnectClient',
33
+ setClientUserInfoName: 'changeName',
34
+ setClientUserInfoModel: 'changeModel',
35
+ clientUserInfoChanged: 'userInfoChange',
36
+ addPoints: 'addFrags',
37
+ addPointsToTeam: 'addTeamScore',
38
+ takeHealth: 'heal',
39
+ addPlayerItem: 'addItem',
40
+ removePlayerItem: 'removeItem',
41
+ canHavePlayerItem: 'canHaveItem',
42
+ deadPlayerWeapons: 'dropWeaponsOnDeath',
43
+ kickBack: 'recoil',
44
+ isPenetrableEntity: 'canShootThrough',
45
+ startObserver: 'startSpectating',
46
+ observerFindNextPlayer: 'spectateNext',
47
+ observerIsValidTarget: 'canSpectate',
48
+ impulseCommands: 'impulse',
49
+ onSpawnEquip: 'spawnEquip',
50
+ onEvent: 'gameEvent',
51
+ pmDuck: 'duckMovement',
52
+ pmJump: 'jumpMovement',
53
+ fireBullets3: 'shoot',
54
+ fireBuckshots: 'shootBuckshot',
55
+ };
56
+
57
+ /** Game events out of the API, by the native that hooks them. */
58
+ export const HIDDEN_EVENTS: Record<string, string> = {
59
+ alloc: 'RegisterHookChain(RH_ED_Alloc, ...)',
60
+ free: 'RegisterHookChain(RH_ED_Free, ...)',
61
+ directSet: 'RegisterHookChain(RH_Cvar_DirectSet, ...)',
62
+ emitPings: 'RegisterHookChain(RH_SV_EmitPings, ...)',
63
+ objectCaps: 'RegisterHookChain(RG_CBasePlayer_ObjectCaps, ...) or RegisterHam(Ham_ObjectCaps, ...)',
64
+ getEntityInit: 'RegisterHookChain(RH_GetEntityInit, ...)',
65
+ allowPhysent: 'RegisterHookChain(RH_SV_AllowPhysent, ...)',
66
+ writeFullClientUpdate: 'RegisterHookChain(RH_SV_WriteFullClientUpdate, ...)',
67
+ sendResources: 'RegisterHookChain(RH_SV_SendResources, ...)',
68
+ reportAiState: 'RegisterHam(Ham_ReportAIState, ...)',
69
+ useDecrement: 'RegisterHam(Ham_Weapon_UseDecrement, ...)',
70
+ addDuplicate: 'RegisterHam(Ham_Item_AddDuplicate, ...)',
71
+ overrideReset: 'RegisterHam(Ham_OverrideReset, ...)',
72
+ onControls: 'RegisterHam(Ham_OnControls, ...)',
73
+ updateOwner: 'RegisterHam(Ham_UpdateOwner, ...)',
74
+ };
75
+
76
+ /** What a value is, as far as the code says: the class whose names apply to it. */
77
+ export type Kind = 'Player' | 'Weapon' | 'Entity' | 'Game';
78
+
79
+ /** Every entity's fields and methods, a player's and a weapon's too. */
80
+ const ENTITY: Record<string, string> = {
81
+ armorValue: 'armor',
82
+ takeHealth: 'heal',
83
+ };
84
+
85
+ /** Fields and methods by their old name, for each kind of value. */
86
+ export const RENAMED: Record<Kind, Record<string, string>> = {
87
+ Entity: ENTITY,
88
+ Player: {
89
+ ...ENTITY,
90
+ account: 'money',
91
+ authid: 'steamId',
92
+ lastAccount: 'lastSentMoney',
93
+ nextAccountHealthUpdate: 'nextScoreboardUpdate',
94
+ clientHealth: 'healthSent',
95
+ clientFov: 'fovSent',
96
+ clientBattery: 'batterySent',
97
+ clientHideHud: 'hideHudSent',
98
+ clientActiveItem: 'activeItemSent',
99
+ flashBattery: 'flashlightBattery',
100
+ flashLightTime: 'flashlightTime',
101
+ flgeigerDelay: 'geigerDelay',
102
+ flgeigerRange: 'geigerRange',
103
+ igeigerRangePrev: 'geigerRangePrev',
104
+ idrowndmg: 'drownDamage',
105
+ idrownrestored: 'drownRestored',
106
+ currentammo: 'currentAmmo',
107
+ gaitframe: 'gaitFrame',
108
+ gaitsequence: 'playerGaitSequence',
109
+ gaityaw: 'gaitYaw',
110
+ prevgaitorigin: 'prevGaitOrigin',
111
+ lastx: 'lastX',
112
+ lasty: 'lastY',
113
+ maxammoBuckshot: 'maxAmmoBuckshot',
114
+ maxammo9mm: 'maxAmmo9mm',
115
+ maxammo556nato: 'maxAmmo556nato',
116
+ maxammo556natobox: 'maxAmmo556natobox',
117
+ maxammo762nato: 'maxAmmo762nato',
118
+ maxammo45acp: 'maxAmmo45acp',
119
+ maxammo50ae: 'maxAmmo50ae',
120
+ maxammo338mag: 'maxAmmo338mag',
121
+ maxammo57mm: 'maxAmmo57mm',
122
+ maxammo357sig: 'maxAmmo357sig',
123
+ sbarString0: 'statusBarText',
124
+ nextSBarUpdateTime: 'nextStatusBarUpdate',
125
+ sndLast: 'lastSoundEntity',
126
+ sndRange: 'soundRange',
127
+ sndRoomtype: 'roomType',
128
+ tbdPrev: 'timeBasedDamagePrev',
129
+ punishedForTk: 'punishedForTeamKill',
130
+ longJump: 'hasLongJump',
131
+ velocityModifier: 'slowdown',
132
+ sneaking: 'sneakingUntil',
133
+ tank: 'mountedGun',
134
+ train: 'trainControls',
135
+ weapon: 'weaponHudValid',
136
+ menu: 'openMenu',
137
+ numSpawns: 'spawnCount',
138
+ autoWepSwitch: 'autoSwitchWeapon',
139
+ progressStart: 'progressBarStart',
140
+ progressEnd: 'progressBarEnd',
141
+ onTarget: 'aimingAtTarget',
142
+ addPoints: 'addFrags',
143
+ addPointsToTeam: 'addTeamScore',
144
+ addPlayerItem: 'addItem',
145
+ removePlayerItem: 'removeItem',
146
+ },
147
+ Weapon: {
148
+ ...ENTITY,
149
+ inReload: 'isReloading',
150
+ inSpecialReload: 'shotgunReloadStage',
151
+ clientClip: 'clipSent',
152
+ glock18Shoot: 'glockNextBurstShot',
153
+ glock18ShotsFired: 'glockBurstShots',
154
+ famasShoot: 'famasNextBurstShot',
155
+ famasShotsFired: 'famasBurstShots',
156
+ decreaseShotsFired: 'recoilResetTime',
157
+ timeWeaponIdle: 'nextIdle',
158
+ primaryAmmoType: 'ammoType',
159
+ },
160
+ Game: {
161
+ accountCt: 'ctRoundBonus',
162
+ accountTerrorist: 'terroristRoundBonus',
163
+ c4Guy: 'bomber',
164
+ c4Timer: 'bombTimer',
165
+ numCt: 'ctCount',
166
+ numTerrorist: 'terroristCount',
167
+ numCtWins: 'ctWins',
168
+ numTerroristWins: 'terroristWins',
169
+ numSpawnableCt: 'spawnableCts',
170
+ numSpawnableTerrorist: 'spawnableTerrorists',
171
+ numConsecutiveCtLoses: 'ctLossStreak',
172
+ numConsecutiveTerroristLoses: 'terroristLossStreak',
173
+ restartRoundTime: 'newRoundTime',
174
+ roundStartTimeReal: 'freezeStartTime',
175
+ introRoundTime: 'freezeTime',
176
+ freezePeriod: 'isFreezeTime',
177
+ roundTerminating: 'roundEnding',
178
+ tCantBuy: 'terroristsCantBuy',
179
+ ctCantBuy: 'ctsCantBuy',
180
+ unBalancedRounds: 'unbalancedRounds',
181
+ levelInitialized: 'mapInitialized',
182
+ endIntermissionButtonHit: 'intermissionSkipped',
183
+ forceCameraValue: 'forceCamera',
184
+ forceChaseCamValue: 'forceChaseCam',
185
+ fadeToBlackValue: 'fadeToBlack',
186
+ gameDesc: 'gameName',
187
+ },
188
+ };
189
+
190
+ /** Every entity's field out of the API, by its member. */
191
+ const ENTITY_HIDDEN: Record<string, string> = { armorType: 'var_armortype' };
192
+
193
+ /** Fields out of the API, by the member a native reads them with, for each kind of value. */
194
+ export const HIDDEN: Record<Kind, Record<string, string>> = {
195
+ Entity: ENTITY_HIDDEN,
196
+ Player: {
197
+ ...ENTITY_HIDDEN,
198
+ activity: 'm_Activity',
199
+ idealActivity: 'm_IdealActivity',
200
+ monsterState: 'm_MonsterState',
201
+ idealMonsterState: 'm_IdealMonsterState',
202
+ conditions: 'm_afConditions',
203
+ memory: 'm_afMemory',
204
+ enemyLkp: 'm_vecEnemyLKP',
205
+ hackedGunPos: 'm_HackedGunPos',
206
+ targetEnt: 'm_hTargetEnt',
207
+ teamName: 'm_szTeamName',
208
+ },
209
+ Weapon: {
210
+ ...ENTITY_HIDDEN,
211
+ fireFamas: 'm_Weapon_usFireFamas',
212
+ fireGlock18: 'm_Weapon_usFireGlock18',
213
+ lastFire: 'm_Weapon_flLastFire',
214
+ },
215
+ Game: {
216
+ careerMatchMenuTime: 'm_fCareerMatchMenuTime',
217
+ careerMatchWins: 'm_iCareerMatchWins',
218
+ careerRoundMenuTime: 'm_fCareerRoundMenuTime',
219
+ inCareerGame: 'm_bInCareerGame',
220
+ roundWinDifference: 'm_iRoundWinDifference',
221
+ msgPlayerVoiceMask: 'm_msgPlayerVoiceMask',
222
+ msgRequestState: 'm_msgRequestState',
223
+ },
224
+ };
225
+
226
+ /**
227
+ * Old names common enough elsewhere - a menu's `menu`, a message's `weapon`,
228
+ * a memory's `memory`, a ban's `authid` - that a use on a value the code does
229
+ * not say is one of ours is not listed.
230
+ */
231
+ export const COMMON = new Set(['menu', 'weapon', 'memory', 'conditions', 'activity', 'train', 'tank', 'sneaking', 'onTarget', 'lastFire', 'teamName', 'authid']);
232
+
233
+ /**
234
+ * Server events by their old names - the Pawn forward's and the short one
235
+ * before it - each by the name in the author's words
236
+ * (scripts/generate-host.ts's EVENT_NAMES).
237
+ */
238
+ export const SERVER_EVENTS: Record<string, string> = {
239
+ plugin_init: 'init',
240
+ plugin_precache: 'precache',
241
+ cfg: 'pluginsLoaded',
242
+ plugin_cfg: 'pluginsLoaded',
243
+ plugin_end: 'end',
244
+ plugin_pause: 'pause',
245
+ plugin_unpause: 'unpause',
246
+ plugin_log: 'log',
247
+ plugin_modules: 'modules',
248
+ changelevel: 'changeLevel',
249
+ server_changelevel: 'changeLevel',
250
+ server_frame: 'frame',
251
+ OnConfigsExecuted: 'configsExecuted',
252
+ OnAutoConfigsBuffered: 'configsQueued',
253
+ client_connect: 'connect',
254
+ connectex: 'connectAttempt',
255
+ client_connectex: 'connectAttempt',
256
+ client_authorized: 'authorized',
257
+ putinserver: 'putInServer',
258
+ client_putinserver: 'putInServer',
259
+ disconnect: 'disconnected',
260
+ client_disconnect: 'disconnected',
261
+ client_disconnected: 'disconnected',
262
+ client_remove: 'remove',
263
+ client_command: 'command',
264
+ kill: 'suicide',
265
+ client_kill: 'suicide',
266
+ client_impulse: 'impulse',
267
+ client_cmdStart: 'cmdStart',
268
+ inconsistent_file: 'inconsistentFile',
269
+ CS_InternalCommand: 'internalCommand',
270
+ pfnSpawn: 'entitySpawn',
271
+ pfn_spawn: 'entitySpawn',
272
+ pfnThink: 'entityThink',
273
+ pfn_think: 'entityThink',
274
+ pfnKeyvalue: 'keyValue',
275
+ pfn_keyvalue: 'keyValue',
276
+ pfnPlaybackevent: 'playbackEvent',
277
+ pfn_playbackevent: 'playbackEvent',
278
+ playerchange: 'playerChange',
279
+ };
280
+
281
+ /** Server events that are a game event: `game.addEventListener` by this name. */
282
+ export const SERVER_GAME_EVENTS: Record<string, string> = {
283
+ PreThink: 'preThink',
284
+ client_PreThink: 'preThink',
285
+ PostThink: 'postThink',
286
+ client_PostThink: 'postThink',
287
+ pfnTouch: 'touch',
288
+ pfn_touch: 'touch',
289
+ infochanged: 'userInfoChange',
290
+ client_infochanged: 'userInfoChange',
291
+ };
292
+
293
+ /** Server events whose game events ask it otherwise: listed, with what to write. */
294
+ export const SERVER_EVENTS_BY_HAND: Record<string, string> = {
295
+ CS_OnBuy: 'a purchase is game.addEventListener("buyWeapon" | "buyItem" | "buyAmmo", ...), one per kind of thing bought, with what is bought by its name',
296
+ CS_OnBuyAttempt: 'whether a purchase is allowed is game.addEventListener("itemRestricted", ...) where event.restriction == "buying": return true to forbid it',
297
+ };
298
+
299
+ /** Server events' classes by their old names, where the event is another's now. */
300
+ export const SERVER_EVENT_CLASSES: Record<string, string> = {
301
+ Client_PreThinkEvent: 'PreThinkEvent',
302
+ Client_PostThinkEvent: 'PostThinkEvent',
303
+ PfnTouchEvent: 'TouchEvent',
304
+ ClientInfochangedEvent: 'UserInfoChangeEvent',
305
+ ClientDisconnectEvent: 'ClientDisconnectedEvent',
306
+ };
307
+
308
+ /** A server event's fields by their old names, for each event by its name. */
309
+ export const SERVER_EVENT_FIELDS: Record<string, Record<string, string>> = {
310
+ internalCommand: { cmd: 'command' },
311
+ inconsistentFile: { filename: 'file' },
312
+ playbackEvent: { eventid: 'eventIndex' },
313
+ authorized: { authid: 'steamId' },
314
+ };
315
+
316
+ /** The server events whose fields are renamed, by the class an annotation names them with. */
317
+ export const SERVER_FIELD_CLASSES: Record<string, string> = {
318
+ CS_InternalCommandEvent: 'internalCommand',
319
+ InconsistentFileEvent: 'inconsistentFile',
320
+ PfnPlaybackeventEvent: 'playbackEvent',
321
+ ClientAuthorizedEvent: 'authorized',
322
+ };
323
+
324
+ const TRACE = { tracehandle: 'trace' };
325
+ const SHOT = { src: 'start', dirShooting: 'direction' };
326
+
327
+ /** A game event's fields by their old names - reapi's parameters as they were - for each event (scripts/generate-hooks.ts's WORDS). */
328
+ export const GAME_EVENT_FIELDS: Record<string, Record<string, string>> = {
329
+ addResource: { filename: 'file' },
330
+ airAccelerate: { wishdir: 'direction', wishspeed: 'speed', accel: 'acceleration' },
331
+ buyAmmo: { weapon_entity: 'weapon' },
332
+ canShootThrough: { src: 'start' },
333
+ changeModel: { infobuffer: 'info' },
334
+ changeName: { infobuffer: 'info' },
335
+ chatMessage: { cmd: 'command', teamonly: 'teamOnly' },
336
+ checkUserInfo: { adr: 'address' },
337
+ consoleMessage: { string: 'text' },
338
+ createWeaponBox: { weaponent: 'weapon' },
339
+ defaultDeploy: { anim: 'animation', animExt: 'animationExtension', skiplocal: 'skipLocal' },
340
+ defaultReload: { anim: 'animation' },
341
+ defaultShotgunReload: { anim: 'animation', startAnim: 'startAnimation' },
342
+ disconnectClient: { fmt: 'reason' },
343
+ explodeBomb: TRACE,
344
+ explodeFlashbang: TRACE,
345
+ explodeHeGrenade: TRACE,
346
+ fireBullets: SHOT,
347
+ playStepSound: { fvol: 'volume' },
348
+ precacheFile: { string: 'file' },
349
+ precacheModel: { string: 'file' },
350
+ precacheSound: { string: 'file' },
351
+ printf: { string: 'text' },
352
+ radio: { msg_id: 'sound', msg_verbose: 'text' },
353
+ recoil: {
354
+ up_base: 'upBase',
355
+ lateral_base: 'lateralBase',
356
+ up_modifier: 'upModifier',
357
+ lateral_modifier: 'lateralModifier',
358
+ p_max: 'upMax',
359
+ lateral_max: 'lateralMax',
360
+ direction_change: 'directionChange',
361
+ },
362
+ sendWeaponAnim: { anim: 'animation', skiplocal: 'skipLocal' },
363
+ serverCommand: { cmd: 'command' },
364
+ setAnimation: { playerAnim: 'animation' },
365
+ shoot: { ...SHOT, shared_rand: 'randomSeed' },
366
+ shootBuckshot: SHOT,
367
+ spectateNext: { arg2: 'reverse' },
368
+ startSound: { sample: 'sound' },
369
+ takeDamageImpulse: { velModifier: 'velocityModifier' },
370
+ teamFull: { team_id: 'team' },
371
+ teamStacked: { newTeam_id: 'newTeam', curTeam_id: 'currentTeam' },
372
+ throwGrenade: { src: 'start', usEvent: 'eventIndex' },
373
+ throwHeGrenade: { usEvent: 'eventIndex' },
374
+ throwSmokeGrenade: { usEvent: 'eventIndex' },
375
+ traceAttack: { dir: 'direction', ...TRACE },
376
+ traceLine: { src: 'start', spot: 'end', ...TRACE },
377
+ userInfoChange: { infobuffer: 'info' },
378
+ };
379
+
380
+ /** The fake server's options for a test's player by their old names: `server.join(name, { authid })`. */
381
+ export const JOIN_OPTIONS: Record<string, string> = { authid: 'steamId' };
382
+
383
+ /** Flag names that are more than the old one in lowerCamelCase: KillRarity's `"ThruSmoke"` is `"throughSmoke"`. */
384
+ export const FLAG_NAMES: Record<string, string> = { ThruSmoke: 'throughSmoke' };