HogWarp
  • Home
    • Frequently Asked Questions
    • Supported Versions
  • Client
    • Quick start
      • Playstation Controller not working
      • Epic Games Version Saves Fix
  • Server
    • Quick Start
    • Server Requirements
    • Troubleshooting
  • Scripting
    • Getting Started
    • Client side
      • Basics
      • Setting up mods
        • Local Cooking / Building
    • Server side
      • Plugin
        • Creating a Plugin
        • Structure / Lifecycle
        • Plugin ↔ Plugin
      • Debugging
      • Event handling
      • API Documentation
        • Server
          • IPlugin
          • PlayerSystem
          • RpcManager
          • Timer
          • World
        • Game
          • Types
        • Systems
          • Logger
          • ClientRpcAttribute
          • ReplicatedAttribute
          • ServerRpcAttribute
Powered by GitBook
On this page
  • Creating a Network Actor
  • Rpc
  • Putting it all together
  • Client
  • Server
  1. Scripting
  2. Client side

Basics

PreviousClient sideNextSetting up mods

Last updated 3 months ago

To create a mod in the Creator Kit, refer to the official documentation, we only address HogWarp specifics here.

The Creator Kit is where you define the networking interface between Blueprint and C#

HogWarp will synchronize specific functions from selected Actors. We do not synchronize Blueprints that are not derived from the Actor class!

Creating a Network Actor

When you create a new Blueprint that needs networking functionality, ensure you pick Actor as the parent class as shown below.

Only Blueprints with Actor as the parent class can be synchronized

Once you have created your Blueprint, you can add it to the HogWarp generator, by clicking on the "HogWarp" button in the tool bar. It will open a window

This window will be the main tool you use to make your server mod aware of everything that can be synchronized. The first step will be to set the output directory to the location of your server C# mod by clicking on the three dots to open the file explorer (See Creating a Plugin if you don't have a server mod yet).

You can now click on Add Network Blueprint to add your newly creator Blueprint. Note that you can add as many as you want, but do not add the same one multiple times. A new empty entry will appear. You can either browse directly from the entry to find your Blueprint, or drag and drop from the content browser the Blueprint as shown below.

You are all set! Now just click on "Generate". Nothing special should happen, but if you browse to the output directory, you should see a new .cs file named like your Blueprint.

If you open the file you will see something an empty class with some attributes.

// This file is automatically generated

#pragma warning disable CS8618
#pragma warning disable CS0219

using HogWarpSdk;
using HogWarpSdk.Game;
using HogWarpSdk.Systems;
using HogWarpSdk.Internal;

namespace HogWarp.Replicated
{
    [Replicated(Class = "/HogWarpTest/TestMultiActor.TestMultiActor", Hash = 15294942688974238979)]
    public partial class TestMultiActor : Actor
    {
    }
}

That's it, your server now knows about this Actor and can spawn/destroy these actors on the client. You may think that this is great but a bit limited and you would be right, now let's add networked functions!

Rpc

A Remote Procedure Call (Rpc) is a way to call a function from the client on the server or from the server on the client. The mapping is done automatically by HogWarp.

In HogWarp, a Rpc is declared from the Blueprint class. There are two types of Rpcs:

  • Server which is a function that runs on the server and gets triggered by the client in Blueprint.

  • Client which is a function that runs on the game client in Blueprint but is triggered by the server in C#.

Let's start by creating a very simple "Ping" mod that logs the player's latency in the server console every 5 seconds. The latency is the time it takes for a roundtrip message, meaning the time between the moment the server sends a message until it receives a response.

We can model this as two Rpcs:

  • Ping: a function called by the server

  • Pong: a function called by the client to respond to the Ping request.

Let's create these two functions in our Blueprint class.

If we were to Generate the C# code again, nothing would change. That is because we need to categorize the functions, otherwise HogWarp wouldn't know which functions are Rpcs and which are Client or Server.

We therefore need to put our functions in the correct Category, and it is very simple, Client Rpcs go in the Client category and Server Rpcs go in the Server category.

After we have set Pong to the Server category and Ping to the Client category we should see our functions like so:

If we now compile and save the Blueprint, we can click Generate again and we will see brand new code in the C# file.

// This file is automatically generated

#pragma warning disable CS8618
#pragma warning disable CS0219

using HogWarpSdk;
using HogWarpSdk.Game;
using HogWarpSdk.Systems;
using HogWarpSdk.Internal;

namespace HogWarp.Replicated
{
    [Replicated(Class = "/HogWarpTest/TestMultiActor.TestMultiActor", Hash = 15294942688974238979)]
    public partial class TestMultiActor : Actor
    {
        public partial void Pong(Player player);

        [ServerRpc(Function = "Pong", Hash = 12918253484382636919)]
        public void Pong_Impl(Player player, IBuffer data)
        {
            ushort length = 0;
            
            Pong(player);
        }
        
        [ClientRpc(Function = "Ping", Hash = 16843147157235268489)]
        public void Ping(Player player)
        {
            ushort length = 0;
            var data = IBuffer.Create();
            try
            {
                
                IRpc.Get().Call(player.InternalPlayer, Id, 15294942688974238979, 16843147157235268489, data);
            }
            finally
            {
                IBuffer.Release(data);
            }
        }
    }
}

At this point we could call the Ping function from the server, the Pong function from the client, and implement the Pong function on the server. But we need to know when the Ping function was called so we should pass a number that represents the time at which it was called to both the Ping and Pong functions.

Let's add the parameter to both functions as input, called time and of type Integer64:

Now we can save and generate the code again.

// This file is automatically generated

#pragma warning disable CS8618
#pragma warning disable CS0219

