-
Notifications
You must be signed in to change notification settings - Fork 9
Multiplayer
Multiplayer.Preview.mp4
(Note that all visual glitch-like artifacts seen in the video have been resolved, I'm just too lazy to make a new video)
The 2D Top Down genre includes a client-authoritative multiplayer setup, demonstrating how player positions update on each other's screens.
Note
Each packet comes with a small overhead—either 1 or 2 bytes, depending on reliability configured—and a one-byte opcode to identify its purpose. Everything else in the packet is strictly the data we need to send.
Below is an example of a client packet. The client uses this packet to inform the server of its position. To actually do something with Position
on the server, override the Handle
method from ClientPacket
.
public class CPacketPlayerInfo : ClientPacket
{
// The NetSend parameter indicates the order of what gets sent first
[NetSend(1)]
public string Username { get; set; }
[NetSend(2)]
public Vector2 Position { get; set; }
}
Below is an example of a server packet. The server uses this packet to inform each client about the position updates of all other clients. To actually do something with Positions
on the client, override the Handle
method from ServerPacket
.
public class SPacketPlayerPositions : ServerPacket
{
[NetSend(1)]
public Dictionary<uint, Vector2> Positions { get; set; }
}
Note
If you need more control on how data is sent in a packet, read this.
// Player.cs
Net.Client.Send(new CPacketPlayerInfo { Username = playerUsername, Position = playerPosition });
// GameServer.cs
Send(new SPacketPlayerPositions { Positions = Positions }, peerId);
Lets say you want to [NetSend]
this PlayerData
but you don't want to send the PrevPosition
.
public class PlayerData
{
public string Username { get; set; }
public Vector2 Position { get; set; }
[NetExclude] // [NetSend] will ignore PrevPosition
public Vector2 PrevPosition { get; set; }
}
-
Do not directly access properties or methods across threads unless they are explicitly marked as thread safe. Not following thread safety will result in random crashes with no errors logged to the console. Things on the client thread should stay on the client thread and things on the server thread should stay on the server thread. If you need to communicate between them use the existing ConcurrentQueues.
-
A common oversight is using one data type for writing and another for reading. For example, if you have an integer
playerCount
and you write it withwriter.Write(playerCount)
, but then read it as a byte withplayerCount = reader.ReadByte()
, the data will be malformed becauseplayerCount
wasn't converted to a byte prior to writing. To avoid this, ensure you cast your data to the correct type before writing, even if it feels redundant.
You thought there was more? Nope! That's it! Multiplayer has never felt easier.