Jump to content

Guide: How to Modify RenX Servers (or: Mutators)


aca20031

Recommended Posts

This post is to help server owners get a grasp on Unrealscript and modifying servers (arguably, though not limited to, with mutators). This post REQUIRES that you have an understanding of OOP concepts, namely: inheritance! If you don't, quit reading, this is not for you. I am not an unreal expert by any means, but I spent a long time figuring this stuff out so I want to share the primary points with you to help save you that time. If I say something incorrect here, and you're an unrealscript expert, feel free to PM me.

Overview

Let's get down to it. UnrealScript is built around a strict hierarchy of classes and subclasses. To do anything of note, you need to extend (aka inherit from, write a subclass of, etc) one of the base classes in the game. For example, a Game is a subclass of Game. UDKGame is a subclass of Game. UTGame (unreal tourny) is a subclass of UDKGame. RX_Game (the RenX game) is a subclass of UTGame. You could even write a subclass of Rx_Game and change your server startup parameters from ?game=Rx_Game.Rx_Game to ?game=St0rm.St0rmGame (yeah I may have done this...works like a charm. Well almost.).

One of the simplest ways to extend a server is to write a Mutator, because this does not require you to replace things entirely like replacing Rx_Game does. Mutators for RenX are classes which extend Mutator. You should extend UTMutator, though, because RenX supports it, and it's a bit more specific and thus a bit more powerful. I hope the devs will eventually add an Rx_Mutator we can be extended with a bit more RX specificness.

Once you have a mutator added to the server's packages, you can simply add ?mutator=MyProject.MyMutatorClassName to the command line, and it will be added to the list of mutators whose functions will be called. Mutators are recommended over class replacement because they can stack and play nice with others. I can have 3-4 mutators on the command line, but only one game. In the future I suspect we'll be adding ?mutator=Extra.ExplodingHarvies?mutator=GameTypes.SniperServer?mutator=Extra.FasterMammoths etc, for example. Of course, this is less powerful than replacing the entire Game, but it's still pretty damn solid.

An Example

Let's write a mutator that makes harvies go faster. First, install the UDK. Currently RenX was released with the July 2013 version of the UDK so lets install that. From http://download.udk.com/UDKInstall-2013-07.exe, if you're lazy. It probably goes to C:\udk\udk-2013-07. Now we make a project. Let's call our project Extra and our mutator FasterHarvies (following the example above).

Go to UDKROOT\Development\src\ and make the folder Extra. Then make the folder Extra\Classes.

Now go to UDKROOT\UDKGame\Config\DefaultEngineUDK.ini and find the section "UnrealEd.EditorEngine". This is where we add packages to be compiled. At the end of this section, add +EditPackages=Extra

