Basic Unity Multiplayer Game With Networker – Part 4

Sending and Receiving Packets

Minor note: If you’re starting part 4 after 23rd March 2019 you may have to look at the previous parts and update your project names, and Networker versions to 3.0.0

We’re ready to start sending and receiving packets! For this example we are going to send a basic chat message.

Packet Library

Let’s go ahead and add a new project. This should be a .NET Standard Class Library. Call it Tutorial.Common.

Edit your .csproj to add the NuGet references for the packet serialiser.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Networker.Extensions.ProtobufNet" Version="3.0.0" />
  </ItemGroup>

</Project>

Create a new class called ChatPacket, and give it a few properties.

using ProtoBuf;

namespace Tutorial.Common
{
	[ProtoContract]
	public class ChatPacket
	{
		[ProtoMember(1)] 
		public virtual string Name { get; set; }

		[ProtoMember(2)] 
		public virtual string Message { get; set; }
	}
}

Handling Packets

Then in Tutorial.Server make sure you have added a reference to the Tutorial.Common project, and add a new class called ChatPacketHandler.

This class should derive from PacketHandlerBase<T> where T is type of the packet we have just created. The PacketHandlerBase class will handle deserialisation for us, we just need to tell it what type we are expecting. Implement the Process method as shown below.

public class ChatPacketHandler : PacketHandlerBase<ChatPacket>
{
	public override async Task Process(ChatPacket packet, IPacketContext packetContext)
	{
	}
}

One of the most important features of Networker is dependency injection, and it’s built-in logging. We can really easily create a logger to see exactly what’s happening in our packet handler.

public class ChatPacketHandler : PacketHandlerBase<ChatPacket>
{
	private readonly ILogger<ChatPacketHandler> _logger;

	public ChatPacketHandler(ILogger<ChatPacketHandler> logger)
	{
		_logger = logger;
	}

	public override async Task Process(ChatPacket packet, IPacketContext packetContext)
	{
		_logger.LogDebug("I received the chat message: " + packet.Message);
	}
}

We need to tell the server about this packet type, and which handler it should use when it receives it. This is done in our builder class in Program.

var server = new ServerBuilder()
	.UseTcp(networkerSettings.GetValue<int>("TcpPort"))
	.UseUdp(networkerSettings.GetValue<int>("UdpPort"))
	.ConfigureLogging(loggingBuilder =>
	{
		loggingBuilder.AddConfiguration(config.GetSection("Logging"));
		loggingBuilder.AddConsole();
	})
	.UseProtobufNet()
	.RegisterPacketHandler<ChatPacket, ChatPacketHandler>()
	.Build();

Sending Packets

In our Program class we have access to the Client. Let’s try sending some packets to the server, populating the Messsage property with the current date and time.

var gameClient = new GameClient();
gameClient.Client.Connect();
 
while (server.Information.IsRunning)
{
	gameClient.Client.Send(new ChatPacket
	{
		Message = DateTime.Now.ToString()
	});

	Thread.Sleep(10000);
}

If everything is set up correctly, you’ll start seeing some new debug logs popping up in the console.

Responding to packets

In the Tutorial.Client project add a new class ClientChatPacketHandler.

This class is going to log that it received a packet in response.

public class ClientChatPacketHandler : PacketHandlerBase<ChatPacket>
{
	private readonly ILogger<ClientChatPacketHandler> _logger;

	public ClientChatPacketHandler(ILogger<ClientChatPacketHandler> logger)
	{
		_logger = logger;
	}

	public override async Task Process(ChatPacket packet, IPacketContext packetContext)
	{
		_logger.LogDebug("Client received the response packet: " + packet.Message);
	}
}

Then in our existing ChatPacketHandler in the server add the following line.

public class ChatPacketHandler : PacketHandlerBase<ChatPacket>
{
	private readonly ILogger<ChatPacketHandler> _logger;

	public ChatPacketHandler(ILogger<ChatPacketHandler> logger)
	{
		_logger = logger;
	}

	public override async Task Process(ChatPacket packet, IPacketContext packetContext)
	{
		_logger.LogDebug("I received the chat message: " + packet.Message);
		packetContext.Sender.Send(packet);
	}
}

Then in our GameClient class we need to register the packet type and it’s handler.

Client = new ClientBuilder()
	.UseIp(networkerSettings.GetValue<string>("Address"))
	.UseTcp(networkerSettings.GetValue<int>("TcpPort"))
	.UseUdp(networkerSettings.GetValue<int>("UdpPort"))
	.ConfigureLogging(loggingBuilder =>
	{
		loggingBuilder.AddConfiguration(config.GetSection("Logging"));
		loggingBuilder.AddConsole();
	})
	.UseProtobufNet()
	.RegisterPacketHandler<ChatPacket, ClientChatPacketHandler>()
	.Build();

Hopefully you will now see log output like below!

That’s it for part 4!

A short note, please be patient in waiting for the next parts of the tutorial as I write this in my spare time and I have been busy moving house. We have a new community Discord ( https://discord.gg/NdEqhAe), you can join to ask questions or discuss new features.

Leave a comment

Your email address will not be published. Required fields are marked *