Fix: Add periodic villager position sync from server

- Server syncs villager positions every ~3 seconds to clients
- Only syncs villagers that moved more than 0.5 units (bandwidth optimization)
- Maintains position cache to detect movement
- Clears cache on lobby leave to prevent stale data

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-14 10:58:37 +01:00
parent 8d599e13ad
commit dd17030e56
2 changed files with 60 additions and 27 deletions

86
Main.cs
View File

@@ -182,43 +182,75 @@ namespace KCM
#endregion
public static int FixedUpdateInterval = 0;
private static Dictionary<Guid, Vector3> lastVillagerPositions = new Dictionary<Guid, Vector3>();
public static void ClearVillagerPositionCache()
{
lastVillagerPositions.Clear();
}
private void FixedUpdate()
{
// send batched building placement info
/*if (PlaceHook.QueuedBuildings.Count > 0 && (FixedUpdateInterval % 25 == 0))
// Periodic villager position sync from server to clients
// Sync every 150 frames (~3 seconds at 50 FPS) to reduce bandwidth
if (KCServer.IsRunning && KCClient.client.IsConnected && FixedUpdateInterval % 150 == 0)
{
foreach (Building building in PlaceHook.QueuedBuildings)
try
{
new WorldPlace()
{
uniqueName = building.UniqueName,
customName = building.customName,
guid = building.guid,
rotation = building.transform.GetChild(0).rotation,
globalPosition = building.transform.position,
localPosition = building.transform.GetChild(0).localPosition,
built = building.IsBuilt(),
placed = building.IsPlaced(),
open = building.Open,
doBuildAnimation = building.doBuildAnimation,
constructionPaused = building.constructionPaused,
constructionProgress = building.constructionProgress,
life = building.Life,
ModifiedMaxLife = building.ModifiedMaxLife,
//CollectForBuild = CollectForBuild,
yearBuilt = building.YearBuilt,
decayProtection = building.decayProtection,
seenByPlayer = building.seenByPlayer
}.Send();
SyncVillagerPositions();
}
PlaceHook.QueuedBuildings.Clear();
}*/
catch (Exception e)
{
helper.Log($"Error in villager sync: {e.Message}");
}
}
FixedUpdateInterval++;
}
private void SyncVillagerPositions()
{
// Only sync villagers that have moved significantly
const float movementThreshold = 0.5f;
foreach (var kcPlayer in kCPlayers.Values)
{
if (kcPlayer.inst == null) continue;
for (int i = 0; i < kcPlayer.inst.Workers.Count; i++)
{
var villager = kcPlayer.inst.Workers.data[i];
if (villager == null) continue;
Vector3 currentPos = villager.Pos;
Vector3 lastPos;
bool shouldSync = false;
if (!lastVillagerPositions.TryGetValue(villager.guid, out lastPos))
{
// First time seeing this villager
lastVillagerPositions[villager.guid] = currentPos;
shouldSync = true;
}
else if (Vector3.Distance(currentPos, lastPos) > movementThreshold)
{
// Villager has moved significantly
lastVillagerPositions[villager.guid] = currentPos;
shouldSync = true;
}
if (shouldSync)
{
new Packets.Game.GameVillager.VillagerTeleportTo()
{
guid = villager.guid,
pos = currentPos
}.SendToAll(KCClient.client.Id);
}
}
}
}
#region "TransitionTo"
public static void TransitionTo(MenuState state)
{

View File

@@ -160,6 +160,7 @@ namespace Riptide.Demos.Steam.PlayerHosted
Main.helper.Log("clear players");
Main.kCPlayers.Clear();
Main.clientSteamIds.Clear(); // Clear client-to-steam ID mapping
Main.ClearVillagerPositionCache(); // Clear villager sync cache
LobbyHandler.ClearPlayerList();
LobbyHandler.ClearChatEntries();
Main.helper.Log("end clear players");