Modding:Network API: Difference between revisions

From Vintage Story Wiki
m
VeryGoodDog moved page Network API to Modding:Network API
No edit summary
m (VeryGoodDog moved page Network API to Modding:Network API)
(19 intermediate revisions by 5 users not shown)
Line 1: Line 1:
__FORCETOC__
__FORCETOC__
{{GameVersion|1.12}}


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!
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!
'''Hint''': If you need to send data for Entities or BlockEntities, it may be more appropriate to use the inbuilt network methods for these. Check out the [http://apidocs.vintagestory.at/api/Vintagestory.API.Server.IServerNetworkAPI.html IServerNetworkAPI] and [http://apidocs.vintagestory.at/api/Vintagestory.API.Client.IClientNetworkAPI.html IClientNetworkAPI] interfaces for more information.


== Introduction ==
== Introduction ==


In this example mod we will show you how to send custom data back and forth between the Server and the Client. To do this, we will set up a network channel through which string messages will be sent through a network channel.
In this example mod we will show you how to send custom data back and forth between the Server and the Client. To do this, we will set up a network channel through which string messages will be sent in a network channel.


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
== 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:
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:
Line 30: Line 33:
== 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 [https://github.com/protobuf-net/protobuf-net Protobuf-net] library to serialize classes declared with the <code>ProtoContract</code> attribute.  


Inside our namespace block, let's create our classes:
Inside our namespace block, let's create our <code>ProtoContract</code> classes:


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
    [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
     public class NetworkApiTestMessage
     public class NetworkApiTestMessage
     {
     {
Line 47: Line 50:
     }
     }
</syntaxhighlight>
</syntaxhighlight>
The argument we pass to <code>ProtoContract</code> makes it so all <code>public</code> fields will be serialized; without this, we'd declare fields for serialization by using <code>ProtoMember</code> attributes. If we chose this approach, we'd write our classes like so:
<syntaxhighlight lang="c#">
    [ProtoContract]
    public class NetworkApiTestMessage
    {
        [ProtoMember(1)]
        public string message;
    }
    [ProtoContract]
    public class NetworkApiTestResponse
    {
        [ProtoMember(1)]
        public string response;
    }
</syntaxhighlight>
The argument we pass to <code>ProtoMember</code> is a unique unsigned integer called a tag, and it's used to identify the fields flagged for serialization. When the client receives the packet containing the serialized <code>ProtoContract</code>, it won't know the type of its members, and will identify them by the tag number. We use the <code>ImplicitFields.AllPublic</code> to flag all public fields for serialization, and tags are assigned in alphabetic order. 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].


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 ==  
== 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.
Now we will create our main class that 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#">
<syntaxhighlight lang="c#">
public class NetworkApiTest : ModSystem
    public class NetworkApiTest : ModSystem
     {
     {
         #region Client
         #region Client
Line 67: Line 89:
</syntaxhighlight>
</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.
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
#region Server
IServerNetworkChannel serverChannel;
        IServerNetworkChannel serverChannel;
         ICoreServerAPI serverApi;
         ICoreServerAPI serverApi;


Line 94: Line 116:
</syntaxhighlight>
</syntaxhighlight>


The Core Server API contains the Network member, which provides us with the RegisterChannel method. We pass this method a string containing the name we choose for our channel, in this case we'll call our channel "networkapitest". A channel with this exact name will be registered on the Client side.
The Core Server API contains the Network member, which provides us with the <code>RegisterChannel</code> method. We pass this method a string containing the name we choose for our channel, in this case we'll call our channel "networkapitest". A channel with this exact name will be registered on the Client side.


We then register the ProtoContract classes that we'll be using to this channel with the <code>RegisterMessageType</code> method.
We then register the ProtoContract classes that we'll be using to this channel with the <code>RegisterMessageType</code> method.


Finally, we call <code>SetMessageHandler<T></code>, which takes a delegate that will be called every time a ProtoContract of type <code>T</code> is received from a client, in this case <code>NetworkApiTestResponse</code> will be handled by <code>OnClientMessage</code> which we'll declare later.
Finally, we call <code>SetMessageHandler<T></code>, which takes a delegate that will be called every time a <code>ProtoContract</code> of type <code>T</code> is received from a client, in this case <code>NetworkApiTestResponse</code> will be handled by <code>OnClientMessage</code> which we'll declare later.


Now that our channel is set up on the Server side, we'll want to register a server command that will broadcast a message to all the clients listening to our "networkapitest" channel.
Now that our channel is set up on the Server side, we'll want to register a server command that will broadcast a network message to all the clients listening to our "networkapitest" channel.


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
#region Server
#region Server
IServerNetworkChannel serverChannel;
        IServerNetworkChannel serverChannel;
         ICoreServerAPI serverApi;
         ICoreServerAPI serverApi;


         public override void StartServerSide(ICoreServerAPI api)...
         public override void StartServerSide(ICoreServerAPI api)...


private void OnNwTestCmd(IServerPlayer player, int groupId, CmdArgs args)
        private void OnNwTestCmd(IServerPlayer player, int groupId, CmdArgs args)
         {
         {
             serverChannel.BroadcastPacket(new NetworkApiTestMessage()
             serverChannel.BroadcastPacket(new NetworkApiTestMessage()
Line 119: Line 141:
</syntaxhighlight>
</syntaxhighlight>


The server channel gives us the BroadcastPacket method which we'll use to send out a <code>NetworkApiTestMessage</code> instance to all clients listening to the "networkapitest" channel, in this case we send "Hello World!" as the message.
The server channel gives us the <code>BroadcastPacket</code> method which we'll use to send out a <code>NetworkApiTestMessage</code> instance to all clients listening to the "networkapitest" channel, in this case we send "Hello World!" as the message.


== Client Setup ==
== Client Setup ==
Line 127: Line 149:
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
#region Client
#region Client
IClientNetworkChannel clientChannel;
        IClientNetworkChannel clientChannel;
         ICoreClientAPI clientApi;
         ICoreClientAPI clientApi;


Line 150: Line 172:
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
#region Client
#region Client
IClientNetworkChannel clientChannel;
        IClientNetworkChannel clientChannel;
         ICoreClientAPI clientApi;
         ICoreClientAPI clientApi;


         public override void StartClientSide(ICoreClientAPI api)...
         public override void StartClientSide(ICoreClientAPI api)...


private void OnServerMessage(NetworkApiTestMessage networkMessage)
        private void OnServerMessage(NetworkApiTestMessage networkMessage)
         {
         {
             clientApi.ShowChatMessage("Received following message from server: " + networkMessage.message);
             clientApi.ShowChatMessage("Received following message from server: " + networkMessage.message);
Line 175: Line 197:
== Handling Client Responses ==
== Handling Client Responses ==


In our Server side region, let's write a final method:
In our Server side region, let's write a final delegate method:


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
#region Server
#region Server
IServerNetworkChannel serverChannel;
        IServerNetworkChannel serverChannel;
         ICoreServerAPI serverApi;
         ICoreServerAPI serverApi;


         public override void StartServerSide(ICoreServerAPI api)...
         public override void StartServerSide(ICoreServerAPI api)...


private void OnNwTestCmd(IServerPlayer player, int groupId, CmdArgs args)...
        private void OnNwTestCmd(IServerPlayer player, int groupId, CmdArgs args)...


private void OnClientMessage(IPlayer fromPlayer, NetworkApiTestResponse networkMessage)
        private void OnClientMessage(IPlayer fromPlayer, NetworkApiTestResponse networkMessage)
         {
         {
             serverApi.SendMessageToGroup(
             serverApi.SendMessageToGroup(
Line 197: Line 219:
</syntaxhighlight>
</syntaxhighlight>


Messages handlers on the server have an additional parameter compared to client handlers, which is the player whose client sent the message.
Message handlers on the server have an additional parameter compared to client handlers, which is the player whose client sent the message.


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.
Line 305: Line 327:


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!
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 ==
== Distribution ==
Line 311: Line 335:


Here are the official versions:
Here are the official versions:
* for VS v1.10-rc.4: [https://wiki.vintagestory.at/images/7/73/NetworkApiTest_vs1.10-rc.4_v1.0.0.zip NetworkApiTest_vs1.10_v1.0.0.zip]
* for VS v1.10-rc.4: [https://wiki.vintagestory.at/images/7/73/NetworkApiTest_vs1.10-rc.4_v1.0.0.zip NetworkApiTest_vs1.10-rc.4_v1.0.0.zip]
* for VS v1.6: [https://wiki.vintagestory.at/images/d/d1/NetworkApiTest_vs1.6_v1.0.0.zip NetworkApiTest_vs1.6_v1.0.0.zip]


{{Navbox/modding|Vintage Story}}
{{Navbox/modding|Vintage Story}}
Confirmedusers, Bureaucrats, editor, Administrators
1,522

edits