Compare commits

...

4 Commits

Author SHA1 Message Date
c3e79c9adf Merge pull request 'codex-2' (#3) from codex-2 into main
Reviewed-on: #3
2025-12-14 15:10:39 +01:00
dca0140aab Improve multiplayer sync stability 2025-12-14 14:51:46 +01:00
55e3cd57e7 yes 2025-12-14 14:45:37 +01:00
4685bc61c2 asd 2025-12-14 14:41:35 +01:00
5 changed files with 63 additions and 18 deletions

5
.gitignore vendored
View File

@@ -1,7 +1,5 @@
# Logs / local debug output
output*.txt
*.log
/log.txt
# OS junk
.DS_Store
@@ -24,4 +22,5 @@ Desktop.ini
**/*.pdb
/.claude
/*.png
/*.png
/*.txt

View File

@@ -110,7 +110,15 @@ namespace KCM
private void OnApplicationQuit()
{
if (server != null && server.IsRunning)
{
new ShowModal
{
title = "Host disconnected",
message = "The host has left the game."
}.SendToAll();
server.Stop();
}
}
private void Preload(KCModHelper helper)

40
Main.cs
View File

@@ -14,6 +14,7 @@ using KCM.Packets.Game.GameWorld;
using KCM.Packets.Handlers;
using KCM.Packets.Lobby;
using KCM.StateManagement.BuildingState;
using KCM.Packets.State;
using KCM.StateManagement.Observers;
using KCM.UI;
using Newtonsoft.Json;
@@ -53,6 +54,7 @@ namespace KCM
public static Dictionary<string, KCPlayer> kCPlayers = new Dictionary<string, KCPlayer>();
public static Dictionary<ushort, string> clientSteamIds = new Dictionary<ushort, string>();
public static Dictionary<Guid, BuildingStatePacket> pendingBuildingStatePackets = new Dictionary<Guid, BuildingStatePacket>();
public static KCPlayer GetPlayerByClientID(ushort clientId)
{
@@ -81,6 +83,26 @@ namespace KCM
return Player.inst;
}
public static void QueuePendingBuildingState(BuildingStatePacket packet)
{
if (packet == null)
return;
pendingBuildingStatePackets[packet.guid] = packet;
}
public static void ApplyPendingBuildingState(Building building)
{
if (building == null)
return;
if (pendingBuildingStatePackets.TryGetValue(building.guid, out var pending))
{
pending.ApplyToBuilding(building);
pendingBuildingStatePackets.Remove(building.guid);
}
}
public static Player GetPlayerByBuilding(Building building)
{
try
@@ -1226,8 +1248,22 @@ namespace KCM
Stream file = new FileStream(path, FileMode.Open);
try
{
MultiplayerSaveContainer loadData = (MultiplayerSaveContainer)bf.Deserialize(file);
loadData.Unpack(null);
object deserialized = bf.Deserialize(file);
if (deserialized is MultiplayerSaveContainer loadData)
{
loadData.Unpack(null);
}
else if (deserialized is LoadSaveContainer legacyLoadData)
{
Main.helper.Log($"Deserialized fallback save type ({legacyLoadData.GetType().FullName}), applying base container.");
legacyLoadData.Unpack(null);
}
else
{
throw new InvalidCastException($"Unexpected save data type: {deserialized?.GetType().FullName}");
}
Broadcast.OnLoadedEvent.Broadcast(new OnLoadedEvent());
}
catch (Exception e)

View File

@@ -102,6 +102,7 @@ namespace KCM.Packets.Game.GameWorld
Main.helper.Log(building.LandMass().ToString());
Main.helper.Log("Player add Building unpacked");
player.inst.AddBuilding(building);
Main.ApplyPendingBuildingState(building);
try
{

View File

@@ -36,25 +36,33 @@ namespace KCM.Packets.State
{
if (clientId == KCClient.client.Id) return; //prevent double placing on same client
//Main.helper.Log("Received building state packet for: " + uniqueName + " from " + Main.kCPlayers[Main.GetPlayerByClientID(clientId).steamId].name + $"({clientId})");
Building building = player.inst.GetBuilding(guid);
if (building == null)
{
Main.helper.Log("Building not found.");
Main.QueuePendingBuildingState(this);
return;
}
ApplyToBuilding(building);
}
public override void HandlePacketServer()
{
//throw new NotImplementedException();
}
public void ApplyToBuilding(Building building)
{
if (building == null)
return;
try
{
//PrintProperties();
building.UniqueName = uniqueName;
building.customName = customName;
building.transform.position = this.globalPosition;
building.transform.GetChild(0).rotation = this.rotation;
building.transform.GetChild(0).localPosition = this.localPosition;
@@ -63,7 +71,6 @@ namespace KCM.Packets.State
SetPrivateFieldValue(building, "placed", placed);
SetPrivateFieldValue(building, "resourceProgress", resourceProgress);
building.Open = open;
building.doBuildAnimation = doBuildAnimation;
building.constructionPaused = constructionPaused;
@@ -71,7 +78,6 @@ namespace KCM.Packets.State
building.Life = life;
building.ModifiedMaxLife = ModifiedMaxLife;
//building.yearBuilt = yearBuilt;
SetPrivateFieldValue(building, "yearBuilt", yearBuilt);
@@ -86,11 +92,6 @@ namespace KCM.Packets.State
}
}
public override void HandlePacketServer()
{
//throw new NotImplementedException();
}
private void SetPrivateFieldValue(object obj, string fieldName, object value)
{
Type type = obj.GetType();