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)
{
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 bool started = false;
private static readonly Dictionary<ushort, Queue<SaveTransferPacket>> saveTransferQueues = new Dictionary<ushort, Queue<SaveTransferPacket>>();
private const int SaveTransferPacketsPerUpdatePerClient = 10;
static KCServer()
{
//server.registerMessageHandler(typeof(KCServer).GetMethod("ClientJoined"));
@@ -50,6 +53,7 @@ namespace KCM
}
ev.Client.CanQualityDisconnect = false;
ev.Client.MaxSendAttempts = 50;
Main.helper.Log("Client ID is: " + ev.Client.Id);
@@ -85,6 +89,8 @@ namespace KCM
if (entry != null)
Destroy(entry.gameObject);
saveTransferQueues.Remove(ev.Client.Id);
Main.helper.Log($"Client disconnected. {ev.Reason}");
}
catch (Exception ex)
@@ -125,6 +131,68 @@ namespace KCM
private void 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()

34
Main.cs
View File

@@ -55,6 +55,8 @@ namespace KCM
public static Dictionary<string, KCPlayer> kCPlayers = new Dictionary<string, KCPlayer>();
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)
{
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
{
try
{
var player = kCPlayers.Values.FirstOrDefault(p => p.inst.PlayerLandmassOwner.teamId == teamId).inst;
KCPlayer match = kCPlayers.Values.FirstOrDefault(p =>
p != null &&
p.inst != null &&
p.inst.PlayerLandmassOwner != null &&
p.inst.PlayerLandmassOwner.teamId == teamId);
return player;
}
catch (Exception e)
if (match != null && match.inst != null)
return match.inst;
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(string.Join(", ", kCPlayers.Values.Select(p => p.inst.PlayerLandmassOwner.teamId.ToString())));
Main.helper.Log(e.Message);
Main.helper.Log(e.StackTrace);
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())));
}
}
return Player.inst;
}

View File

@@ -222,7 +222,13 @@ namespace KCM.Packets.Handlers
try
{
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)
{

View File

@@ -92,36 +92,7 @@ namespace KCM.Packets.Network
return;
byte[] bytes = LoadSaveLoadAtPathHook.saveData;
int chunkSize = 900; // 900 bytes per chunk to fit within packet size limit
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");
KCServer.EnqueueSaveTransfer(clientId, bytes);
}
else
{

View File

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

View File

@@ -11,6 +11,7 @@ namespace KCM.Packets.State
public class BuildingStatePacket : Packet
{
public override ushort packetId => (ushort)Enums.Packets.BuildingStatePacket;
public override Riptide.MessageSendMode sendMode => Riptide.MessageSendMode.Unreliable;
public string customName { 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ó)
- 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)
- 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):
@@ -36,6 +39,8 @@ A mellékelt log (`output.txt`) alapján több tipikus hiba okozta a szerver ind
- `KCServer.cs`
- `Packets/Handlers/LobbyHandler.cs`
- `RiptideSteamTransport/LobbyManager.cs`
- `Packets/Handlers/PacketHandler.cs`
- `Packets/State/BuildingStatePacket.cs`
## Telepítés / használat