using HogWarpSdk;
using HogWarpSdk.Game;
using HogWarpSdk.Systems;
using HogWarpSdk.Internal;

namespace HogWarp.Replicated
{
    [Replicated(Class = "/HogWarpTest/TestMultiActor.TestMultiActor", Hash = 15294942688974238979)]
    public partial class TestMultiActor : Actor
    {
        public partial void Pong(Player player, long time);

        [ServerRpc(Function = "Pong", Hash = 12918253484382636919)]
        public void Pong_Impl(Player player, IBuffer data)
        {
            ushort length = 0;
            var time = data.ReadInt64();
            
            Pong(player, time);
        }
        
        [ClientRpc(Function = "Ping", Hash = 16843147157235268489)]
        public void Ping(Player player, long time)
        {
            ushort length = 0;
            var data = IBuffer.Create();
            try
            {
                data.WriteInt64(time);
                
                IRpc.Get().Call(player.InternalPlayer, Id, 15294942688974238979, 16843147157235268489, data);
            }
            finally
            {
                IBuffer.Release(data);
            }
        }
    }
}

Great! Now we see our functions Pong and Ping, both have a long time parameter.

At this point you should take some time to play around, add more parameters, more functions, and see how this affects the generated code.

Putting it all together

We now have our Rpcs properly setup but if we were to launch the game with this mod nothing would happen.

Client

First let's wire the Blueprint Ping function to call Pong , you may notice that we never explictly call Ping anywhere, this is completely normal as remember the server will call it! Also, the Pong function is empty, this is expected, HogWarp will intercept calls to Pong and replace with the necessary code to call the server function on the correct instance.

That's all! Nothing more is required!

Let's recap, the flow:

  1. The server will trigger a call to Ping with the current time.

  2. The client will receive the Ping call and execute our Ping function

  3. The Ping function which we just implemented in Blueprint triggers a call to Pong with the time it received

  4. The server receives the Pong call and executes the Pong function (which we will implement next)

In Blueprint you only implement the Client Rpcs, the Server Rpcs are just declarations and HogWarp will replace them with the appropriate code to execute on the server!

You may now upload the mod to CurseForge so that they cloud builds it while we work on the server!

See Setting up mods on how to deploy your Creator Kit mod.

Server

Rpcs

If we try to build our server mod now, we will get an error:

error CS8795: Partial method 'TestMultiActor.Pong(Player, long)' must have an implementation part because it has accessibility modifiers.

This is because the codegen creates all the boiler plate for the server Rpcs but you must implement the server Rpcs! These errors ensure that you don't forget to implement server Rpcs that you declared in Blueprint.

The error in this example says that TestMultiActor.Pong(Player, long) is not implemented, so let's just do that. For simplicity we will keep everything in Plugin.cs but feel free to create a new file.

namespace Tutorial
{
    public class Plugin : HogWarpSdk.IPlugin
    {
        public string Author => "HogWarp Team";
        public string Name => "My Awesome Plugin";
        public Version Version => new(0, 1);
    
        public Plugin()
        {
            var logger = new HogWarpSdk.Systems.Logger("MyMod!");
            logger.Info("Hello, World!");
        }
        
        public void PostLoad()
        {
        }
        
        public void Shutdown()
        {
        }
    }
}

namespace HogWarp.Replicated
{
    public partial class TestMultiActor
    {
        public partial void Pong(HogWarpSdk.Game.Player player, long time)
        {
            // This is the implementation of the Pong function
        }
    }
}

As you can see we added a namespace, a class, and a function that match exactly the generated class and function for our Blueprint actor. You will notice that now the mod builds!

We can implement a very basic display to give us the ping:

namespace HogWarp.Replicated
{
    public partial class TestMultiActor
    {
        public partial void Pong(HogWarpSdk.Game.Player player, long time)
        {
            long elapsedTicks = DateTime.Now.Ticks - time;

            var logger = new HogWarpSdk.Systems.Logger("TestMultiActor");
            logger.Info($"Player {player.Id} has a latency of {elapsedTicks / 100} nanoseconds");
        }
    }
}

At this point everything is wired up, all we need is a TestMultiActor instance and to call our Ping function.

Actors

As explained above we need an instance of our Blueprint class, this implies that we need to create it somehow.

To create an Actor we use the HogWarpSdk.Server.World.Spawn<T>() function which will return an instance of our actor. We can call this function multiple times if we want to create multiple instances.

namespace Tutorial
{
    public class Plugin
    {
        public string Author => "HogWarp Team";
        public string Name => "My Awesome Plugin";
        public Version Version => new(0, 1);
        
        TestMultiActor? MyActor;

        public Plugin()
        {
            var logger = new HogWarpSdk.Systems.Logger("MyMod!");
            logger.Info("Hello, World!");
        }

        void SendPings(float Delta)
        {
            foreach (var player in HogWarpSdk.Server.PlayerSystem.Players)
            {
                MyActor!.Ping(player, DateTime.Now.Ticks);
            }
        }
        
        public void PostLoad()
        {
            MyActor = HogWarpSdk.Server.World.Spawn<TestMultiActor>()!;

            HogWarpSdk.Server.Timer.Add(SendPings, 5.0f);
        }
        
        public void Shutdown()
        {
        }
    }
}

I have also added a timer, that calls the SendPings function every 5 seconds. And as you can see the SendPings function will iterate over all connected players and call the Ping function on the instance MyActor.

The Spawn function will create an instance on the server AND all the clients. If a new player joins the server, all actors that have been spawned will also be spawned for the new player.

If we build our server mod now, copy the DLL to the appropriate server directory, launch the server and connect to the server from the game client we will see every 5 seconds the latency of the player in the server console being logged.

Category for "Pong"