Files
DTrierFlood_New/Assets/Scripts/Networking/Client/SweQuestControlClient.cs

267 lines
7.2 KiB
C#

using System;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
namespace FloodSWE.Networking
{
public sealed class SweQuestControlClient : MonoBehaviour
{
[Header("Server")]
public string serverHost = "127.0.0.1";
public int serverCommandPort = 29010;
[Header("Connect")]
public bool autoConnectOnEnable = true;
public bool sendHelloOnConnect = true;
public bool verboseLogging = false;
public float ackTimeoutSeconds = 3.0f;
public float helloKeepAliveIntervalSeconds = 1.0f;
private UdpClient socket;
private IPEndPoint serverEndpoint;
private float lastAckTimeUnscaled = -1.0f;
private float lastHelloSentTimeUnscaled = -1.0f;
private string lastAckMessage = "";
private bool connectRequested;
public bool HasReceivedAck => lastAckTimeUnscaled >= 0.0f;
public float LastAckAgeSeconds => HasReceivedAck ? Time.unscaledTime - lastAckTimeUnscaled : float.PositiveInfinity;
public string LastAckMessage => lastAckMessage;
public bool IsConnectionAlive => HasReceivedAck && LastAckAgeSeconds <= Mathf.Max(0.1f, ackTimeoutSeconds);
public bool IsWaitingForAck => connectRequested && !IsConnectionAlive && !HasReceivedAck;
private void OnEnable()
{
if (autoConnectOnEnable && !Connect())
{
enabled = false;
}
}
private void OnDisable()
{
Disconnect();
}
private void Update()
{
PollAcks();
TickKeepAlive();
}
public bool Connect()
{
try
{
EnsureSocket();
ResolveServerEndpoint();
connectRequested = true;
if (sendHelloOnConnect)
{
SendHello();
}
if (verboseLogging)
{
Debug.Log($"SweQuestControlClient: connected to {serverEndpoint}.");
}
return true;
}
catch (Exception ex)
{
Debug.LogError($"SweQuestControlClient: failed to connect UDP sender. {ex.Message}");
connectRequested = false;
Disconnect();
return false;
}
}
public void Disconnect()
{
if (socket != null)
{
socket.Close();
socket = null;
}
serverEndpoint = null;
lastAckTimeUnscaled = -1.0f;
lastAckMessage = "";
connectRequested = false;
}
public void SendHello()
{
Send(new SweControlCommand
{
command = "hello",
});
lastHelloSentTimeUnscaled = Time.unscaledTime;
}
public void SetSourceLevel(int sourceId, float level)
{
Send(new SweControlCommand
{
command = "set_source_level",
sourceId = sourceId,
sourceLevel = level,
});
}
public void SetSinkLevel(int sinkId, float level)
{
Send(new SweControlCommand
{
command = "set_sink_level",
sinkId = sinkId,
sinkLevel = level,
});
}
public void SetActiveTile(string lod, int tileX, int tileY)
{
Send(new SweControlCommand
{
command = "set_active_tile",
lod = lod,
tileX = tileX,
tileY = tileY,
});
}
public void SaveCheckpoint(string checkpointName)
{
Send(new SweControlCommand
{
command = "save_checkpoint",
checkpoint = checkpointName,
});
}
public void ResetCheckpoint(string checkpointName)
{
Send(new SweControlCommand
{
command = "reset_checkpoint",
checkpoint = checkpointName,
});
}
public void ApplyPorosityStamp(float u, float v, float radius, float porosity)
{
Send(new SweControlCommand
{
command = "apply_porosity_stamp",
u = Mathf.Clamp01(u),
v = Mathf.Clamp01(v),
radius = Mathf.Clamp01(radius),
porosity = Mathf.Clamp01(porosity),
});
}
public void SendDisconnect()
{
Send(new SweControlCommand
{
command = "disconnect",
});
}
private void ResolveServerEndpoint()
{
IPAddress[] addresses = Dns.GetHostAddresses(serverHost.Trim());
if (addresses.Length == 0)
{
throw new InvalidOperationException($"No address found for '{serverHost}'.");
}
serverEndpoint = new IPEndPoint(addresses[0], serverCommandPort);
}
private void EnsureSocket()
{
if (socket == null)
{
socket = new UdpClient();
socket.Client.Blocking = false;
}
}
private void Send(SweControlCommand command)
{
if (socket == null)
{
return;
}
if (serverEndpoint == null)
{
try
{
ResolveServerEndpoint();
}
catch
{
return;
}
}
byte[] payload = SweUdpProtocol.EncodeControl(command);
socket.Send(payload, payload.Length, serverEndpoint);
}
private void PollAcks()
{
if (socket == null)
{
return;
}
while (socket.Available > 0)
{
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
byte[] payload;
try
{
payload = socket.Receive(ref sender);
}
catch (SocketException)
{
break;
}
if (!SweUdpProtocol.TryDecodeAck(payload, out string message))
{
continue;
}
lastAckTimeUnscaled = Time.unscaledTime;
lastAckMessage = message ?? "";
if (verboseLogging)
{
Debug.Log($"SweQuestControlClient: ack from {sender}: {lastAckMessage}");
}
}
}
private void TickKeepAlive()
{
if (!connectRequested || socket == null || serverEndpoint == null)
{
return;
}
float interval = Mathf.Max(0.1f, helloKeepAliveIntervalSeconds);
if (lastHelloSentTimeUnscaled < 0.0f || (Time.unscaledTime - lastHelloSentTimeUnscaled) >= interval)
{
SendHello();
}
}
}
}