Modding:Network API: Difference between revisions

From Vintage Story Wiki
no edit summary
No edit summary
No edit summary
Line 1: Line 1:
__FORCETOC__
__FORCETOC__
Before starting, you should have a development environment set up. If you don't have one already you should read the tutorial [[Setting up your Development Environment]]. Furthermore, we assume that you have a basic understanding of the C# language and Object Oriented Programming. Let's get started!


== Introduction ==
== Introduction ==
Line 6: Line 8:


We will create a server command that will send out a message to all clients, and if a client receives this, it will send a response back to the server; upon receiving this response, the server will display it along with the responding client's player name.
We will create a server command that will send out a message to all clients, and if a client receives this, it will send a response back to the server; upon receiving this response, the server will display it along with the responding client's player name.
== Preparation
Let's start by creating a new .cs file for this mod, and adding our imports and the namespace in which we'll wrap all of our classes:
<syntaxhighlight lang="c#">
using System;
using ProtoBuf;
using Vintagestory.API.Client;
using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;
using Vintagestory.API.Config;
using Vintagestory.API.Server;
namespace Vintagestory.ServerMods
{
}
</syntaxhighlight>


== ProtoContracts ==
== ProtoContracts ==


VintageStory uses the ProtoBuf library to serialize classes declared with the ProtoContract attribute. ProtoBuf is beyond the scope of this tutorial, but you can read more about this powerful library [https://developers.google.com/protocol-buffers/docs/csharptutorial here].
VintageStory uses the ProtoBuf library to serialize classes declared with the ProtoContract attribute. ProtoBuf is beyond the scope of this tutorial, but you can read more about this powerful library [https://developers.google.com/protocol-buffers/docs/csharptutorial here].
Inside our namespace block, let's create our classes:


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
Line 26: Line 49:


The fields in these classes hold the data we wish to send through our channel. We'll use <code>NetworkApiTestMessage</code> to encase the initial message from the server, while <code>NetWorkApiTestResponse</code> will be used by the Client to send its response.
The fields in these classes hold the data we wish to send through our channel. We'll use <code>NetworkApiTestMessage</code> to encase the initial message from the server, while <code>NetWorkApiTestResponse</code> will be used by the Client to send its response.
== NetWorkApiTest ==
Now we will create our main class which inherits from the <code>ModSystem</code> class, which is the base class for mod systems. You can read more about this [http://apidocs.vintagestory.at/api/Vintagestory.API.Common.ModSystem.html here]. Moreover, we'll be separating our Client and Server sides within the class with region blocks for readability purposes.
<syntaxhighlight lang="c#">
public class NetworkApiTest : ModSystem
    {
        #region Client
            //Client side here
        #endregion
        #region Server
            //Server side here
        #endregion
    }
</syntaxhighlight>


Next, we'll set up our network channel on the server side, and register a server command to dispatch our message.
Next, we'll set up our network channel on the server side, and register a server command to dispatch our message.


== Server Setup ==
== Server Setup ==
After declaring our Server Side fields, we create an override for the <code>StartServerSide</code> method. This method of the <code>ModSystem</code> is called for all mods on the Server Side by the game.


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
#region Server
IServerNetworkChannel serverChannel;
IServerNetworkChannel serverChannel;
         ICoreServerAPI serverApi;
         ICoreServerAPI serverApi;
Line 48: Line 91:
             api.RegisterCommand("nwtest", "Send a test network message", "", OnNwTestCmd, Privilege.controlserver);
             api.RegisterCommand("nwtest", "Send a test network message", "", OnNwTestCmd, Privilege.controlserver);
         }
         }
#endregion
</syntaxhighlight>
</syntaxhighlight>


Line 59: Line 103:


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
#region Server
IServerNetworkChannel serverChannel;
        ICoreServerAPI serverApi;
        public override void StartServerSide(ICoreServerAPI api)...
private void OnNwTestCmd(IServerPlayer player, int groupId, CmdArgs args)
private void OnNwTestCmd(IServerPlayer player, int groupId, CmdArgs args)
         {
         {
Line 66: Line 116:
             });
             });
         }
         }
#endregion
</syntaxhighlight>
</syntaxhighlight>


Line 71: Line 122:


== Client Setup ==
== Client Setup ==
Above the Server Side region, we'll create our Client fields and an override for the <code>StartClientSide</code>, which is a Client side version of its Server side counterpart.


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
#region Client
IClientNetworkChannel clientChannel;
IClientNetworkChannel clientChannel;
         ICoreClientAPI clientApi;
         ICoreClientAPI clientApi;
Line 87: Line 141:
             ;
             ;
         }
         }
#endregion
</syntaxhighlight>
</syntaxhighlight>


Line 94: Line 149:


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
#region Client
IClientNetworkChannel clientChannel;
        ICoreClientAPI clientApi;
        public override void StartClientSide(ICoreClientAPI api)...
private void OnServerMessage(NetworkApiTestMessage networkMessage)
private void OnServerMessage(NetworkApiTestMessage networkMessage)
         {
         {
Line 103: Line 164:
             });
             });
         }
         }
