We all try to make our code as stable as possible, but sometimes crashes still happen. API crashes can easily stall a game, lead to confusion, slow script development, break game immersion, and cause enormous frustration as you struggle to understand what actually went wrong. Users receive no warning a crash has occurred and must instead figure it out on their own and then navigate to the API page to restart the API. Even then, all you have is a single console line lacking formatting, making it difficult to draw conclusions regarding the source of the problem. Airbag Airbag is a two-part script that wraps the rest of your codebase, isolating the fragile API from exceptions thrown by your installed scripts and providing direct insight to the user when a crash occurs as well as the ability to force an API restart. How Roll20's API concatenates all of your scripts into a single enormous file, hence the sometimes-astronomical line numbers when you receive an exception. Ordinarily, all scripts are self-contained and are themselves compilable, but Airbag's halves are not individually valid, instead relying on each other to function. By sandwiching the rest of your API code in the middle, Airbag acts as an oversized try-catch block and can override API functions called by your other scripts. This means that exceptions within your installed scripts will be caught by Airbag, rather than the API, allowing Airbag to inform the user and prompt them to restart the dead scripts. Airbag does not neuter the value of in-script error handling. Airbag will treat any unhandled exception that bubbles up to it as an unintended fatal error and do everything in its power to shut down the codebase and alert the user without killing the API as a whole. Airbag will catch exceptions from... API Main Thread : If initial API startup would immediately fail due to a logic error such as a bad reference, Airbag will deploy. on(type, handler) : This API function is shadowed to allow Airbag to wrap the asynchronous handler code. setTimeout() : functions scheduled with setTimeout will be handled by Airbag. Airbag will not catch exceptions from... Infinite Loops : No support is planned at this time. Halting on while(true) before API crash would require shadowing the while keyword. Scheduled Functions (other than setTimeout) : Support is planned (but is not yet implemented) for many scheduling functions, specifically setInterval() , _.delay() , and _.defer() . I am open to supporting other functions, but this effort has diminishing returns. Asynchronous Get/Set : Things like reading from the gmnotes section are functions of objects that Airbag might not have access to or get the chance to shadow. If someone can figure out a safe way to do this, even in select cases, I'm all ears, but I'm skeptical. Operation Should an exception occur while Airbag is installed, Airbag will catch the exception, dump the message and stack trace to the console log and chat log, and finally prompt the GM to restart the API at their leisure with a chat button. In chat, you'll specifically see three things: SRC : Airbag attempts to ascertain what source file and local line number actually threw the exception. (This may be inaccurate if the offending script or the one before it is minified.) This specific item will also not display unless the offending script is Marked (see below for developers). MSG : This is the exception message. STK : The stack trace, which is printed in global line numbers. Code To run Airbag, you must install both scripts. AirbagStart MUST be the very first script installed in a game (unfortunately, this means uninstalling and reinstalling all your existing scripts if you already have some, or at minimum prepending AirbagStart to your first script if you have the source). Similarly, AirbagEnd MUST be the very last script installed. This allows them to wrap the rest of your scripts. If you have scripts that are outside the Airbag sandwich, Airbag will not be able to catch the exceptions they throw. Source Changelog 1.0: Initial Release 1.1: Add on() support 1.2: Fix duplicate Airbag on() registration 1.3 Add setTimeout and clearTimeout handling, add line localization For Developers Utility Functions Airbag supports some functions to help your development. // Call on the very first line of your file (even before boilerplate)
void MarkStart(string scriptName) // Call on the last meaningful line of your file (whitespace afterwards won't hurt it, but isn't recommended)
void MarkStop(string scriptName) // Converts a global line number into a local line number for a script
// Returns: {string Name, int Line} where Name is the name of the script it is from and Line is the local line number within that script.
// Requires: Your script must be Marked with MarkStart and MarkStop.
obj ConvertGlobalLineToLocal(int globalLineNumber) By Marking your file, Airbag will know where your file starts and stops, meaning it will be able to mark your file as the source of exceptions and even tell you the line number in your file . In case you don't trust Airbag to be installed, you can always do something like... if (MarkStart) MarkStart('MyScriptName'); What does Airbag Deployment Do? When Airbag detects a fatal error, it performs the following operations in order: The codebaseRunning internal flag is set to false . (All shadowed functions check this first, so any shadowed functions called after this point will do nothing.) All registrations by other scripts to the on() function are purged. All timeouts are purged. globalconfig is set to a blank object. Error is logged. User is alerted and prompted with a [Reboot] button. Future Development Current plans for the future are most immediately the remaining schedule functions, but I am open to providing new development tools if they are popular. I would also like to expand the line number conversion system to include the full stack trace and even provide guesses when it detects an error outside a Marked script.