Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Source/Client/AsyncTime/AsyncTimeComp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ public void ExposeData()
// nevertheless still left.
public int DecreasePlayerCount() => CurrentPlayerCount = Math.Max(0, CurrentPlayerCount - 1);

public void SetCurrentPlayerCount(int count) => CurrentPlayerCount = Math.Max(0, count);

public void FinalizeInit()
{
cmds = new Queue<ScheduledCommand>(
Expand Down
8 changes: 8 additions & 0 deletions Source/Client/AsyncTime/AsyncWorldTimeComp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public TimeSpeed DesiredTimeSpeed
public int CurrentPlayerCount { get; private set; }
public int VTR => CurrentPlayerCount > 0 ? VTRSync.MinimumVtr : VTRSync.MaximumVtr;

public void SetCurrentPlayerCount(int count) => CurrentPlayerCount = Math.Max(0, count);

public int TickableId => -1;

public World world;
Expand Down Expand Up @@ -295,6 +297,12 @@ private static void CreateJoinPointAndSendIfHost()
// Hosted: only host/arbiter uploads world data
SaveLoad.SendGameData(Multiplayer.session.dataSnapshot, true);
}

if (!Multiplayer.IsReplay)
{
Patches.VTRSync.ReportCurrentViewedMap();
Patches.VTRSync.RequestPlayerCountsSync();
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions Source/Client/Networking/HostUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public static async ClientTask HostServer(ServerSettings settings, bool fromRepl
SaveLoad.SendGameData(Multiplayer.session.dataSnapshot, false);

StartLocalServer();

Patches.VTRSync.ReportCurrentViewedMap();
Patches.VTRSync.RequestPlayerCountsSync();
}

private static void CreateSession(ServerSettings settings) =>
Expand Down
5 changes: 5 additions & 0 deletions Source/Client/Networking/State/ClientLoadingState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,5 +145,10 @@ public void HandleWorldData(ByteReader data)
var loadingMs = watch.ElapsedMilliseconds;
Log.Message($"Loaded game in {loadingMs}ms");
connection.ChangeState(ConnectionStateEnum.ClientPlaying);
if (!Multiplayer.IsReplay)
{
OnMainThread.Enqueue(Patches.VTRSync.ReportCurrentViewedMap);
OnMainThread.Enqueue(Patches.VTRSync.RequestPlayerCountsSync);
}
}
}
11 changes: 11 additions & 0 deletions Source/Client/Networking/State/ClientPlayingState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,17 @@ public void HandleSetFaction(ServerSetFactionPacket packet)
Session.myFactionId = factionId;
}
}

[TypedPacketHandler]
public void HandlePlayerCounts(ServerPlayerCountsPacket packet)
{
// The packet is a complete snapshot: maps absent from it have zero players.
var targets = new List<(int mapId, Action<int> setCount)>();
foreach (var map in Find.Maps)
targets.Add((map.uniqueID, map.AsyncTime().SetCurrentPlayerCount));
targets.Add((Patches.VTRSync.WorldMapId, Multiplayer.AsyncWorldTime.SetCurrentPlayerCount));
packet.Apply(targets);
}
}

}
45 changes: 44 additions & 1 deletion Source/Client/Patches/VTRSyncPatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using HarmonyLib;
using Multiplayer.Client.Util;
using Multiplayer.Common;
using Multiplayer.Common.Networking.Packet;
using RimWorld.Planet;
using Verse;

Expand Down Expand Up @@ -48,7 +49,7 @@ static bool Prefix(ref int __result, WorldObject __instance)
}
}

static class VTRSync
public static class VTRSync
{
// Special identifier for the world map (since it doesn't have a uniqueID like regular maps)
public const int WorldMapId = -2;
Expand All @@ -75,6 +76,48 @@ public static void SendViewedMapUpdate(int previous, int current)
MpLog.Debug($"VTR MapSwitchPatch: {lastMovedToMapId}->{current} @ tick {currentTick}{warn}");
Multiplayer.Client.SendCommand(CommandType.PlayerCount, ScheduledCommand.Global, ByteWriter.GetBytes(previous, current));
lastMovedToMapId = current;

ReportViewedMap(current);
}

public static void ReportViewedMap(int mapId)
{
var client = Multiplayer.Client;
if (client == null || Multiplayer.IsReplay)
return;

if (client.State != ConnectionStateEnum.ClientPlaying)
return;

int currentTick = Find.TickManager?.TicksGame ?? 0;
MpLog.Debug($"VTR report: map={mapId} @ tick {currentTick}");
lastMovedToMapId = mapId;
client.Send(new ClientViewedMapReportPacket { mapId = mapId });
}

public static void ReportCurrentViewedMap()
{
if (Multiplayer.Client == null || Multiplayer.IsReplay)
return;

int current = WorldRendererUtility.CurrentWorldRenderMode == WorldRenderMode.Planet
? WorldMapId
: Find.CurrentMap?.uniqueID ?? InvalidMapId;

ReportViewedMap(current);
}

public static void RequestPlayerCountsSync()
{
var client = Multiplayer.Client;
if (client == null || Multiplayer.IsReplay)
return;

if (client.State != ConnectionStateEnum.ClientPlaying)
return;

MpLog.Debug($"VTR resync request @ tick {Find.TickManager?.TicksGame ?? 0}");
client.Send(new ClientRequestPlayerCountsPacket());
}

