Modding Wiki
  • 🏠Home
  • 🫂How to contribute
  • Playing with Mods
    • Discovering Mods
    • Installing Mods
      • On Game Clients
      • For Multiplayer
      • On Dedicated Servers
      • No Console Support
    • Troubleshooting
    • Reporting Bugs
    • Uninstalling Mods
  • Creating Mods
    • Getting Started Modding
      • Setting Up the Modding SDK
      • Testing the Example Mods
      • Viewing Console Logs
    • Modding Limitations
    • Inspecting Other Mods' Code
    • Modding Tools
      • Scripting IDE
      • Unity Explorer
      • Asset Ripper
      • NG Tools Missing Script Recovery
      • DnSpy
      • Attaching a Debugger
    • Common Concepts
      • Unique Names and IDs
    • Modding Libraries
      • CoreLib
    • Modding Examples
      • Items
        • Weapons and Tools
        • Armor
        • Food
      • Obtaining items
        • Adding Crafting Recipe
        • Adding your items to crafters
        • Adding items to Enemy loot
      • Placeables
        • Tiles
      • NPCs and Enemies
      • UI and Interactions
      • Client-Server communications
    • Inspecting Base Game Content
      • Importing Ripped Assets to your Editor
      • Inspecting Assets In-Game
      • Inspecting Game Code
    • Updating your Modding SDK
    • Releasing Mods
      • Create a mod.io Page
      • Mod Files Upload
  • Concepts
    • Important Folder Paths
    • Technologies and Tools
    • Elevated Access
  • Archive
    • General Reference
    • Outdated Unity Setup Guide
    • Outdated IL2CPP Guides
      • Getting started with modding
      • How to view game source code
      • How to setup your Unity project
      • How to install Core Keeper mono version
Powered by GitBook
On this page
  • Ghost Components
  • Remote Procedure Calls
  • Prediction

Was this helpful?

Edit on GitHub
Export as PDF
  1. Creating Mods
  2. Modding Examples

Client-Server communications

This page describes how to handle various things related to client-server communications. This includes sending messages, implementing prediction, etc.

Core Keeper is a server authoritative game. This means for an action to occur, the server must be aware of it and approve. When we write mods we must be able to create logic that allows for Client-Server communication to occur.

Note that Unity uses extensive code gen to implement features outlined here. If you ever get errors regarding CodeGen ensure your mod output Scripts folder has Generated folder. If it doesn't your Unity project might have an error. A restart usually helps

Additionally you might want to utilize Burst

Ghost Components

Ghost component is one of the simplest ways to transfer data between Client and server. In its essence Ghost Component is just a normal ECS component that is synced over network.

To make a Ghost Component first Create IComponentData deriving ECS component:

public struct ExampleCD : IComponentData
{
    public int importantField;
    
    public float normalField;
}

To make this component a Ghost Component all you have to do is add a GhostComponent attribute to it. Additionally you must mark every field you want to be synced with GhostField attribute:

[GhostComponent]
public struct ExampleCD : IComponentData
{
    // This field will end up synced
    [GhostField] 
    public int importantField;
    
    // This field isn't marked, so it won't be synced
    public float normalField;
}

Remote Procedure Calls

Remote Procedure Calls (or RPC's) are a way to send information to the Server or clients in one-shot manner. They allow you to send arbitrary data or commands to the other side, be it requests or data

This example will focus on Client to Server communication, as that is the most common scenario, however you can send RPC from Server to Clients or even both ways

To make a RPC first add RPC command data definition:

public enum MyModCommandType : byte
{
    UNDEFINED,
    DO_VERY_IMPORTANT_THING,
    ANOTHER_IMPORTANT_TASK
    
    //etc
}

public struct MyModCommandRPC : IRpcCommand
{
    // This enum will define what kind of command this is
    public MyModCommandType commandType;
    
    // This is bundled request data.
    // This can be target entities, such as players
    // Or any other neccessary data
    public Entity entity0;
    public int value;
    
    // you can add more fields as needed
}

Now define two systems. One for Clients:

[UpdateInGroup(typeof(RunSimulationSystemGroup))]
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public partial class ClientModCommandSystem : PugSimulationSystemBase
{
    private NativeQueue<MyModCommandRPC> rpcQueue;
    private EntityArchetype rpcArchetype;

    protected override void OnCreate()
    {
        UpdatesInRunGroup();
        rpcQueue = new NativeQueue<MyModCommandRPC>(Allocator.Persistent);
        rpcArchetype = EntityManager.CreateArchetype(typeof(MyModCommandRPC), typeof(SendRpcCommandRequest));

        base.OnCreate();
    }

    #region Commands

    // This is the most important section
    // Here you can make send functions to have 
    // easy interface with the rest of your code
    public void DoVeryImportantThing(Entity target, int value)
    {
        rpcQueue.Enqueue(new MyModCommandRPC()
        {
            commandType = MyModCommandType.DO_VERY_IMPORTANT_THING,
            entity0 = target,
            value = value
        });
    }

    #endregion

    protected override void OnUpdate()
    {
        EntityCommandBuffer entityCommandBuffer = CreateCommandBuffer();
        while (rpcQueue.TryDequeue(out MyModCommandRPC component))
        {
            Entity e = entityCommandBuffer.CreateEntity(rpcArchetype);
            entityCommandBuffer.SetComponent(e, component);
        }
    }
}

And one for Server:

[UpdateInGroup(typeof(SimulationSystemGroup))]
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
public partial class ServerModCommandSystem : PugSimulationSystemBase
{
    protected override void OnUpdate()
    {
        var ecb = CreateCommandBuffer();

        Entities.ForEach((Entity rpcEntity, in MyModCommandRPC rpc, in ReceiveRpcCommandRequest req) =>
            {
                switch (rpc.commandType)
                {
                    case MyModCommandType.DO_VERY_IMPORTANT_THING:

                        // Do very important thing
                        // This code will be executed on the server
                        break;
                }
                ecb.DestroyEntity(rpcEntity);
            })
            .WithoutBurst()
            .Schedule();

        base.OnUpdate();
    }
}

Now to use these systems you just have to add this code in your IMod class:

public static ClientModCommandSystem clientSystem;

// You might already have this method
// if so, just add method body to your code
public void EarlyInit() 
{
    API.Client.OnWorldCreated += ClientWorldReady;
}

private static void ClientWorldReady()
{
    var world = API.Client.World;
    clientSystem = world.GetOrCreateSystemManaged<ClientModCommandSystem>();
}

Now you can use MyMod.clientSystem.DoVeryImportantThing() from anywhere in your code

Prediction

PreviousUI and InteractionsNextInspecting Base Game Content

Last updated 8 months ago

Was this helpful?

Making Ghost Components is just as easy as this. Now any data set to this component of the server will be automatically replicated to all clients. Refer to guide to learn more

This guide has not been written yet! If you know how to do this, !

this
add it