network fix

This commit is contained in:
2025-12-13 16:39:02 +01:00
parent efa6016fe5
commit cb59d5a918
8 changed files with 119 additions and 43 deletions

View File

@@ -76,7 +76,19 @@ namespace KCM
private static void Client_Connected(object sender, EventArgs e) private static void Client_Connected(object sender, EventArgs e)
{ {
try
{
if (client != null && client.Connection != null)
{
client.Connection.CanQualityDisconnect = false;
client.Connection.MaxSendAttempts = 50;
}
}
catch (Exception ex)
{
Main.helper.Log("Error configuring client connection");
Main.helper.Log(ex.ToString());
}
} }

View File

@@ -21,6 +21,9 @@ namespace KCM
public static Server server = new Server(Main.steamServer); public static Server server = new Server(Main.steamServer);
public static bool started = false; public static bool started = false;
private static readonly Dictionary<ushort, Queue<SaveTransferPacket>> saveTransferQueues = new Dictionary<ushort, Queue<SaveTransferPacket>>();
private const int SaveTransferPacketsPerUpdatePerClient = 10;
static KCServer() static KCServer()
{ {
//server.registerMessageHandler(typeof(KCServer).GetMethod("ClientJoined")); //server.registerMessageHandler(typeof(KCServer).GetMethod("ClientJoined"));
@@ -50,6 +53,7 @@ namespace KCM
} }
ev.Client.CanQualityDisconnect = false; ev.Client.CanQualityDisconnect = false;
ev.Client.MaxSendAttempts = 50;
Main.helper.Log("Client ID is: " + ev.Client.Id); Main.helper.Log("Client ID is: " + ev.Client.Id);
@@ -85,6 +89,8 @@ namespace KCM
if (entry != null) if (entry != null)
Destroy(entry.gameObject); Destroy(entry.gameObject);
saveTransferQueues.Remove(ev.Client.Id);
Main.helper.Log($"Client disconnected. {ev.Reason}"); Main.helper.Log($"Client disconnected. {ev.Reason}");
} }
catch (Exception ex) catch (Exception ex)
@@ -125,6 +131,68 @@ namespace KCM
private void Update() private void Update()
{ {
server.Update(); server.Update();
ProcessSaveTransfers();
}
private static void ProcessSaveTransfers()
{
if (!KCServer.IsRunning)
return;
if (saveTransferQueues.Count == 0)
return;
var clients = saveTransferQueues.Keys.ToList();
foreach (var clientId in clients)
{
Queue<SaveTransferPacket> queue;
if (!saveTransferQueues.TryGetValue(clientId, out queue) || queue == null)
continue;
int sentThisUpdate = 0;
while (sentThisUpdate < SaveTransferPacketsPerUpdatePerClient && queue.Count > 0)
{
var packet = queue.Dequeue();
packet.Send(clientId);
sentThisUpdate++;
}
if (queue.Count == 0)
saveTransferQueues.Remove(clientId);
}
}
public static void EnqueueSaveTransfer(ushort toClient, byte[] bytes)
{
if (bytes == null)
return;
int chunkSize = 900;
int sent = 0;
int totalChunks = (int)Math.Ceiling((double)bytes.Length / chunkSize);
var queue = new Queue<SaveTransferPacket>(totalChunks);
for (int i = 0; i < totalChunks; i++)
{
int currentChunkSize = Math.Min(chunkSize, bytes.Length - sent);
var chunk = new byte[currentChunkSize];
Array.Copy(bytes, sent, chunk, 0, currentChunkSize);
queue.Enqueue(new SaveTransferPacket()
{
saveSize = bytes.Length,
saveDataChunk = chunk,
chunkId = i,
chunkSize = chunk.Length,
saveDataIndex = sent,
totalChunks = totalChunks
});
sent += currentChunkSize;
}
saveTransferQueues[toClient] = queue;
Main.helper.Log($"Queued {totalChunks} save data chunks for client {toClient}");
} }
private void OnApplicationQuit() private void OnApplicationQuit()

34
Main.cs
View File