Now our environment is ready. Create UDKROOT\Development\src\Extra\Classes\FasterHarvies.uc and open it with notepad (or notepad++ if you're smart).

Type "class FasterHarvies extends UTMutator;". Save the file. Now, run Unreal Frontend (this is installed with UDK), click the arrow dropdown under Script, and click Compile Scripts. If all goes well, the output should end like this

--------------------Extra - Release--------------------

Analyzing...

Scripts successfully compiled - saving package 'C:\UDK\UDK-2013-07\Binaries\Win32\..\..\UDKGame\Script\Extra.u'

Congratulations, you just made a mutator which does nothing. To test, copy Extra.u (from the path above) to your Renegade X server, under ROOT\UDKGame\CookedPC and run your server, adding to the command line ?Mutator=Extra.FasterHarvies

Mine now looks like:

"Binaries\Win32\UDK.exe" server CNC-Islands?game=Rx_Game.Rx_Game?dedicated=true?VehicleLimit=14?PlayerLimit=4?MineLimit=60?AdminPassword=password?Mutator=Extra.FasterHarvies -log -port=7000

Note the console, once run, will say ScriptLog: Mutators Extra.FasterHarvies

Making it work, and Pawns vs Controllers

Ok. Now let's code something useful. First let me introduce a small UDK concept (again this is NOT a UDK tutorial, learn UDK on your own. However Pawn and Controller are fundamental concepts that I can introduce quickly here)

A pawn in the game is essentially a moving character. A vehicle (empty or not) is a pawn. A character (soldier, hottie, etc) is a pawn. A Controller is either a player (PlayerController) or an AI which Possess and moves a pawn. So for example:

You are a hottie. The Hottie is a pawn and is possessed by "you", the player controller.

You purchase a medium tank. The tank is a pawn and is possessed by AI, a controller which will drive it out of the WF. Once it is out of the WF, the Pawn is no longer possessed and has no controller. When you approach it and press E, you enter the tank and become its Controller. Your hottie pawn is destroyed until you get out of the tank.

Finally, most classes (including Pawn and Controller) descend from the Actor class.

That said, the harvester is a Pawn controlled by an AI Controller. Mutators have a function which we can overwrite that is called when an Actor spawns. This function returns true to allow the spawn, or false to prevent it. The signature looks like this:

function bool IsRelevant(Actor Other)

(See UDKROOT\Development\Src\Engine\Classes\Mutator.uc)

So we are going to overwrite this in our mutator, check if the Actor is a harvester, and if so, change its speed. Let's open up FasterHarvies.uc again and write this:

function bool IsRelevant(Actor Other)
{
     return super.IsRelevant(other);
}

What we need to do is check if Other is an instance of the class "Rx_VehicleHarvester" which is a RenegadeX class. It extends Rx_Vehicle_Treaded, which extends Rx_Vehicle, which extends UTVehicle (UDKROOT\Development\Src\UTGame\Classes\UTVehicle.uc), which extends UDKVehicle (UDKBase\classes\UDKVehicle.uc), which extends UDKVehicleBase, which extends SVehicle (Engine\classes\SVehicle.uc) which extends Vehicle, which extends Pawn.

Phew. I figured all this out by following the hierarchy in notepad. Someone more dedicated than me should make (or generate) a chart of all this crap!

Now in UnrealScript, we test if something is another class by trying to cast it and seeing if the result is None (read: null). So it would look like this.

 if (Rx_Vehicle_Harvester(Other) != None) { }

Now Pawn has a variable called GroundSpeed which we can change. So to make the harvester 10x as fast, let's do this:

function IsRelevant(Actor other)
{
     if (Rx_Vehicle_Harvester(Other) != None) { Pawn(Other).GroundSpeed = 10.0 * Pawn(Other).GroundSpeed; }
     return super.IsRelevant(other);
}

Note we have to cast Other to Pawn to access the GroundSpeed member. This will work since we already rpoved Other is an Rx_Vehicle_Harvester, which descends from Pawn.

Now compile it in frontend.

Wait.

It didn't work. That's because we need the source of Rx_Vehicle_Harvester in order to use it, else the compiler says "WTF is this type?" Now at the time of writing, they have not released the source yet. But what we CAN do is write a fake type just to let us compile.

The Vehicle types are in Rx_Vehicles.u. So we need to create an Rx_Vehicles project folder just like we did with "Extra", and add it to the INI file. Since "Extra" depends on "Rx_Vehicles" we need to add +EditPackages=Rx_Vehicles ABOVE Extra in the file. Create a Classes subfolder and in it let's create some new files called "Rx_Vehicle_Harveser.uc", "Rx_Vehicle_Treaded.uc", and "Rx_Vehicle.uc". In them, lets only put the information we need to compile. That would be.

class Rx_Vehicle_Harvester extends Rx_Vehicle_Treaded;

class Rx_Vehicle_Treaded extends Rx_Vehicle;

class Rx_Vehicle extends UTVehicle;

respectively.

Now hit compile again. It should work. If it didn't make sure you edited the INI file, put them in the right order, and made the right Development\Src\Rx_Vehicles\Classes folder. Deploy it like you did before (copy Extra.u to the CookedPC folder on the server), restart the server, and voila. Your harvy should be on crack.

Tips and Tricks

  • Until the source is released, you can use UE Explorer and open the RenX .u file in CookedPC to "view the source" http://eliotvu.com/portfolio/view/21/ue-explorer - This will let you figure out what the classes are named, and what members they have access to.
  • WorldInfo is a member of the Actor class (from which Mutator inherits) that has TONS of access, including a reference to Rx_Game under WorldInfo.Game
  • The only way I know of to get info about RenX specific events, like building destruction, is to replace WorldInfo.Game with your own subclass of Rx_Game. This is not compatible with other mutators...You might get away with this by using the Decorator Pattern.
  • Mutators and their children are destroyed on map change.
  • From Pr0eX: If all you want to do is check if an object is of a certain type, you can use the isA function instead of trying the cast (like isA('Rx_Vehicle_Harvester') ), this will prevent any type requirement checks at compile time, so you can get away with not needing dummy classes. Dummy classes are still necessary for using functions or members of that class.

Feel free to post any questions or find me on IRC or http://www.st0rm.net or irc.st0rm.net

Edited by Guest
Link to comment
Share on other sites

Nice!

Tip from me:

- Use "IsA()" instead if you only want to test if it's an object of some sort (like IsA('Rx_Vehicle_Harvester') ), this will prevent any type requirement checks at compile time, so for example if you are not using members of this class (no need to cast!) you can get away with not needing dummy classes.

Reference:

click: IsA()

Link to comment
Share on other sites

Nice!

Tip from me:

- Use "IsA()" instead if you only want to test if it's an object of some sort (like isA('Rx_Vehicle_Harvester') ), this will prevent any type requirement checks at compile time, so for example if you are not using members of this class (no need to cast!) you can get away with not needing dummy classes.

Reference:

click: isA()

Good tip! I'll add it to the Tips and Tricks, but I'm going to leave the tutorial the way it is as demonstrating making prototype classes is pretty important for doing a lot of things :)

