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!
Warcraft Studio Code is an in-game code editor, compiler, and debugger, 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.
Your code can be easily added to the editor by wrapping it in your map script with a WSCode function:
Lua:
WSCode.Transpile([[
function MyFunction(a, b, c)
return a + b + c
end
]])
Adding a library to the editor for debugging this way will not interfere with your map initialization, and your libraries will be able to initialize as usual.
Features
Import code to be automatically loaded in the Code Editor on map launch.
Syntax highlighting and variable and function preview.
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.
Recompile a library and alter function behavior in-game.
Export code as a text file and reimport it in a later session.
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 the majority of your code in your IDE of choice, then import it into WS Code and get your script to the finish line completely in-game.
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
Copy the Warcraft Studio Code script file into your map. Next, make sure you have all the requirements imported. These are:
Requirement
Description
DebugUtils
Warcraft Studio Code requires an altered version of DebugUtils. I'm in contact with Eikonium to see if he can update his live version to make it compatible. Until then, get it from the test map.
.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.
FileIO (optional)
Required for loading code from a file into the editor.
After importing all required files, launch your map and test if the editor is working correctly before doing anything else. Type -wscode to open it. Once it has been initialized, you can hide or show it by pressing F1 (customizeable).
Importing Code
Compiling Code
Debugging Code
Exporting Code
Importing Code
Smaller snippets of code can be directly copied into the code editor with Copy & Paste. The editbox has a limit to how many characters it can hold, so anything about four or five lines will not be processed.
To import larger script, use can use the WSCode.Store, WSCode.Compile, or WSCode.Transpile functions.
Lua:
function MyFunction(arg1, arg2)
return arg1 + 2*arg2
end
--==>
WSCode.Store([[
function MyFunction(arg1, arg2)
return arg1 + 2*arg2
end
]])
WSCode.Store: Simply stores the code in the editor upon map launch.
WSCode.Compile: Executes the provided code and stores it in the editor. This function is especially useful for libraries that need to initialize on map initialization because other libraries rely on them as it will not alter its initialization in any way.
WSCode.Transpile: The same as Compile, but transforms the code into debug mode. You can only use breakpoints and view variables of code that has been transpiled into debug mode.
These functions can safely be called from the Lua root as long as WSCode is above.
The Parse Function
The Parse function is a more powerful evolution of the Store, Compile, and Transpile function. It enables you to import code much more conveniently, adds function previews to your custom functions, and protects you from being yeeted into the main menu at map initialization. The Parse function is used similarly to the other functions, except it is designed to wrap your entire map script.
Create a new script directly below your essential libraries, which should be arranged in this order:
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.
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
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.
@transpile
Transpiles the current script file into debug form.
@store
Skips executing the current script file at map initialization and adds it to the editor.
@persistent
Sets the variable declared in the current line as a persistent variable. A persistent variable will be copied over from the previous iteration of a library when it is recompiled. This is useful when you have data tables in your library that get filled at runtime and you don't want them to be overwritten when you make changes. Alternatively, you can enable the "Persistent Upvalues" flag. Both the current and previous iteration of the library must have been compiled in debug mode.
@persistents
@endpersistents
Declares all variables between these two statements as persistent variables.
@ephemeral
Sets the variable declared in the current line as explicitly not persistent when "Persistent Upvalues" is enabled.
Side Effects
Using the Parse function to wrap your map script has some minor side effects; some are good and some are bad:
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.
If you have a runtime error in the Lua root in one of your scripts, you will also not be yeeted into the main menu and all subsequent scripts will still attempt to compile.
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.
Compiling Code
To open the Code Editor, type -wscode or call WSCode.Enable. You execute your code by pressing the Execute button.
In general, there are two types of code chunks that you can write:
Lua:
local u = CreateUnit(Player(0), FourCC "hfoo", 0, 0, 0)
SetUnitVertexColor(u, 255, 0, 0, 0)
A set of instructions written into the root of your code chunk will be executed immediately as you press the Execute button. This is useful for doing quick tests of natives or other functions.
Lua:
function ReturnSumOfThree(a, b, c)
return a + b + c
end
Functions defined in your code chunk will not be executed immediately, but instead defined, just as if the interpreter read them in during map initialization. Global functions will overwrite old ones. You can now call those functions externally, either through WSCode, IngameConsole or through ingame events.
You can recompile an entire library this way. You can set the "Run Init" flag to automatically execute all OnInit functions (Total Initialization). These OnInit functions most likely create triggers and other handles, which should be replaced to properly and fully overwrite the previous iteration of your library. To clean-up the handles of previous compilations, enable the "Clean Handles" flag. However, recompiling a complex library this way will most likely lead to unpredictable side effects.
Therefore, it is crucial that the "Persistent Upvalues" flag and the WRAP_ALL_FUNCTIONS_IN_CALLER config option are enabled. The former prevents data tables or variables that do not get initialized immediately from being overwritten and the latter ensures that references to functions declared in the library are correctly updated to their new definitions.
Debugging Code
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 in the variable viewer.
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 unforseen consequences. The best solution here is to write your code using ALICE, as you can halt the cycle and advance in the execution step-by-step.
Do not put breakpoints before all event response functions such as GetTriggerUnit have been called as those functions will return nil otherwise.
Exporting Code
Once you have debugged your code successfully, you can export it using the Export button. The code will be written to a file in the CustomMapData folder using the Preload natives. To extract your code from the file, simply replace all occurences of call Preload( " and " ). If you've made only minor changes to your code, copying them over manually might be more convenient.
If you've imported FileIO, you can now also easily reimport your code later using the Load button. The import may sometimes fail and crash the game (unresolved issue with FileIO).
Thank you to @Eikonium for help with error handling and for updating DebugUtils to be compatible.
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.
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.