Modding:Network API: Difference between revisions

From Vintage Story Wiki
no edit summary
m (Updated navbox to new code navbox.)
No edit summary
Line 24: Line 24:
</translate>
</translate>
<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
using System;
using ProtoBuf;
using ProtoBuf;
using Vintagestory.API.Client;
using Vintagestory.API.Client;
using Vintagestory.API.Common;
using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;
using Vintagestory.API.Config;
using Vintagestory.API.Config;
using Vintagestory.API.Server;
using Vintagestory.API.Server;
Line 42: Line 40:
VintageStory uses multiple [[Modding:Serialization_Formats|serialization formats]]. This tutorial uses the protobuf format. The [https://github.com/protobuf-net/protobuf-net Protobuf-net] library can serialize classes declared with the <code>ProtoContract</code> attribute.  
VintageStory uses multiple [[Modding:Serialization_Formats|serialization formats]]. This tutorial uses the protobuf format. The [https://github.com/protobuf-net/protobuf-net Protobuf-net] library can serialize classes declared with the <code>ProtoContract</code> attribute.  


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


<syntaxhighlight lang="c#">
    [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
    public class NetworkApiTestMessage
    {
        public string message;
    }
    [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
    public class NetworkApiTestResponse
    {
        public string response;
    }
</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#">
<syntaxhighlight lang="c#">
     [ProtoContract]
     [ProtoContract]
Line 75: Line 58:
</syntaxhighlight>
</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 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. If we use the <code>ImplicitFields.AllPublic</code> to flag all public fields for serialization, then 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.
Line 86: Line 69:
     public class NetworkApiTest : ModSystem
     public class NetworkApiTest : ModSystem
     {
     {
        public override void Start(ICoreAPI api)
        {
            api.Network
                .RegisterChannel("networkapitest")
                .RegisterMessageType(typeof(NetworkApiTestMessage))
                .RegisterMessageType(typeof(NetworkApiTestResponse))
            ;
        }
         #region Client
         #region Client
             //Client side here
             // Client side here
         #endregion
         #endregion


         #region Server
         #region Server
             //Server side here
             // Server side here
         #endregion  
         #endregion  
     }
     }
</syntaxhighlight>
</syntaxhighlight>


Next, we'll set up our network channel on the Server side, and register a server command to dispatch our message.
The Core API contains the Network property, 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". The code in Start() is executed on the server as well as the client. We now have two connected endpoints.
 
The system still needs to know what kind of packages you will want to send. We do this with the <code>RegisterMessageType</code> method.
 


== 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.
Next, we'll set up our network channel on the Server side, and register a server command to dispatch our message.


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
Line 111: Line 105:
             serverApi = api;
             serverApi = api;


             serverChannel =
             serverChannel = api.Network.GetChannel("networkapitest")
                api.Network.RegisterChannel("networkapitest")
                .RegisterMessageType(typeof(NetworkApiTestMessage))
                .RegisterMessageType(typeof(NetworkApiTestResponse))
                 .SetMessageHandler<NetworkApiTestResponse>(OnClientMessage)
                 .SetMessageHandler<NetworkApiTestResponse>(OnClientMessage)
             ;
             ;
Line 126: Line 117:
</syntaxhighlight>
</syntaxhighlight>


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 call <code>SetMessageHandler<T></code> on our channel, which takes a delegate that will be called every time when the given type of Packet is received from a client, in this case <code>NetworkApiTestResponse</code> will be handled by <code>OnClientMessage</code> which we'll declare later.
 
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 <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 network message to all the clients listening to our "networkapitest" channel.
Now that our channel is fully 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#">
Line 167: Line 154:
             clientApi = api;
             clientApi = api;


             clientChannel =
             clientChannel = api.Network.GetChannel("networkapitest")
                api.Network.RegisterChannel("networkapitest")
                .RegisterMessageType(typeof(NetworkApiTestMessage))
                .RegisterMessageType(typeof(NetworkApiTestResponse))
                 .SetMessageHandler<NetworkApiTestMessage>(OnServerMessage)
                 .SetMessageHandler<NetworkApiTestMessage>(OnServerMessage)
             ;
             ;
Line 177: Line 161:
</syntaxhighlight>
</syntaxhighlight>


On the Client side, we set up the "networkapitest" channel much in the same way as we did on the Server side. The <code>IClientNetworkChannel</code> type is the client side equivalent of the <code>IServerNetworkChannel</code>, and shares much of the same functionality.
Also on the client we call <code>SetMessageHandler</code> to listen for messages of type <code>NetworkApiTestMessage</code>, using a delegate named <code>OnServerMessage</code>.
 
This time we call <code>SetMessageHandler</code> to listen for messages of type <code>NetworkApiTestMessage</code>, using a delegate named <code>OnServerMessage</code>.


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
Line 239: Line 221:


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
using System;
using ProtoBuf;
using ProtoBuf;
using Vintagestory.API.Client;
using Vintagestory.API.Client;
using Vintagestory.API.Common;
using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;
using Vintagestory.API.Config;
using Vintagestory.API.Config;
using Vintagestory.API.Server;
using Vintagestory.API.Server;
Line 249: Line 229:
namespace Vintagestory.ServerMods
namespace Vintagestory.ServerMods
{
{
     [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
     [ProtoContract]
     public class NetworkApiTestMessage
     public class NetworkApiTestMessage
     {
     {
        [ProtoMember(1)]
         public string message;
         public string message;
     }
     }


     [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
     [ProtoContract]
     public class NetworkApiTestResponse
     public class NetworkApiTestResponse
     {
     {
        [ProtoMember(1)]
         public string response;
         public string response;
     }
     }
Line 266: Line 248:
     public class NetworkApiTest : ModSystem
     public class NetworkApiTest : ModSystem
     {
     {
        public override void Start(ICoreAPI api)
        {
            api.Network
                .RegisterChannel("networkapitest")
                .RegisterMessageType(typeof(NetworkApiTestMessage))
                .RegisterMessageType(typeof(NetworkApiTestResponse))
            ;
        }
         #region Server
         #region Server
         IServerNetworkChannel serverChannel;
         IServerNetworkChannel serverChannel;
Line 274: Line 265:
             serverApi = api;
             serverApi = api;


             serverChannel =
             serverChannel = api.Network.GetChannel("networkapitest")
                api.Network.RegisterChannel("networkapitest")
                .RegisterMessageType(typeof(NetworkApiTestMessage))
                .RegisterMessageType(typeof(NetworkApiTestResponse))
                 .SetMessageHandler<NetworkApiTestResponse>(OnClientMessage)
                 .SetMessageHandler<NetworkApiTestResponse>(OnClientMessage)
             ;
             ;
Line 314: Line 302:
             clientApi = api;
             clientApi = api;


             clientChannel =
             clientChannel = api.Network.GetChannel("networkapitest")
                api.Network.RegisterChannel("networkapitest")
                .RegisterMessageType(typeof(NetworkApiTestMessage))
                .RegisterMessageType(typeof(NetworkApiTestResponse))
                 .SetMessageHandler<NetworkApiTestMessage>(OnServerMessage)
                 .SetMessageHandler<NetworkApiTestMessage>(OnServerMessage)
             ;
             ;
Confirmedusers, Bureaucrats, editor, Administrators
1,793

edits