Oh first off let me point out, I suck at C# so forgive me if this isn't one of the best tutorials. Hope you guys learn something from it. I'll be making a speed/jumpheight/gravity and falldamage plugin. You should have a basic understanding of C# before doing this else you might get lost. I'll try to explain this piece by piece.
So let's get right to it. You'll need the Nukem addon (1.204 or 1.206) and Visual C#.
In Visual C#, Create a new project as a "Class Library" and based on the ".NET Framework 3.0". Name it whatever you want. Im naming mine SpeedPluginX.
After goto the properties and make sure "Allow unsafe code" is checked since we'll be using direct methods to write to the addresses...
Now you should get the following code:
Code:
using System;
using System.Collections.Generic;
using System.Text;
namespace SpeedPluginX
{
public class Class1
{
}
}
Now we need to add the plugin reference so in the "Solution Navigator", right click "References" and select "Add Reference".
At the bottom, click "Browse" and select where you have the "addon.dll" (Aka Nukem's addon). For me it was at: "C:\Users\Zachary\Desktop\IW5M\addon\dist\addon.dl l". Click Add then close it.
So we need to include the addon in the class library so right below the "using System.Text" we need to add some code. Also since this is gonna be editing the memory, we need to include an additional library to allow WINAPI methods to be used...
Copy and paste this right below the using System.Text:
Code:
using System.Runtime.InteropServices; //We need this for the ImportDll to work.(Used later)
using Addon; //Includes the addon.
So now that that's through, we can rename the class to something and also after this, we need to give it the functions of the Addon. To do this we tell it to inherit the functions from "CPlugin" which is included in "addon.dll". We do this by adding " : CPlugin" right after the class name. All plugins will inherit from this "CPlugin" class as it contains all the code needed.
So we change the line "public class Class1" to:
Code:
public class SpeedPlugin : CPlugin
So all our code goes in the "{ }" brackets of our class. Now we need to get some functions from the Windows kernel. We'll be using 2 functions: VirtualProtect and WriteProcessMemory. So in our class we add:
Code:
[DllImport("kernel32.dll")]
private static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(IntPtr hProcess, int lpBaseAddress, byte[] lpBuffer, int nSize, out int lpNumberOfBytesWritten);
The parameters or how this works isn't important. For reference, we took two functions (not present in C# normally) from the kernel (kernel.dll) and defined them. VirtualProtect is used to change the access to a specific memory. Sometimes the memory is write protects which simply won't do if we want to write values to it. WriteProcessMemory is used to, as it's name implies, write something to the memory. We can write values like: integers, floating points, bytes and strings.
Now we need variables to store the addresses we'll be editing and also some to store the values. So we create them like so: (I should tell you we are coding in the class so ALL further code goes there)
Code:
IntPtr g_speed, g_gravity, jump_height, fall_min, fall_max; //I think this is self explaining...
//We use these to keep the addresses to each DVAR. The IntPtr is a variable to denote what is stored
//in these are address pointers.
//We'll use this for for our WriteProcessMemory, it's used to store the # of bytes written in an operation.
//However it's not necessary and you can replace the dwOut parameter with 0.
int dwOut;
//These are for our VirtualProtect.
uint dwSize = 4, flNewProtect = 0x40, lpflOldProtect = 0;//They store the size of the variable, write access code and old protection code
bool isFallDamageEnabled = true; //By default fall damage is enabled.
//Now our variables to store the current values.
int g_speed_var, g_gravity_var, jump_height_var;
That should be it for now. If you need to add anymore, I'll let you know.(You'll add them here)
Now let's create some functions to make our life easier shall we?
Code:
public void SetOffsets()
{
//We set the offsets to the designated IntPtr we declared. These values are for IW5M and TeknoMW3. (AKA 1.4.382)
g_gravity = (IntPtr) 0x4768c6;
g_speed = (IntPtr) 0x4760ea;
jump_height = (IntPtr) 0x6da708;
fall_max = (IntPtr) 0x6de490;
fall_min = (IntPtr) 0x6d9634;
}
public unsafe void DisableFallDamage()
{
isFallDamageEnabled = false; //Sets the boolean to false.
*(float*)fall_min = 999999f; //Set the minimum height for fall damage to a very high value. The "f" extension says it's a floating point.
*(float*)fall_max = 1000000f; //This must always be higher than your minimum fall damage.
}
public unsafe void EnableFallDamage()
{
isFallDamageEnabled = true; //Enables the boolean
*(float*)fall_min = 128f; //Sets the origional values
*(float*)fall_max = 300f; //Again the origional value is set.
}
public void DisableGravityProtection()
{
VirtualProtect(g_gravity, dwSize, flNewProtect, out lpflOldProtect);
//Format: VirtualProtect(IntPtrToAddress, bytesToApplyProtectionTo, newProtectionCode, out variableToStoreOldProtection);
//A reference to all virtual protection codes can be found here.
//Here's where the virtual protect comes in. We set 4 bytes from the address to writable so we can write to it.
}
public void DisableJumpHeightProtection()
{
//To avoid any crashing, we use a try{} catch{} loop here. The try loop executes and if an error is formed, the catch is performed.
try
{
//Just as we did with gravity, we do with this.
VirtualProtect(jump_height, dwSize, flNewProtect, out lpflOldProtect);
VirtualProtect(fall_min, dwSize, flNewProtect, out lpflOldProtect);
VirtualProtect(fall_max, dwSize, flNewProtect, out lpflOldProtect);
}
catch (Exception exception)
{
//ServerLog is a function of the Addon:CPlugin, it logs the error to (in this case) the console.
ServerLog(LogType.LogConsole, "SPEED PLUGIN ERROR: " + exception.ToString());
}
}
public void DisableSpeedProtection()
{
//As above, we do this for speed.
try
{
VirtualProtect(g_speed, dwSize, flNewProtect, out lpflOldProtect);
}
catch (Exception exception)
{
ServerLog(LogType.LogConsole, "SPEED PLUGIN ERROR: " + exception.ToString());
}
}
Cool? Cool. Now here's how we parse commands: The OnSay() method in CPlugin is called every time a person says something in chat. We can intercept that and according to what is in the message, we can do a specific action.
So let's beginN.B. Our return type is called not "int" or "void" or anything else, it's "ChatType" since we need to pass the text back to the game or another plugin after we processed it. Also since this is a public method of Addon.dll, we need to intercept it so we use "override")
Code:
//This is how it usually works:
public override unsafe ChatType OnSay(string Message, ServerClient Client)
{
return base.OnSay(Message, Client);
}
//But we're gonna change that a bit.
Code:
public override ChatType OnSay(string Message, ServerClient Client)
{
string str = Message.ToLower(); //Converts message to lowercase for easier processing
if(str.StartsWith("!jumpheight")) /* If the chat command starts with "!jumpheight" then do this:
{
string[] strArray1 = str.Split(new char[] { ' ' }); //Splits the text into arrays according to a space being present
//Checks if the user entered just "!jumpheight" and displays the current jumpheight if this is true
if(strArray1.Length == 1) TellClient(Client.ClientNum, "^1Current Jump Height: ^6" + *(float*)jump_height, true);
float num = Convert.ToInt32(strArray1[1]); //Converts the string to an integer
try
{
*(float*)jump_height = (float)num; //Writes the value to the jump_height address
iPrintLnBold("^6Jumpheight Changed To ^6" + num, null); //Tells all clients the jumpheight has changed
}
catch (Exception e)
{
TellClient(Client.ClientNum, "^1INVALID VALUE" + e, true); //If the value is not valid, an error is printed to the said client.
}
}
//Ok guys im beat, so I figured from this you can do the rest.
//g_speed and g_gravity are integers. fall_min and fall_max are floating points (as was jumpheight)
//So you can just replicate the above. And you need to exit here by using:
return ChatType.ChatContinue; //Allows the chat to be passed to the next plugin.
}
A thanks or +rep would be nice if this helped you.