Mostly Harmless
Legacy:Modular Mod
Contents
Modularity
The Unreal engine has been built modular, so you can extend the game with your own creations. This is a great feature and ofcourse used a lot.
So why don't you make your mod modular, besides giving other people the power to extend your mod without needing to recompile it completely, it will also be quite handy to categorise some functionality within your mod.
Not every mod can be made modular, you have to find out yourself if your mod lends itself to contain modules. Somethings that often can be build modular:
- command handling
- power-ups/bonusses
- ....
An example of a modular system in UT2003 is the new WebAdmin (module system is broken in 2136 and earlier tho). It will allow you to add extra pages to the system.
Main class
In the main class you have to load and call the modules.
class MyModMod extends Info config; var globalconfig array<class<MyModule>> MyModuleClasses; var array<MyModule> MyModules; function LoadModules() { local int i, j, cnt; local MyModule MMI; local class<MyModule> MMC; cnt = 0; for (i=0; i < MyModuleClasses.Length; i++) { MMC = MyModuleClasses[i]; // Skip invalid classes; if (MMC != None) { // Make sure we dont have duplicate instance of the same class for (j=0;j<TelnetHelpers.Length; j++) { if (MyModules[j].Class == MMC) { MMC = None; break; } } if (MMC != None) { MMI = new MMC; if (MMI != None) { if (MMI.Init()) { MyModules.Length = MyModules.Length+1; MyModules[MyModules.Length - 1] = MMI; } else { Log("MyModule:"@MMC@"could not be initialized"); } } } } } }
MythOpus: In the code above, I think there is a slight typo and I'm not sure how to fix it. I'm not sure how to fix it... anyways the typo is 'TelnetHelper' where you check for the duplicates.
As you can see above we have defined two variables, one of them is configurable: MyModuleClasses. In the config file we will define the Sub Classes of MyModule that we want to load:
[MyPackage.MyModMod] MyModuleClasses=class'MyFirstModule' MyModuleClasses=class'MySecondModule' MyModuleClasses=class'MyExtras.AnotherModule' etc...
The method LoadModules() will load all module classes defined in the variable MyModuleClasses and place the instance handle in the var MyModules (this is what we use in the rest of the system). For every instance of MyModule it will class a method called Init() to allow the module to do some initialization. You should call the method LoadModules when your Mod starts.
Now we have loaded all modules we should call them when it's needed, for example:
function SomethingHappend(Controller C, string description) { local int i; for (i = 0; i < MyModules.length; i++) { // break loop when handled if (MyModules[i].DoSomething(C, description)) break; } // do some more }
When this function is called it will loop through the MyModules list and call the method DoSomething on every MyModule until it returns true. Breaking the loop when a MyModule has handled the method is something you usualy want to do. In some cases you want every module to perform some actions, like saving stuff. But most of the time you want to ask a module if it wants to do something with the currect aparameters or not.
Now that this is defined you need to program the Base Class of the Module.
Base Module Class
class MyModule extends Object within MyModMod; function bool Init() { return true; } function bool DoSomething(Controller C, string description) { return false; } function bool DoSomethingElse(Controller C1, Controller C2) { return false; } function SaveStatus() { }
The base class defines all methods that can be called on the module. The base class should not contain a lot of code, only code that is realy common to all modules.
Also on methods that return a bool to define if they handled it should return false by default. Init should return true by default.
The base class is defined to execute within the context of MyModMod, thus it should extend Object since it's already a sub class of whatever MyModMod is a subclass off.
A module
Now that we have defined a base class we can create the modules.
class MyFirstModule extends MyModule; function bool Init() { log("MyFirstModule loaded succesfully"); return true; } function bool DoSomething(Controller C, string description) { if (description != "") { Log("Dosomething called with an non empty description"); return true; } return false; }
MyFirstModule implements the methods Init and DoSomething, the other methods are left to it's default. In Init we just say that we have loaded.
In DoSomething we add a log line when the description is not empty, and return true. Because we return true we will prevent other Modules to be called. If description is not empty we will return false and give other modules a chance.
Releasing a SDK
When you release your mod it's very wise to provide documentation on how the modules work and how somebody can extend them. Provide the source of the base class(es) with this documentation, document every function declared.
Solid Snake: This is what Chaos Engine is doing.