Debugging

From Sven Co-op
Revision as of 19:32, 13 February 2025 by Outerbeast (talk | contribs)
Jump to navigation Jump to search

There are a few things you should know when debugging scripts. This page will cover as many of them as possible.

1 Console output

Before you do anything, you should always check the console output produced by the Angelscript compiler. This will tell you whether it was able to find scripts or not, whether it successfully compiled or not, and what kind of non-fatal problems may have occurred.

To do so, you should do the following:

Use developer 1 or higher to debug scripts. Most output doesn't show up if developer is disabled. Set as_log_level to a higher value to get more output.

1.1 Log Level

Angelscript has a number of log levels to control console output. This can be set by changing the as_log_level console variable.

Log Level	Name	Description
0	Critical	Always output to log. Used for critical information.
1	Normal	General information.
2	Verbose	Extended information.

2 Host errors and their interaction with Angelscript

Certain actions can cause a host error to occur. Host errors happen when the engine encounters an error that it cannot recover from. If the game encounters an error while loading a map, like precaching too late or using a model that isn't precached, the game will throw you back to the main menu. Dedicated servers will shut down instead. If you're using a listen server, this will cause Angelscript to break. If you attempt to start another server and run scripts, you will see many errors in the console. Scripts will malfunction in strange ways.

The reason why this happens is because the engine handles errors in such a manner that it corrupts Angelscript's context. When a script is being invoked, and a host error occurs, the engine will wipe all stack data that the context is currently using. Essentially, it's like wiping a person's memory while they're driving a car, only their car ends up frozen in place instead of crashing into something.

If you encounter a host error, you must restart the game in order for Angelscript to work properly again.

2.1 Technical explanation

The reason why this happens is because the engine uses a language feature called longjump (no relation to long jumping in the game). longjmp will reset the program's stack to a previously saved state, which causes it to jump over Angelscript's context. This article covers the functionality and behavior of longjmp. Angelscript's context is not designed for this, and will no longer function if a longjmp is executed while the context is being used to invoke a script. This issue has been reported to Angelscript's developer, who responded confirming that Angelscript itself cannot be updated to handle longjmp safely.

2.2 Why is longjmp used?

longjmp is a C language feature, and is a primitive means of exception handling. In C, there is no such thing as an object destructor, so objects created on the stack are not destroyed. At the time when this was implemented (mid 1990's), computers were not powerful enough to handle errors like this effectively. Every conditional statement that has to be added to detect errors and handle them can cause performance issues, so a compromise was used: longjmp allows effective error handling, at the cost of memory leaks and data corruption.

Quake itself doesn't use C++, instead using QuakeC. Since Quake C is run in a virtual machine, it was less likely to cause problems. Further, all memory allocations in Quake were made using a memory pool that is cleared on map start, preventing memory leaks from persisting. All game data was usually reset or reloaded, so very little actual corruption would persist past the error handling code.

Unfortunately, GoldSource (and Svengine) are not designed in the same manner. While GoldSource does still use the same memory allocation strategy and data management, the same does not extend to games that use the engine. Sven Co-op for example, uses the default memory allocator (usually malloc) for any dynamic memory allocation that occurs, meaning memory leaks are not cleaned up. Many objects outlive the engine's objects and data, and objects like Angelscript's context are not built for longjmp. Most C code, as well as older C++ code will leak memory because they have no means of detecting longjmps or responding to it in a meaningful manner. There is no efficient solution to this.

One possible workaround that was accidentally discovered during development was that an exception handler defined to handle any exception could stop longjmps. Angelscript's context has optional support for such a catch handler, which resulted in host errors being stopped by it. This is likely to work on Windows only, due to the platform specific nature of exceptions. Windows uses Structured Exception Handling, or SEH, which covers both hardware and software exceptions. Unfortunately, the decision was made to disable this exception handler as Sven Co-op does not use exceptions, removing the only means of protecting against corruption.

2.3 How can this be fixed?

Removing longjmp usage is the only solution to this problem. While some people think that turning Angelscript into a dynamically loaded library and reloading it would fix all of the problems, it would only make things worse.

Turning it into a dynamic library creates a security risk. Now anyone can replace the library with their own. Since Angelscript is given the memory addresses of a great deal of functions in the game, it could be used to inject code at certain points. A modified library could provide a particular person or group of persons complete access to a server, including administrative access, cheats, and the ability to trigger events in the game.

Even worse, since the client also has Angelscript in it (due to the planned client side scripting), it would allow players to cheat by using the library as an entry point into the client's code. It would allow the library to analyze the game's memory and alter its graphical representation, for example by turning all objects transparent, or by clearing the screen and drawing different elements on it.

In addition to the security issues mentioned above, there is also the problem of memory leaks. A longjmp call can cause memory leaks in any function call, in any library. To deal with this, you'd have to reload not only the Angelscript library, but also the server and the client (since clients can also encounter host errors). You'd also have to reset the engine's internal data so it doesn't point to any memory that was part of those libraries. At that point, restarting the game is easier than performing such a complex series of actions.

Removing longjmp may not be easy. Many engine operations are quick to use it when unexpected events occur, such as precaching models after the map has started, requesting the index of a model that wasn't precached, precaching too many models (8192 models in Svengine). These would all have to be updated to handle these cases gracefully. Some already do, such as precaching a model that doesn't exist. It will be replaced with a generic error model instead. Unfortunately, this change has not propagated through the engine's model handling code and so not all operations will change the model to the error model.