@@ -55,6 +55,8 @@ namespace KCM
public static Dictionary<string, KCPlayer> kCPlayers = new Dictionary<string, KCPlayer>(); public static Dictionary<string, KCPlayer> kCPlayers = new Dictionary<string, KCPlayer>();
public static Dictionary<ushort, string> clientSteamIds = new Dictionary<ushort, string>(); public static Dictionary<ushort, string> clientSteamIds = new Dictionary<ushort, string>();
private static readonly Dictionary<int, long> lastTeamIdLookupLogMs = new Dictionary<int, long>();
public static KCPlayer GetPlayerByClientID(ushort clientId) public static KCPlayer GetPlayerByClientID(ushort clientId)
{ {
return kCPlayers[clientSteamIds[clientId]]; return kCPlayers[clientSteamIds[clientId]];
@@ -62,23 +64,33 @@ namespace KCM
public static Player GetPlayerByTeamID(int teamId) // Need to replace building / production types so that the correct player is used. IResourceStorage and IResourceProvider, and jobs public static Player GetPlayerByTeamID(int teamId) // Need to replace building / production types so that the correct player is used. IResourceStorage and IResourceProvider, and jobs
{ {
try KCPlayer match = kCPlayers.Values.FirstOrDefault(p =>
{ p != null &&
var player = kCPlayers.Values.FirstOrDefault(p => p.inst.PlayerLandmassOwner.teamId == teamId).inst; p.inst != null &&
p.inst.PlayerLandmassOwner != null &&
p.inst.PlayerLandmassOwner.teamId == teamId);
return player; if (match != null && match.inst != null)
} return match.inst;
catch (Exception e)
if (KCServer.IsRunning || KCClient.client.IsConnected)
{ {
if (KCServer.IsRunning || KCClient.client.IsConnected) long now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
long last;
if (!lastTeamIdLookupLogMs.TryGetValue(teamId, out last) || (now - last) > 2000)
{ {
Main.helper.Log("Failed finding player by teamID: " + teamId + " My teamID is: " + Player.inst.PlayerLandmassOwner.teamId); lastTeamIdLookupLogMs[teamId] = now;
string myTeamId = (Player.inst != null && Player.inst.PlayerLandmassOwner != null)
? Player.inst.PlayerLandmassOwner.teamId.ToString()
: "unknown";
Main.helper.Log("Failed finding player by teamID: " + teamId + " My teamID is: " + myTeamId);
Main.helper.Log(kCPlayers.Count.ToString()); Main.helper.Log(kCPlayers.Count.ToString());
Main.helper.Log(string.Join(", ", kCPlayers.Values.Select(p => p.inst.PlayerLandmassOwner.teamId.ToString()))); Main.helper.Log(string.Join(", ", kCPlayers.Values.Where(p => p != null && p.inst != null && p.inst.PlayerLandmassOwner != null).Select(p => p.inst.PlayerLandmassOwner.teamId.ToString())));
Main.helper.Log(e.Message);
Main.helper.Log(e.StackTrace);
} }
} }
return Player.inst; return Player.inst;
} }

View File

@@ -222,7 +222,13 @@ namespace KCM.Packets.Handlers
try try
{ {
var packetRef = Packets[packet.packetId]; var packetRef = Packets[packet.packetId];
Message message = Message.Create(MessageSendMode.Reliable, packet.packetId);
MessageSendMode sendMode = MessageSendMode.Reliable;
Packet basePacket = packet as Packet;
if (basePacket != null)
sendMode = basePacket.sendMode;
Message message = Message.Create(sendMode, packet.packetId);
foreach (var prop in packetRef.properties) foreach (var prop in packetRef.properties)
{ {

View File

@@ -92,36 +92,7 @@ namespace KCM.Packets.Network
return; return;
byte[] bytes = LoadSaveLoadAtPathHook.saveData; byte[] bytes = LoadSaveLoadAtPathHook.saveData;
int chunkSize = 900; // 900 bytes per chunk to fit within packet size limit KCServer.EnqueueSaveTransfer(clientId, bytes);
List<byte[]> chunks = SplitByteArrayIntoChunks(bytes, chunkSize);
Main.helper.Log("Save Transfer started!");
int sent = 0;
int packetsSent = 0;
for (int i = 0; i < chunks.Count; i++)
{
var chunk = chunks[i];
new SaveTransferPacket()
{
saveSize = bytes.Length,
saveDataChunk = chunk,
chunkId = i,
chunkSize = chunk.Length,
saveDataIndex = sent,
totalChunks = chunks.Count
}.Send(clientId);
Main.helper.Log(" ");
packetsSent++;
sent += chunk.Length;
}
Main.helper.Log($"Sent {packetsSent} save data chunks to client");
} }
else else
{ {

View File

@@ -11,6 +11,7 @@ namespace KCM.Packets
{ {
public abstract ushort packetId { get; } public abstract ushort packetId { get; }
public ushort clientId { get; set; } public ushort clientId { get; set; }
public virtual Riptide.MessageSendMode sendMode => Riptide.MessageSendMode.Reliable;
public KCPlayer player public KCPlayer player
{ {

View File

@@ -11,6 +11,7 @@ namespace KCM.Packets.State
public class BuildingStatePacket : Packet public class BuildingStatePacket : Packet
{ {
public override ushort packetId => (ushort)Enums.Packets.BuildingStatePacket; public override ushort packetId => (ushort)Enums.Packets.BuildingStatePacket;
public override Riptide.MessageSendMode sendMode => Riptide.MessageSendMode.Unreliable;
public string customName { get; set; } public string customName { get; set; }
public Guid guid { get; set; } public Guid guid { get; set; }

View File

@@ -23,6 +23,9 @@ A mellékelt log (`output.txt`) alapján több tipikus hiba okozta a szerver ind
- Mentés betöltéskor a `ProcessBuilding` útvonal kiegészítése `World.inst.PlaceFromLoad(...)` + `UnpackStage2(...)` hívásokkal (különösen fontos a “világba helyezés” mellékhatásai miatt, pl. farm/field regisztráció) - Mentés betöltéskor a `ProcessBuilding` útvonal kiegészítése `World.inst.PlaceFromLoad(...)` + `UnpackStage2(...)` hívásokkal (különösen fontos a “világba helyezés” mellékhatásai miatt, pl. farm/field regisztráció)
- Save transfer kliens oldalon robusztusabb inicializálás/reset (ne ragadjon be a statikus állapot több betöltés után, plusz bounds/null ellenőrzések) - Save transfer kliens oldalon robusztusabb inicializálás/reset (ne ragadjon be a statikus állapot több betöltés után, plusz bounds/null ellenőrzések)
- Kompatibilitási fix: `World.inst.liverySets` lista esetén `.Count` használata `.Length` helyett (különben `Compilation failed` lehet egyes verziókon) - Kompatibilitási fix: `World.inst.liverySets` lista esetén `.Count` használata `.Length` helyett (különben `Compilation failed` lehet egyes verziókon)
- Hálózati stabilitás: `BuildingStatePacket` most `Unreliable` módban megy (state jellegű csomagoknál jobb, ha a legfrissebb állapot érkezik meg és nem torlódik fel a megbízható sor)
- Mentés-szinkron stabilitás: szerver oldalon a save chunkok már nem egy nagy for-ciklusban mennek ki, hanem ütemezve (csökkenti a “The gap between received sequence IDs…” / “Poor connection” diszkonnekteket)
- Kapcsolat tuning: kliens és szerver oldalon emelt `MaxSendAttempts`, és tiltott minőség-alapú auto-disconnect (különösen save transfer közben volt agresszív)
Érintett fájlok (főbb pontok): Érintett fájlok (főbb pontok):
@@ -36,6 +39,8 @@ A mellékelt log (`output.txt`) alapján több tipikus hiba okozta a szerver ind
- `KCServer.cs` - `KCServer.cs`
- `Packets/Handlers/LobbyHandler.cs` - `Packets/Handlers/LobbyHandler.cs`
- `RiptideSteamTransport/LobbyManager.cs` - `RiptideSteamTransport/LobbyManager.cs`
- `Packets/Handlers/PacketHandler.cs`
- `Packets/State/BuildingStatePacket.cs`
## Telepítés / használat ## Telepítés / használat