Link to comment
Share on other sites

Nice!

IsA('Rx_Vehicle_Harvester')

Why do you do that and not just get the ::StaticClass()?

That's not UnrealScript...

Other.isA(class'Rx_Vehicle'Harvester) is.

Tips and Tricks

neat little tool, although if they decide to pack/encrypt the files you will be SOL. Then what?

They've already suggested they would release the source, I doubt they're going to do a 180 and encrypt what they have (if you can even do that and still have it runnable). At any rate we'll worry about it then.

Link to comment
Share on other sites

  • 2 weeks later...

Just an update:

I spent a while figuring out why GroundSpeed does nothing, and this is why:

To acctually modify a vehicles speed you need to edit the vehicles SimObject (in this instance it's SVehicleSimTank).

Instead of

Pawn(Other).GroundSpeed = 10.0 * Pawn(Other).GroundSpeed;

Use this instead

SVehicleSimTank(UTVehicle(Other).SimObj).MaxEngineTorque = SVehicleSimTank(UTVehicle(Other).SimObj).MaxEngineTorque * 4;

Here is the current SimObject settings for Rx_Vehicle_Harvester:

begin object name=SimObject class=SVehicleSimTank
       MaxEngineTorque=1700.0
       EngineDamping=4.0
       InsideTrackTorqueFactor=0.30
       TurnInPlaceThrottle=0.250
       TurnMaxGripReduction=0.990
       TurnGripScaleRate=0.80
       StopThreshold=20.0
       WheelSuspensionStiffness=200.0
       WheelSuspensionDamping=50.0
       WheelSuspensionBias=0.1250
       WheelLongExtremumSlip=0.50
       WheelLongExtremumValue=2.0
       WheelLatExtremumSlip=0.50
       WheelLatExtremumValue=4.0
       WheelLatAsymptoteValue=2.0
       bClampedFrictionModel=true
   object end

Link to comment
Share on other sites

  • 1 year later...

This helped me a lot. A shame to see it so far buried. This was made a whole year it seems, before modding and mutating became real popular for this game. The steam that the modding community is building up is terrific for their modest size, and I can't wait to see all the things a year from now that we will have!

Link to comment
Share on other sites

  • Agent unpinned this topic

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...