Development with Visual Studio Code

From RAGE Multiplayer Wiki

This tutorial explains a basic to develop & debug a RAGE-MP resource / server-side script using VSCode on C#


Note: it is not required for you to compile the .cs scripts to run the resource, you can let the server compile them when server starts. See the comparison below.

This tutorial will mainly describe about compiling your server-side resource into .dll binary which you can attach a debugger on it. A similar step can be also done in Visual Studio IDE, except the launch & task part.

Requirement

Setting Up Project

  1. Open Visual Studio Code on dotnet/resources folder (if you can't find this folder, just create it)
  2. Create a new folder, example mygamemode then select the folder
  3. Open the terminal Ctrl+` (control + tilde)
  4. Enter >dotnet new classlib -f netcoreapp3.1 to create a new .NET Core class library
  5. A .csproj file will be created, click to edit it
    1. Change the TargetFramework to netcoreapp3.1
    2. Set the RuntimeFrameworkVersion tothe one matching your serverside .NET Core SDK version
    3. Include GTANetworkAPI by adding reference to dotnet/runtime/Bootstrapper.dll
    4. Your mygamemode.csproj should look like this:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  </PropertyGroup>

  <ItemGroup>
    <Reference Include="Bootstrapper">
      <HintPath>..\..\runtime\Bootstrapper.dll</HintPath>
      <Private Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">False</Private>
    </Reference>
  </ItemGroup>

</Project>

Save the files and enter in terminal: >dotnet restore.

Setting up Launch and Task command

Visual Studio Code comes with two kind of project commands: launch.json and tasks.json

These commands are saved inside a .vscode folder of your project, so you have to do this on every different projects you created.

launch.json

The launch command is used for running server debug with F5 key (or from Debug menu) later

  1. Proceed to Debugging Tab (Ctrl+Shift+D)
  2. Click the Gear button (next to DEBUG button) to configure the launch.json, select .NET Core or Others
  3. Modify the configuration like this:
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "RAGE-MP Launch (build)",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "compile",
            "program": "server.exe", // The RAGEMP server executable
            "args": [],
            "cwd": "${workspaceFolder}/../../../", // Or absolute path to your X:/RAGEMP/server-files/
            "stopAtEntry": false,
            "console": "externalTerminal",
            "internalConsoleOptions": "neverOpen"
        }
    ]
}

Save the launch.json file.

tasks.json

We will create two kind of tasks:

  • compile - to compile the resource into .dll, required for debugging in launch task we created before (located in bin/Debug/ by default)
  • release - to build the end product of the resource with optimized .dll ready to deploy & run in real server (located in bin/Release/ by default)

You can run these tasks by selecting Terminal>Run Task... later time.

  1. Select on menu bar Terminal>Configure Tasks..., select .NET Core or Others
  2. Modify the configuration like this:
{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "compile",
            "type": "shell",
            "command": "dotnet build",
            "presentation": {
                "reveal": "silent"
            },
            "problemMatcher": "$msCompile",
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "promptOnClose": true
        },
        {
            "label": "release",
            "type": "shell",
            "command": "dotnet build -c Release",
            "problemMatcher": "$msCompile",
            "group": "build"
        }
    ]
}

Save the tasks.json file.

Setting Up Resource

Now you can create your script in Class1.cs, rename it if you wish (make sure the class name is also renamed)

Writing the gamemode

  1. Add the C# server-side bridge dependency with using GTANetworkAPI
  2. Extends your class to Script API like public class Class1 : Script
  3. Add Hello World! code in the entry point or class constructor of the script, your code would look like this:
using System;
using GTANetworkAPI;

namespace mygamemode
{
    public class Class1 : Script
    {
        public Class1()
        {
            Console.WriteLine("Hello World!");
        }
    }
}

Configuring resource meta file

  1. Create a new file meta.xml in your resource folder (if not exist)
  2. Put some resource information, and add the mygamemode.dll file

Note that after running compile task, your .cs scripts will be compiled into .dll by default located on bin/Debug/netcoreapp3.1/ folder relative to your resource directory.

If you are not sure why your .dll does not work, try running the server with raw Class1.cs source code (uncomment the Uncompiled and comment the Compiled script tag).

Configuring settings.xml file

  1. Edit dotnet/settings.xml file in your server folder, add your resource like the following:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <acl_enabled>false</acl_enabled>
  <resource src="mygamemode" />
</config>

Save the files.

Debugging the Script

When you are set up ready, it's time to build the resource!

If there are errors on compilation, try to close the folder and re-open it in your Visual Studio Code, usually the OmniSharp server need to refresh new files.

You can also try >dotnet clean in case you have changes on the referenced files (i.e. Bootstrapper update).

Since we have not added any gameplay code in this tutorial, we do not have to play & join the server.

  1. Open your script Class1.cs, add a code breakpoint by clicking on the left side of the line number
  2. For example, you may want to put a pause before the server prints "Hello World" line
        public Class1()
        {
            Console.WriteLine("Hello World!");
        }
  1. Go to Debug tab
  2. Hit F5 button
  3. Your server will run, and the server threads will pause on your breakpoint line
  4. Click ► Continue button (F5) on top of your Visual Studio window
  5. You will get output "Hello World" in your server console.
Loading resources..
-> Starting mygamemode resource..
mygamemode: loading scripts...
mygamemode: instantiating mygamemode.Class1..
Hello world!
-> Resource mygamemode started!

Close the server/hit the ■ Stop debugging button (Shift+F5).

After you made changes to your script, you can always hit F5 again to restart debugging.

Deploying the Resource

Now that you have learn how to build and debug your script, later when your resource is ready, you may want to compile it with the Release task to deploy an optimized binary.

Simply, click on menu Terminal>Run Task... then select release

You can find your .dll in bin/Release/netcoreapp3.1/ as you may notice it contains cleaner files, since we configured to not copy the .dll files from runtime folder with the <Private> option. This build will be not debugable as the symbols are already removed and optimized for run time.

Proceed to upload these .dll file to your server, and be sure to set your meta.xml differently as it will be using the non-debugged version.

You might want to have the .pdb file together with it, it will create useful traceable dump in case your code causes server crash during runtime, even though it is not really possible to debug it.

Client-side

You can also do similar thing to work with client-side scripts, except you do not deal with launch or task commands. However, please note that in RAGE MP Client-side C-Sharp scripts are NOT compiled nor build into .dll. Which means you cannot debug it like in this tutorial.

You can still make the project, reference to rage-sharp.dll in RAGEMP/dotnet/ folder to get IntelliSense auto completion benefit in VSCode, similar way we did on Bootstrapper:

  <ItemGroup>
    <Reference Include="RAGE">
      <HintPath>..\..\..\dotnet\rage-sharp.dll</HintPath> <!-- Or absolute path to your X:/RAGEMP/dotnet/rage-sharp.dll -->
      <Private>False</Private>
    </Reference>
  </ItemGroup>

And you just have to save the .cs file, they will be downloaded to your player's RAGE-MP client and will be compiled as how you configured it.

A brief comparison how C# works in RAGE-MP
Server-side (Compiled) Server-side (Raw) Client-side
Output IL "binary" (.dll) C-Sharp source code (.cs)
Dependency Bootstrapper.dll runtimes rage-sharp.dll runtimes
Compile Time By server developer with C# Compiler (in SDK) By ragemp-server.exe By ragemp_v.exe
.NET Core Version 3.1 1) 2.2.0 2)
Debugging Developer's debugger tool choice (in SDK) RAGE MP app built in error handling & logging

See also