Jump to content
  • RDR2 Modding Tutorials


    LMS
    This is part 1 in a series dedicated to creating your first plugin! You can already find the finished project and its source code at the bottom of this article, but this article so far only covers getting started.
     
    With the arrival of RAGE Plugin Hook for Red Dead Redemption 2 creating modifications has become a whole lot easier. Leveraging the full power of the .NET ecosystem combined with an easy to use API for RDR2, it allows you to quickly create new mods without having to worry too much about all the scary stuff.

    This tutorial is aimed at beginners, but ideally you already have some experience with programming to make things easier. If not, I am sure you will still understand most of the concepts, though it might take you a little longer. We will use C# as the language of choice and Visual Studio as our IDE. You can download a free copy of the "Community Edition" here: https://visualstudio.microsoft.com/vs/. When installing Visual Studio, make sure to at least select the ".NET desktop development" tools. Please note that this tutorial is aimed at beginners and does not necessarily reflect best practices for plugin development.
     
     
     

    Setting up our development environment
     
    Once you've installed Visual Studio, you can download my empty test project here: NPC Taxi Empty Start Project. It already contains a skeleton for a new RAGE Plugin Hook plugin called "NPC Taxi", which we will be developing in this tutorial. If you browse its folder structure, you will find a folder called "Dependencies" in it. This is where RPH's so called SDK is located.
     
    This SDK allows us to make use of RAGE Plugin Hook and all its functions. It is regularly updated to include new functionality and all you need to do is update the file in there with the one included in the "SDK" folder when downloading a new version of RAGE Plugin Hook.
     
    You should see this overview of your project's structure on the right when opening up the project. It shows you all files that are currently in the project, in this case two code files called "EntryPoint.cs" and "NPCTaxi.cs". Now I know I said the project was going to be empty, but it is empty in terms of not doing anything yet 😛
     
     
     
     
     
     
     
     
     
     
    Entry point and exit point
     
    Let's have a look at the two files. First, open up "EntryPoint.cs". At the top you can find metadata associated with your mod such as the name of the modification, its author, description and two more technical attributes: The entry point and the exit point. They are called whenever RPH loads and unloads your Plugin respectively and you can see them defined as OnEntry and OnUnloading if you check the code right below the metadata. We will not worry about unloading too much just now (who would dare to unload our amazing plugin anyways?), but focus on our entry point:
     
    private static void OnEntry() { NPCTaxi.Start(); while (true) { NPCTaxi.Process(); GameFiber.Yield(); } }  
    The first thing that happens is that we call a function in a different class, namely the function Start in a class called NPCTaxi. This function will be called once when our plugin is loaded and can be used to do one-time initialization such as loading a settings file. If you click somewhere on Start and press F12 Visual Studio will take you to the function. It will also open a new tab with the source file (NPCTaxi.cs) where this function is located in. As you can see, all it currently does it print a new help message and presents it to the user.
     
    Let's go back to OnEntry again and see what happens next. We enter what is called a "while-loop". This means all instructions in the {} are executed as long as the condition for the loop is true. Since our condition is true, it is always true and the loop will run indefinitely. Hence, we have our main execution loop here. Every time the game processes its logic, we also get processed here until we are unloaded. There are two calls inside this loop, one to a function in NPCTaxi again (if you use F12 you can see that it does nothing) and a call to GameFiber.Yield. To keep things simple, what this call does is hand back control to the game after we have finished doing our shenanigans. If we do not do this, the game will freeze since the game still thinks we are doing stuff (and we run this loop indefinitely, remember?). When this function is called, the game resumes execution, processes its subsystems like physics and its other scripts and presents a new frame to the user. Once that is done, it hands control back to us and we return from GameFiber.Yield. We now run the loop again as our condition is still true.A more in-depth explanation can be found her: http://ragepluginhook.net/RPH2PreDoc/ under "Fibers".
     
    Running our plugin
    Now that we are experts on all things plugin development, let's compile and load our test plugin. Press Shift+F6 to build your current project (if that keybinding does nothing, open the "Build" menu at the top and choose "Build NPCTaxi"). This means that the code you have written is translated so that a computer can run it. This compiled code can then be found in the project folder under "bin\Debug" where you will have three files: "NPCTaxi.dll" (your compiled code), "NPCTaxi.pdb" (additional information for your code to help you when something goes wrong) and "RagePluginHook2.dll". The latter is the SDK we talked about earlier and is placed there for your convenience so you always know what files you would need to run your project. We do not need it in this case as it is included in the RAGE Plugin Hook download. Head over to https://www.mod-rdr.com/files/file/16-rage-plugin-hook/if you haven't already and download the latest version of RPH. Copy all its files into the game directory (for more details on the installation please refer to the file description).
     
     
    Copy the two "NPCTaxi" files into a folder called "C#Plugins" in your game root directory, for instance: C:\Rockstar Games\Red Dead Redemption 2\C#Plugins. Now start the game with RPH and wait until you are fully loaded and can move your character. I strongly recommend to use windowed borderless (or just windowed) mode for development. By default RAGE Plugin Hook loads all plugins so chances are NPC Taxi is already running when you started the game. To reload it so we can see our help message, press F5 to bring up the console, type in ReloadPlugins and press Enter. If RPH did not load your plugin automatically, try typing in LoadPlugin "NPCTaxi.dll". Your plugin is now loaded and you can press F5 again to get back to the game. You should now see our help text in the left corner welcoming you to your new plugin. Amazing! 🙂
     
    The finished project can be found here: NPC Taxi Source and part two of the tutorial is here
     

    Ruskivotka
    Video: 

    Extract models from rdr2 using Ninjaripper & CodeX
     
    IMPORTANT!
    CodeX is currently a file/map viewer only, and it's still under development!
    You can get CodeX for every Codewalker Patreon tiers (starting 1€ month)

    CodeX links:
    Codewalker Discord: https://discord.gg/NRWqyDmf5z
    Codewalker Patreon: https://www.patreon.com/dexyfex 
     
    IMPORTANT!
    Ninja Ripper is not for enabling illegal activity. The program is intended only for research of the places of the levels of games located "behind" the camera and does not pursue the goal of piracy.

    Ninjaripper links:
    Ninjaripper Patreon: https://www.patreon.com/ninjaripper
    Ninjaripper Discord: https://discord.gg/SECGb6ZKGH 
    Ninjaripper Youtube:  https://www.youtube.com/user/BodreyAndrey/videos 
     
    3ds Max Student version:
    https://www.autodesk.com/education/edu-software/overview?sorting=featured&filters=individual

    My links: 
    Patreon: https://www.patreon.com/ruskivotka
    Discord: https://discord.gg/xjKaHXJfnH
    Tebex: https://redm-scripts.tebex.io/
    Youtube: https://www.youtube.com/c/RedMScripts/videos
    Twitter: https://twitter.com/ruskivotka

    Hayden Almeida
    Today i will try to teach how to spawn your first ped in RedM with C#.

    first you need to create a "ENUM" to make your list of your peds (you can see they here: https://www.mod-rdr.com/wiki/peds/ )
    public enum PedHash : uint { AcHorseAndalusianDarkbay = 0xE57FC660, AcHorseAndalusianRosegray = 0x2C80A080 } ATENTION!  If you are using other Class to create this enum, you put "public enum". If this enum was created in the same class, you use "private enum".
     
    Second lets create our function to spawn and load the ped:
    public static async Task<bool> LoadModel(int hash) { if (Function.Call<bool>(Hash.IS_MODEL_VALID, hash)) { Function.Call((Hash)0xFA28FE3A6246FC30, hash); while (!Function.Call<bool>(Hash.HAS_MODEL_LOADED, hash)) { Debug.WriteLine($"Esperando o modelo {hash} carregar!"); await BaseScript.Delay(200); } Debug.WriteLine("Modelo carregado!"); return true; } else { Debug.WriteLine($"Model {hash} nao e valido!"); return false; } } public static async Task<bool> LoadPed(PedHash ped) { if (!await LoadModel((int)ped)) return true; else return false; } This function ^ will load the ped. Before creating the main function to spawn the ped, we need to create other function to "randomize" the outfit of the ped. Without this the ped will not spawn! So i only created in a "beautiful" name 😄
    public static void SetRandomOutfitVariation(int ped) { Function.Call((Hash)0x283978A15512B2FE, ped, true); }  
    Now lets create the main function to spawn de ped:
    public static async Task<int> CriarPed(PedHash ped, float posx, float posy, float posz, float heading) { var task = LoadPed(ped); // this will call the function we already created before bool result = await task; // this line will only proceed if we have something in return from "LoadPed" int ped_ = API.CreatePed((uint)ped, posx, posy, posz, heading, true, true, true, true); Function.Call(Hash.SET_ENTITY_AS_MISSION_ENTITY, ped_, true, true); // setting this true for this ped not "despawn" SetRandomOutfitVariation(ped_); // every ped to spawn we need to random his outfit or variation... API.SetModelAsNoLongerNeeded((uint)ped); // clear the model in memory return ped_; } Now with our functions created, we can now create our command to spawn any ped that are inside ENUM.
    The command will work this way: /createped [ped name]
    Lets go to our main CLIENT file, inside our Main public:
    public Main() { API.RegisterCommand("createped", new Action<int, List<object>, string>((src, argumentos, raw) => { CMD_CriarPed(argumentos); // this is the function we will excute }), false); } This is the function our command will execute:
    private async void CMD_CriarPed(List<object> argumentos) { playerid = API.PlayerPedId(); var argList = argumentos.Select(o => o.ToString()).ToList(); if (argList.Any() && Enum.TryParse(argList[0], true, out PedHash pedi)) // this line will compare if the param we write, exists inside ENUM. { // argList[0] is to get the first argumento we wrote. // TIP: // if we want another parameter for our command we can call this: // int number = Convert.ToInt32(argumentos[1]); // Now our command can use: /createped [ped name] [number] // and so on... Vector3 pos = API.GetEntityCoords(playerid, false, false); Vector3 forwardpos = API.GetEntityForwardVector((uint)playerid); float hdg = API.GetEntityHeading(playerid); pos += (forwardpos * 5); var ped_criado = CriarPed(pedi, pos.X, pos.Y, pos.Z + 2.0F, hdg); int result = await ped_criado; Debug.WriteLine($"Ped ID created={result}"); } } Hope this will help begginers to program in RedM! Cya 🧲
     

    LMS
    This is a list of open source mods for Red Dead Redemption 2. Mods that are hosted here include their download link. Please leave a comment if you find a mod that should be included here!
     
    https://github.com/thatoneguy650/DifficultyScaler (Download: https://www.mod-rdr.com/files/file/33-difficulty-scaler/)
    https://www.mod-rdr.com/files/file/27-force-first-person-on-foot/ (Code in mod description)
    https://github.com/ss-gnalvesteffer/red-dead-redemption-2-mods (includes Point Cloud Capture, Red Hot Redemption 2 and Ragdoll Redemption 2)
    https://github.com/Saltyq/ScriptHookRDR2DotNet/blob/master/examples/ExampleScript.cs (Community Script Hook .NET by @Saltyq example)
    https://github.com/Disquse/rdr3noclip (Noclip)

    LMS
    This is part 2 in a series dedicated to creating your first plugin!
     
    NPC Taxi
    The idea of our mod is to let us hop onto random NPC's horses and wagons and travel a bit differently. Whenever you target an NPC, either by holding down rightclick or by aiming at them, and then press G you can ride with them. In this part we are looking at how to actually detect a keypress and then figure out whether we are targetting a valid NPC.
     
    Check for keypresses
    Now that we have created the first version of our plugin, let's take a look at adding some actual functionality. All code changes will be made in NPCTaxi.cs as that is the core of our project. First, we need to figure out whether the player wants to hop onto someone's horse or wagon. For this, we can use a key check to determine whether a certain key is down. The Game class in Rage Plugin Hook provides many useful methods for that. Let's look at all that's needed:
    // If the G key was pressed. if (Game.WasKeyJustPressed(Keys.G)) { // Do something! }  
    Pretty easy! The Keys enumeration contains a definition for all of your keyboard keys so feel free to change the key. Whenever the player now presses the key, the if-check is passed and we will execute the code between {}. It is important to note that this keycheck only evaluates to true once while the key is being pressed, i.e. if the key is being held down for a few seconds, the code is still only being executed once. If you need to check for a key being held down every tick, you can use Game.IsKeyDown.
     
    Getting the player's target
    So we now know when the player would like to ride with a NPC, but we don't know who that NPC is or whether there even is a valid one. For that we need to get the target the player is currently either focusing (by holding down right mouse) or aiming at. Let's create a small helper function for that to cover both cases:
    private static Entity GetPlayerTarget() { if (Game.LocalPlayer.IsFreeAimingAtAnyEntity) { return Game.LocalPlayer.FreeAimingTarget; } return Game.LocalPlayer.Target; }  
    We first check whether the player is aiming at any entity. An entity is the base type of peds, wagons, horses, objects and other things you can see in the game. They all share the properties of an entity, such as having a visual representation in the game, a position and much more. Since we do not know what the player is aiming at exactly (could be a wagon, a ped etc.) it is presented to us as the most basic type (the lowest common denominator if you will). This gives us access to all functionality of an entity, e.g. changing its position, but not to ped or vehicle specific stuff as we do not yet know what type it really is. If we are aiming at any entity, we return the current free aiming target. If not, we return the player's current target, which is the current interaction target. We do not worry about whether any of these targets are actually valid and exist in the game in this function yet. We will do this from where we call the function instead. Since we return an entity, which is a base type, we need to verify that it is a ped as we want the rider/driver. (If you want to expand the mod, you could for instance also support targeting/aiming at horses and wagons directly instead of relying on the driver and create a driver if there is none.) We can do this using the following line, also checking whether it actually exists in the game world first:
    if (targetEntity.Exists() && targetEntity.IsPed)
     
    Now follows some voodoo as we want to make sure that Visual Studio now treats our Entity as a Ped since we just confirmed it is one. Every Ped is an Entity, but not every Entity is a Ped. To do that, we can use the "as" keyword in C# which essentially tells Visual Studio that it should try to make the entity a ped if it is one. Please note that there is nothing actually happening in the game during this conversion, it really is just telling Visual Studio that we would like to treat this type as another type (because we know it is something else!). If it's successful, we have a new variable with access to all ped functions. This looks like this:
    var target = targetEntity as Ped;
     
    This might be a bit scary, but it is quite an important concept to understand. If we had not confirmed that our entity was a ped and it turned out that it was a vehicle, then target would have the value null since the conversion was invalid. These tricks are necessary when dealing with base types such as Entity that could in reality be for instance a Ped or a Vehicle. Or full code looks like this now:
     
    // If the G key is down. if (Game.WasKeyJustPressed(Keys.G)) { // Get the current player target. var targetEntity = GetPlayerTarget(); if (targetEntity.Exists() && targetEntity.IsPed) { // We confirmed that it is a ped, so turn it into one. var target = targetEntity as Ped; } }  
    If you want to test the code, you can add something like target.Kill(); after the conversion. This will kill any ped you target or aim at when pressing G. Press Shift+F6 (Build NPCTaxi) again to build your project and copy the files in the same folder as before. Note: Your game can still be running while doing that, RPH is smart enough to realize that you have updated the files with new code and will automatically reload your plugin with your with your changes. Magic! Now walk around and bring death upon everyone for at least 10 minutes.
     
    Getting the mount/vehicle and native invocation
    Since we are unfortunately working on a taxi mod and not a destruction mod, we remove the kill code again and focus on getting the rider's mod or driver's wagon respectively. Let's first consider the mount logic. This is an interesting case as we will be using a game function that is not yet supported by RPH (i.e. nicely wrapped for us), but instead we will have to call the game function directly. We will be using IS_PED_ON_MOUNT and I recommend to check out that link to see for yourself how the documentation for game functions work. These functions are usually referred to as native functions and are called by using their hash or their name (hash will always work, name only if RPH knows the name). This might sound a bit confusing, so let's look at the code instead to clear things up a bit:
    // Check if they are on a mount first. This is not yet implemented in RPH so we call the native via its name. var isOnMount = Game.CallNative<bool>("IS_PED_ON_MOUNT", target); if (isOnMount) { }  
    There is a lot going on here, but it is important to understand since many functions will not yet be available through RPH. So we can see that we are assigning a variable called isOnMount with the result from CallNative. The return type is a bool, i.e. true or false. The name of the native we are calling is IS_PED_ON_MOUNT as in the link above and we pass it one parameter, our target ped. If the ped is on a horse, this function will hence return true; otherwise it will return false.
     
    Stay tuned for part 3 to actually interact with the mount!

×
×
  • Create New...