network fix
This commit is contained in:
14
KCClient.cs
14
KCClient.cs
@@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
68
KCServer.cs
68
KCServer.cs
@@ -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
34
Main.cs
@@ -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);
|
||||||
|
|
||||||
|
if (match != null && match.inst != null)
|
||||||
|
return match.inst;
|
||||||
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
if (KCServer.IsRunning || KCClient.client.IsConnected)
|
if (KCServer.IsRunning || KCClient.client.IsConnected)
|
||||||
{
|
{
|
||||||
Main.helper.Log("Failed finding player by teamID: " + teamId + " My teamID is: " + Player.inst.PlayerLandmassOwner.teamId);
|
long now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||||
|
long last;
|
||||||
|
if (!lastTeamIdLookupLogMs.TryGetValue(teamId, out last) || (now - last) > 2000)
|
||||||
|
{
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user