Cheaters Never Prosper
Welcome back to the second part of the UDK Save Game System tutorial. If you skipped the first one, you can still use most of the principles I go over here to create an unmanaged dll using C# for dll binding in the UDK, but it will probably all be out of context. You have been warned. ;)
In this part of the tutorial, I'm going to show you how to implement a simple text mangling dll for use with the save game system in the last part. Notice the word "simple". I'm not going to show you any fancy cryptography algorithms to encode your strings with 256-bit encryption or anything like that. I'm just going to show you how to write a dll using Visual Studio Express and C# that will mangle the JSON data in your save files. It's up to you to use your own creativity to come up with an algorithm to stump those would-be cheaters. I'll show you how to use a Visual Studio template created by Robert Giesecke for exporting unmanaged dll's using C#. I've also provided a modified version of his template on my downloads page here. The only things I changed were to make it more "UDK friendly." I changed the icon for the template to a UDK icon (I hope that's not illegal :S) and provided some example functions to show the structure. I also fixed a glitch I found in the template when working with Express versions of Visual Studio where part of the template wasn't extracting correctly.
Managed vs Unmanaged Code
You may have heard these terms before, and maybe you know exactly what they mean, but if not, here's a simple explanation. Managed code is code that runs through another layer of software rather than directly on the hardware of the machine like unmanaged code. Java runs through the Java Runtime Environment and C# runs through the .NET Framework. These additional software layers take care of a lot of the low level stuff that can be troublesome and annoying hangups for programmers who aren't careful (like garbage collection, memory allocation, exception handling etc.). C and C++ are both unmanaged code languages, and when compiled run directly on the machine's hardware.
The DllBind capabilities of the UDK will only work with unmanaged dll's. This is bad news if you're uncomfortable or unfamiliar with C or C++, or at the very least annoying if you're like me and just prefer C#. However, Robert Giesecke's Visual Studio template for unmanaged dll exports makes that problem a thing of the past.
Using the Template
There's a very good tutorial here by shadowisadog that goes into great detail about using this template and how to write functions for use with UDK DllBind, so instead of going into such great detail about that, I'll just recommend that your read that tutorial. What I'll show you instead is how to use these tools to mangle the JSON text in your save game files. First, you should download and install both Visual Studio Express C# and Visual Studio Express C++ (or Visual Studio Professional) if you don't already have them. You need them both for the template to work. Grab the template from my downloads page (leave it as a zipped archive file) and drop it in the Templates\ProjectTemplates directory of your workspace directory (defaults to Documents\Visual Studio 2010). Create a new project and select this template (UDKdllTemplate). Give it a name of SaveGame and when it opens, do a "Save All" (Ctrl + Shift + S or File -> Save All) to create the project directory.
You'll see in the solution explorer a class called UDKdll.cs. Rename it to SaveGame.cs. Visual Studio should ask you if you want to rename all the references to that class, so click yes. Open up the file and you'll see two example functions: Hello and Add. These are just to show you what kind of structure the functions should have. Notice that if you're working with strings, you need to marshal them as an UnmanagedType.LPWStr. This is a two byte (word length), null terminated, unicode string, which is how the Unreal engine does strings.
Text Encoding
Let's get to work on our text mangling/unmangling functions. Delete the Add function and rename the Hello function to EncodeString. Also replace the "Hello" string in the DllExport alias above the function. This is the name of the function that UDK will see, so it's a good idea to also call this EncodeString to keep things easy. Make a copy of this function and call it DecodeString. Your file should now look like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RGiesecke.DllExport;
using System.Runtime.InteropServices;
namespace SaveGame
{
internal static class StringEncode
{
[DllExport("EncodeString", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static string EncodeString([MarshalAs(UnmanagedType.LPWStr)]string name)
{
return "Hello, " + name;
}
[DllExport("DecodeString", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static string DecodeString([MarshalAs(UnmanagedType.LPWStr)]string name)
{
return "Hello, " + name;
}
}
}
Now that we've got our basic functions, let's do something interesting with them. Change the name parameter to text (variables should always be descriptive of what they are :)). As I said before, we're going to keep this simple, so let's just Base64 encode the string and return it. Now I know that any hacker worth more than the pimples on his face is going to recognize a Base64 encoded string when he sees it and know exactly how to decode it, but I'm not trying to make this bulletproof, just a little more difficult for the average Joe. It's up to you to decide how you want to encode your text, but this is a nice, simple way of doing it.
One of the reasons I like C# so much is because of the plethora of built-in libraries available. It just so happens that there are static functions available to the Convert class to Base64 encode and decode a byte array called ToBase64String and FromBase64String. There are also functions for instances of the UTF8Encoding class to convert a string to a byte array and back called GetBytes and GetString. So, replace the return line for the EncodeString function with this:
return Convert.ToBase64String(new UTF8Encoding().GetBytes(text));
and the return line of the DecodeString function with this:
return new UTF8Encoding().GetString(Convert.FromBase64String(text));
Simple enough right? One is just the reverse of the other. Before we compile the dll, there are two dropdown boxes next to the play button just under the menu bar that probably say "Debug" and "Any CPU" (unless your defaults are setup differently). Click on the one that says "Any CPU" and change it to x86. As of the time of this tutorial, UDK's DllBind only supports 32bit dll's. You can change the "Debug" to "Release" as well, though that's not entirely necessary; the dll will just be under the Debug folder instead of the Release folder if you don't. Now right click on the project name in the solution explorer and select "Build". Go to the SaveGame\bin\Release\x86 folder and you'll see your SaveGame.dll file. Copy this file over to the UDK\<UDK version>\Binaries\Win32\UserCode folder and it will be accessible to Unrealscript.
Save Game State DLL
Let's jump back into Unrealscript. Create a new class called SaveGameStateDLL. This will be the DllBind class where we declare the functions we just defined in C#. The Unreal engine will bind these functions to the compiled dll code so we can call them from within Unrealscript. Place this code in the newly created class:
class SaveGameStateDLL extends Object dllbind(SaveGame);
dllimport final function string EncodeString(string text);
dllimport final function string DecodeString(string text);
DefaultProperties
{
}
The code "dllbind(SaveGame)" binds this class to the SaveGame.dll and the dllimport statements tie the functions to the ones in the dll. If you're using nFringe and you're not using the Project Suite Management Utility, you may have trouble compiling as these keywords are not recognized by the nFringe compiler. Luckily, Pixel Mine built into their IDE a way to define your own keywords. You can download their ScriptModifiers.xml file and drop it in the Development\Src\Core folder. Open it up and add a couple of Keyword tags for dllbind and dllimport to the function category tag:
<Category Name="function">
<Keyword>dllimport</Keyword>
<Keyword>dllbind</Keyword>
...
</Category>
Now all we have to do to use these functions is create an instance of the SaveGameStateDLL. Open up the SaveGameState class and add a local SaveGameStateDLL variable to the SaveGameSate and LoadGameState functions. It extends Object, so to create one we use the new operator:
local SaveGameStateDLL Sdll;
...
Sdll = new class'SaveGameStateDLL';
In the SaveGameState function, we want to encode the ActorData string with our dll before we add it to the WorldData array. Replace the line:
ActorData = class'JsonObject'.static.EncodeJson(SJsonObject);
With this:
ActorData = Sdll.EncodeString(class'JsonObject'.static.EncodeJson(SJsonObject));
In the LoadGameState function, we'll decode the dll encoded data before decoding the JSON data. Replace this line:
SJSonObject = class'JSonObject'.static.DecodeJson(ObjectData);
With this:
SJSonObject = class'JSonObject'.static.DecodeJson(Sdll.DecodeString(ObjectData));
Now if you run the game and run the SaveGame command, you can open the .sav file with a text editor and you'll notice that all the JSON string data is now a big long Base64 encoded string. You should know that encoding data with Base64 expands it by roughly 33%, so your file could actually get pretty big. If this is a concern, you'll definitely want to mangle your text some other way.
That's it for this tutorial. I hope it's been informative, easy to understand and enjoyable. Thanks for reading and good luck!