Skip to content

LEGACY: Codebase Basics

Tyler edited this page Feb 6, 2021 · 1 revision

Brief Overview

Neverwinter Nights normally uses a language called NWScript for game rules and logic. It's a simple language and very easy to learn. However, it's quite limited. It doesn't offer functionality you'd find in more robust languages. For example, there's no access to arrays, lists, and so on. If you're used to having this functionality it's quite difficult and inefficient to work without it.

Thankfully there's a solution which lets us interface with NWN using a modern language and proper tooling.

NWNX, Mono, and NWNX_Mono

Neverwinter Nights Extender (NWNX) is a separate application which runs side-by-side with the NWN server. In simple terms, NWNX is basically a memory editor for the server process. It hooks into memory addresses used by the server and injects code by way of plugins. If you're interested in more detail about how this works please head over to their repository as it's outside the scope of this guide. (Link: https://github.com/nwnxee/unified )

Mono is a set of packages built by the open source community to enable the Microsoft .NET framework to run on Linux environments. You can learn more about this on their website. (Link: https://www.mono-project.com/ )

Finally, there is NWNX_Mono which is a plugin built for NWNX which enables the use of Mono for the NWN server. If this is confusing, just think of it as a "bridge" which lets you talk from your C# code to the NWN server. For the purposes of this project, it's where the magic happens.

Entry Points

Whenever a script is called from NWN, NWNX_Mono will step in and say "I see you're running a script. Let me see if we have that in the C# code base". If it finds a C# class with the same name as the script you're running AND it's in the NWN.Scripts namespace, it will execute that instead of any script which may be in the module. We refer these C# classes as "entry points".

Entry points should be very light on logic. Generally speaking they should only publish an event internally. Here's an example of how that might look:

Notice that the class exists in the NWN.Scripts namespace, as required by the NWNX_Mono plugin. Also notice that the class name is "item_use". This means any time the server calls a script named "item_use", it will execute the Main() function in this class. Recall that NWN requires all scripts to be lower case and between 1 and 16 characters long.

Finally note that only an event is being published here. By publishing this event, we're now able to subscribe to it from anywhere within the C# code base. Refer to the "Event Hooking, Subscriptions, and Publishing" guide for more information on how this works. (Link: https://github.com/zunath/SWLOR_NWN/wiki/Event-Hooking,-Subscriptions,-and-Publishing )

NOTE: There are some helpers in here to prevent our tools from complaining about invalid namespaces and incorrect class names. Just include these - it'll prevent unnecessary warnings from popping up during the build process and it'll stop automatic refactoring tools from putting the class in an invalid namespace.

Benefits and Trade-Offs

There's a lot to gain from handling server code in this way. You get the full power of the C# language and Mono framework. You can use modern integrated development environments like Visual Studio. You're able to write unit tests around your game logic. You can structure and organize your code however you wish. You can use any third party libraries or technologies without being constrained by the limitations of NWN. Source control comes standard and you don't have to do anything out of the ordinary to get it working.

All of these are excellent benefits and the primary reasons we chose to go with it. However, by going down this route we also had to give up several things in exchange.

The biggest trade off is that it's more complicated. Your programmers need to be familiar with setting up their environment, the C# language, and understanding some of the "black magic" that's happening behind the scenes for them. Professional programmers shouldn't have a problem with this but the majority of the NWN community don't come from that background. Many of them are hobbyists and something like this will scare them away.

The next trade off is you're now dealing with many different pieces of technology which can fail. If there's a bug with NWNX, you've got a problem. If there's a bug in Mono, you've got a problem. You've got more moving parts and it's important to be careful about what else you introduce which could fail.

The last major trade off has to do with how embedded Mono works in NWN. There are several "gotchas" which can bring your server down. For example, if you get a null reference exception at any point then your server will crash. It doesn't matter if it's in a try/catch block or not - the server will fail and you won't be able to pinpoint where it happened. These occurrences can be reduced but you have to take very careful steps to do so.

Closing

This guide should have given you a brief overview of how NWNX, Mono, and NWNX_Mono relate to each other. You should be more familiar with how to create entry points into the code base. And finally, you should be more familiar with the benefits and trade offs of this approach.