public static void Reset()
Expand Down
6 changes: 6 additions & 0 deletions Source/Client/Saving/Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ private static void PostLoad()
Multiplayer.AsyncWorldTime.cmds = new Queue<ScheduledCommand>(
Multiplayer.session.dataSnapshot.MapCmds.GetValueSafe(ScheduledCommand.Global) ?? []);
// Map cmds are added in MapAsyncTimeComp.FinalizeInit

if (!Multiplayer.IsReplay && Multiplayer.Client is ClientPlayingState)
{
OnMainThread.Enqueue(Patches.VTRSync.ReportCurrentViewedMap);
OnMainThread.Enqueue(Patches.VTRSync.RequestPlayerCountsSync);
}
}

private static XmlDocument DataSnapshotToXml(GameDataSnapshot dataSnapshot, List<int> mapsToLoad)
Expand Down
53 changes: 53 additions & 0 deletions Source/Common/Networking/Packet/ViewedMapPackets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;

namespace Multiplayer.Common.Networking.Packet;

[PacketDefinition(Packets.Client_ViewedMapReport)]
public record struct ClientViewedMapReportPacket : IPacket
{
public int mapId;

public void Bind(PacketBuffer buf)
{
buf.Bind(ref mapId);
}
}

[PacketDefinition(Packets.Client_RequestPlayerCounts)]
public record struct ClientRequestPlayerCountsPacket : IPacket
{
public void Bind(PacketBuffer buf)
{
}
}

// A complete snapshot: maps not included are considered to have zero players.
[PacketDefinition(Packets.Server_PlayerCounts)]
public record struct ServerPlayerCountsPacket : IPacket
{
public int[] mapIds;
public int[] counts;

public void Bind(PacketBuffer buf)
{
buf.Bind(ref mapIds, BinderOf.Int());
buf.Bind(ref counts, BinderOf.Int());
}

public Dictionary<int, int> ToCountMap()
{
var countById = new Dictionary<int, int>();
int len = Math.Min(mapIds?.Length ?? 0, counts?.Length ?? 0);
for (int i = 0; i < len; i++)
countById[mapIds[i]] = counts[i];
return countById;
}

public void Apply(IEnumerable<(int mapId, Action<int> setCount)> targets)
{
var countById = ToCountMap();
foreach (var (mapId, setCount) in targets)
setCount(countById.TryGetValue(mapId, out int count) ? count : 0);
}
}
4 changes: 4 additions & 0 deletions Source/Common/Networking/Packets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public enum Packets : byte
Client_FrameTime,
Client_StandaloneWorldSnapshotUpload,
Client_StandaloneMapSnapshotUpload,
Client_ViewedMapReport,
Client_RequestPlayerCounts,

// Joining
Server_ProtocolOk,
Expand Down Expand Up @@ -65,6 +67,8 @@ public enum Packets : byte
Server_SetFaction,
Server_RequestRejoin,

Server_PlayerCounts,

// All states (Joining, Loading, Playing)
Server_Disconnect,

Expand Down
32 changes: 32 additions & 0 deletions Source/Common/Networking/State/ServerPlayingState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,38 @@ public void HandleClientCommand(ClientCommandPacket packet)
Server.SendMapResponse(Player, currentMapId);
}

[TypedPacketHandler]
public void HandleViewedMapReport(ClientViewedMapReportPacket packet)
{
Player.currentMapId = packet.mapId;
Player.hasReportedCurrentMap = true;
}

[TypedPacketHandler]
public void HandleRequestPlayerCounts(ClientRequestPlayerCountsPacket packet)
{
var countsByMap = new Dictionary<int, int>();
foreach (var player in Server.PlayingPlayers)
{
if (player.currentMapId == -1)
continue;
countsByMap.TryGetValue(player.currentMapId, out int count);
countsByMap[player.currentMapId] = count + 1;
}

var mapIds = new int[countsByMap.Count];
var counts = new int[countsByMap.Count];
int i = 0;
foreach (var kv in countsByMap)
{
mapIds[i] = kv.Key;
counts[i] = kv.Value;
i++;
}

Player.SendPacket(new ServerPlayerCountsPacket { mapIds = mapIds, counts = counts });
}

public const int MaxChatMsgLength = 128;

[TypedPacketHandler]
Expand Down
2 changes: 1 addition & 1 deletion Source/Common/Version.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Multiplayer.Common
public static class MpVersion
{
public const string SimpleVersion = "0.11.5";
public const int Protocol = 56;
public const int Protocol = 57;

public static readonly string? GitHash = Assembly.GetExecutingAssembly()
.GetCustomAttributes<AssemblyMetadataAttribute>()
Expand Down
3 changes: 3 additions & 0 deletions Source/Tests/Helper/RecordingConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace Tests;
public class RecordingConnection : ConnectionBase
{
public List<Packets> SentPackets { get; } = new();
/// Full frames (id byte + payload) of everything sent, for content assertions.
public List<byte[]> SentPacketData { get; } = new();

public RecordingConnection(string username)
{
Expand All @@ -21,6 +23,7 @@ protected override void SendRaw(byte[] raw, bool reliable)
return;

SentPackets.Add((Packets)(raw[0] & 0x3F));
SentPacketData.Add(raw);
}

protected override void OnClose(ServerDisconnectPacket? goodbye) { }
Expand Down
Loading
Loading