#endregion
</syntaxhighlight>
</syntaxhighlight>


Line 112: Line 174:


== Handling Client Responses ==
== Handling Client Responses ==
In our Server side region, let's write a final method:


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
#region Server
IServerNetworkChannel serverChannel;
        ICoreServerAPI serverApi;
        public override void StartServerSide(ICoreServerAPI api)...
private void OnNwTestCmd(IServerPlayer player, int groupId, CmdArgs args)...
private void OnClientMessage(IPlayer fromPlayer, NetworkApiTestResponse networkMessage)
private void OnClientMessage(IPlayer fromPlayer, NetworkApiTestResponse networkMessage)
         {
         {
Line 122: Line 194:
             );
             );
         }
         }
#endregion
</syntaxhighlight>
</syntaxhighlight>


Line 128: Line 201:
In this handler we simply broadcast a server-wide message containing the responding player's name and the content of the response.
In this handler we simply broadcast a server-wide message containing the responding player's name and the content of the response.


== Final words ==
== Conclusion ==
 
If you followed the steps correctly, you should have the following code:
 
<syntaxhighlight lang="c#">
using System;
using ProtoBuf;
using Vintagestory.API.Client;
using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;
using Vintagestory.API.Config;
using Vintagestory.API.Server;
 
namespace Vintagestory.ServerMods
{
    [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
    public class NetworkApiTestMessage
    {
        public string message;
    }
 
    [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
    public class NetworkApiTestResponse
    {
        public string response;
    }
 
    /// <summary>
    /// A basic example of client<->server networking using a custom network communication
    /// </summary>
    public class NetworkApiTest : ModSystem
    {
        #region Client
        IClientNetworkChannel clientChannel;
        ICoreClientAPI clientApi;
 
        public override void StartClientSide(ICoreClientAPI api)
        {
            clientApi = api;
 
            clientChannel =
                api.Network.RegisterChannel("networkapitest")
                .RegisterMessageType(typeof(NetworkApiTestMessage))
                .RegisterMessageType(typeof(NetworkApiTestResponse))
                .SetMessageHandler<NetworkApiTestMessage>(OnServerMessage)
            ;
        }
 
        private void OnServerMessage(NetworkApiTestMessage networkMessage)
        {
            clientApi.ShowChatMessage("Received following message from server: " + networkMessage.message);
            clientApi.ShowChatMessage("Sending response.");
            clientChannel.SendPacket(new NetworkApiTestResponse()
            {
                response = "RE: Hello World!"
            });
        }
 
        #endregion
 
        #region Server
        IServerNetworkChannel serverChannel;
        ICoreServerAPI serverApi;
 
        public override void StartServerSide(ICoreServerAPI api)
        {
            serverApi = api;
 
            serverChannel =
                api.Network.RegisterChannel("networkapitest")
                .RegisterMessageType(typeof(NetworkApiTestMessage))
                .RegisterMessageType(typeof(NetworkApiTestResponse))
                .SetMessageHandler<NetworkApiTestResponse>(OnClientMessage)
            ;
 
            api.RegisterCommand("nwtest", "Send a test network message", "", OnNwTestCmd, Privilege.controlserver);
        }
 
        private void OnNwTestCmd(IServerPlayer player, int groupId, CmdArgs args)
        {
            serverChannel.BroadcastPacket(new NetworkApiTestMessage()
            {
                message = "Hello World!",
            });
        }
 
        private void OnClientMessage(IPlayer fromPlayer, NetworkApiTestResponse networkMessage)
        {
            serverApi.SendMessageToGroup(
                GlobalConstants.GeneralChatGroup,
                "Received following response from " + fromPlayer.PlayerName + ": " + networkMessage.response,
                EnumChatType.Notification
            );
        }
 
       
        #endregion
    }
}
</syntaxhighlight>
 
== Testing ==
 
Let's run the mod now! Once you're ingame, try entering <code>/nwtest</code>. You should be met with an initial "Hello World!" message as well as a confirmation message that the server received the response from the Client!
 
== Distribution ==


You now understand how to set up custom network communication between client and server!
To distribute this mod, you may run the following command in the modtools cli <code>pack <your mod id></code>, then copy the .zip file into your VintageStory mods folder.


Please note that despite the ease of making your own custom network channel, it may be more appropriate to use the prebuilt network methods in the cases of <code>Entity</code> and <code>BlockEntity</code> packets. For more information on this, check out the [http://apidocs.vintagestory.at/api/Vintagestory.API.Server.IServerNetworkAPI.html IServerNetworkAPI] and [http://apidocs.vintagestory.at/api/Vintagestory.API.Client.IClientNetworkChannel.html IClientNetworkAPI].
Here are the official versions:
* for VS v1.10.: [https://wiki.vintagestory.at/index.php?title=File:NetworkApiTest_vs1.10_v1.0.0.zip NetworkApiTest_vs1.10_v1.0.0.zip]


Happy coding!
{{Navbox/modding|Vintage Story}}