Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
Note: Some of the information shown in these videos is outdated.
Overview
Installation
Tutorial
Change Log
Credits
Overview
Warcraft Studio Code is an in-game debugger, compiler, and editor, designed to be a complement to your other coding tools by allowing you to rapidly iterate between different versions of your functions and locate those extremely elusive bugs. WS Code allows you to effortlessly import code snippets, functions, or even entire libraries and debug them in-game.
Features
Import code to be automatically loaded in the Code Editor on map launch.
Halt function execution with breakpoints and execute functions line-by-line.
View the values of local variables and function parameters at various steps within the function.
View the content of tables in the variable viewer.
Syntax highlighting and variable and function preview.
Recompile a library and alter function behavior in-game or use the quick restart feature to relaunch the map with an altered map script.
Remember changes made in the editor and reimport code snippets automatically on map relaunch.
Limitations
Warcraft Studio Code is not meant to be a replacement for an external IDE, but a complement to it. The editor has many basic features, but it is limited by the Warcraft 3 API and, of course, cannot compete with the plethora of features and improvements that have been added to modern IDEs over the years. However, its real strength lies in giving you the ability to rapidly iterate your code and to debug it much more efficiently. I recommend that you write all of your code that's supposed to make it into your final map in your IDE of choice, then import it into WS Code with Ctrl+A, Ctrl+C, and Ctrl+V. The code that you're writing to just test stuff, however, you can write completely inside the in-game editor.
WS Code uses an editbox frame to capture your text inputs, which is similar to the frame that pops up when you chat to other players. This frame captures all key inputs, which means that they cannot be registered by the game. Therefore, hotkeys such as Ctrl + Z do not work. There are some workarounds used to achieve these features, but it still stands as the biggest limitation of the editor.
The editor will not work if the mouse cursor is not over terrain. This will eventually be fixed when the full version of the Mouse Tracker is released (sometime after the release of Winds of Winter).
To-Do List
Variable viewer slider for when too many variables are shown.
Auto-sync export files with IDE folder.
Installation
Read the installation instructions carefully. Installing Warcraft Studio Code correctly requires some time, but after that it should be smooth sailing.
Copy the Warcraft Studio Code script file into your map. Next, make sure you have all the requirements imported. These are:
.fdf files and textures required for the Code Editor can be found in the test map or in the appended zip-file. Importing the 350kB Consolas.ttf file is optional.
I recommend that you save your map in folder mode and then copy&paste all assets from the zip file into its root directory.
Makes mouse-click detection more accurate. If you have mostly flat terrain, this is not important. Otherwise, you might sometimes click on the wrong line or character, which can be annoying.
Make sure the imported scripts are in the correct order. The order should be
DebugUtils at the top.
Then, WarcraftStudioCode, TotalInitialization, WSCodeFunctionPreviews, and FileIO in any order.
TotalInitialization is not required, but if you're using it, it should be here.
Do not copy the WSCodeParseBegin file from the test map. First, launch launch your map and test if the editor is working correctly. You should be able to open the editor by pressing Ctrl + F1.
Once the editor is working correctly, you should continue by setting up the Parse wrap, which is explained under Tutorial -> Parsing the Map Script.
Using the Editor
Parsing the Map Script
Initialization
In-Game Code Editing
Wrapping GUI Triggers
Enabling Quick Restart
Using the Editor
Open the editor by pressing Ctrl + F1 (customizable). You can start typing code and execute it by pressing the Execute button at the bottom.
You can paste any code from outside the game into the editor by focusing the text field and pressing Ctrl + V.
You can only enter text while the hidden editbox of the editor is focused. While it is focused, hotkeys do not work. To defocus the editor, left-click anywhere on the map.
Breakpoints
To the right, next to the line numbers are hidden buttons that you can click (left-click or right-click) to insert breakpoints or spyglasses. When your script encounters a breakpoint, the code execution will halt and you can view all currently visible variables in the variable viewer. When your script encounters a spyglass, the code execution will continue, but the variable viewer will be updated with a snapshot of the variables currently visible at that line of code. For breakpoints and spyglasses to work, your code must have been transpiled into debug form.
Once the code execution has been halted, you can resume it by pressing F3 or F4. F3 will execute the next line; F4 will advance to the next breakpoint. Note that hotkeys don't work as long as a Code Editor chat input box is focused, so always click somewhere outside of the editor to defocus it first.
All currently visible variables and their values are displayed in the variable viewer. You can view the full value of variables and the content of tables by clicking on them.
You have to be careful that a periodic callback function is not invoked repeatedly by a periodic event as you're trying to debug it. This might lead to unforeseen consequences. The best solution here is to write your code using ALICE, as the cycle is automatically halted on a breakpoint.
Do not put breakpoints before all event response functions such as GetTriggerUnit have been called as those functions will return nil otherwise.
Note that transpiling a script into debug form will incur approximately a 20x performance decrease. Some computation-intensive scripts (such as ALICE) might not be able to be debugged by WSCode because of this.
Save & Load
You can save your code to a text file, then reload it on a later session with the Save & Load buttons at the bottom. The filenames are based on the name of the current tab. To use this feature, FileIO needs to be imported.
You can also use this feature to export your code if you've done substantial edits to it in-game. The code will be written to a file in the CustomMapData. To extract your code from the file, simply replace all occurences of call Preload( " and " ).
Context Menu
You can right-click anywhere in your code to open the context menu. This menu gives you various options such as Executing a specific line of code.
The Parse Function
The Parse function is the main way to import your code, work on it, and debug it. It works by wrapping your entire map script, storing it as a string, and then executing it in chunks. This enables you to import code much more conveniently, adds function previews to your custom functions, and enables various options to protect you from game crashes or main menu yeets.
Create a new script directly below your essential libraries. Into that script, write:
Lua:
WSCode.Parse([===[
Then, at the very end of your map script, close the long string and function call by creating another script with the content:
Lua:
]===])
Now, your entire map script is wrapped and gets parsed before being executed.
Warning: Do not wrap any of DebugUtils, WarcraftStudioCode, WSCodeFunctionPreviews, TotalInitialization, and FileIO. These should be on top of the map script as explained in Installation.
To enable the Parse function to correctly separate your map script into individual files, you need to begin each script file with a Debug.beginFile call:
Lua:
if Debug then Debug.beginFile "ScriptName" end
or alternatively with
Lua:
---@beginFile ScriptName
WSCode will execute your map script in protected mode in chunks separated by these delimiting tokens. If the initialization of one script file fails, the rest will still execute and once the map initialization completes, the editor will pop up and show you the encountered error.
Parser Instructions
To direct how the parser treats an individual script file, you can use various parser instructions. These are added as comments anywhere in the code and preceded by an @-sign.
@debug
Transpiles the current script file into debug form and adds it to the editor upon map launch, allowing you to do further edits in game, look at its local variables, and halt its execution with breakpoints. If you're writing a new spell or system, simply add this instruction, so that, if the code doesn't behave as expected, you can immediately investigate it in-game.
@nodebug
Prevents the automatic transpilation of a file into debug mode when it is added as a tab in the editor. Use this for scripts that are just too slow in debug mode.
@transpile
Transpiles the current script file into debug form, but does not add it to the debugger. This instruction could be useful for investigating a rare bug that cannot be reproduced.
@store
Skips executing the current script file at map initialization and adds it to the editor. Useful for testing code snippets.
You can also load any script that was parsed with the Pull Script button at the bottom of the editor.
Side Effects
Using the Parse function to wrap your map script has some minor side effects:
You do not get a compile error in World Editor when you have a syntax error in one of your scripts. However, if you launch your map from an external tool, you will not be yeeted into the main menu. Instead, you will get an error showing you the exact location of the problem.
All individual scripts must be able to able to be compiled on their own. Having another wrapper similar to the Parse wrapper inside the wrapped script will lead to syntax errors as the Parse function executes the map script in chunks.
Your map takes slightly longer to load. This increase in loading time is quite small, though, as the parser is well optimized. In my map with 50k+ lines of code, the increase is ~0.3 seconds.
Initialization
WSCode can help you debug your map initialization process by identifying the causes of script errors or map crashes. It also offers its own initialization methods, similar to Total Initialization, but with a few advantages when it comes to debugging.
To debug your map initialization, your map script must be wrapped by the WSCode.Parse function, and to be effective, your script files must be delimited with either Debug.beginFile calls or ---@beginFile tokens.
We cannot set breakpoints via the editor during map initialization, so instead, we use a WSCode.BreakHere() call to halt the map script. Once the map initialization completes, the editor will initialize and pull up the halted script file. The script must be compiled in debug mode by adding ---@debug token.
The behavior of your map script now depends on the initialization method used:
WSCode Initialization: The entire map script will be halted. Blizzard functions will be executed as normal. The map initialization will resume once the halted script reaches the end.
Any other initialization: The script file will be halted, but all other scripts will initialize as normal. If another script depends on the script being halted, it will most likely throw an error.
WSCode initializers offer additional diagnostic options, such as the INIT_DUMP, which will write the last map script being initialized into a text file in the CustomMapData folder, potentially preserving your sanity when trying to find the cause of a game crash. Once you have identified the map script responsible, add a breakpoint with WSCode.BreakHere() and go through line by line until the game crahses. If it fails to crash, this probably means that you are calling a Blizzard native before it is safe to do so.
How to use WSCode Initializers
To use a WSCode initializer, wrap your function in a WSCode.Init call, similar to TotalInitialization:
Lua:
WSCode.InitGlobal(function()
--Do stuff
end)
The initializers and their order of execution are:
WSCode.InitMain
WSCode.InitGlobal
WSCode.InitTrig
WSCode.InitMap
WSCode.InitFinal
Unlike TotalInitialization, WSCode initializers do not use a Require function. Instead, the order of execution is solely determined by the order in which the initializers are called. Therefore, we must control the order of execution of the root of our script files. This is done with the ---@require token. This command tells the Parse function to wait with executing the current map script until all requirements have been executed. The requirements are listed after the require keyword, separated by commas. A ? denotes an optional requirement.
Using WSCode, a script file may look like this:
Lua:
---@beginFile Cataclysm
---@require SpellLibrary, TriggerUtils?
do
local MAX_RANGE = 500*SpellLibrary.RangeModifier --Guaranteed to execute after SpellLibrary, so the SpellLibrary table exists.
WSCode.InitGlobal(function()
SpellLibary.CreateSpell("Cataclysm") --Any InitGlobal function inside SpellLibrary executes first, so the CreateSpell function is guaranteed to work properly.
if TriggerUtils then
TriggerUtils.CreateTrigger("Cataclysm")
end
end)
end
In the unlikely case that you need even more control over the order of initialization, you can set the optional onWhichPass parameter.
Using WSCode initializers has the downside that the system can no longer be removed from the release version. However, with all debugging options disabled, the loading time increase incured from the WSCode.Parse function is negligible. In addition, WSCode is able to write the compiled map script into a text file, which you could import and replace the Parse wrapper with. It even opens up the possibility of a script optimizer in the future.
InitEssential
WSCode.InitEssential is an initializer variant that should be used for libraries that the map or the editor cannot function without. It is executed right before InitFinal and is executed even if the initialization has been halted with a breakpoint. The World2Screen and PrecomputedHeightMap libraries in the test map are initialized with InitEssential.
In-Game Code Editing
WSCode is an excellent tool to prototype code snippets and adjust numbers quickly. Tabs you create and code you write into those tabs are saved between game sessions, so you don't have to worry about losing your progress by not writing in your external editor. You can also pull files from your map script or from Preload files with the respective buttons at the bottom. If your changes require a map restart, you can use the Quick Restart button at the bottom to save the altered map script, then restart the map.
Adjusting numbers
Variables stored in tables or as upvalues can be adjusted directly with the Assign button in the context menu. You can also recompile your library with the numbers adjusted. However, this may break things if you're not careful.
Manipulating game objects
If you want to, for example, fine-tune a special effect or fix the position a frame, you can use the Expose button in the context menu. This will store the selected variable in a global, which you can then manipulate in a second tab.
Fixing functions
Redefining buggy functions requires a complete recompilation of the library. Adjust the faulty logic inside the function, then press Execute to recompile the library. This will have, in many situations, unintended side effects and cause errors unless the compiler setting that protect against those errors are correctly set:
The Smart Persist flag ensures that upvalues inside the library are not overwritten with uninitialized values. For example, you could have a data table that is initialized as an empty table and filled by external function calls. If the Smart Persist flag is not set, it would overwrite that table and delete all stored data when you recompile the library.
WRAP_IN_CALLER_FUNCTIONS must be enabled in the config and the library must have already been compiled in debug mode the first time. Otherwise, existing function references will not be correctly updated to the newly compiled function.
The Run Init flag must be disabled in most cases to make sure handles are not created a second time.
One important problem to solve is how you keep the changes you make in-game synced with your external files. One way to solve this problem is to do all changes in your external IDE, then copy & paste the entire modified script files into the in-game editor. If a pasted chunk contains a "beginFile" anywhere, it is recognized as a script file and automatically added to the appropriate tab, overwriting the code previously stored in that tab. All you need to do is focus the editor's text field anywhere and press Ctrl + V.
Wrapping GUI Triggers
If your map has GUI triggers in it, you can choose to wrap them with the Parse function, so that they can be pulled into the editor just like any Lua script.
The lowest script file in your map will be compiled above all GUI triggers, so if you close the Parse wrap with a script file containing ]===]), only the Lua scripts will be wrapped. To wrap your GUI triggers as well, you have to create a trigger instead with the only content being a custom script line:
Custom Script: end ]===]) if true then
Put this trigger below all other triggers to wrap them.
If you disable this trigger and attempt to re-enable it, you will get an error because the syntax checker will think that this is invalid syntax. To circumvent this, you have to remove the custom script line, enable the trigger, then add it again.
To transpile a GUI trigger into debug mode, you can add a custom script line with the content --@debug anywhere in your trigger. Alternatively, you can create a boolean variable called wsdebug and add the line
Set VariableSet wsdebug = true
to the trigger.
Enabling Quick Restart
Quick Restart is a powerful feature that allows you to do changes to your map script and reload the map without recompiling your map or relaunching Warcraft 3. This is achieved by dumping the map script you edited in the in-game editor to a text file, then discarding the default map script and loading the script from that text file instead.
This feature is disabled by default because it is not compatible with the default version of TotalInitialization that most people are using. You can enable it in the config.
Because the Preload natives that are used to read from text files are not available in the Lua root, reading in the map script is postponed until main. Even if the map script is loaded regularly, reading it in is postponed as WS Code cannot know whether to use the regular map script or the one from the text file until it reads that information from another text file. This does not cause any issues that I know of, but there might be some scripts that rely on being initialized in the Lua root.
To make your map compatible with Quick Restart, update your TotalInitialization to the custom version by @Insanity_AI or, better yet, switch to WSCode initializers. Whether Quick Restart works with that version of TotalInit is untested, but it should work in theory. WSCode initializers are guaranteed to work.
Using Quick Restart
Once Quick Restart is enabled, the Quick Restart button appears in the bottom row. Note that, if you accidentally restart the map from the F10 menu out of habit, all changes made since the last regular map launch are lost.
The best way to make quick changes and keep them synced with your IDE is to edit the script files in your IDE, then copy&paste them into the editor before restarting.
Debugging replays
Quick Restart also allows you to modify the map script before watching a replay, mostly by adding print statements that help you investigate bugs that appeared in the game prior. Modifying the map script by changing the game logic or creating additional objects will most likely desync the replay and is not advised.
Change Log
Version 3.6
Added the MEMORY_LEAK_DETECTION config option. If enabled, all handles created and not destroyed will be tracked and the number of handles created in each tab and line can be viewed by typing -leakreport.
Added the GLOBAL_DEBUG config option. This will enable debug mode for all parsed scripts. Use with caution!
Removed the WRAP_IN_CALLER_FUNCTIONS config option. It is now always enabled.
The Traceback button now prints the ALICE traceback of callbacks if possible (requires ALICE 2.12).
The Quick Restart and Export Modified Script buttons have been merged into one button.
Version 3.5.2
Editor frames are now only created the first time the editor is pulled up.
Fixed an issue where errors on map initialization would cause secondary errors inside WSCode when it is set to RELEASE_VERSION.
Fixed an error that would occur when using the Traceback button on a function with no arguments.
Version 3.5
Varargs are now viewable in the variable viewer.
Added the Export Modified Script Button. This will export the modified script, just like the Quick Restart button, without restarting the map. This can be useful for modifying the map script by adding print statements, then launching the replay to investigate a bug. (both buttons will be merged into one eventually)
Added the PARSE_GUI_TRIGGERS config option to toggle the splitting of GUI triggers into separate scripts. This option is disabled by default as it could lead to incorrectly parsed map scripts.
Removed the AUTO_CLOSE_BRACKETS_AND_QUOTES config option. (was not working correctly)
Removed the VALIDATE_FRAMES config option. (was not working correctly)
Removed the AUTO_ENABLE config option and the -wscode command. It is now always enabled.
Fixed a severe bug that caused the return value of Condition to be discarded when WRAP_ALL_LUA_ENTRY_POINTS is enabled.
Fixed a parsing error on hexadecimal numbers.
Fixed default macros.
Version 3.4.4
Fixed a parser error that would occur when parsing a statement comprised of a function call followed by a table index.
WSCode now automatically sets itself to RELEASE_VERSION if DebugUtils is not present, preventing crashes to main menu.
Fixed an error that would occur when a handle creator function returns nil in debug mode.
Files with names that are truncated due to being too long are now correctly displayed in tracebacks.
Version 3.4.3
Hotfixed an error introduced in 3.4.2.
Added the OVERWRITE_TOTAL_INITIALIZATION config option. This will replace OnInit functions with the corresponding WSCode.Init functions.
The init profiler now also prints the total script time and total parser time.
Version 3.4.2
The visible variables are now updated across all tabs and locked in when an error occurs.
When executing a script file in debug mode, the visible variables are now locked in at the last line until a breakpoint or spyglass is reached.
Fixed a bug where a global could appear twice in the editor when added with View Global.
Fixed an error that was occuring when parsing a script that has a Debug.beginFile occurence within the script (TotalInitialization).
The Parse function is now wrapped in a protected call to prevent crashes to main menu.
@persistent and @ephemeral transpiler commands have been removed.
Version 3.4
Tooltips are now shown for all flags menu and context menu options.
Added a second functionality to the Traceback button. When pressed while no thread is halted, opens the prompt window, allowing you to hook any global function. The hook prints the traceback and arguments when that function is called.
All config options are now stored in the global WSCodeConfig table.
Tabs that are added to the editor through a @debug token are now removed on map launch if that token is removed from the code.
Last Function Call parameters are now displayed correctly.
Fixed a bug that caused all variables beyond the first in an assignment of multiple variables to sometimes appear incorrectly as nil in the variable viewer.
Fixed an issue where naming a local variable "table" in a script would cause that script to crash in debug mode.
Version 3.3.2
Fixed a bug that caused the pointer to sometimes jump to the wrong file when switching tabs.
Fixed an issue that caused files with a single backslash in their names to not be compileable.
Fixed an issue where adding a global with the View Global button would cause that global to be visible twice in a tab in which it is visible natively.
Fixed an issue where globals would sometimes not be updated in the variable viewer when modified outside of the current script.
Version 3.3
Tabs with uncompiled changes now get an asterisk before their name.
Tabs that were not compiled in debug mode now have their names displayed in blue.
Tabs for which the changes have not yet been saved to the disk now have their names displayed in red.
Added the Reset all line highlights option to the Demolish button.
The World2Screen transform function for mouse-click detection can now be customized in the config.
Any subfolders within file names displayed in the tab navigators will now be removed.
String table keys that could be converted to a number will now get quotation marks added in the variable viewer to make it clear they are strings.
Fixed an issue where you could not use Assign on upvalues declared without an assignment.
Fixed a bug that caused semicolons to be removed from strings in debug mode.
Fixed a bug that caused functions declared inside other functions to sometimes not be correctly transpiled into their debug form.
Slightly improved performance of code transpiled to debug mode.
Version 3.2
All tabs added to the editor will now consistently be transpiled into debug mode.
Added the @nodebug parser command to prevent the automatic debug mode transpilation.
Added the ALWAYS_HALT_ALICE_ON_BREAKPOINT config option.
Added the PROFILE_INIT diagnostic option, which prints the total times spent initializing each script file during map initialization.
Added the INIT_OUTSIDE_OF_WRAP_WARNING diagnostic option, which warns you if you accidently call a WSCode.Init function from outside the Parse wrap.
Metatables for tables are now listed under__metatable in the variable viewer.
You can now go to a specific line using the search bar by typing ":X", where X is the desired line number.
Fixed a bug that caused "\\" to transform into "\" when loading a script from a text file.
Fixed an issue that caused upvalues passed to dynamically generated functions to not be displayed correctly in the variable viewer.
Fixed an issue where a closed tab would not be reopened when a breakpoint is reached in that tab.
Fixed an issue where escaped quotation marks were recognized as string delimiters for syntax highlighting.
Fixed an issue where the syntax highlighter would not recognize a long string being closed if it was opened on the same line.
Fixed an issue where the traceback button was not working if the code was halted through WSCode.BreakHere.
Fixed an various fringe errors with parsing script files in debug mode.
Version 3.1
Added the Easy Import feature: When pasting a code chunk into the editor, if a "beginFile" is found anywhere in the text, the chunk will automatically be added to a new tab with the appropriate name or overwrite the code currently in that tab if it already exists. To do an easy import, simply press Ctrl + V while the editor is focused.
When pressing the Quick Restart button, instead of restarting immediately, first a dialog opens which allows you to add additional script files to be transpiled into debug mode upon restart.
Scripts that were previously imported into the editor are now automatically transpiled into debug mode when the map is started (ENABLE_QUICK_RESTART must be set).
Consolidated the Remove Breakpoints and Destroy Handles button into a single Demolish button, which can be used to delete all code, remove all breakpoints, or destroy all handles.
Fixed an issue where breakpoints in a tab would not work if an error was generated previously in that tab.
Fixed a bug that caused scripts with long strings delimited by [=[ to not compile in debug mode.
Version 3.0.4
Fixed a bug that caused tracebacks to disappear in WSCode initializer functions.
Fixed a transpiler error that was caused by a missing whitespace.
Fixed a bug that caused tabs to sometimes not automatically reopen on a Quick Restart.
Fixed a bug that caused the variable viewer to sometimes display nil instead of the variable values in table fields of tables added with the View Global button.
Version 3.0.3
The Quick Restart button now instantly restarts the game using the RestartGame native.
Fixed transpiler problems with quotation mark literals, goto, and function definitions inside functions.
Fixed a bug where a script would sometimes be loaded twice when using Quick Restart.
Fixed an issue where the ON_EXPAND and ON_COLLAPSE callback functions where switched up.
Version 3.0.2
Hotfixed an issue that caused the WRAP_ALL_LUA_ENTRY_POINTS config option to break Condition and Filter.
Removed the functions that were accidentally added to callbacks set in the config.
Version 3.0
Added the WSCode.Init functions. These functions provide a way to initialize scripts and handle dependencies using the Parse function with the ---@require parser instruction. Initializations using WSCode.Init functions are fully yieldable, and can be analyzed and debugged, before being resumed.
Incorporated the "-exec" command from IngameConsole.
Added the Quick Restart button. When pressed, WSCode exports the map script to a text file, respecting all changes made within the editor. The map script will be loaded from that file on the next map launch.
(Experimental) Added the ENABLE_MAP_SCRIPT_SHARING setting. With this setting, you can write and read the map script to and from a save file. This allows you to update your map script for playtests without compiling a new version of the map if only the map script changes. (This feature may also be the starting point for a map optimizer later down the road).
Added the INIT_DUMP config option. When enabled, the script name of the last script being executed during initialization will be written to the file WSCodeInitDump.txt. Useful for investigating map crashes. This feature works with WSCode.Init functions, but not TotalInit functions.
Added the View Global button, which adds a global variable to the variable viewer.
Added the Show Traceback button, which prints the traceback of the currently halted thread.
Added the Expose button to the context menu, which stores the value of a local variable to a global with the specified name.
Added the Assign button to the context menu, which allows you to easily set a new value for any upvalue or table field.
Removed the Export Script button and changed the Load Script feature to work with auto-saves instead.
Tabs that were not created by the parser on a previous session are now automatically reloaded on a map restart.
Added the RELEASE_VERSION config option. When set to a release version, all code alterations and all features other than the Parser are disabled.
Added the ON_ERROR callback function.
Added the Hide Functions flag, which will hide all functions from the variable viewer while in the root.
The line numbers in the editor now light up slightly if a line in a script has been executed at least once.
The flags which are enabled by default are no longer set in the config, but instead saved and retrieved from the previous session.
The Execute function in the context menu can now be activated without having text selected and will execute the current line of the cursor when doing so.
(Untested) WS Code should now be multiplayer-safe. For use in multiplayer, importing World2ScreenSynced is required.
Removed the Compile, Transpile, Store, and Execute functions.
Removed the STORE_HANDLES_IN_NO_DEBUG_MODE setting.
Removed the Go to Next Step (ALICE) button.
WRAP_ALL_LUA_ENTRY_POINTS "editoropen" option removed. It is now either true or false.
The variable viewer will now show the number of hidden variables in the header.
The variable viewer now correctly updates when switching to a different tab.
Fixed a bug where the variable viewer would not automatically open on an error.
Fixed a bug that caused local functions to not be callable with Evaluate.
Fixed an issue where special characters in the search bar were not being escaped.
Fixed a bug where the Last Function Call would pop up in the variable viewer even if no function was executed yet in the current tab.
Fixed a bug that caused line numbers in tracebacks for scripts compiled in debug mode to be inaccurate.
Fixed a bug that caused text to sometimes reappear after deleting it with Undo.
Fixed a bug that caused crashes when a triggeraction was added to a trigger with nil as the callback argument.
Version 2.4
You can now set an execution limit to protect against accidental infinite loops.
Fixed the Evaluate feature.
When a breakpoint is added to the map initialization with BreakHere, it will now wait with initializing the editor until it is safe to do so.
Executing code with the Execute feature will no longer interrupt currently halted threads.
Fixed a faulty pattern that would sometimes result in wrong file names in error messages.
Fixed an error that would occur when TimerStart is called with nil as the timer argument.
HALT_ALICE_ON_BREAKPOINT is no longer a flag. ALICE is now always halted if the code execution stops within the ALICE main thread.[/CODE]
Version 2.3.2
Fixed the missing ON_EXPAND and ON_COLLAPSE callback functions.
Fixed a bug that caused thread crashes when WRAP_ALL_LUA_ENTRY_POINTS was not enabled.
Fixed an issue where the editor would add an additional string delimiter when entering one to close a string if AUTO_CLOSE_BRACKETS_AND_QUOTES is enabled.
Version 2.2 & 2.3
All of the bug fixes. ALL OF THEM! That's right, no more bugs from here on out!
The Persist Upvalues feature has been replaced by the Smart Persist feature. Smart Persist attempts to keep data tables and variables that are initialized as nil intact when recompiling a library while keeping all other variables unaffected. It now also affects globals.
The Clear Tab button has been replaced with the Clear Handles button.
Version 2.1
Significantly reduced text input lag. Thanks to @moddiemads for the suggestion.
Tabs can now be closed.
Reduced the amount of memory leaks caused by the coroutine wraps.
Version 2.0
Added the Parse function to make importing code super easy, barely an inconvenience. Also enables a multitude of other goodies.
Added a search bar.
Added the option to wrap all Blizzard API points in coroutines, so that the main thread can be yielded on a breakpoint.
Added a context menu that is enabled by right-clicking on the text field.
Thank you to @Eikonium for help with error handling and for updating DebugUtils to be compatible.
Thank you to alrun3 for repeated help with bugfixing.
Thank you to @Insanity_AI for suggestions and help with initialization.
Thank you to @moddiemads for suggesting the async key press event detection.
Made possible by the foundational work of @Bribe and others on initialization.
This system revolutionizes the debug workflow one step further. Combined with DebugUtils it basically provides an extremely less painful experience while debugging, and is perfect for quick prototyping.
The only thing that felt odd was the...
The full version is up. Here are the main new features added:
Completed parser: The parser should now be able to take any code chunk and transform it into its debug form. This means that you should be able to import an entire library, debug it, and recompile it in-game. There might be one or two weird syntax combinations where it fails, so if you encounter them, please report them so I can address the issue.
Transpile function: The transpile function takes a code chunk and transpiles it into its debug form, adds it to the Code Editor, and then executes it. If wrapped around a library in the Lua root, the library initialization will be unimpaired, and all other libraries that depend on it will also be able to initialize. Adding a library to the debugger this way only requires two extra lines of code.
Run Init / Clean Handles: New compilation options allow you to properly recompile your library by automatically running OnInit functions and cleaning up triggers and other handles created in previous compilations.
Textmacros: You can now define textmacros in the config, which can be used, for example, to insert the current camera or mouse coordinates into your code.
Make sure you're using the newest version of DebugUtils if you're using WS Code. Thank you to @Eikonium for implementing the new features.
Version 1.3
The table viewer has been removed. Clicking on a variable now shows it in the variable viewer.
Added the ability to view nested tables.
Added the Load button, which loads the code previously saved by the Export button. You can use this to quickly store the code you wrote in-game, restart the game, and add the code back into the editor. Requires FileIO.
Added function previews for natives and BJ functions. Requires the FunctionPreviews script.
Moved the hide and expand buttons to the top bar with new icons.
The parser can now parse multiline comments opened by [=[ correctly.
Fixed a bug with the parser introduced in previous version.
You can now add additional tabs to the editor.
Added the "spyglass" feature. By clicking on a breakpoint button twice, you put a spyglass on that line. The variable viewer will display a snapshot of the variables on that line, even when the code continues executing.
In this update for Version 1.5, I rewrote the editor and changed the individual lines from editboxes to regular text frames with only one hidden editbox to capture user input. This reduced the jankiness by 73.6% and opened up many new possibilities such as syntax highlighting and text selection.
Other noteable features added include the last function call display, which displays the arguments with which a function was called.
All of the bug fixes. ALL OF THEM! That's right, no more bugs from here on out!
The Persist Upvalues feature has been replaced by the Smart Persist feature. Smart Persist attempts to keep data tables and variables that are initialized as nil intact when recompiling a library while keeping all other variables unaffected. It now also affects globals.
The Clear Tab button has been replaced with the Clear Handles button.
Fixed the missing ON_EXPAND and ON_COLLAPSE callback functions.
Fixed a bug that caused thread crashes when WRAP_ALL_LUA_ENTRY_POINTS was not enabled.
Fixed an issue where the editor would add an additional string delimiter when entering one to close a string if AUTO_CLOSE_BRACKETS_AND_QUOTES is enabled.
I come back to this community after a decade and this is the first thing I see lmao, absolutely insane
(I'm inspired to keep learning how to code spells xD)
I come back to this community after a decade and this is the first thing I see lmao, absolutely insane
(I'm inspired to keep learning how to code spells xD)
There was some talk about making a proper JASS2Lua transpiler, and someone wanted to commission me, but that came to nothing unfortunately. I think that's the route to go; not manually translating everything. It would be a considerable amount of work, but something a lot of people would be interested in, so you could maybe find other people who would be willing to throw some bucks in for a commission.
There was some talk about making a proper JASS2Lua transpiler, and someone wanted to commission me, but that came to nothing unfortunately. I think that's the route to go; not manually translating everything. It would be a considerable amount of work, but something a lot of people would be interested in, so you could maybe find other people who would be willing to throw some bucks in for a commission.
We can discuss it. For the time being though, I think you should try vJass2Lua and see how far you can get with it. I don't know exactly what the limitations are.
3.0 is a large update, fixing many of the remaining bugs with the editor, simplifying the error handling and making it more consistent, and introducing new comfort features, such as the Quick Restart. In-Game code editing has been improved by giving you the ability to easily change numbers on the fly, even in multiplayer playtests. FileIO has been integrated even more deeply into the system, being used to save settings, tabs, and code between sessions. 3.0 requires the latest version of Debug Utils (2.5+) as well as the stable version of FileIO (2.0+). Special thanks to @Eikonium for helping with the error handling and for making his DebugUtils fully compatible!
Version 3.0
Added the WSCode.Init functions. These functions provide a way to initialize scripts and handle dependencies using the Parse function with the ---@require parser instruction. Initializations using WSCode.Init functions are fully yieldable, and can be analyzed and debugged, before being resumed.
Incorporated the "-exec" command from IngameConsole.
Added the Quick Restart button. When pressed, WSCode exports the map script to a text file, respecting all changes made within the editor. The map script will be loaded from that file on the next map launch.
(Experimental) Added the ENABLE_MAP_SCRIPT_SHARING setting. With this setting, you can write and read the map script to and from a save file. This allows you to update your map script for playtests without compiling a new version of the map if only the map script changes. (This feature may also be the starting point for a map optimizer later down the road).
Added the INIT_DUMP config option. When enabled, the script name of the last script being executed during initialization will be written to the file WSCodeInitDump.txt. Useful for investigating map crashes. This feature works with WSCode.Init functions, but not TotalInit functions.
Added the View Global button, which adds a global variable to the variable viewer.
Added the Show Traceback button, which prints the traceback of the currently halted thread.
Added the Expose button to the context menu, which stores the value of a local variable to a global with the specified name.
Added the Assign button to the context menu, which allows you to easily set a new value for any upvalue or table field.
Removed the Export Script button and changed the Load Script feature to work with auto-saves instead.
Tabs that were not created by the parser on a previous session are now automatically reloaded on a map restart.
Added the RELEASE_VERSION config option. When set to a release version, all code alterations and all features other than the Parser are disabled.
Added the ON_ERROR callback function.
Added the Hide Functions flag, which will hide all functions from the variable viewer while in the root.
Added the DUMP_TRANSPILED_SCRIPTS_TO_FILE config option. When enabled, the transpiled scripts of files in debug mode will be written to a text file.
The line numbers in the editor now light up slightly if a line in a script has been executed at least once.
The flags which are enabled by default are no longer set in the config, but instead saved and retrieved from the previous session.
The Execute function in the context menu can now be activated without having text selected and will execute the current line of the cursor when doing so.
(Untested) WS Code should now be multiplayer-safe. For use in multiplayer, importing World2ScreenSynced is required.
Removed the Compile, Transpile, Store, and Execute functions.
Removed the STORE_HANDLES_IN_NO_DEBUG_MODE setting.
Removed the Go to Next Step (ALICE) button.
WRAP_ALL_LUA_ENTRY_POINTS "editoropen" option removed. It is now either true or false.
The variable viewer will now show the number of hidden variables in the header.
The variable viewer now correctly updates when switching to a different tab.
Fixed a bug where the variable viewer would not automatically open on an error.
Fixed a bug that caused local functions to not be callable with Evaluate.
Fixed an issue where special characters in the search bar were not being escaped.
Fixed a bug where the Last Function Call would pop up in the variable viewer even if no function was executed yet in the current tab.
Fixed a bug that caused line numbers in tracebacks for scripts compiled in debug mode to be inaccurate.
Fixed a bug that caused text to sometimes reappear after deleting it with Undo.
Fixed a bug that caused crashes when a triggeraction was added to a trigger with nil as the callback argument.
Fixed a bug that caused tracebacks to disappear in WSCode initializer functions.
Fixed a transpiler error that was caused by a missing whitespace.
Fixed a bug that caused tabs to sometimes not automatically reopen on a Quick Restart.
Fixed a bug that caused the variable viewer to sometimes display nil instead of the variable values in table fields of tables added with the View Global button.
Continuing the quest to increase the convenience of the debugger. You can now easily do all changes to your code in your external IDE, then copy&paste them into the editor, and either recompile the library (super-fast) or do a quick restart (not as fast, but still faster than relaunching the game).
Version 3.1
Added the Easy Import feature: When pasting a code chunk into the editor, if a "beginFile" is found anywhere in the text, the chunk will automatically be added to a new tab with the appropriate name or overwrite the code currently in that tab if it already exists. To do an easy import, simply press Ctrl + V while the editor is focused.
When pressing the Quick Restart button, instead of restarting immediately, first a dialog opens which allows you to add additional script files to be transpiled into debug mode upon restart.
Scripts that were previously imported into the editor are now automatically transpiled into debug mode when the map is started (ENABLE_QUICK_RESTART must be set).
Consolidated the Remove Breakpoints and Destroy Handles button into a single Demolish button, which can be used to delete all code, remove all breakpoints, or destroy all handles.
Fixed an issue where breakpoints in a tab would not work if an error was generated previously in that tab.
Fixed a bug that caused scripts with long strings delimited by [=[ to not compile in debug mode.
Tooltips are now shown for all flags menu and context menu options.
Clicking Evaluate while having no text selected will now open the prompt window, allowing to enter any expression to evaluate.
Added a second functionality to the Traceback button. When pressed while no thread is halted, opens the prompt window, allowing you to hook any global function. The hook prints the traceback and arguments when that function is called.
All config options are now stored in the global WSCodeConfig table.
Tabs that are added to the editor through a @debug token are now removed on map launch if that token is removed from the code.
Last Function Call parameters are now displayed correctly.
Fixed a bug that caused all variables beyond the first in an assignment of multiple variables to sometimes appear incorrectly as nil in the variable viewer.
Fixed an issue where naming a local variable "table" in a script would cause that script to crash in debug mode.
This system revolutionizes the debug workflow one step further. Combined with DebugUtils it basically provides an extremely less painful experience while debugging, and is perfect for quick prototyping.
The only thing that felt odd was the benchmarking part: while the fps lowers, there was a point where the game froze for a few seconds, and then came back to life. No idea why that happened.
No major bugs found.
Updated with various bug fixes of bugs found by @Wrda and @Illbean . Also updated the tutorial with a better installation guide and a section on Quick Restart.
Version 3.4.2
The visible variables are now updated across all tabs and locked in when an error occurs.
When executing a script file in debug mode, the visible variables are now locked in at the last line until a breakpoint or spyglass is reached.
Fixed a bug where a global could appear twice in the editor when added with View Global.
Fixed an error that was occuring when parsing a script that has a Debug.beginFile occurence within the script (TotalInitialization).
The Parse function is now wrapped in a protected call to prevent crashes to main menu.
@persistent and @ephemeral transpiler commands have been removed.
This looks amazing. I wish I would have seen it sooner.
Do you think you can make a VScode extension out of this maybe? I for one would probably have much more fun debugging with VScode.
I wouldn't suggest this if it didn't look like you're putting thousands of hours into making this thing great
My idea is to use a socket (like I did here: Wc3Interpreter) and send all the info to the VScode. Not sure if it's really possible, and how hard it is to integrate.
My idea is to use a socket (like I did here: Wc3Interpreter) and send all the info to the VScode. Not sure if it's really possible, and how hard it is to integrate.
You'd have to send information about breakpoints and code changes made in the external IDE to WSCode and return the current state of all local variables via the preload files. The variable state could be exported with an encoder, which then has to decoded into whatever format the external debugger requires. Shouldn't be too hard.
Communication the other direction would be more difficult. The game caches the content of any preload file the first time it is read, so every time you do anything, you'd have to create a new file, so you'd amass a lot of garbage after just a little while. You'd also have to do a periodic check of the file content. One FileIO.Load call is on the order of ~1 millisecond, so doing this on a very fast loop is not adviseable. That means you'd get a noticeable lag in the communication, which I think would be a too big of a turn-off, at least for me.
I'm not sure what the upside of debugging in VSCode is, but I am happy to be convinced of it if there is one. WSCode already makes importing code as easy as possible. My current workflow is that I make all the changes externally and import them with Copy&Paste. I only write tests directly in-game.
Where it is lacking, though, is when it comes to exporting code and syncing that with the IDE (if you do make changes in-game). This is where I think a python script could come in to make things more convenient.
You'd have to send information about breakpoints and code changes made in the external IDE to WSCode and return the current state of all local variables via the preload files. The variable state could be exported with an encoder, which then has to decoded into whatever format the external debugger requires. Shouldn't be too hard.
Personally I have 2 screen and wouldn't really mind having the variable states show ingame (with an option for the user to manually pull what he wants to vscode). Not sure if it's good enough for people with a single screen.
Breakpoints and code changes only need to be synced when you execute/step so it's once per user click so I think not too bad.
Communication the other direction would be more difficult. The game caches the content of any preload file the first time it is read, so every time you do anything, you'd have to create a new file, so you'd amass a lot of garbage after just a little while. You'd also have to do a periodic check of the file content. One FileIO.Load call is on the order of ~1 millisecond, so doing this on a very fast loop is not adviseable. That means you'd get a noticeable lag in the communication, which I think would be a too big of a turn-off, at least for me.
idk, I think having a loop of 0.1 seconds should be alright (see what I did in the Interpreter - most of the time the loop is pretty long, but once I see communication I reduce the period for a while). There will be a lot of garbage, but you can clear old files once new ones are created. You basically need just a few files at each time.
I'm not sure what the upside of debugging in VSCode is, but I am happy to be convinced of it if there is one. WSCode already makes importing code as easy as possible. My current workflow is that I make all the changes externally and import them with Copy&Paste. I only write tests directly in-game.
Where it is lacking, though, is when it comes to exporting code and syncing that with the IDE (if you do make changes in-game). This is where I think a python script could come in to make things more convenient.
Need to use it for a month and see what I think could be better. It already looks a lot better than I hoped for, so maybe I'll just say this is good enough.
idk, I think having a loop of 0.1 seconds should be alright (see what I did in the Interpreter - most of the time the loop is pretty long, but once I see communication I reduce the period for a while). There will be a lot of garbage, but you can clear old files once new ones are created. You basically need just a few files at each time.
Added the Export Modified Script Button. This will export the modified script, just like the Quick Restart button, without restarting the map. This can be useful for modifying the map script by adding print statements, then launching the replay to investigate a bug. (both buttons will be merged into one eventually)
Added the PARSE_GUI_TRIGGERS config option to toggle the splitting of GUI triggers into separate scripts. This option is disabled by default as it could lead to incorrectly parsed map scripts.
Removed the AUTO_CLOSE_BRACKETS_AND_QUOTES config option. (was not working correctly)
Removed the VALIDATE_FRAMES config option. (was not working correctly)
Removed the AUTO_ENABLE config option and the -wscode command. It is now always enabled.
Fixed a severe bug that caused the return value of Condition to be discarded when WRAP_ALL_LUA_ENTRY_POINTS is enabled.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.