52
edits
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. | ||
== | == 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 == | |||
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. | |||
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] | |||
{{Navbox/modding|Vintage Story}} |
edits