I currently have a very weird issue. I’m currently working on some test code for implementing multiplayer using UDP
. Because I know I need to use UDP
in multiple places, I created a helper class for it. Here’s what the helper class looks like:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;
public class UdpHelper : MonoBehaviour
{
UdpClient client;
IPEndPoint mpEndPoint;
[SerializeField]
int udpPort = 5600;
[SerializeField]
public delegate void onReceiveStringDelegate(String payload, IPEndPoint sender);
public event onReceiveStringDelegate onReceiveStringEvent;
public delegate void onReceiveByteArrDelegate(byte[] payload, IPEndPoint sender);
public event onReceiveByteArrDelegate onReceiveByteArrEvent;
bool isStarted = false;
public bool isServer = false;
void Awake()
{
}
void Start()
{
isStarted = true;
}
void Update()
{
}
public void Connect(string ip, int port)
{
client = new UdpClient();
mpEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
client.BeginReceive(ReceiveData, null);
}
public void Connect(int port)
{
client = new UdpClient(port);
mpEndPoint = new IPEndPoint(IPAddress.Any, port);
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
// client.BeginReceive(ReceiveData, null);
ReceiveData();
}
public void Send(string payload)
{
try
{
byte[] data = Encoding.UTF8.GetBytes(payload);
print("sending str: " + BitConverter.ToString(data));
client.Send(data, data.Length, mpEndPoint);
}
catch (Exception err)
{
print("UDP Send str: " + err.ToString());
}
}
public void Send(byte[] payload)
{
try
{
print("sending bytes: " + BitConverter.ToString(payload));
client.Send(payload, payload.Length, mpEndPoint);
}
catch (Exception err)
{
print("UDP Send byte arr: " + err.ToString());
}
}
public void Send(byte[] payload, IPEndPoint recepient)
{
try
{
print("sending bytes: " + BitConverter.ToString(payload));
client.Send(payload, payload.Length, recepient);
}
catch (Exception err)
{
print("UDP Send byte arr: " + err.ToString());
}
}
private void ReceiveData(IAsyncResult result)
{
try
{
byte[] receivedBytes = client.EndReceive(result, ref mpEndPoint);
onReceiveByteArrEvent?.Invoke(receivedBytes, mpEndPoint);
print("received bytes: " + BitConverter.ToString(receivedBytes));
string receivedMessage = Encoding.UTF8.GetString(receivedBytes);
onReceiveStringEvent?.Invoke(receivedMessage, mpEndPoint);
// Continue receiving data asynchronously
client.BeginReceive(ReceiveData, null);
}
catch (Exception err)
{
print("UDP Receive: " + err.ToString());
}
}
private void ReceiveData()
{
try
{
while (true)
{
byte[] receivedBytes = client.Receive(ref mpEndPoint);
onReceiveByteArrEvent?.Invoke(receivedBytes, mpEndPoint);
print("received bytes: " + BitConverter.ToString(receivedBytes));
string receivedMessage = Encoding.UTF8.GetString(receivedBytes);
onReceiveStringEvent?.Invoke(receivedMessage, mpEndPoint);
}
}
catch (Exception err)
{
print("UDP Receive: " + err.ToString());
}
finally
{
client.Close();
}
}
void OnDestroy()
{
isStarted = false;
if(client != null){
client.Close();
}
}
}
I add this helper to a GameObject, then access them like this:
private UdpHelper udpHelper;
udpHelper = GetComponent<UdpHelper>();
udpHelper.Connect(multiplayerServerIp, multiplayerServerPort);
udpHelper.onReceiveByteArrEvent += OnUdpReceive;
2 scripts currently needs this UDP
helper class, MultiplayerEntityHelperUDP
for entities and MultiplayerHandlerUDP
for the “server”. After some testing, it seems like if MultiplayerHandlerUDP
exist in a GameObject
, it breaks the engine, enabled or not. Here’s what MultiplayerHandlerUDP
looks like:
using System;
using System.Diagnostics;
using System.Net;
using UnityEngine;
[RequireComponent(typeof(UdpHelper))]
public class MultiplayerHandlerUDP : MonoBehaviour
{
#region Transport
private UdpHelper udpHelper;
[SerializeField]
public string multiplayerServerIp = "127.0.0.1";
[SerializeField]
public int multiplayerServerPort = 3254;
#endregion
int TICK_RATE = 20;
bool mpUpdaterActive = false;
Utils utils = Utils.Instance;
byte[] playersStateByteArr = new byte[36 * 10];
void Awake()
{
udpHelper = GetComponent<UdpHelper>();
// udpHelper.Connect(multiplayerServerPort);
// udpHelper.isServer = true;
// udpHelper.onReceiveByteArrEvent += OnUdpReceive;
}
// Start is called before the first frame update
void Start()
{
mpUpdaterActive = true;
// Thread thread1 = new Thread(new ThreadStart(MultiplayerUpdater));
// thread1.Start();
}
// Update is called once per frame
void Update()
{
}
void MultiplayerUpdater()
{
Stopwatch timer = new Stopwatch();
timer.Start();
while (mpUpdaterActive)
{
if (timer.ElapsedMilliseconds % TICK_RATE == 0)
{
print("Update time: " + timer.ElapsedMilliseconds);
var tempStateData = new float[] {
0.00f, 0.00f, 0.00f,
0.00f, 0.00f, 0.00f,
0.00f, 0.00f, 0.00f,
};
Buffer.BlockCopy(tempStateData, 0, playersStateByteArr, 0, playersStateByteArr.Length);
udpHelper.Send(playersStateByteArr);
}
}
}
void OnUdpReceive(byte[] payload, IPEndPoint sender)
{
print("server udp received: " + BitConverter.ToString(payload) + " sender: " + sender.Address + ":" + sender.Port);
var byteArray = new byte[payload.Length * 4];
Buffer.BlockCopy(payload, 0, byteArray, 0, byteArray.Length);
}
void OnDestroy()
{
mpUpdaterActive = false;
}
}
After some testing, it seems like if MultiplayerHandlerUDP
calls the helper class’ Connect
function that accepts just a port, the engine hangs. I don’t see why that would be a problem. Because, in my head, it uses UDP
differently and shouldn’t clash with the client’s usage of UDP
. As I’m still currently testing stuff out, I currently activate both the “server” and the client simultaneously, which might be causing this issue. But, if I’m not mistaken, at the end of the day, the final setup would be close to that.