overpy 9.6.10 → 9.7.3

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 (3) hide show
  1. package/README.md +119 -97
  2. package/overpy.js +54844 -54818
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -33,56 +33,7 @@ You first need to install the [pnpm package manager](https://pnpm.io/installatio
33
33
  - Build `out/overpy_standalone.js`: `pnpm run package`
34
34
  - Build and publish to prod: `pnpm run publish`
35
35
 
36
- # NPM usage
37
-
38
- ![NPM Version](https://img.shields.io/npm/v/overpy) ![NPM Type Definitions](https://img.shields.io/npm/types/overpy)
39
-
40
- Install:
41
-
42
- - As a dependency: `pnpm i overpy`
43
- - Global CLI (optional): `pnpm i -g overpy`
44
- - One-off CLI without install: `npx overpy --help`
45
-
46
- JS/TS API usage:
47
-
48
- ```js
49
- // JavaScript (CommonJS)
50
- const overpy = require("overpy");
51
-
52
- async function main() {
53
- await overpy.readyPromise;
54
- const compileResult = await overpy.compile(
55
- 'rule "hello":\n @Event global\n wait(1)\n',
56
- "en-US",
57
- process.cwd(),
58
- "inline.opy"
59
- );
60
- console.log(compileResult.result);
61
- }
62
-
63
- main().catch(console.error);
64
- ```
65
-
66
- ```ts
67
- // TypeScript
68
- import * as overpy from "overpy";
69
-
70
- async function main() {
71
- await overpy.readyPromise;
72
- const workshopText = "rule(\"hello\") { event { Ongoing - Global; } actions { Wait(1, Ignore Condition); } }";
73
- const decompiled = overpy.decompileAllRules(workshopText, "en-US");
74
- console.log(decompiled);
75
- }
76
- ```
77
-
78
- CLI usage:
79
-
80
- - Compile a file: `overpy compile -i script.opy -o script.txt`
81
- - Compile from stdin to stdout: `cat script.opy | overpy compile > script.txt`
82
- - Decompile a file: `overpy decompile -i workshop.txt -o script.opy`
83
- - Decompile from stdin to stdout: `cat workshop.txt | overpy decompile --ignore-variable-index > script.opy`
84
-
85
- Run `overpy --help` to see all options (`-l/--language`, `--root`, `--main-file`, `--ignore-variable-index`, `--ignore-subroutine-index`).
36
+ See at the bottom for NPM usage and LSP building.
86
37
 
87
38
  # Installation
88
39
 
@@ -132,7 +83,7 @@ You may get warnings when compiling; do not ignore them, as they can lead to bug
132
83
 
133
84
  It is recommended to also decompile one of your existing gamemodes to get a better feel of the OverPy syntax.
134
85
 
135
- ```py
86
+ ```opy
136
87
 
137
88
  # Line comments are made with "#".
138
89
  # Multiline comments are made with /* */.
@@ -296,7 +247,6 @@ If a function is not in that list, then the name is the English name in camelCas
296
247
  <code>Loop If(A == 2)</code> | <code>if A == 2:</code><br><code>&nbsp;&nbsp;&nbsp;&nbsp;loop()</code>
297
248
  <code>Loop If Condition Is False</code> | <code>if not ruleCondition:</code><br><code>&nbsp;&nbsp;&nbsp;&nbsp;loop()</code>
298
249
  <code>Loop If Condition Is True</code> | <code>if ruleCondition:</code><br><code>&nbsp;&nbsp;&nbsp;&nbsp;loop()</code>
299
- <code>Magnitude Of</code> | <code>magnitude()</code>
300
250
  <code>Map(Workshop Island)</code> | <code>Map.WORKSHOP_ISLAND</code>
301
251
  <code>Mapped Array(<i>array</i>, Current Array Element + 2)</code><br><code>Mapped Array(<i>array</i>, Current Array Element * Current Array Index)</code> | <code><i>array</i>.map(lambda <i>elem</i>: <i>elem</i>+2)</code><br><code><i>array</i>.map(lambda <i>elem</i>, <i>idx</i>: <i>elem</i> * <i>idx</i>)</code><br>Python-style comprehension syntax also works but is not recommended: <code>[<i>elem</i>+2 for <i>elem</i> in <i>array</i>]</code>
302
252
  <code>Modify Global Variable(A, Add, 2)</code> | <code>A += 2</code>
@@ -321,7 +271,6 @@ If a function is not in that list, then the name is the English name in camelCas
321
271
  <code>Not(<i>a</i>)</code> | <code>not <i>a</i></code>
322
272
  <code>Number(1234)</code> | <code>1234</code>
323
273
  <code>Objective Index</code> | <code>getCurrentObjective()</code>
324
- <code>Objective Position</code> | <code>getObjectivePosition()</code>
325
274
  <code>Opposite Team Of(Team Of(Event Player))</code> | <code>getOppositeTeam(eventPlayer.getTeam())</code><br>Or for a player: `eventPlayer.getOppositeTeam()`
326
275
  <code>Or(A == 2, B == 4)</code> | <code>A == 2 or B == 4</code>
327
276
  <code>Player Carrying Flag</code> | <code>getFlagCarrier()</code>
@@ -332,9 +281,6 @@ If a function is not in that list, then the name is the English name in camelCas
332
281
  <code>Random Real</code> | <code>random.uniform()</code>
333
282
  <code>Random Value In Array</code> | <code>random.choice()</code>
334
283
  <code>Randomized Array</code> | <code>random.shuffle()</code>
335
- <code>Raycast Hit Normal(<i>start</i>, <i>end</i>, <i>include</i>, <i>exclude</i>, <i>includePlayerObjects</i>)</code> | <code>raycast(<i>start</i>, <i>end</i>, <i>include</i>, <i>exclude</i>, <i>includePlayersObjects</i>).getNormal()</code>
336
- <code>Raycast Hit Player(<i>start</i>, <i>end</i>, <i>include</i>, <i>exclude</i>, <i>includePlayerObjects</i>)</code> | <code>raycast(<i>start</i>, <i>end</i>, <i>include</i>, <i>exclude</i>, <i>includePlayersObjects</i>).getPlayerHit()</code>
337
- <code>Raycast Hit Position(<i>start</i>, <i>end</i>, <i>include</i>, <i>exclude</i>, <i>includePlayerObjects</i>)</code> | <code>raycast(<i>start</i>, <i>end</i>, <i>include</i>, <i>exclude</i>, <i>includePlayersObjects</i>).getHitPosition()</code>
338
284
  <code>Remove From Array(<i>array</i>, 2)</code> | <code><i>array</i>.exclude(2)</code>
339
285
  <code>Remove Player</code> | <code><i>player</i>.removeFromGame()</code>
340
286
  <code>Right</code> | <code>Vector.RIGHT</code>
@@ -358,7 +304,6 @@ If a function is not in that list, then the name is the English name in camelCas
358
304
  <code>Start Forcing Dummy Bot Name</code> | <code><i>player</i>.startForcingName()</code>
359
305
  <code>Start Holding Button</code> | <code><i>player</i>.startForcingButton()</code>
360
306
  <code>Start Modifying Hero Voice Lines</code> | <code><i>player</i>.startModifyingVoicelinePitch()</code>
361
- <code>Start Rule(<i>subroutine</i>, <i>behavior</i>)</code> | <code>async(<i>subroutine</i>, <i>behavior</i>)</code>
362
307
  <code>Start Scaling Player</code> | <code><i>player</i>.startScalingSize()</code>
363
308
  <code>Stop Holding Button</code> | <code><i>player</i>.stopForcingButton()</code>
364
309
  <code>Stop Modifying Hero Voice Lines</code> | <code><i>player</i>.stopModifyingVoicelinePitch()</code>
@@ -388,7 +333,7 @@ If a function is not in that list, then the name is the English name in camelCas
388
333
 
389
334
  OverPy supports including custom game settings, using the `settings` keyword. The settings are parsed with OverPy's parser, meaning you can do things such as:
390
335
 
391
- ```py
336
+ ```opy
392
337
  macro VERSION = "1.4.3"
393
338
  macro DEBUG = false
394
339
  macro CLIP_SIZE_MULTIPLIER = 2
@@ -419,7 +364,7 @@ settings {
419
364
 
420
365
  Note that every value has to eventually resolve to a dict/array/string/number/boolean through the optimizer (you can't do `200 * A` where `A` is a variable).
421
366
 
422
- There are quite a lot of changes regarding the syntax, and it is recommended that you edit settings within Overwatch, then use the decompile command to convert to OverPy.
367
+ Because I made the unfortunate mistake of trying to rename pretty much all settings (and because Overwatch periodically renames a setting or two), unknown settings are accepted by the compiler, so you can just put what Overwatch accepts (but they won't be translated if you are compiling for another language).
423
368
 
424
369
  You can also put the settings in a .json file and then import it with `settings "gamesettings.opy.json"`. This has the advantage of adding autocompletion (if ending with .opy.json), although it will display syntax errors if it doesn't perfectly conform to the JSON syntax (trailing comma, comment, etc).
425
370
 
@@ -431,7 +376,7 @@ Extensions are activated using the `#!extensions` compiler directive.
431
376
 
432
377
  An if/elif/else statement is simply represented by the following structure:
433
378
 
434
- ```python
379
+ ```opy
435
380
  if A == 1:
436
381
  B = 2
437
382
  elif A == 2:
@@ -446,7 +391,7 @@ else:
446
391
 
447
392
  A while loop is represented by the following structure:
448
393
 
449
- ```python
394
+ ```opy
450
395
  while A == 1:
451
396
  B = 2
452
397
  wait()
@@ -457,7 +402,7 @@ It compiles to the workshop's `While` instruction.
457
402
  ## For loop
458
403
 
459
404
  A for loop is represented by the following structure:
460
- ```python
405
+ ```opy
461
406
  globalvar i
462
407
  rule "for loop":
463
408
  for i in range(1, 5, 2):
@@ -467,7 +412,7 @@ rule "for loop":
467
412
 
468
413
  Note that the `range(start, stop, step)` function, that can only be used here, has other forms:
469
414
 
470
- ```python
415
+ ```opy
471
416
  for i in range(1,5) -> for i in range(1,5,1)
472
417
  for i in range(5) -> for i in range(0,5,1)
473
418
  ```
@@ -480,7 +425,7 @@ Although not recommended to use, gotos can be used in conjunction with a label.
480
425
 
481
426
  For example:
482
427
 
483
- ```python
428
+ ```opy
484
429
  if A == 4:
485
430
  goto lbl_0
486
431
  B = 5
@@ -493,7 +438,7 @@ Labels are declared on their own line, and must include a colon at the end (but
493
438
  Due to the limitations of the workshop, labels must be in the same rule as the `goto` instruction, and cannot be before it.
494
439
 
495
440
  Additionally, dynamic gotos can be specified using the special keyword `loc`:
496
- ```python
441
+ ```opy
497
442
  goto loc+A
498
443
  ```
499
444
 
@@ -566,7 +511,7 @@ Compiler options begin with the `#!` operator, not indented.
566
511
 
567
512
  Inserts the text of the specified file. The file path can be relative; if so, it is relative to the file with the `#!include` directive. For example:
568
513
 
569
- ```c
514
+ ```opy
570
515
  #!include "heroes/zenyatta.opy"
571
516
 
572
517
  /* includes a file in the parent directory (".." is the parent directory) */
@@ -580,7 +525,7 @@ Inserts the text of the specified file. The file path can be relative; if so, it
580
525
 
581
526
  Specifies an .opy file as the main file (implying the current file is a module). This directive MUST be placed at the very beginning of the file. For example:
582
527
 
583
- ```hs
528
+ ```opy
584
529
  #!mainFile "../main.opy"
585
530
  ```
586
531
 
@@ -590,7 +535,7 @@ This is so that you can compile your gamemode from any file and OverPy knows the
590
535
 
591
536
  Suppresses the specified warnings globally across the program. Warnings must be separated by a space. Example:
592
537
 
593
- ```hs
538
+ ```opy
594
539
  #!suppressWarnings w_type_check w_unsuitable_event
595
540
  ```
596
541
 
@@ -634,7 +579,7 @@ This directive is added by default upon decompilation. Only remove it if you are
634
579
 
635
580
  All 3 of these directives are only effective in the current block scope (indentation level) once they are declared, and can be cancelled by `#!enableOptimizations`, `#!disableOptimizeForSize` and `#!disableOptimizeStrict` respectively. For example:
636
581
 
637
- ```py
582
+ ```opy
638
583
  rule "default: optimizations, no optimize for size, no optimize strict"
639
584
  A = B+0 #will be optimized
640
585
  if A:
@@ -654,7 +599,7 @@ rule "optimize strict, no optimize for size":
654
599
 
655
600
  Several compiler options are available to automatically replace some constants, in order to save elements:
656
601
 
657
- ```hs
602
+ ```opy
658
603
  #!replaceTeam1ByControlScoringTeam
659
604
  #!replace0ByCapturePercentage
660
605
  #!replace0ByPayloadProgressPercentage
@@ -674,7 +619,7 @@ Preprocessing is an important part of OverPy and allows extending its power way
674
619
 
675
620
  Use the `macro` keyword to declare a macro, which is an inline function or constant. For example:
676
621
 
677
- ```python
622
+ ```opy
678
623
  macro BOSS_HP = 1000 + getNumberOfPlayers() * 300
679
624
 
680
625
  macro add(a, b):
@@ -689,7 +634,7 @@ Note that the replacement is done in the AST, meaning the order of operations wi
689
634
 
690
635
  You can also declare member macros, where the member is referenced with `self`. For example:
691
636
 
692
- ```python
637
+ ```opy
693
638
  macro Vector.sum = self.x + self.y
694
639
 
695
640
  macro Player.setPowerLevel(powerLevel):
@@ -709,7 +654,7 @@ rule "power level":
709
654
 
710
655
  Default parameters can also be specified, and just like normal functions, you can use keyword arguments:
711
656
 
712
- ```python
657
+ ```opy
713
658
  macro Player.setPowerLevel(powerLevel=1, damageDealt=null):
714
659
  self.setMaxHealth(powerLevel*200)
715
660
  self.setDamageDealt(damageDealt or powerLevel*2)
@@ -724,7 +669,7 @@ The `#!define` directive declares a text-based macro, meaning the replacement is
724
669
 
725
670
  For example, given this function:
726
671
 
727
- ```hs
672
+ ```opy
728
673
  #!define sum(a,b) a+b
729
674
  ```
730
675
 
@@ -738,7 +683,7 @@ This problem does not occur with `macro`, which is why you should always use the
738
683
 
739
684
  You can do script macros with `#!define` and the special `__script__` function. For example:
740
685
 
741
- ```hs
686
+ ```opy
742
687
  #!define addFive(x) __script__("addfive.js")
743
688
  ```
744
689
 
@@ -755,7 +700,7 @@ For the technical details:
755
700
 
756
701
  Enums can be declared to avoid manually declaring several macros:
757
702
 
758
- ```java
703
+ ```opy
759
704
  enum GameStatus:
760
705
  GAME_NOT_STARTED = 1
761
706
  GAME_IN_PROGRESS
@@ -780,7 +725,7 @@ Note that enum members are inlined, so if you use a value such as `getAllPlayers
780
725
 
781
726
  Runs a JavaScript post-processing script after OverPy finishes compilation. This can effectively resolve certain compilation errors caused by language translation.
782
727
 
783
- ```hs
728
+ ```opy
784
729
  #!postCompileHook "hooks/postCompileHook.js"
785
730
  ```
786
731
 
@@ -793,7 +738,7 @@ Runs a JavaScript post-processing script after OverPy finishes compilation. This
793
738
 
794
739
  Example `hooks/postCompileHook.js`:
795
740
 
796
- ```js
741
+ ```opy
797
742
  content = content.replace(/abc/g, "def");
798
743
 
799
744
  // If the last operation returns an interpreter object,
@@ -809,7 +754,7 @@ Sets a prefix for all subsequent rules. The prefix is applied to the rule name u
809
754
 
810
755
  If a `#!rulePrefix` directive is in an included file, it only takes effect for rules within that file (and its child includes, if they don't have their own `#!rulePrefix`), after the directive. When the included file ends, the prefix is restored to what it was before the include.
811
756
 
812
- ```hs
757
+ ```opy
813
758
  #!rulePrefix "Effects"
814
759
 
815
760
  rule "Spawn particles":
@@ -846,7 +791,7 @@ The expression has to evaluate to a string without arguments.
846
791
 
847
792
  Switches are a good way to transform an if/elif/else chain into something more optimized:
848
793
 
849
- ```python
794
+ ```opy
850
795
  switch A:
851
796
  case Hero.ANA:
852
797
  B = 2
@@ -859,7 +804,7 @@ switch A:
859
804
  ```
860
805
 
861
806
  This is equivalent to:
862
- ```python
807
+ ```opy
863
808
  if A == Hero.ANA:
864
809
  B = 2
865
810
  elif A == Hero.ASHE:
@@ -876,7 +821,7 @@ Keep in mind that fallthrough is enabled, so if you don't have a `break` instruc
876
821
 
877
822
  In the above case, we always set the same variable. Therefore, a dictionary can be used:
878
823
 
879
- ```python
824
+ ```opy
880
825
  B = {
881
826
  0: 4,
882
827
  Hero.ANA: 2,
@@ -887,7 +832,7 @@ B = {
887
832
  If the key is not found in the dictionary, `null` will be returned. However, you can specify a `default` key for a default value.
888
833
 
889
834
  This dictionary is internally converted to:
890
- ```python
835
+ ```opy
891
836
  B = [4, 2, 3][[0, Hero.ANA, Hero.ASHE].index(A)]
892
837
  ```
893
838
 
@@ -913,7 +858,7 @@ The `spacesForLength` and `strVisualLength` macros can also be used. `spacesForS
913
858
 
914
859
  This is useful to do alignment tricks. For example, the following code displays a key:value attribute list:
915
860
 
916
- ```py
861
+ ```opy
917
862
  #We use strVisualLength("M")*20 to specify a string that is longer than the longest key/value.
918
863
  #!define dictLine(key, value) "{}{}{}{}".format(spacesForLength(strVisualLength("M")*20 - strVisualLength(key)), (key), (value), spacesForLength(strVisualLength("M")*20 - strVisualLength(value)))
919
864
  rule "iwt":
@@ -933,7 +878,7 @@ The `ruleCondition` value compiles to all the conditions of the rule, joined wit
933
878
 
934
879
  This is mostly useful in `waitUntil`, so you can do the following:
935
880
 
936
- ```py
881
+ ```opy
937
882
  rule "":
938
883
  @Event eachPlayer
939
884
  @Condition ...
@@ -952,7 +897,7 @@ You can use the `compressed()` function to store a large array of numbers or vec
952
897
 
953
898
  For example:
954
899
 
955
- ```py
900
+ ```opy
956
901
  mapData = compressed([vect(-65.048, -18.007, -80.036), vect(0.92, 12.58, -18.32), vect(194.041, 6.128, -74.097), ...])
957
902
  ```
958
903
 
@@ -966,7 +911,7 @@ You can also use the `#!useVariableForCompressionAlphabet` compiler option to us
966
911
 
967
912
  `splitDictArray` maps an array of dictionaries to variables. For example:
968
913
 
969
- ```python
914
+ ```opy
970
915
  splitDictArray({
971
916
  hero: waveHeroes,
972
917
  length: waveLengths
@@ -979,14 +924,14 @@ splitDictArray({
979
924
 
980
925
  Will yield the following:
981
926
 
982
- ```python
927
+ ```opy
983
928
  waveHeroes = [Hero.ANA, Hero.SOLDIER, Hero.HAMMOND]
984
929
  waveLengths = [3, 8, null]
985
930
  ```
986
931
 
987
932
  For a terser syntax, `tabular` can be used, where the second argument is a raw array (not an array of arrays!)
988
933
 
989
- ```
934
+ ```opy
990
935
  tabular([waveHeroes, waveLengths], [
991
936
  Hero.ANA, 3,
992
937
  Hero.SOLDIER, 8,
@@ -1075,13 +1020,13 @@ The `___` function is the same as the `_` function, but will never resolve the t
1075
1020
 
1076
1021
  This is useful when several strings are used in a single display action. For example:
1077
1022
 
1078
- ```python
1023
+ ```opy
1079
1024
  bigMessage(text=[t"Choice 1", t"Choice 2"][eventPlayer.choice])
1080
1025
  ```
1081
1026
 
1082
1027
  This will add the code to resolve the translation twice, but it can be optimized to:
1083
1028
 
1084
- ```python
1029
+ ```opy
1085
1030
  bigMessage(text=_([___("Choice 1"), ___("Choice 2")][eventPlayer.choice]))
1086
1031
  ```
1087
1032
 
@@ -1095,7 +1040,7 @@ Wrapping a string with `___` has the same caveats as putting a translated string
1095
1040
 
1096
1041
  **Example**:
1097
1042
 
1098
- ```py
1043
+ ```opy
1099
1044
  #!translations en fr zh
1100
1045
 
1101
1046
  rule "Player got kill":
@@ -1136,7 +1081,7 @@ The way translations work is by casting a value to string (usually `Color.WHITE`
1136
1081
 
1137
1082
  However, this cast to string **must** be done client-side. You cannot do the following:
1138
1083
 
1139
- ```py
1084
+ ```opy
1140
1085
  # Will not work properly, do not use!
1141
1086
  eventPlayer.language = ["White", "Blanc"].index("{}".format(Color.WHITE))
1142
1087
  ```
@@ -1145,7 +1090,7 @@ This will appear to work, but this is evaluated server-side, and what it actuall
1145
1090
 
1146
1091
  When in a reevaluated HUD text, `t"Some string"` resolves to:
1147
1092
 
1148
- ```py
1093
+ ```opy
1149
1094
  ["Some string", "Une chaîne"][["White", "Blanc"].index("{}".format(Color.WHITE))]
1150
1095
  ```
1151
1096
 
@@ -1167,7 +1112,7 @@ Note: I used arrays in the example. OverPy concatenates arrays to strings using
1167
1112
 
1168
1113
  This is a bug introduced in Overwatch 2 where a rule condition check does not properly trigger while a variable is chased. For example:
1169
1114
 
1170
- ```py
1115
+ ```opy
1171
1116
  playervar chasebug = 0
1172
1117
 
1173
1118
  rule "debug":
@@ -1190,9 +1135,21 @@ You would expect "Test chase" to display after 1 second, but the variable goes w
1190
1135
 
1191
1136
  To solve this bug, refactor your code to stop chasing the variable when you need to check it in a rule condition. Eg here, we could put the stop at 1 instead of 10. Then put `@SuppressWarnings w_ow2_rule_condition_chase` on the rule with the rule condition you checked is working. (It is not recommended to disable it for the whole project.)
1192
1137
 
1138
+ ## w_start_rule_crash
1139
+
1140
+ Found by Psyrius:
1141
+
1142
+ It seems like repeatedly calling a `Start Rule` with its option `If Already Executing` set to `Restart Rule` and the subroutine being called has a `Wait` action inside (doesn't matter if there is more code before or after the wait inside the subroutine), the server will eventually crash - even if the server load is low. It will crash more frequently if the rule is called more often. In this workshop code/video, the calling frequency goes from every 1 to every 0.1s
1143
+
1144
+ Edit 1: Crashes at ~465 accumulated restarts, confirmed stack overflow behavior.
1145
+ Edit 2: ~465 = shared pool, not per subroutine.
1146
+ Edit 3: It only accumulates toward the crash if the restarted subroutine has any remaining wait duration inside of it (doesn't matter how much duration is remaining).
1147
+
1148
+ See https://discord.com/channels/570672959799164958/1485108807855247540/1485108807855247540 for more info.
1149
+
1193
1150
  ## w_wait_until
1194
1151
 
1195
- ```py
1152
+ ```opy
1196
1153
  globalvar waitUntilBug = 0
1197
1154
 
1198
1155
  rule "debug":
@@ -1216,7 +1173,72 @@ Since non-zero numbers are truthy, you would expect both `print` statements to r
1216
1173
 
1217
1174
  You can safely ignore this warning if you are using a variable that can only be a boolean.
1218
1175
 
1219
- ----------------------
1176
+ # Language Server Protocol
1220
1177
 
1178
+ OverPy can be built as a standalone Language Server Protocol server for editors that support custom LSP commands.
1179
+
1180
+ ```sh
1181
+ pnpm install
1182
+ pnpm run compile-lsp
1183
+ node out/languageServer.js --stdio
1184
+ ```
1185
+
1186
+ Configure your editor to run `node /path/to/overpy/out/languageServer.js --stdio` for `.opy` files. The server currently provides compiler diagnostics, completions (including type-aware argument values), signature help, hover documentation, semantic tokens, parameter inlay hints, document symbols, folding ranges, workspace go to definition, references and rename for user symbols, and warning suppression code actions by reusing the OverPy compiler metadata. You can also document your own `globalvar`/`playervar`/`def`/`enum` declarations with comments and they show up in hover and completion popups.
1187
+
1188
+ See [`docs/language-server.md`](docs/language-server.md) for the architecture, per-feature internals, and the comment-documentation conventions.
1189
+
1190
+ # NPM usage
1191
+
1192
+ ![NPM Version](https://img.shields.io/npm/v/overpy) ![NPM Type Definitions](https://img.shields.io/npm/types/overpy)
1193
+
1194
+ Install:
1195
+
1196
+ - As a dependency: `pnpm i overpy`
1197
+ - Global CLI (optional): `pnpm i -g overpy`
1198
+ - One-off CLI without install: `npx overpy --help`
1199
+
1200
+ JS/TS API usage:
1201
+
1202
+ ```js
1203
+ // JavaScript (CommonJS)
1204
+ const overpy = require("overpy");
1205
+
1206
+ async function main() {
1207
+ await overpy.readyPromise;
1208
+ const compileResult = await overpy.compile(
1209
+ 'rule "hello":\n @Event global\n wait(1)\n',
1210
+ "en-US",
1211
+ process.cwd(),
1212
+ "inline.opy"
1213
+ );
1214
+ console.log(compileResult.result);
1215
+ }
1216
+
1217
+ main().catch(console.error);
1218
+ ```
1219
+
1220
+ ```ts
1221
+ // TypeScript
1222
+ import * as overpy from "overpy";
1223
+
1224
+ async function main() {
1225
+ await overpy.readyPromise;
1226
+ const workshopText = "rule(\"hello\") { event { Ongoing - Global; } actions { Wait(1, Ignore Condition); } }";
1227
+ const decompiled = overpy.decompileAllRules(workshopText, "en-US");
1228
+ console.log(decompiled);
1229
+ }
1230
+ ```
1231
+
1232
+ CLI usage:
1233
+
1234
+ - Compile a file: `overpy compile -i script.opy -o script.txt`
1235
+ - Compile from stdin to stdout: `cat script.opy | overpy compile > script.txt`
1236
+ - Decompile a file: `overpy decompile -i workshop.txt -o script.opy`
1237
+ - Decompile from stdin to stdout: `cat workshop.txt | overpy decompile --ignore-variable-index > script.opy`
1238
+
1239
+ Run `overpy --help` to see all options (`-l/--language`, `--root`, `--main-file`, `--ignore-variable-index`, `--ignore-subroutine-index`).
1240
+
1241
+
1242
+ ----------------------
1221
1243
 
1222
1244
  If you are still confused about something, or want to discuss a feature, please [join the discord](https://workshop.codes/discord) #hll-scripting :)