diff --git a/CS2MultiplayerMod.Steam/SteamRelayConnections.cs b/CS2MultiplayerMod.Steam/SteamRelayConnections.cs
index cfafeeb..91cf505 100644
--- a/CS2MultiplayerMod.Steam/SteamRelayConnections.cs
+++ b/CS2MultiplayerMod.Steam/SteamRelayConnections.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
+using CS2MultiplayerMod.Core.Diagnostics;
using Steamworks;
namespace CS2MultiplayerMod.Core.Networking.Steam
@@ -63,8 +64,8 @@ private void OnConnectionStatusChanged(SteamNetConnectionStatusChangedCallback_t
if (endpoint != null && !endpoint.Announced)
{
endpoint.Announced = true;
- _log.Info("Steam relay connection " + endpoint.Id + " established with " +
- endpoint.RemoteAddress + ".");
+ _log.Detail(LogTopic.Transport, "Steam relay connection " + endpoint.Id +
+ " established with " + endpoint.RemoteAddress + ".");
Enqueue(TransportEvent.Connected(endpoint.Id));
}
break;
@@ -89,7 +90,8 @@ private void AcceptIncoming(SteamNetConnectionStatusChangedCallback_t evt)
lock (_gate) { open = _byId.Count; }
if (open >= MaxConnections)
{
- _log.Warn("Refused Steam relay connection from " + steamId + ": too many open connections.");
+ _log.Warn(LogTopic.Transport, "Refused Steam relay connection from " + steamId +
+ ": too many open connections.");
SteamNetworkingSockets.CloseConnection(evt.m_hConn, 0, "too many connections", false);
return;
}
@@ -97,7 +99,8 @@ private void AcceptIncoming(SteamNetConnectionStatusChangedCallback_t evt)
EResult accepted = SteamNetworkingSockets.AcceptConnection(evt.m_hConn);
if (accepted != EResult.k_EResultOK)
{
- _log.Warn("Could not accept Steam relay connection from " + steamId + ": " + accepted + ".");
+ _log.Warn(LogTopic.Transport, "Could not accept Steam relay connection from " +
+ steamId + ": " + accepted + ".");
SteamNetworkingSockets.CloseConnection(evt.m_hConn, 0, "accept failed", false);
return;
}
@@ -108,13 +111,14 @@ private void AcceptIncoming(SteamNetConnectionStatusChangedCallback_t evt)
{
// Outside the poll group this connection is deaf; better to refuse it than
// to leave a peer that handshakes and then goes quiet forever.
- _log.Warn("Could not add Steam relay connection " + id + " from " + steamId +
- " to the poll group; refusing it.");
+ _log.Warn(LogTopic.Transport, "Could not add Steam relay connection " + id +
+ " from " + steamId + " to the poll group; refusing it.");
Close(endpoint, "poll group rejected the connection", linger: false);
return;
}
- _log.Info("Accepted Steam relay connection " + id + " from " + steamId + ".");
+ _log.Detail(LogTopic.Transport, "Accepted Steam relay connection " + id + " from " +
+ steamId + ".");
// Connected is announced on the Connected state, so the session never talks to
// a connection that is still negotiating.
}
diff --git a/CS2MultiplayerMod.Steam/SteamRelayGovernor.cs b/CS2MultiplayerMod.Steam/SteamRelayGovernor.cs
index 08450a5..7aac88a 100644
--- a/CS2MultiplayerMod.Steam/SteamRelayGovernor.cs
+++ b/CS2MultiplayerMod.Steam/SteamRelayGovernor.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
+using CS2MultiplayerMod.Core.Diagnostics;
using Steamworks;
namespace CS2MultiplayerMod.Core.Networking.Steam
@@ -48,13 +49,15 @@ private bool SetInt32(ESteamNetworkingConfigValue setting, ESteamNetworkingConfi
ESteamNetworkingConfigDataType.k_ESteamNetworkingConfig_Int32,
pin.AddrOfPinnedObject());
if (!ok)
- _log.Warn("Steam refused the relay " + description + " setting; transfers may be slow.");
+ _log.Warn(LogTopic.Transport, "Steam refused the relay " + description +
+ " setting; transfers may be slow.");
return ok;
}
catch (Exception ex)
{
// Non-fatal: the transfer still completes, just slower.
- _log.Warn("Could not set the relay " + description + " (" + ex.Message + ").");
+ _log.Warn(LogTopic.Transport, "Could not set the relay " + description + " (" +
+ ex.Message + ").");
return false;
}
finally
@@ -157,8 +160,8 @@ private void Govern()
{
string finished = endpoint.FinishBulk();
if (finished != null)
- _log.Info("[relay] " + endpoint.Id + " " + finished + " over a " +
- RouteOf(endpoint) + " route.");
+ _log.Detail(LogTopic.Transport, "Relay " + endpoint.Id + " " + finished +
+ " over a " + RouteOf(endpoint) + " route.");
int idle = Math.Min(SendRateStartBytesPerSecond, endpoint.SafeRate);
if (endpoint.SendRate != idle) ApplySendRate(endpoint, idle);
@@ -219,14 +222,13 @@ private void Govern()
}
if (!report) continue;
- _log.Info("[relay] " + endpoint.Id + " sending: " + (outstanding / 1024) +
- " KB left at " + (goodput / 1024) + " KB/s (paced " +
- (endpoint.SendRate / 1024) + " KB/s, held " +
- (endpoint.SafeRate / 1024) + " KB/s, wire " +
- ((int)status.m_flOutBytesPerSec / 1024) + " KB/s), ping " +
- status.m_nPing + " of " + pingBudget + " ms, peer received " +
- (quality < 0f ? "?" : ((int)(quality * 100)).ToString()) + "%, " +
- RouteOf(endpoint) + " route.");
+ _log.Detail(LogTopic.Transport, "Relay " + endpoint.Id + " sending: " +
+ (outstanding / 1024) + " KB left at " + (goodput / 1024) + " KB/s (paced " +
+ (endpoint.SendRate / 1024) + " KB/s, held " + (endpoint.SafeRate / 1024) +
+ " KB/s, wire " + ((int)status.m_flOutBytesPerSec / 1024) + " KB/s), ping " +
+ status.m_nPing + " of " + pingBudget + " ms, peer received " +
+ (quality < 0f ? "?" : ((int)(quality * 100)).ToString()) + "%, " +
+ RouteOf(endpoint) + " route.");
}
}
}
diff --git a/CS2MultiplayerMod.Steam/SteamRelayIo.cs b/CS2MultiplayerMod.Steam/SteamRelayIo.cs
index fc42331..ccbe818 100644
--- a/CS2MultiplayerMod.Steam/SteamRelayIo.cs
+++ b/CS2MultiplayerMod.Steam/SteamRelayIo.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol;
using Steamworks;
@@ -111,14 +112,15 @@ private SendOutcome SendFrame(Endpoint endpoint, byte[] frame)
// rejected, and a half-delivered payload can never be completed.
if (result == EResult.k_EResultLimitExceeded) return SendOutcome.Backpressure;
- _log.Warn("Steam relay send to " + endpoint.Id + " failed (" + result + "); dropping the connection. " +
- DescribeConnection(endpoint));
+ _log.Warn(LogTopic.Transport, "Steam relay send to " + endpoint.Id + " failed (" +
+ result + "); dropping the connection. " + DescribeConnection(endpoint));
Close(endpoint, "relay send failed: " + result, linger: false);
return SendOutcome.Failed;
}
catch (Exception ex)
{
- _log.Warn("Steam relay send to " + endpoint.Id + " threw (" + ex.Message + "); dropping the connection.");
+ _log.Warn(LogTopic.Transport, "Steam relay send to " + endpoint.Id + " threw (" +
+ ex.Message + "); dropping the connection.");
Close(endpoint, "relay send error", linger: false);
return SendOutcome.Failed;
}
@@ -164,7 +166,7 @@ private void Receive()
}
catch (Exception ex)
{
- _log.Warn("Steam relay receive failed: " + ex.Message);
+ _log.Warn(LogTopic.Transport, "Steam relay receive failed: " + ex.Message);
return;
}
diff --git a/CS2MultiplayerMod.Steam/SteamRelayLifecycle.cs b/CS2MultiplayerMod.Steam/SteamRelayLifecycle.cs
index 2ff026e..308b015 100644
--- a/CS2MultiplayerMod.Steam/SteamRelayLifecycle.cs
+++ b/CS2MultiplayerMod.Steam/SteamRelayLifecycle.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
+using CS2MultiplayerMod.Core.Diagnostics;
using Steamworks;
namespace CS2MultiplayerMod.Core.Networking.Steam
@@ -92,7 +93,7 @@ public void Shutdown()
_statusCallback = null;
}
- _log.Info("Steam relay transport stopped.");
+ _log.Detail(LogTopic.Transport, "Steam relay transport stopped.");
}
public void ShutdownAfterFlush(int timeoutMs)
@@ -124,8 +125,8 @@ public void ShutdownAfterFlush(int timeoutMs)
long left = PendingSendBytes;
if (left > 0)
- _log.Warn("Steam relay stopping with " + left + " byte(s) still queued after " +
- timeoutMs + " ms; closing anyway.");
+ _log.Warn(LogTopic.Transport, "Steam relay stopping with " + left +
+ " byte(s) still queued after " + timeoutMs + " ms; closing anyway.");
// Linger lets Steam make a final attempt after the handles leave our maps.
lock (_gate)
diff --git a/CS2MultiplayerMod.Steam/SteamRelayTransport.cs b/CS2MultiplayerMod.Steam/SteamRelayTransport.cs
index ef8d976..88b85a0 100644
--- a/CS2MultiplayerMod.Steam/SteamRelayTransport.cs
+++ b/CS2MultiplayerMod.Steam/SteamRelayTransport.cs
@@ -206,7 +206,8 @@ public static SteamRelayTransport StartHost(IModLogger log, int virtualPort)
"Steam refused to create a relay poll group. Restart Steam and try again.");
}
- transport._log.Info("Hosting over the Steam relay on virtual port " + virtualPort +
+ transport._log.Detail(LogTopic.Transport,
+ "Hosting over the Steam relay on virtual port " + virtualPort +
"; join code " + SteamRelayProvider.LocalSteamId() + ".");
return transport;
}
@@ -238,7 +239,7 @@ public static SteamRelayTransport Connect(IModLogger log, string joinCode, int v
// The client's single connection is the session's well-known Server id, bound
// before any callback can fire so the first status change already resolves.
transport.Bind(ConnectionId.Server, connection, steamId);
- transport._log.Info("Connecting to " + steamId + " over the Steam relay.");
+ transport._log.Detail(LogTopic.Transport, "Connecting to " + steamId + " over the Steam relay.");
return transport;
}
@@ -247,7 +248,7 @@ private void Begin()
// Warming the relay network here means the first connection does not also pay
// for fetching the relay topology.
try { SteamNetworkingUtils.InitRelayNetworkAccess(); }
- catch (Exception ex) { _log.Warn("Could not pre-warm the Steam relay network: " + ex.Message); }
+ catch (Exception ex) { _log.Warn(LogTopic.Transport, "Could not pre-warm the Steam relay network: " + ex.Message); }
ConfigureForBulkTransfer();
diff --git a/CS2MultiplayerMod/Core/Diagnostics/IModLogger.cs b/CS2MultiplayerMod/Core/Diagnostics/IModLogger.cs
index 22d28e6..1cff9a8 100644
--- a/CS2MultiplayerMod/Core/Diagnostics/IModLogger.cs
+++ b/CS2MultiplayerMod/Core/Diagnostics/IModLogger.cs
@@ -3,16 +3,46 @@ namespace CS2MultiplayerMod.Core.Diagnostics
///
/// Logging abstraction for the multiplayer core.
///
- /// The core deliberately does not reference Colossal.Logging (or any game
- /// assembly) so it stays portable and unit-testable. The game layer supplies
- /// a concrete adapter; tests can pass .
+ /// The core deliberately does not reference Colossal.Logging (or any game assembly) so it
+ /// stays portable and unit-testable. The game layer supplies a concrete adapter; tests can
+ /// pass .
+ ///
+ /// The shape mirrors the game layer's logger exactly, so there is one vocabulary across the
+ /// whole mod: every line names a , and the severity decides whether the
+ /// topic's switch is consulted at all. is troubleshooting chatter and is
+ /// gated; is kept in the crash log either way; and everything from
+ /// upwards is written to both logs whatever the switches say, because a
+ /// player cannot be expected to have turned on the right switch before the thing they are
+ /// reporting happened.
///
public interface IModLogger
{
- void Debug(string message);
- void Info(string message);
- void Warn(string message);
- void Error(string message);
- void Error(string message, System.Exception exception);
+ ///
+ /// Whether a line on this topic would be written. Ask before
+ /// computing a diagnostic, not only before logging one: a counter nobody reads
+ /// must not cost a frame.
+ ///
+ bool IsEnabled(LogTopic topic);
+
+ /// Troubleshooting detail. Written only while the topic is switched on.
+ void Detail(LogTopic topic, string message);
+
+ ///
+ /// A short breadcrumb: always kept in the crash log, shown in the readable log only while
+ /// the topic is switched on.
+ ///
+ void Trace(LogTopic topic, string message);
+
+ /// A milestone worth having in every player's log. Never gated.
+ void Event(LogTopic topic, string message);
+
+ /// Something went wrong but the mod worked around it. Never gated.
+ void Warn(LogTopic topic, string message);
+
+ /// Something went wrong that the mod could not work around. Never gated.
+ void Error(LogTopic topic, string message);
+
+ /// As , with the exception that caused it.
+ void Error(LogTopic topic, string message, System.Exception exception);
}
}
diff --git a/CS2MultiplayerMod/Core/Diagnostics/LogTopic.cs b/CS2MultiplayerMod/Core/Diagnostics/LogTopic.cs
new file mode 100644
index 0000000..9d39401
--- /dev/null
+++ b/CS2MultiplayerMod/Core/Diagnostics/LogTopic.cs
@@ -0,0 +1,78 @@
+namespace CS2MultiplayerMod.Core.Diagnostics
+{
+ ///
+ /// What a log line is about.
+ ///
+ /// Every line the mod writes names one of these, and each one can be switched on by itself.
+ /// That is the whole point: a player chasing "roads do not appear on my partner's screen"
+ /// turns on and gets a log about roads, instead of a general "debug"
+ /// switch that buries the one interesting line under twenty thousand others. A log nobody
+ /// can read is a log nobody reads.
+ ///
+ /// The topics are named after the thing that went wrong from the player's side, not after
+ /// the class that noticed it - "my transit lines are missing" is , and
+ /// the reporter may be a channel, a system or the pipeline.
+ ///
+ /// This lives in the portable core so the networking and session code can name the same
+ /// topics as the game layer without referencing a game assembly.
+ ///
+ public enum LogTopic
+ {
+ ///
+ /// The mod itself: load, settings, system registration, compatibility and DLC checks.
+ /// Deliberately first, so an unattributed line lands somewhere honest.
+ ///
+ Startup = 0,
+
+ /// Connecting, disconnecting, the handshake, peers joining and leaving, kicks and bans.
+ Session,
+
+ /// The wire underneath a session: sockets, the Steam relay, port forwarding, framing, rates.
+ Transport,
+
+ /// Sending, receiving, staging and loading the world a joining player downloads.
+ WorldTransfer,
+
+ /// Divergence: what was detected, what the arbiter decided, and what the repair did.
+ Resync,
+
+ /// The command pipeline: inbox, observers, authority holds, definition gates, realization.
+ Pipeline,
+
+ /// Roads, tracks, pipes and wires - placement, upgrades, replacement, topology.
+ Nets,
+
+ /// Placed objects: buildings, props and trees - placement, move, upgrade, delete.
+ Buildings,
+
+ /// The map itself: zoning, areas and districts, terrain, tile purchases.
+ Land,
+
+ /// City-wide state: names, policies, money, milestones, the development tree, statistics.
+ City,
+
+ /// Transit: lines, stops, vehicles and fares.
+ Routes,
+
+ /// Households, residents and their homes.
+ Residential,
+
+ /// Shops: tenancy, figures and stock.
+ Commercial,
+
+ /// Factories and extractors: tenancy, figures and stock.
+ Industrial,
+
+ /// Offices: tenancy, figures and stock.
+ Office,
+
+ /// The other players: their cursors, markers, map pings and chat.
+ Players,
+
+ /// The mod's own screens: the main-menu button, the join dialog, the options page.
+ Ui,
+
+ /// Frame times and the mod's own main-thread cost, including the per-zone split.
+ Performance,
+ }
+}
diff --git a/CS2MultiplayerMod/Core/Diagnostics/NullModLogger.cs b/CS2MultiplayerMod/Core/Diagnostics/NullModLogger.cs
index 5152058..f51ce40 100644
--- a/CS2MultiplayerMod/Core/Diagnostics/NullModLogger.cs
+++ b/CS2MultiplayerMod/Core/Diagnostics/NullModLogger.cs
@@ -9,10 +9,12 @@ public sealed class NullModLogger : IModLogger
private NullModLogger() { }
- public void Debug(string message) { }
- public void Info(string message) { }
- public void Warn(string message) { }
- public void Error(string message) { }
- public void Error(string message, Exception exception) { }
+ public bool IsEnabled(LogTopic topic) { return false; }
+ public void Detail(LogTopic topic, string message) { }
+ public void Trace(LogTopic topic, string message) { }
+ public void Event(LogTopic topic, string message) { }
+ public void Warn(LogTopic topic, string message) { }
+ public void Error(LogTopic topic, string message) { }
+ public void Error(LogTopic topic, string message, Exception exception) { }
}
}
diff --git a/CS2MultiplayerMod/Core/Networking/PortForward.cs b/CS2MultiplayerMod/Core/Networking/PortForward.cs
index 3af9850..cad7fd7 100644
--- a/CS2MultiplayerMod/Core/Networking/PortForward.cs
+++ b/CS2MultiplayerMod/Core/Networking/PortForward.cs
@@ -116,15 +116,18 @@ private void Run()
_localAddress = RoutableLocalAddress();
if (_localAddress == null)
{
- _log.Warn("[upnp] No local network address to forward to; skipping automatic port forwarding.");
+ _log.Warn(LogTopic.Transport,
+ "UPnP: No local network address to forward to; skipping automatic port forwarding.");
Settle(PortForwardState.NoRouter);
return;
}
if (!Discover())
{
- _log.Info("[upnp] No UPnP router answered. If players outside your network cannot " +
- "connect, forward TCP port " + _port + " to " + _localAddress + " by hand.");
+ _log.Event(LogTopic.Transport,
+ "UPnP: No router answered. If players outside your network cannot " +
+ "connect, forward TCP port " + _port + " to " + _localAddress +
+ " by hand.");
Settle(PortForwardState.NoRouter);
return;
}
@@ -150,25 +153,26 @@ private void Run()
if (failure != null)
{
- _log.Warn("[upnp] The router refused to open TCP port " + _port + " (" + failure +
- "). Forward it to " + _localAddress + " by hand, or host over the Steam relay.");
+ _log.Warn(LogTopic.Transport, "UPnP: The router refused to open TCP port " +
+ _port + " (" + failure + "). Forward it to " + _localAddress +
+ " by hand, or host over the Steam relay.");
Settle(PortForwardState.Refused);
return;
}
if (!Verify())
{
- _log.Warn("[upnp] The router accepted the request for TCP port " + _port +
- " but does not report the mapping back. Treating it as not forwarded.");
+ _log.Warn(LogTopic.Transport,
+ "UPnP: The router accepted the request for TCP port " + _port +
+ " but does not report the mapping back. Treating it as not forwarded.");
Settle(PortForwardState.Refused);
return;
}
Settle(PortForwardState.Open);
- _log.Info("[upnp] TCP port " + _port + " forwarded to " + _localAddress + " automatically." +
- (_externalAddress != null
- ? " Players outside your network connect to " + _externalAddress + ":" + _port + "."
- : ""));
+ _log.Event(LogTopic.Transport, "UPnP: TCP port " + _port + " forwarded to " +
+ _localAddress + " automatically." +
+ (_externalAddress != null ? " Players outside your network connect to " + _externalAddress + ":" + _port + "." : ""));
// Disposed while we were still negotiating: the mapping exists now and has
// to come back down, because nothing else knows about it.
@@ -176,8 +180,9 @@ private void Run()
}
catch (Exception ex)
{
- _log.Warn("[upnp] Automatic port forwarding failed (" + ex.Message +
- "). Forward TCP port " + _port + " by hand if players cannot reach you.");
+ _log.Warn(LogTopic.Transport, "UPnP: Automatic port forwarding failed (" +
+ ex.Message + "). Forward TCP port " + _port +
+ " by hand if players cannot reach you.");
Settle(PortForwardState.Refused);
}
}
@@ -269,7 +274,8 @@ private bool ReadServices(string location, string viaLocal)
_controlUrl = controlUri.ToString();
_serviceType = type;
_localAddress = viaLocal;
- _log.Info("[upnp] Router found at " + controlUri.Host + " (" + type + ").");
+ _log.Detail(LogTopic.Transport, "UPnP: Router found at " + controlUri.Host + " (" +
+ type + ").");
return true;
}
@@ -342,10 +348,10 @@ private void DeleteMapping(bool announce)
out error);
if (!announce) return;
- _log.Info(error == null
- ? "[upnp] Released the automatic forward of TCP port " + _port + "."
- : "[upnp] Could not release TCP port " + _port + " (" + error +
- "); it will expire when the router restarts.");
+ _log.Detail(LogTopic.Transport,
+ error == null ? "UPnP: Released the automatic forward of TCP port " + _port +
+ "." : "UPnP: Could not release TCP port " + _port + " (" + error +
+ "); it will expire when the router restarts.");
}
// ---- transport ------------------------------------------------------------
diff --git a/CS2MultiplayerMod/Core/Networking/Tcp/TcpClientTransport.cs b/CS2MultiplayerMod/Core/Networking/Tcp/TcpClientTransport.cs
index 376a121..0bad1e8 100644
--- a/CS2MultiplayerMod/Core/Networking/Tcp/TcpClientTransport.cs
+++ b/CS2MultiplayerMod/Core/Networking/Tcp/TcpClientTransport.cs
@@ -59,7 +59,8 @@ public void Connect(string host, int port, bool useTls = true)
private void ConnectLoop(string host, int port, bool useTls)
{
var elapsed = Stopwatch.StartNew();
- _log.Info("Connecting to " + host + ":" + port + (useTls ? " (TLS)..." : " (plaintext)..."));
+ _log.Detail(LogTopic.Transport, "Connecting to " + host + ":" + port +
+ (useTls ? " (TLS)..." : " (plaintext)..."));
IPAddress literal;
if (!IPAddress.TryParse(host, out literal))
@@ -70,12 +71,13 @@ private void ConnectLoop(string host, int port, bool useTls)
try
{
IPAddress[] resolved = Dns.GetHostAddresses(host);
- _log.Info("Resolved '" + host + "' to " +
- string.Join(", ", Array.ConvertAll(resolved, a => a.ToString())) + ".");
+ _log.Detail(LogTopic.Transport, "Resolved '" + host + "' to " +
+ string.Join(", ", Array.ConvertAll(resolved, a => a.ToString())) + ".");
}
catch (Exception ex)
{
- _log.Warn("DNS lookup for '" + host + "' failed: " + ex.Message);
+ _log.Warn(LogTopic.Transport, "DNS lookup for '" + host + "' failed: " +
+ ex.Message);
}
}
@@ -93,15 +95,17 @@ private void ConnectLoop(string host, int port, bool useTls)
try { client.Close(); } catch { /* ignore */ }
if (canceled)
{
- _log.Info("Join canceled while connecting to " + host + ":" + port + ".");
+ _log.Detail(LogTopic.Transport, "Join canceled while connecting to " + host +
+ ":" + port + ".");
return;
}
var socketEx = ex as SocketException;
string errorCode = socketEx != null ? " [" + socketEx.SocketErrorCode + "]" : "";
Enqueue(TransportEvent.Disconnected(ConnectionId.Server,
"connect failed" + errorCode + ": " + ex.Message));
- _log.Warn("Connect to " + host + ":" + port + " failed after " + elapsed.ElapsedMilliseconds +
- " ms: " + ex.Message + DescribeConnectFailure(ex));
+ _log.Warn(LogTopic.Transport, "Connect to " + host + ":" + port + " failed after " +
+ elapsed.ElapsedMilliseconds + " ms: " + ex.Message +
+ DescribeConnectFailure(ex));
return;
}
_dialing = null;
@@ -112,14 +116,16 @@ private void ConnectLoop(string host, int port, bool useTls)
if (!_active)
{
try { client.Close(); } catch { /* ignore */ }
- _log.Info("Join canceled while connecting to " + host + ":" + port + ".");
+ _log.Detail(LogTopic.Transport, "Join canceled while connecting to " + host + ":" +
+ port + ".");
return;
}
string local = "?";
try { local = client.Client.LocalEndPoint.ToString(); } catch { /* cosmetic only */ }
- _log.Info("TCP connected to " + host + ":" + port + " in " + elapsed.ElapsedMilliseconds +
- " ms (local endpoint " + local + ")" + (useTls ? "; starting TLS handshake." : "."));
+ _log.Detail(LogTopic.Transport, "TCP connected to " + host + ":" + port + " in " +
+ elapsed.ElapsedMilliseconds + " ms (local endpoint " + local + ")" +
+ (useTls ? "; starting TLS handshake." : "."));
var connection = new FramedConnection(ConnectionId.Server, client, null, useTls)
{
@@ -128,7 +134,8 @@ private void ConnectLoop(string host, int port, bool useTls)
OnReady = cid =>
{
Enqueue(TransportEvent.Connected(cid));
- _log.Info("Connected to host " + host + ":" + port + (useTls ? " (TLS)." : " (PLAINTEXT)."));
+ _log.Event(LogTopic.Transport, "Connected to host " + host + ":" + port +
+ (useTls ? " (TLS)." : " (PLAINTEXT)."));
},
OnData = (cid, payload) => Enqueue(TransportEvent.Data(cid, payload)),
OnClosed = (cid, reason) =>
@@ -164,7 +171,8 @@ private void Enqueue(TransportEvent evt)
if (Interlocked.Increment(ref _queuedEvents) > MaxQueuedEvents)
{
Interlocked.Decrement(ref _queuedEvents);
- _log.Warn("Transport event queue full; disconnecting from host.");
+ _log.Warn(LogTopic.Transport,
+ "Transport event queue full; disconnecting from host.");
var c = _connection;
if (c != null) c.Close("event queue overflow");
return;
@@ -259,7 +267,7 @@ public void Shutdown()
if (connection != null) connection.Close("client shutting down");
_connection = null;
- _log.Info("Client stopped.");
+ _log.Detail(LogTopic.Transport, "Client stopped.");
}
public void ShutdownAfterFlush(int timeoutMs)
@@ -278,7 +286,7 @@ public void ShutdownAfterFlush(int timeoutMs)
connection.Close("client shutting down");
_connection = null;
- _log.Info("Client stopped.");
+ _log.Detail(LogTopic.Transport, "Client stopped.");
}
public void Dispose() => Shutdown();
diff --git a/CS2MultiplayerMod/Core/Networking/Tcp/TcpServerTransport.Reachability.cs b/CS2MultiplayerMod/Core/Networking/Tcp/TcpServerTransport.Reachability.cs
index 805ad37..f922628 100644
--- a/CS2MultiplayerMod/Core/Networking/Tcp/TcpServerTransport.Reachability.cs
+++ b/CS2MultiplayerMod/Core/Networking/Tcp/TcpServerTransport.Reachability.cs
@@ -3,6 +3,7 @@
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
+using CS2MultiplayerMod.Core.Diagnostics;
namespace CS2MultiplayerMod.Core.Networking.Tcp
{
@@ -28,19 +29,20 @@ private void LogReachability(int port, bool lanOnly)
}
}
- _log.Info(locals.Count > 0
- ? "Players on your network join via: " + string.Join(", ", locals.ToArray())
- : "Could not find any local network address - is this machine connected to a network?");
+ _log.Event(LogTopic.Transport,
+ locals.Count > 0 ? "Players on your network join via: " +
+ string.Join(", ", locals.ToArray()) : "Could not find any local network address - is this machine connected to a network?");
if (!lanOnly)
- _log.Info("Players on the internet need your PUBLIC IP and TCP port " + port +
- " reaching this machine through the Windows Firewall. The router is being asked " +
- "to forward it automatically - see the [upnp] lines below for whether it agreed. " +
- "If it did not, forward the port by hand or host over the Steam relay instead.");
+ _log.Event(LogTopic.Transport,
+ "Players on the internet need your PUBLIC IP and TCP port " + port +
+ " reaching this machine through the Windows Firewall. The router is being asked " +
+ "to forward it automatically - see the [upnp] lines below for whether it agreed. " +
+ "If it did not, forward the port by hand or host over the Steam relay instead.");
}
catch (Exception ex)
{
- _log.Warn("Could not enumerate local addresses: " + ex.Message);
+ _log.Warn(LogTopic.Transport, "Could not enumerate local addresses: " + ex.Message);
}
}
diff --git a/CS2MultiplayerMod/Core/Networking/Tcp/TcpServerTransport.cs b/CS2MultiplayerMod/Core/Networking/Tcp/TcpServerTransport.cs
index 3e3edd1..8dea80e 100644
--- a/CS2MultiplayerMod/Core/Networking/Tcp/TcpServerTransport.cs
+++ b/CS2MultiplayerMod/Core/Networking/Tcp/TcpServerTransport.cs
@@ -80,8 +80,9 @@ public void Start(int port, bool lanOnly = true, X509Certificate2 certificate =
};
_acceptThread.Start();
- _log.Info("Host listening on " + _listener.LocalEndpoint + " (" + (lanOnly ? "LAN-only" : "PUBLIC") + ", " +
- (certificate != null ? "TLS" : "PLAINTEXT") + ").");
+ _log.Event(LogTopic.Transport, "Host listening on " + _listener.LocalEndpoint + " (" +
+ (lanOnly ? "LAN-only" : "PUBLIC") + ", " +
+ (certificate != null ? "TLS" : "PLAINTEXT") + ").");
LogReachability(port, lanOnly);
}
@@ -108,8 +109,8 @@ private void AcceptLoop()
try { remote = client.Client.RemoteEndPoint.ToString(); } catch { /* socket already dead */ }
var id = new ConnectionId(Interlocked.Increment(ref _nextConnectionId));
- _log.Info("Accepted TCP connection " + id + " from " + remote +
- (_certificate != null ? "; starting TLS handshake." : "."));
+ _log.Detail(LogTopic.Transport, "Accepted TCP connection " + id + " from " + remote +
+ (_certificate != null ? "; starting TLS handshake." : "."));
var connection = new FramedConnection(id, client, _certificate)
{
// Connected is announced only once the connection is actually usable
@@ -140,7 +141,8 @@ private bool Admit(TcpClient client)
if (_lanOnly && !IsPrivateAddress(remote))
{
- _log.Warn("Refused connection from " + remote + ": session is LAN-only.");
+ _log.Warn(LogTopic.Transport, "Refused connection from " + remote +
+ ": session is LAN-only.");
try { client.Close(); } catch { }
return false;
}
@@ -149,7 +151,8 @@ private bool Admit(TcpClient client)
{
// Coarse global cap: handshaked peers are bounded by the session's player
// limit, so runaway growth here means a pending-socket flood.
- _log.Warn("Refused connection from " + remote + ": too many open connections.");
+ _log.Warn(LogTopic.Transport, "Refused connection from " + remote +
+ ": too many open connections.");
try { client.Close(); } catch { }
return false;
}
@@ -165,7 +168,8 @@ private void Enqueue(TransportEvent evt, ConnectionId from)
Interlocked.Decrement(ref _queuedEvents);
// The game thread is not draining fast enough or someone is flooding;
// either way, shedding the producer beats unbounded memory growth.
- _log.Warn("Transport event queue full; dropping connection " + from.Value + ".");
+ _log.Warn(LogTopic.Transport, "Transport event queue full; dropping connection " +
+ from.Value + ".");
FramedConnection connection;
if (_connections.TryGetValue(from.Value, out connection))
connection.Close("event queue overflow");
@@ -241,7 +245,7 @@ public void Shutdown()
pair.Value.Close("host shutting down");
_connections.Clear();
- _log.Info("Host stopped.");
+ _log.Detail(LogTopic.Transport, "Host stopped.");
}
public void ShutdownAfterFlush(int timeoutMs)
@@ -263,14 +267,14 @@ public void ShutdownAfterFlush(int timeoutMs)
Thread.Sleep(5);
if (!_connections.IsEmpty)
- _log.Warn("Host stopping with " + _connections.Count +
- " connection(s) still draining after " + timeoutMs + " ms; closing them now.");
+ _log.Warn(LogTopic.Transport, "Host stopping with " + _connections.Count +
+ " connection(s) still draining after " + timeoutMs + " ms; closing them now.");
foreach (var pair in _connections)
pair.Value.Close("host shutting down");
_connections.Clear();
- _log.Info("Host stopped.");
+ _log.Detail(LogTopic.Transport, "Host stopped.");
}
public void Dispose() => Shutdown();
diff --git a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Administration.cs b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Administration.cs
index 06fa69a..f473cd2 100644
--- a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Administration.cs
+++ b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Administration.cs
@@ -1,3 +1,4 @@
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Protocol;
using CS2MultiplayerMod.Core.Protocol.Messages;
@@ -25,7 +26,7 @@ public bool UnbanAddress(string address)
{
if (string.IsNullOrEmpty(address)) return false;
bool removed = _hostBannedAddresses.Remove(address.Trim());
- if (removed) _log.Info("[security] Ban lifted for " + address.Trim() + ".");
+ if (removed) _log.Event(LogTopic.Session, "Ban lifted for " + address.Trim() + ".");
return removed;
}
@@ -75,7 +76,8 @@ private bool RemovePlayer(int playerId, bool ban)
_administrativeRemovals.Add(selected.Connection.Value);
SendTo(selected.Connection, new DisconnectNoticeMessage(reason));
_transport.DisconnectAfterFlush(selected.Connection);
- _log.Info("Host " + (ban ? "banned " : "removed ") + selected + " from the session.");
+ _log.Event(LogTopic.Session, "Host " + (ban ? "banned " : "removed ") + selected +
+ " from the session.");
return true;
}
diff --git a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Blob.cs b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Blob.cs
index b424e3f..70cbfdf 100644
--- a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Blob.cs
+++ b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Blob.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Protocol;
using CS2MultiplayerMod.Core.Protocol.Messages;
@@ -26,22 +27,25 @@ private void ChunkAndSend(string channel, long transferId, byte[] data, Connecti
// Blobs flow host → client only; a client has no business streaming one.
if (Role != SessionRole.Host)
{
- _log.Warn("Ignoring outgoing blob '" + channel + "': only the host streams blobs.");
+ _log.Warn(LogTopic.WorldTransfer, "Ignoring outgoing blob '" + channel +
+ "': only the host streams blobs.");
return;
}
if ((transferId > 0 && (!_worldSyncSuspended || transferId != _worldSyncEpoch)) ||
(_worldSyncSuspended && transferId == 0))
{
- _log.Warn("Ignoring outgoing blob '" + channel + "' transfer " + transferId +
- ": it does not match the active world-sync epoch.");
+ _log.Warn(LogTopic.WorldTransfer, "Ignoring outgoing blob '" + channel +
+ "' transfer " + transferId +
+ ": it does not match the active world-sync epoch.");
return;
}
int total = data.Length;
int chunkBytes = ProtocolConstants.BlobChunkBytes;
int chunkCount = (total + chunkBytes - 1) / chunkBytes;
- _log.Info("Sending blob '" + channel + "': " + total + " bytes in " + chunkCount + " chunk(s) to " +
- (target.IsNone ? "all peers" : target.ToString()) + ".");
+ _log.Detail(LogTopic.WorldTransfer, "Sending blob '" + channel + "': " + total +
+ " bytes in " + chunkCount + " chunk(s) to " +
+ (target.IsNone ? "all peers" : target.ToString()) + ".");
int offset = 0;
do
@@ -69,8 +73,9 @@ private void ChunkAndSend(string channel, long transferId, byte[] data, Connecti
_outgoingBlobSent = 0;
_outgoingBlobActive = _outgoingBlobTotal > 0;
- _log.Info("Finished queueing blob '" + channel + "' (" + total + " bytes, " +
- chunkCount + " chunk(s)) to " + (target.IsNone ? "all peers" : target.ToString()) + ".");
+ _log.Detail(LogTopic.WorldTransfer, "Finished queueing blob '" + channel + "' (" + total +
+ " bytes, " + chunkCount + " chunk(s)) to " +
+ (target.IsNone ? "all peers" : target.ToString()) + ".");
}
private void HandleBlobChunk(ConnectionId from, Peer peer, BlobChunkMessage chunk, long nowUnixMs)
@@ -87,9 +92,10 @@ private void HandleBlobChunk(ConnectionId from, Peer peer, BlobChunkMessage chun
(_worldSyncSuspended && chunk.TransferId != _worldSyncEpoch) ||
(!_worldSyncSuspended && chunk.TransferId != 0))
{
- _log.Warn("Dropping blob '" + (chunk.Channel ?? "") + "' transfer " +
- chunk.TransferId + ": it does not match active world-sync epoch " +
- (_worldSyncSuspended ? _worldSyncEpoch.ToString() : "none") + ".");
+ _log.Warn(LogTopic.WorldTransfer, "Dropping blob '" + (chunk.Channel ?? "") +
+ "' transfer " + chunk.TransferId +
+ ": it does not match active world-sync epoch " +
+ (_worldSyncSuspended ? _worldSyncEpoch.ToString() : "none") + ".");
return;
}
@@ -99,15 +105,16 @@ private void HandleBlobChunk(ConnectionId from, Peer peer, BlobChunkMessage chun
if (string.IsNullOrEmpty(chunk.Channel) ||
!_allowedBlobChannels.TryGetValue(chunk.Channel, out maxBytes))
{
- _log.Warn("[security] Dropping blob chunk on unregistered channel '" +
- (chunk.Channel ?? "") + "'.");
+ _log.Warn(LogTopic.WorldTransfer, "Dropping blob chunk on unregistered channel '" +
+ (chunk.Channel ?? "") + "'.");
return;
}
if (chunk.TotalBytes <= 0 || chunk.TotalBytes > maxBytes)
{
- _log.Warn("[security] Dropping blob '" + chunk.Channel + "': announced " +
- chunk.TotalBytes + " bytes is outside (0, " + maxBytes + "].");
+ _log.Warn(LogTopic.WorldTransfer, "Dropping blob '" + chunk.Channel +
+ "': announced " + chunk.TotalBytes + " bytes is outside (0, " + maxBytes +
+ "].");
_blobs.Remove(chunk.Channel);
_blobTransferIds.Remove(chunk.Channel);
ClearBlobProgress();
@@ -120,8 +127,8 @@ private void HandleBlobChunk(ConnectionId from, Peer peer, BlobChunkMessage chun
(!_blobTransferIds.TryGetValue(chunk.Channel, out activeTransferId) ||
activeTransferId != chunk.TransferId))
{
- _log.Warn("Replacing incomplete blob '" + chunk.Channel + "' transfer " +
- activeTransferId + " with transfer " + chunk.TransferId + ".");
+ _log.Warn(LogTopic.WorldTransfer, "Replacing incomplete blob '" + chunk.Channel +
+ "' transfer " + activeTransferId + " with transfer " + chunk.TransferId + ".");
_blobs.Remove(chunk.Channel);
_blobTransferIds.Remove(chunk.Channel);
reassembler = null;
@@ -130,14 +137,16 @@ private void HandleBlobChunk(ConnectionId from, Peer peer, BlobChunkMessage chun
{
if (_blobs.Count >= MaxActiveBlobs)
{
- _log.Warn("[security] Dropping blob '" + chunk.Channel + "': too many active transfers.");
+ _log.Warn(LogTopic.WorldTransfer, "Dropping blob '" + chunk.Channel +
+ "': too many active transfers.");
return;
}
reassembler = new BlobReassembler(chunk.TotalBytes, nowUnixMs);
_blobs[chunk.Channel] = reassembler;
_blobTransferIds[chunk.Channel] = chunk.TransferId;
- _log.Info("Receiving blob '" + chunk.Channel + "' transfer " + chunk.TransferId +
- ": expecting " + chunk.TotalBytes + " bytes.");
+ _log.Detail(LogTopic.WorldTransfer, "Receiving blob '" + chunk.Channel +
+ "' transfer " + chunk.TransferId + ": expecting " + chunk.TotalBytes +
+ " bytes.");
}
try
@@ -161,7 +170,8 @@ private void HandleBlobChunk(ConnectionId from, Peer peer, BlobChunkMessage chun
}
catch (ProtocolException ex)
{
- _log.Warn("[security] Dropping blob '" + chunk.Channel + "': " + ex.Message);
+ _log.Warn(LogTopic.WorldTransfer, "Dropping blob '" + chunk.Channel + "': " +
+ ex.Message);
_blobs.Remove(chunk.Channel);
_blobTransferIds.Remove(chunk.Channel);
ClearBlobProgress();
@@ -182,8 +192,8 @@ private void SweepStalledBlobs(long nowUnixMs)
if (stalled == null) return;
foreach (string channel in stalled)
{
- _log.Warn("Abandoning stalled blob '" + channel + "' (no chunk for " +
- (BlobStallTimeoutMs / 1000) + " s).");
+ _log.Warn(LogTopic.WorldTransfer, "Abandoning stalled blob '" + channel +
+ "' (no chunk for " + (BlobStallTimeoutMs / 1000) + " s).");
_blobs.Remove(channel);
_blobTransferIds.Remove(channel);
}
diff --git a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Handshake.cs b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Handshake.cs
index 5829d32..0ffc3b3 100644
--- a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Handshake.cs
+++ b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Handshake.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Protocol;
using CS2MultiplayerMod.Core.Protocol.Messages;
@@ -26,9 +27,10 @@ private void HandleHandshakeChallenge(ConnectionId connection, HandshakeChalleng
}
_challengeAnswered = true;
- _log.Info("Host challenge received (protocol v" + challenge.ProtocolVersion + ", password " +
- (challenge.PasswordRequired ? "required" : "not required") +
- "); sending handshake as '" + LocalPlayerName + "'.");
+ _log.Detail(LogTopic.Session, "Host challenge received (protocol v" +
+ challenge.ProtocolVersion + ", password " +
+ (challenge.PasswordRequired ? "required" : "not required") +
+ "); sending handshake as '" + LocalPlayerName + "'.");
byte[] binding = _transport.GetChannelBinding(ConnectionId.Server);
byte[] proof = HandshakeAuth.ComputeProof(_config.Password, challenge.Nonce, binding);
SendTo(connection, new HandshakeRequest(
@@ -48,13 +50,15 @@ private void HandleHandshakeRequest(ConnectionId connection, Peer peer, Handshak
return;
}
- _log.Info("Handshake request from " + connection + " (" + (peer.RemoteAddress ?? "?") +
- "): name='" + WireGuard.SanitizePlayerName(request.PlayerName) +
- "' protocol=" + request.ProtocolVersion +
- " mod=" + (request.ModVersion ?? "?") +
- " game=" + (request.GameVersion ?? "?") +
- " dlcs=[" + string.Join(", ", request.DlcList ?? Array.Empty()) + "]" +
- " passwordProof=" + (request.PasswordProof != null && request.PasswordProof.Length > 0 ? "present" : "missing") + ".");
+ _log.Detail(LogTopic.Session, "Handshake request from " + connection + " (" +
+ (peer.RemoteAddress ?? "?") + "): name='" +
+ WireGuard.SanitizePlayerName(request.PlayerName) + "' protocol=" +
+ request.ProtocolVersion + " mod=" + (request.ModVersion ?? "?") + " game=" +
+ (request.GameVersion ?? "?") + " dlcs=[" +
+ string.Join(", ", request.DlcList ?? Array.Empty()) + "]" +
+ " passwordProof=" +
+ (request.PasswordProof != null && request.PasswordProof.Length > 0 ? "present" : "missing") +
+ ".");
if (request.ProtocolVersion != ProtocolConstants.ProtocolVersion)
{
@@ -76,8 +80,9 @@ private void HandleHandshakeRequest(ConnectionId connection, Peer peer, Handshak
if (!HandshakeAuth.FixedTimeEquals(expected, request.PasswordProof))
{
bool nowBanned = _failedAuth.RecordFailure(peer.RemoteAddress, nowUnixMs);
- _log.Warn("[security] Auth failure from " + connection + " (" +
- (peer.RemoteAddress ?? "?") + ")" + (nowBanned ? " - address temporarily banned." : "."));
+ _log.Warn(LogTopic.Session, "Auth failure from " + connection + " (" +
+ (peer.RemoteAddress ?? "?") + ")" +
+ (nowBanned ? " - address temporarily banned." : "."));
Reject(connection, "Incorrect password.");
return;
}
@@ -98,9 +103,9 @@ private void HandleHandshakeRequest(ConnectionId connection, Peer peer, Handshak
return;
}
- _log.Warn("[compatibility] " + mismatch +
- " The host chose to ignore this check at their own risk; " +
- "protocol compatibility is still enforced.");
+ _log.Warn(LogTopic.Session, mismatch +
+ " The host chose to ignore this check at their own risk; " +
+ "protocol compatibility is still enforced.");
}
if (!string.IsNullOrEmpty(_config.GameVersion) &&
@@ -146,7 +151,8 @@ private void HandleHandshakeRequest(ConnectionId connection, Peer peer, Handshak
peer.PlayerId = _nextPlayerId++;
peer.AwaitingApproval = true;
SendTo(connection, new HandshakePendingMessage());
- _log.Info("Join from " + peer + " passed the checks; awaiting host approval.");
+ _log.Detail(LogTopic.Session, "Join from " + peer +
+ " passed the checks; awaiting host approval.");
return;
}
@@ -171,7 +177,7 @@ private void FinalizeJoin(ConnectionId connection, Peer peer, long nowUnixMs)
peer.Handshaked = true;
SendTo(connection, HandshakeResponse.Accept(peer.PlayerId));
- _log.Info("Accepted " + peer + ".");
+ _log.Event(LogTopic.Session, "Accepted " + peer + ".");
NotifyPeerJoined(peer);
// Surface a "joined" system line to everyone — the clients over the wire and
@@ -241,7 +247,8 @@ private void HandleHandshakePending(ConnectionId connection, Peer peer)
}
if (_awaitingHostApproval) return;
_awaitingHostApproval = true;
- _log.Info("The host received the join request; waiting for the host to approve it.");
+ _log.Detail(LogTopic.Session,
+ "The host received the join request; waiting for the host to approve it.");
}
///
@@ -311,7 +318,7 @@ private void Reject(ConnectionId connection, string reason)
// race the asynchronous send and the client would only see "remote closed".
_transport.DisconnectAfterFlush(connection);
_peers.Remove(connection.Value);
- _log.Warn("Rejected " + connection + ": " + reason);
+ _log.Warn(LogTopic.Session, "Rejected " + connection + ": " + reason);
}
private void HandleHandshakeResponse(Peer peer, HandshakeResponse response)
@@ -330,8 +337,8 @@ private void HandleHandshakeResponse(Peer peer, HandshakeResponse response)
LocalPlayerId = response.AssignedPlayerId;
if (peer != null) peer.Handshaked = true;
- _log.Info("Join accepted by host; assigned player #" + LocalPlayerId +
- ". Waiting for host world stream.");
+ _log.Event(LogTopic.Session, "Join accepted by host; assigned player #" + LocalPlayerId +
+ ". Waiting for host world stream.");
SetStatus(SessionStatus.Connected, "Joined as player #" + LocalPlayerId);
if (peer != null) NotifyPeerJoined(peer);
}
diff --git a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Lifecycle.cs b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Lifecycle.cs
index 4c6ebfb..fb3ee11 100644
--- a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Lifecycle.cs
+++ b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Lifecycle.cs
@@ -1,5 +1,6 @@
using System;
using System.Net.Sockets;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Networking.Tcp;
using CS2MultiplayerMod.Core.Protocol;
@@ -47,8 +48,9 @@ private void StartHostCore(MultiplayerConfig config)
// into the city. Said loudly, but allowed — private games with trusted
// friends over a forwarded port are this mod's main use case.
if (!config.LanOnly && string.IsNullOrEmpty(config.Password))
- _log.Warn("[security] Hosting PUBLICLY with NO PASSWORD: anyone who can reach port " +
- config.Port + " can join and receive the city. Setting a password is strongly recommended.");
+ _log.Warn(LogTopic.Session,
+ "Hosting PUBLICLY with NO PASSWORD: anyone who can reach port " + config.Port +
+ " can join and receive the city. Setting a password is strongly recommended.");
_config = config;
LocalPlayerName = WireGuard.SanitizePlayerName(config.PlayerName);
@@ -65,9 +67,9 @@ private void StartHostCore(MultiplayerConfig config)
{
if (config.LanOnly)
{
- _log.Warn("TLS unavailable on this runtime (" + certError +
- "); continuing without TLS because the session is LAN-only. " +
- "Clients must disable encryption too.");
+ _log.Warn(LogTopic.Session, "TLS unavailable on this runtime (" + certError +
+ "); continuing without TLS because the session is LAN-only. " +
+ "Clients must disable encryption too.");
}
else
{
@@ -82,8 +84,9 @@ private void StartHostCore(MultiplayerConfig config)
}
if (!config.LanOnly)
- _log.Warn("PUBLIC HOSTING ENABLED: your machine accepts connections from the internet " +
- "on port " + config.Port + ". Keep the password strong and private.");
+ _log.Warn(LogTopic.Session,
+ "PUBLIC HOSTING ENABLED: your machine accepts connections from the internet " +
+ "on port " + config.Port + ". Keep the password strong and private.");
var server = new TcpServerTransport(_log);
_transport = server;
@@ -238,7 +241,7 @@ public void StopWithNotice(string reason)
if (_transport != null)
{
try { _transport.ShutdownAfterFlush(GracefulCloseTimeoutMs); }
- catch (Exception ex) { _log.Warn("Graceful close failed (" + ex.Message + "); closing now."); }
+ catch (Exception ex) { _log.Warn(LogTopic.Session, "Graceful close failed (" + ex.Message + "); closing now."); }
}
Stop();
diff --git a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Messaging.cs b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Messaging.cs
index 56039fb..b4df465 100644
--- a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Messaging.cs
+++ b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Messaging.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Protocol;
using CS2MultiplayerMod.Core.Protocol.Messages;
@@ -66,7 +67,8 @@ private void ReapTimedOutPeers(long nowUnixMs)
if (dead != null)
foreach (Peer peer in dead)
{
- _log.Warn((peer.Handshaked ? "Peer timed out: " : "Handshake timed out: ") + peer);
+ _log.Warn(LogTopic.Session,
+ (peer.Handshaked ? "Peer timed out: " : "Handshake timed out: ") + peer);
_transport.Disconnect(peer.Connection);
// The transport will also raise Disconnected; removal/notify happens there.
}
@@ -139,15 +141,15 @@ public void RequestWorldSync(string reason = null)
if (reason.Length == 0) reason = ManualSyncReason;
if (_worldSyncSuspended)
{
- _log.Info("World sync request coalesced into active epoch " + _worldSyncEpoch +
- " (" + reason + ").");
+ _log.Detail(LogTopic.Session, "World sync request coalesced into active epoch " +
+ _worldSyncEpoch + " (" + reason + ").");
return;
}
if (Role == SessionRole.Client)
{
SendTo(ConnectionId.Server, new ResyncRequestMessage(LocalPlayerId, reason));
- _log.Info("World sync request sent to host (" + reason + ").");
+ _log.Event(LogTopic.Session, "World sync request sent to host (" + reason + ").");
NotifyChat(null, "World sync requested - the host will stream you its city.");
}
else if (Role == SessionRole.Host)
@@ -155,7 +157,8 @@ public void RequestWorldSync(string reason = null)
// With nobody connected the epoch opens, finds no participants and closes again,
// which from the button looked identical to a sync that had failed silently.
int peers = HandshakedPeerCount();
- _log.Info("Host requested world sync for " + peers + " client(s) (" + reason + ").");
+ _log.Event(LogTopic.Session, "Host requested world sync for " + peers +
+ " client(s) (" + reason + ").");
NotifyResyncRequested(LocalPlayerId, ConnectionId.None);
NotifyChat(null, peers == 0
? "Nothing to sync - no other players are connected."
@@ -182,8 +185,9 @@ private void HandleResyncRequest(ConnectionId from, Peer peer, long nowUnixMs, s
// host in a permanent save+stream loop. (Per-peer budgets run on top.)
if (nowUnixMs - _lastResyncAcceptedUnixMs < ResyncRequestCooldownMs)
{
- _log.Warn("Ignoring /sync from " + (peer != null ? peer.ToString() : from.ToString()) +
- " (" + reason + "): a world sync ran moments ago.");
+ _log.Warn(LogTopic.Session, "Ignoring /sync from " +
+ (peer != null ? peer.ToString() : from.ToString()) + " (" + reason +
+ "): a world sync ran moments ago.");
return;
}
_lastResyncAcceptedUnixMs = nowUnixMs;
@@ -194,7 +198,7 @@ private void HandleResyncRequest(ConnectionId from, Peer peer, long nowUnixMs, s
// Why the other machine gave up belongs in THIS log too. Without it the host's log
// reads "someone asked for a sync" for both a player pressing the button and a client
// pipeline that could not apply an edit - the two cases that need telling apart most.
- _log.Info("World sync requested by " + name + ": " + reason + ".");
+ _log.Event(LogTopic.Session, "World sync requested by " + name + ": " + reason + ".");
// Tell everyone the world is about to snap, then let the game layer stream it.
string notice = name + " requested a world sync.";
diff --git a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Notify.cs b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Notify.cs
index cfcde67..4967f9e 100644
--- a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Notify.cs
+++ b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Notify.cs
@@ -1,4 +1,5 @@
using System;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Protocol;
using CS2MultiplayerMod.Core.Protocol.Messages;
@@ -29,7 +30,7 @@ private void BroadcastToAll(INetMessage message, ConnectionId exclude)
private void SetStatus(SessionStatus status, string detail)
{
Status = status;
- _log.Info("Session status: " + status + " (" + detail + ")");
+ _log.Event(LogTopic.Session, "Session status: " + status + " (" + detail + ")");
for (int i = 0; i < _observers.Count; i++)
try { _observers[i].OnStatusChanged(status, detail); }
catch (Exception ex) { LogObserverError("OnStatusChanged", ex); }
@@ -37,7 +38,7 @@ private void SetStatus(SessionStatus status, string detail)
private void Fault(string message)
{
- _log.Error("Session fault: " + message);
+ _log.Error(LogTopic.Session, "Session fault: " + message);
SetStatus(SessionStatus.Faulted, message);
for (int i = 0; i < _observers.Count; i++)
try { _observers[i].OnError(message); }
@@ -108,8 +109,8 @@ private void NotifyPlayerState(PlayerStateMessage state)
private void NotifyBlob(string channel, long transferId, byte[] data)
{
- _log.Info("Blob '" + channel + "' transfer " + transferId + " received (" +
- data.Length + " bytes).");
+ _log.Detail(LogTopic.Session, "Blob '" + channel + "' transfer " + transferId +
+ " received (" + data.Length + " bytes).");
for (int i = 0; i < _observers.Count; i++)
try { _observers[i].OnBlobReceived(channel, transferId, data); }
catch (Exception ex) { LogObserverError("OnBlobReceived", ex); }
@@ -125,7 +126,8 @@ private void NotifyWorldSync(WorldSyncStage stage, long epoch, float resumeSpeed
private void LogObserverError(string callback, Exception ex)
{
- _log.Error("Observer crashed in " + callback + " (session continues): " + ex);
+ _log.Error(LogTopic.Session, "Observer crashed in " + callback +
+ " (session continues): " + ex);
}
}
diff --git a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Transport.cs b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Transport.cs
index 6787570..9510c5b 100644
--- a/CS2MultiplayerMod/Core/Session/MultiplayerSession/Transport.cs
+++ b/CS2MultiplayerMod/Core/Session/MultiplayerSession/Transport.cs
@@ -1,4 +1,5 @@
using System;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Networking.Tcp;
using CS2MultiplayerMod.Core.Protocol;
@@ -34,16 +35,16 @@ private void OnTransportConnected(ConnectionId connection, long nowUnixMs)
// protocol work happens.
if (_failedAuth.IsBanned(address, nowUnixMs))
{
- _log.Warn("[security] Refused " + connection + " (" + address +
- "): temporarily banned after repeated auth failures.");
+ _log.Warn(LogTopic.Transport, "Refused " + connection + " (" + address +
+ "): temporarily banned after repeated auth failures.");
_transport.Disconnect(connection);
return;
}
if (!string.IsNullOrEmpty(address) && _hostBannedAddresses.Contains(address))
{
- _log.Warn("[security] Refused " + connection + " (" + address +
- "): banned by the host for this session.");
+ _log.Warn(LogTopic.Transport, "Refused " + connection + " (" + address +
+ "): banned by the host for this session.");
SendTo(connection, HandshakeResponse.Reject(
"The host banned this connection for the current hosting session."));
_transport.DisconnectAfterFlush(connection);
@@ -52,8 +53,8 @@ private void OnTransportConnected(ConnectionId connection, long nowUnixMs)
if (IsLobbyLocked)
{
- _log.Info("Refused " + connection + " (" + address +
- "): the host has locked this session.");
+ _log.Event(LogTopic.Transport, "Refused " + connection + " (" + address +
+ "): the host has locked this session.");
SendTo(connection, HandshakeResponse.Reject(
"The host has locked this session to new players."));
_transport.DisconnectAfterFlush(connection);
@@ -66,8 +67,8 @@ private void OnTransportConnected(ConnectionId connection, long nowUnixMs)
if (!pair.Value.Handshaked) pending++;
if (pending >= TcpServerTransport.MaxPendingConnections)
{
- _log.Warn("[security] Refused " + connection + " (" + address +
- "): too many connections awaiting handshake.");
+ _log.Warn(LogTopic.Transport, "Refused " + connection + " (" + address +
+ "): too many connections awaiting handshake.");
_transport.Disconnect(connection);
return;
}
@@ -83,7 +84,8 @@ private void OnTransportConnected(ConnectionId connection, long nowUnixMs)
_peers[connection.Value] = peer;
SendTo(connection, new HandshakeChallenge(
ProtocolConstants.ProtocolVersion, PasswordProtected, peer.ChallengeNonce));
- _log.Info("Client connecting on " + connection + " (" + address + "); challenged, awaiting handshake.");
+ _log.Detail(LogTopic.Transport, "Client connecting on " + connection + " (" +
+ address + "); challenged, awaiting handshake.");
}
else // Client: the socket to the host is up — wait for its challenge.
{
@@ -124,8 +126,9 @@ private void OnTransportDisconnected(ConnectionId connection, string reason)
// Without this line a client that connects but never authenticates
// (TLS failure, crash, wrong build) vanishes without a trace in the
// host's log — the single worst blind spot when debugging joins.
- _log.Info("Connection " + connection + " (" + (peer.RemoteAddress ?? "?") +
- ") closed before completing the handshake: " + reason);
+ _log.Warn(LogTopic.Transport, "Connection " + connection + " (" +
+ (peer.RemoteAddress ?? "?") + ") closed before completing the handshake: " +
+ reason);
}
if (Role == SessionRole.Client)
@@ -151,7 +154,8 @@ private void OnTransportDisconnected(ConnectionId connection, string reason)
///
private void EndByRemote(string reason)
{
- _log.Info("Host ended the session (" + reason + "). Disconnecting cleanly.");
+ _log.Event(LogTopic.Transport, "Host ended the session (" + reason +
+ "). Disconnecting cleanly.");
// Preserve the host notice / transport failure for the game layer. It uses
// this detail to tell the player why their temporary host world is closing.
Stop(reason);
@@ -269,7 +273,8 @@ private void Punt(ConnectionId connection, Peer peer, string reason, string mess
// ends the whole session. Strays are logged and dropped instead.
if (Role == SessionRole.Client)
{
- _log.Warn("[security] Dropping " + messageType + " from host: " + reason + ".");
+ _log.Warn(LogTopic.Transport, "Dropping " + messageType + " from host: " + reason +
+ ".");
return;
}
@@ -280,8 +285,8 @@ private void Punt(ConnectionId connection, Peer peer, string reason, string mess
? peer.RemoteAddress
: (_transport != null ? _transport.GetRemoteAddress(connection) : null) ?? "?";
- _log.Warn("[security] Disconnecting " + connection + " " + who + " (" + address +
- "): " + reason + " [type=" + messageType + "]");
+ _log.Warn(LogTopic.Transport, "Disconnecting " + connection + " " + who + " (" + address +
+ "): " + reason + " [type=" + messageType + "]");
if (_transport != null) _transport.Disconnect(connection);
// Removal + observer notification happen on the transport's Disconnected event.
diff --git a/CS2MultiplayerMod/Core/Session/MultiplayerSession/WorldSync.cs b/CS2MultiplayerMod/Core/Session/MultiplayerSession/WorldSync.cs
index 576a0f3..8f11c4e 100644
--- a/CS2MultiplayerMod/Core/Session/MultiplayerSession/WorldSync.cs
+++ b/CS2MultiplayerMod/Core/Session/MultiplayerSession/WorldSync.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Protocol.Messages;
@@ -26,8 +27,8 @@ public bool BeginWorldSync(long epoch, float resumeSpeed, IList ta
_worldSyncSuspended = true;
var begin = new WorldSyncControlMessage(epoch, WorldSyncStage.Begin, resumeSpeed);
SendWorldSyncToTargets(begin, targets);
- _log.Info("World sync epoch " + epoch + " began for " +
- (targets != null ? targets.Count : 0) + " peer(s); gameplay traffic suspended.");
+ _log.Event(LogTopic.WorldTransfer, "World sync epoch " + epoch + " began for " +
+ (targets != null ? targets.Count : 0) + " peer(s); gameplay traffic suspended.");
return true;
}
@@ -56,7 +57,7 @@ public bool ResumeWorldSync(long epoch, float resumeSpeed, IList t
new WorldSyncControlMessage(epoch, WorldSyncStage.Resume, resumeSpeed), targets);
_worldSyncSuspended = false;
_worldSyncEpoch = 0;
- _log.Info("World sync epoch " + epoch + " resumed.");
+ _log.Event(LogTopic.WorldTransfer, "World sync epoch " + epoch + " resumed.");
return true;
}
@@ -70,7 +71,8 @@ public bool AbortWorldSync(long epoch, float resumeSpeed, IList ta
new WorldSyncControlMessage(epoch, WorldSyncStage.Abort, resumeSpeed), targets);
_worldSyncSuspended = false;
_worldSyncEpoch = 0;
- _log.Warn("World sync epoch " + epoch + " aborted; previous world resumed.");
+ _log.Warn(LogTopic.WorldTransfer, "World sync epoch " + epoch +
+ " aborted; previous world resumed.");
return true;
}
@@ -114,8 +116,8 @@ private void HandleWorldSyncControl(ConnectionId from, Peer peer,
}
if (!_worldSyncSuspended || control.Epoch != _worldSyncEpoch)
{
- _log.Warn("Ignoring stale world-sync " + control.Stage + " for epoch " +
- control.Epoch + " from " + from + ".");
+ _log.Warn(LogTopic.WorldTransfer, "Ignoring stale world-sync " + control.Stage +
+ " for epoch " + control.Epoch + " from " + from + ".");
return;
}
NotifyWorldSync(control.Stage, control.Epoch, 0f, from);
diff --git a/CS2MultiplayerMod/Game/ColossalModLogger.cs b/CS2MultiplayerMod/Game/ColossalModLogger.cs
index 1f77b3d..cd5d4a8 100644
--- a/CS2MultiplayerMod/Game/ColossalModLogger.cs
+++ b/CS2MultiplayerMod/Game/ColossalModLogger.cs
@@ -1,30 +1,34 @@
using System;
-using Colossal.Logging;
using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Game.Diagnostics;
namespace CS2MultiplayerMod.Game
{
///
- /// Adapts the core's game-agnostic onto Colossal's
- /// . This is the single seam where the portable core meets the
- /// game's logging; nothing under Core/ references Colossal types.
+ /// Lets the portable core write through the mod's one logger.
+ ///
+ /// Nothing under Core/ may reference a game assembly, so the networking and session code
+ /// logs against . This is the single seam where that interface meets
+ /// - the core names the same values as the game
+ /// layer, so a transport line and a road line land in one log, tagged the same way, gated the
+ /// same way, and mirrored to the flight log by the same rules.
+ ///
+ /// It holds no state: the destinations are the mod's static log and flight recorder.
///
public sealed class ColossalModLogger : IModLogger
{
- private readonly ILog _log;
+ public static readonly ColossalModLogger Instance = new ColossalModLogger();
- public ColossalModLogger(ILog log)
- {
- _log = log;
- }
+ private ColossalModLogger() { }
- // Redacted here rather than at each call site: IO and asset faults quote the
- // offending path, which sits under the player's profile.
- public void Debug(string message) => _log.Debug(LogPaths.Redact(message));
- public void Info(string message) => _log.Info(LogPaths.Redact(message));
- public void Warn(string message) => _log.Warn(LogPaths.Redact(message));
- public void Error(string message) => _log.Error(LogPaths.Redact(message));
- public void Error(string message, Exception exception) => _log.Error(LogPaths.Redact(message + " :: " + exception));
+ public bool IsEnabled(LogTopic topic) => SyncLog.IsEnabled(topic);
+ public void Detail(LogTopic topic, string message) => SyncLog.Detail(topic, message);
+ public void Trace(LogTopic topic, string message) => SyncLog.Trace(topic, message);
+ public void Event(LogTopic topic, string message) => SyncLog.Event(topic, message);
+ public void Warn(LogTopic topic, string message) => SyncLog.Warn(topic, message);
+ public void Error(LogTopic topic, string message) => SyncLog.Error(topic, message);
+
+ public void Error(LogTopic topic, string message, Exception exception) =>
+ SyncLog.Error(topic, message, exception);
}
}
diff --git a/CS2MultiplayerMod/Game/Diagnostics/FlightRecorder.cs b/CS2MultiplayerMod/Game/Diagnostics/FlightRecorder.cs
index 05fdb49..a318152 100644
--- a/CS2MultiplayerMod/Game/Diagnostics/FlightRecorder.cs
+++ b/CS2MultiplayerMod/Game/Diagnostics/FlightRecorder.cs
@@ -13,11 +13,28 @@
namespace CS2MultiplayerMod.Game.Diagnostics
{
///
- /// Crash forensics for public builds. The recorder appends compact, structured events
- /// and periodic health snapshots to Logs/CS2MP-flight.log. The file is never
- /// truncated on start (it rotates at 4 MB), and every event is flushed before returning.
- /// Its tail therefore survives a hard process exit and shows the exact run, thread,
- /// multiplayer operation and resource trend immediately before the failure.
+ /// Crash forensics for public builds: Logs/CS2MP-flight.log.
+ ///
+ /// This is the mod's second log file, and the only reason it is a second file rather than more
+ /// lines in the first one is that it can promise four things the game log cannot:
+ ///
+ /// * The tail survives. The game's logger buffers; a hard exit - a native access
+ /// violation, an out-of-memory kill, a GPU driver reset - takes the last seconds of it with
+ /// it, which is exactly the part worth reading. Every line here that matters is flushed
+ /// before returns.
+ /// * The previous run survives. The game log is truncated on start, so a player who
+ /// crashes and restarts before writing their report has already destroyed the evidence.
+ /// This file is only rotated after a run that ended cleanly (see ).
+ /// * It sees the whole process. The Unity log callback, the unhandled-exception hook
+ /// and the unobserved-task hook record faults from the game and from other mods, which
+ /// never reach this mod's own logger but are frequently the actual cause.
+ /// * It is machine-readable. Every line carries run id, sequence, elapsed time and
+ /// thread, and the payloads are key=value, so a report can be diffed and sorted
+ /// rather than only read.
+ ///
+ /// It is not a parallel log with different content: mirrors every line
+ /// it writes to the game log here as well, so this file is a superset. Ask a player for this
+ /// one file and nothing is missing.
///
internal static class FlightRecorder
{
@@ -38,6 +55,14 @@ internal static class FlightRecorder
private const int MaxDiagnosticMods = 256;
private const int MaxContentValueChars = 256;
+ ///
+ /// How many unflushed detail lines may sit in the writer's buffer. Detail is the chatty,
+ /// switched-on-by-the-player level, so flushing every one of those would put a disk write
+ /// in the middle of a frame; the next event, warning or fault flushes them anyway, and
+ /// this bound keeps a long quiet stretch of pure detail from outliving a crash.
+ ///
+ private const int MaxBufferedLines = 64;
+
private static readonly object Gate = new object();
private static StreamWriter _writer;
private static Stopwatch _runClock;
@@ -47,6 +72,7 @@ internal static class FlightRecorder
private static int _mirroredErrors;
private static string _lastUnityKey;
private static int _lastUnityRepeats;
+ private static int _bufferedLines;
private static UnityEngine.Application.LogCallback _logHook;
private static UnityEngine.Application.LowMemoryCallback _lowMemoryHook;
private static Action _quittingHook;
@@ -75,7 +101,10 @@ public static void Start(string modVersion)
// the player restarted before sending it; rotate only clean runs.
if (previousRun == "clean") Rotate(path);
- _writer = new StreamWriter(path, true, new UTF8Encoding(false)) { AutoFlush = true };
+ // AutoFlush is off deliberately: Note() flushes anything notable itself, so
+ // the durability promise above is kept without paying a disk write for every
+ // detail line a player switched on.
+ _writer = new StreamWriter(path, true, new UTF8Encoding(false)) { AutoFlush = false };
_runClock = Stopwatch.StartNew();
_runId = Guid.NewGuid().ToString("N").Substring(0, 8);
_sequence = 0;
@@ -83,6 +112,7 @@ public static void Start(string modVersion)
_mirroredErrors = 0;
_lastUnityKey = null;
_lastUnityRepeats = 0;
+ _bufferedLines = 0;
}
catch
{
@@ -117,8 +147,23 @@ public static void Stop()
}
}
- /// Append one structured line. Safe from any thread and never throws.
+ ///
+ /// Append one structured line and flush it. Safe from any thread and never throws.
+ ///
public static void Note(string line)
+ {
+ Note(line, true);
+ }
+
+ ///
+ /// Append one structured line. Safe from any thread and never throws.
+ ///
+ /// is the durability promise: true means the line is on disk
+ /// before this returns, so it survives a hard process exit. Pass false only for chatty
+ /// detail, which is flushed by the next notable line anyway - never for a fault, a
+ /// milestone or anything a bug report would be read for.
+ ///
+ public static void Note(string line, bool flush)
{
if (!Enabled) return;
@@ -137,6 +182,14 @@ public static void Note(string line)
" elapsed=" + elapsedMs.ToString(CultureInfo.InvariantCulture) + "ms" +
" thread=" + Thread.CurrentThread.ManagedThreadId.ToString(CultureInfo.InvariantCulture) +
" " + payload);
+
+ // A flush also commits whatever detail was buffered ahead of this line, so the
+ // context leading up to a fault reaches the disk together with the fault.
+ if (flush || ++_bufferedLines >= MaxBufferedLines)
+ {
+ _writer.Flush();
+ _bufferedLines = 0;
+ }
}
catch { }
}
diff --git a/CS2MultiplayerMod/Game/Diagnostics/FrameProbe.cs b/CS2MultiplayerMod/Game/Diagnostics/FrameProbe.cs
index 646343d..fcc56d6 100644
--- a/CS2MultiplayerMod/Game/Diagnostics/FrameProbe.cs
+++ b/CS2MultiplayerMod/Game/Diagnostics/FrameProbe.cs
@@ -1,4 +1,5 @@
using System.Diagnostics;
+using CS2MultiplayerMod.Core.Diagnostics;
namespace CS2MultiplayerMod.Game.Diagnostics
{
@@ -68,19 +69,19 @@ private static void Report(long now)
long seconds = (now - _lastReportMs) / 1000;
if (seconds <= 0) seconds = 1;
- string line = "[MP] Frames/" + seconds + "s: " + _frames +
+ string line = "Frames/" + seconds + "s: " + _frames +
" (" + (_frames / seconds) + "/s, mean " + (_totalMs / _frames) +
" ms, worst " + _worstMs + " ms) " + Histogram();
- // The flight log keeps both lines regardless of the switch: a performance report is
- // exactly the case where the log was already captured before anyone thought to turn
- // one on.
- SyncLog.Record(LogTopic.Performance, line);
+ // Trace, not Detail: the flight log keeps both lines whether or not the switch is on,
+ // because a performance report is exactly the case where the log was already captured
+ // before anyone thought to turn one on.
+ SyncLog.Trace(LogTopic.Performance, line);
// Immediately after the frame times, so a slow window and the mod's share of it are
// always read together.
string cost = SyncProfiler.Report(now - _lastReportMs);
- if (cost != null) SyncLog.Record(LogTopic.Performance, cost);
+ if (cost != null) SyncLog.Trace(LogTopic.Performance, cost);
_lastReportMs = now;
_frames = 0;
diff --git a/CS2MultiplayerMod/Game/Diagnostics/ResyncArbiter.cs b/CS2MultiplayerMod/Game/Diagnostics/ResyncArbiter.cs
index b57e93c..91bdcbf 100644
--- a/CS2MultiplayerMod/Game/Diagnostics/ResyncArbiter.cs
+++ b/CS2MultiplayerMod/Game/Diagnostics/ResyncArbiter.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
namespace CS2MultiplayerMod.Game.Diagnostics
{
@@ -46,8 +47,8 @@ public enum ResyncVerdict
/// raised the report gets to retry against a world that is standing still. If it succeeds it
/// withdraws the report; if nothing withdraws it, the hold elapses and the reload happens.
///
- /// Every outcome - held, withdrawn, settled - is written at the production log level with the
- /// full report, so the log says why the world was reloaded, or why it nearly was, even when no
+ /// Every outcome - held, withdrawn, settled - is written as an ungated event with the full
+ /// report, so the log says why the world was reloaded, or why it nearly was, even when no
/// diagnostic switch was ever turned on.
///
public static class ResyncArbiter
@@ -104,9 +105,9 @@ public static ResyncVerdict Submit(ResyncReport report, long nowMs, bool recover
if (recovering)
{
- SyncLog.Prod("World sync: " + report.Reason +
- " while a world sync is already running; folded into it (" +
- report.Subsystem + "/" + report.Subject + ").");
+ SyncLog.Event(LogTopic.Resync, "World sync: " + report.Reason +
+ " while a world sync is already running; folded into it (" + report.Subsystem +
+ "/" + report.Subject + ").");
return ResyncVerdict.AlreadyRecovering;
}
@@ -150,24 +151,23 @@ public static ResyncVerdict Submit(ResyncReport report, long nowMs, bool recover
{
// States the VERDICT, not the action. Whether the reload actually runs is the
// service's call - it still has a cooldown - and it reports that itself.
- SyncLog.ProdReport(
+ SyncLog.Event(LogTopic.Resync,
"World sync: this city and the host's have diverged and cannot be reconciled " +
"locally. Reason: " + report.Reason + ".",
Decorate(report, observations, heldForMs, settled: true));
return ResyncVerdict.Settled;
}
- SyncLog.ProdReport(
- "World sync: holding off on a world reload for up to " + (HoldWindowMs / 1000) +
- " s while this is confirmed. Reason: " + report.Reason + ".",
- Decorate(report, observations, heldForMs, settled: false));
+ SyncLog.Event(LogTopic.Resync, "World sync: holding off on a world reload for up to " +
+ (HoldWindowMs / 1000) + " s while this is confirmed. Reason: " + report.Reason +
+ ".", Decorate(report, observations, heldForMs, settled: false));
return ResyncVerdict.Held;
}
///
/// Tell the arbiter a held fault has cleared - the operation resolved, the graph drained.
/// Withdrawing is the whole point of holding: it is a world reload that did not have to
- /// happen, and it is written to the production log in the same shape as one that did.
+ /// happen, and it is logged in the same shape as one that did.
///
/// This is the ONLY way a held report goes away without a reload. A subsystem that drops
/// its work instead of retrying simply never calls it, and its report matures on schedule.
@@ -186,9 +186,8 @@ public static void Withdraw(string subsystem, string reason, string subject, lon
List lines = held.Report.Lines();
lines.Add("held for: " + (nowMs - held.Report.FirstSeenMs) + " ms");
lines.Add("outcome: " + (outcome ?? "the fault cleared on its own"));
- SyncLog.ProdReport(
- "World sync: not needed after all - " + held.Report.Reason +
- " resolved without reloading the world.", lines);
+ SyncLog.Event(LogTopic.Resync, "World sync: not needed after all - " +
+ held.Report.Reason + " resolved without reloading the world.", lines);
}
///
@@ -223,8 +222,7 @@ public static List TakeMatured(long nowMs)
lines.Add("held for: " + (nowMs - report.FirstSeenMs) +
" ms with the net feeders standing down");
lines.Add("verdict: settled - nothing repaired it in that time");
- SyncLog.ProdReport(
- "World sync: the hold expired and " + report.Reason +
+ SyncLog.Event(LogTopic.Resync, "World sync: the hold expired and " + report.Reason +
" is still unresolved, so this city has to be replaced by the host's.", lines);
reports.Add(report);
}
diff --git a/CS2MultiplayerMod/Game/Diagnostics/ResyncReport.cs b/CS2MultiplayerMod/Game/Diagnostics/ResyncReport.cs
index 4bc6e5f..def5a92 100644
--- a/CS2MultiplayerMod/Game/Diagnostics/ResyncReport.cs
+++ b/CS2MultiplayerMod/Game/Diagnostics/ResyncReport.cs
@@ -50,7 +50,7 @@ public enum ResyncEvidence
/// world reloaded. That is enough to grep for and not enough to fix anything: the log never
/// said which operation, which endpoint, what was actually standing there instead, how long the
/// pipeline had been blocked, or whether anything cheaper had been tried. A report carries all
- /// of that, is written at the production log level whether or not the reload follows, and is
+ /// of that, is written as an ungated event whether or not the reload follows, and is
/// what settles before any world is thrown away.
///
/// Build one with and chain calls; every setter returns
@@ -141,7 +141,7 @@ public ResyncReport Fact(string name, long value)
public ResyncReport Fact(string name, bool value) => Fact(name, value ? "yes" : "no");
///
- /// The report as the lines the production log prints under its headline. Written in
+ /// The report as the lines the log prints under its headline. Written in
/// sentences, because the reader is usually a player pasting a log into a bug report.
///
public List Lines()
@@ -155,7 +155,7 @@ public List Lines()
return lines;
}
- /// One-line form for the flight recorder and the chat/system feed.
+ /// One-line form for the flight log and the chat/system feed.
public string Summary()
{
var text = new StringBuilder(Reason);
diff --git a/CS2MultiplayerMod/Game/Diagnostics/SyncLog.cs b/CS2MultiplayerMod/Game/Diagnostics/SyncLog.cs
index 9e33069..c49e561 100644
--- a/CS2MultiplayerMod/Game/Diagnostics/SyncLog.cs
+++ b/CS2MultiplayerMod/Game/Diagnostics/SyncLog.cs
@@ -1,167 +1,291 @@
-using CS2MultiplayerMod.Localization;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using CS2MultiplayerMod.Core.Diagnostics;
namespace CS2MultiplayerMod.Game.Diagnostics
{
///
- /// What a diagnostic line is about. Each one can be turned on by itself, so a player chasing
- /// one problem gets a log about that problem instead of everything at once - which is the
- /// difference between a log someone will actually read and a wall of text.
- ///
- public enum LogTopic
- {
- /// Anything without a more specific home. Follows the general verbose switch.
- General = 0,
-
- ///
- /// The production level: always written, never gated by a setting, and deliberately
- /// unprefixed.
- ///
- /// It exists for the handful of lines that have to be in every player's log because they
- /// are what a bug report is read for - why the world was reloaded, what the pipeline was
- /// holding when it decided, what it tried first. A player never turns these on, so they
- /// cannot be missing from the one log that matters, and a reader who does not know the
- /// mod's topic prefixes still reads them as ordinary sentences.
- ///
- /// It is not a dumping ground: anything that would repeat per frame, per entity or per
- /// command belongs to a topic switch or the flight recorder instead.
- ///
- Prod,
-
- /// Frame times and the mod's own main-thread cost, including the per-zone split.
- Performance,
-
- /// Households, residents and their homes.
- Residential,
-
- /// Shops: tenancy, figures and stock.
- Commercial,
-
- /// Factories and extractors: tenancy, figures and stock.
- Industrial,
-
- /// Offices: tenancy, figures and stock.
- Office,
- }
-
- ///
- /// The mod's diagnostic log, split by topic.
+ /// The mod's logger. Everything the mod writes goes through here - there is no second way in.
+ ///
+ /// A line belongs to a feature, not to a "debug" switch. Every call names a
+ /// , and each topic is switched on by itself, so a player chasing one
+ /// problem gets a log about that problem rather than everything at once. Asking
+ /// is a field read, so a caller can and should ask before building the
+ /// string: a diagnostic nobody reads must not cost a frame.
+ ///
+ /// A fault is never a topic. A warning, an error or a milestone is not a diagnostic a
+ /// player chooses to receive - it is the thing they are about to report. So severity, not the
+ /// switches, decides where a line goes. Nobody turns a switch on before the crash they did not
+ /// know was coming:
///
- /// Two rules make this worth having over a single verbose switch. A topic that is off costs
- /// nothing - is a field read, so a caller can and should ask before
- /// building the string. And a line always says which topic it belongs to, so a log with
- /// several topics on is still readable and greppable.
+ ///
+ /// - troubleshooting chatter; both logs,
+ /// but only while its topic is on.
+ /// - a compact breadcrumb; always in the
+ /// flight log, in the game log only while its topic is on.
+ /// - a milestone; both logs,
+ /// always.
+ /// - , something went
+ /// wrong; both logs, always, flushed.
+ ///
///
- /// A fault is never a topic. A warning or an error is not a diagnostic a player chooses to
- /// receive, so it is never gated by a switch: it goes out through the production level
- /// (, , , ),
- /// which writes to the game log and the flight recorder unconditionally and without a prefix.
+ /// The call site never writes a prefix. It passes a plain sentence; the topic tag and
+ /// the severity marker are attached here, in one place, so every line is uniformly greppable
+ /// and no two subsystems can drift into spelling the same tag differently.
+ ///
+ /// There is one log, written to two files. Both are produced from this one path, so
+ /// they never disagree: the game log is the readable one, and the flight log is the same
+ /// content made durable, structured and process-wide - see for
+ /// why that second file has to exist. Nothing reaches the game log without also reaching the
+ /// flight log, which is what makes "send us the flight log" a complete answer.
///
public static class SyncLog
{
- private static readonly string[] Prefixes =
- {
- "[MP] ",
- "", // Prod: deliberately unprefixed.
- "[MP][perf] ",
- "[MP][residential] ",
- "[MP][commercial] ",
- "[MP][industrial] ",
- "[MP][office] ",
+ ///
+ /// The tag attached to each topic, in order. Lower case and short:
+ /// these are grep targets first and prose second.
+ ///
+ private static readonly string[] Tags =
+ {
+ "startup",
+ "session",
+ "transport",
+ "world",
+ "resync",
+ "pipeline",
+ "nets",
+ "buildings",
+ "land",
+ "city",
+ "routes",
+ "residential",
+ "commercial",
+ "industrial",
+ "office",
+ "players",
+ "ui",
+ "perf",
};
///
- /// Whether anything would come of writing to this topic. Ask before computing a
- /// diagnostic, not only before logging one: a counter nobody reads must not cost a frame.
+ /// Whether a line on this topic would be written anywhere.
+ ///
+ /// Ask before computing a diagnostic, not only before logging one. Warnings, errors
+ /// and events do not consult this and must not be guarded by it - guarding a fault behind
+ /// a switch is how a bug report arrives with the interesting line missing.
///
public static bool IsEnabled(LogTopic topic)
{
- // Asked before the setting exists too: a fault during load still has to be reported,
- // and the production level is the level that is never allowed to be silent.
- if (topic == LogTopic.Prod) return true;
Setting setting = Mod.Setting;
if (setting == null) return false;
- switch (topic)
- {
- case LogTopic.Performance: return setting.LogPerformance;
- case LogTopic.Residential: return setting.LogResidential;
- case LogTopic.Commercial: return setting.LogCommercial;
- case LogTopic.Industrial: return setting.LogIndustrial;
- case LogTopic.Office: return setting.LogOffice;
- default: return setting.VerboseLogging;
- }
+ return setting.VerboseLogging || setting.IsTopicEnabled(topic);
+ }
+
+ ///
+ /// Whether a on this topic would be recorded anywhere. Traces survive
+ /// with every switch off, so this is nearly always true - it exists for the handful of
+ /// callers that walk a batch to build one, and must not do that walk for a build that
+ /// ships no flight log at all.
+ ///
+ public static bool IsRecording(LogTopic topic)
+ {
+ return FlightRecorder.Enabled || IsEnabled(topic);
}
- /// Write one line, if its topic is on.
- public static void Write(LogTopic topic, string message)
+ // ---- Gated: troubleshooting detail ------------------------------------------------
+
+ ///
+ /// One line of troubleshooting detail, written only while its topic is switched on.
+ ///
+ /// This is where the per-action, per-entity and per-interval chatter belongs. Pass a plain
+ /// sentence: the topic tag is added here.
+ ///
+ public static void Detail(LogTopic topic, string message)
{
- if (!IsEnabled(topic)) return;
- Mod.log.Info(Prefix(topic) + message);
+ if (message == null || !IsEnabled(topic)) return;
+ Emit(topic, Severity.Detail, message);
}
///
- /// Write one line to the topic that matches a workplace zone. Used by the shared company
- /// channel, whose three zones are three separate switches for the reader.
+ /// A multi-line detail report. Each line reaches the game log on its own so the file stays
+ /// one statement per line and greppable; the flight log takes the whole report as a single
+ /// event, because there it is one fact rather than many.
///
- public static void WriteZone(SyncZone zone, string message) => Write(TopicFor(zone), message);
+ public static void Detail(LogTopic topic, string headline, IList lines)
+ {
+ if (!IsEnabled(topic)) return;
+ EmitReport(topic, Severity.Detail, headline, lines);
+ }
- public static bool IsZoneEnabled(SyncZone zone) => IsEnabled(TopicFor(zone));
+ ///
+ /// A breadcrumb: always recorded to the flight log, shown in the game log only while the
+ /// topic is on.
+ ///
+ /// This is the tier for the compact key=value traces the sync pipeline leaves as it
+ /// works - "operation dropped malformed", "target retrying", "graph matched". Individually
+ /// they are noise; as the last forty lines before a crash they are the answer, which is why
+ /// they are recorded whether or not anyone asked for the topic. They are buffered rather
+ /// than flushed, and the fault that follows commits them (see
+ /// ).
+ ///
+ /// Keep them short and factual. A trace that needs a sentence is an .
+ ///
+ public static void Trace(LogTopic topic, string message)
+ {
+ if (message == null) return;
+ if (IsEnabled(topic))
+ {
+ try { Mod.log.Info(Tag(topic) + " " + LogPaths.Redact(message)); }
+ catch { }
+ }
+ FlightRecorder.Note("trace " + Tag(topic) + " " + message, false);
+ }
///
- /// A line worth keeping whether or not anyone asked for the topic. The flight log takes it
- /// regardless, because a performance report is exactly the case where the log was already
- /// captured before anyone thought to turn a switch on.
+ /// Detail about the part of the city a piece of work belongs to. The company channel serves
+ /// three zones from one code path, so the zone - not the class - picks the reader's switch.
///
- public static void Record(LogTopic topic, string message)
+ public static void DetailZone(SyncZone zone, string message)
{
- if (IsEnabled(topic)) Mod.log.Info(Prefix(topic) + message);
- FlightRecorder.Note(message);
+ Detail(TopicFor(zone), message);
}
+ /// As , for a caller that only knows the zone.
+ public static bool IsZoneEnabled(SyncZone zone)
+ {
+ return IsEnabled(TopicFor(zone));
+ }
+
+ // ---- Ungated: the lines a bug report is read for -----------------------------------
+
///
- /// Write one production line: always emitted, no prefix, and always mirrored to the flight
- /// recorder so the structured log carries the same statement as the game log.
+ /// A milestone worth having in every player's log: a session opened, a world finished
+ /// transferring, a resync decided. Never gated.
+ ///
+ /// It is not a dumping ground. Anything that repeats per frame, per entity or per command
+ /// is - if a line can arrive twice a second it is not a milestone.
///
- public static void Prod(string message)
+ public static void Event(LogTopic topic, string message)
{
if (message == null) return;
- Mod.log.Info(message);
- FlightRecorder.Note(message);
+ Emit(topic, Severity.Event, message);
}
- /// A production line the player is meant to act on (or explain in a report).
- public static void ProdWarn(string message)
+ /// A multi-line milestone report. Never gated; see .
+ public static void Event(LogTopic topic, string headline, IList lines)
+ {
+ EmitReport(topic, Severity.Event, headline, lines);
+ }
+
+ ///
+ /// Something went wrong and the mod worked around it - a command dropped, a peer timing
+ /// out, a value that had to be corrected. Never gated: this is what "and then it went
+ /// strange" looks like in a log.
+ ///
+ public static void Warn(LogTopic topic, string message)
{
if (message == null) return;
- Mod.log.Warn(message);
- FlightRecorder.Note(message);
+ Emit(topic, Severity.Warn, message);
+ }
+
+ /// A multi-line warning report. Never gated; see .
+ public static void Warn(LogTopic topic, string headline, IList lines)
+ {
+ EmitReport(topic, Severity.Warn, headline, lines);
}
- /// A production line for a fault the mod could not work around.
- public static void ProdError(string message)
+ /// Something went wrong that the mod could not work around. Never gated.
+ public static void Error(LogTopic topic, string message)
{
if (message == null) return;
- Mod.log.Error(message);
- FlightRecorder.Note(message);
+ Emit(topic, Severity.Error, message);
}
///
- /// Write a multi-line production report. Each line goes out on its own so the game log
- /// stays one-statement-per-line and greppable; the flight recorder takes the whole report
- /// as a single compact event, because there it is one fact, not many.
+ /// As , with the exception behind it.
+ ///
+ /// The game log gets the sentence plus the exception's type and message chain, so it stays
+ /// readable; the flight log additionally gets the full stack, because a stack with line
+ /// numbers is usually the entire answer and that is the file people send.
///
- public static void ProdReport(string headline, System.Collections.Generic.IList lines)
+ public static void Error(LogTopic topic, string message, Exception exception)
{
- if (headline != null) Mod.log.Info(headline);
- if (lines != null)
- for (int i = 0; i < lines.Count; i++)
- if (lines[i] != null) Mod.log.Info(" " + lines[i]);
- FlightRecorder.Note(FlattenReport(headline, lines));
+ if (exception == null) { Error(topic, message); return; }
+
+ string text = (message ?? "Unhandled exception") + " :: " + Describe(exception);
+ Emit(topic, Severity.Error, text);
+ FlightRecorder.NoteException(Tag(topic) + " " + (message ?? ""), exception);
+ }
+
+ /// A multi-line error report. Never gated; see .
+ public static void Error(LogTopic topic, string headline, IList lines)
+ {
+ EmitReport(topic, Severity.Error, headline, lines);
}
- private static string FlattenReport(string headline,
- System.Collections.Generic.IList lines)
+ // ---- Machinery ---------------------------------------------------------------------
+
+ private enum Severity { Detail, Event, Warn, Error }
+
+ private static void Emit(LogTopic topic, Severity severity, string message)
{
- var flat = new System.Text.StringBuilder(headline ?? string.Empty);
+ // Redacted here rather than at each call site: IO and asset faults quote the offending
+ // path, and every CS2 folder sits under the player's profile, so a raw path in a log
+ // pasted into a bug report hands out their Windows account name.
+ string line = Tag(topic) + " " + LogPaths.Redact(message);
+
+ // The game log is the readable one, so it keeps the framework's own severity column
+ // rather than repeating the level in the text.
+ try
+ {
+ switch (severity)
+ {
+ case Severity.Warn: Mod.log.Warn(line); break;
+ case Severity.Error: Mod.log.Error(line); break;
+ default: Mod.log.Info(line); break;
+ }
+ }
+ catch { /* diagnostics must never take the mod down */ }
+
+ // The flight log gets every line the game log gets, so a player only ever has to send
+ // that one file. Only a fault or a milestone is worth stalling the caller to flush.
+ FlightRecorder.Note(Level(severity) + " " + line, severity != Severity.Detail);
+ }
+
+ private static void EmitReport(LogTopic topic, Severity severity, string headline,
+ IList lines)
+ {
+ string tag = Tag(topic);
+ string level = Level(severity);
+
+ try
+ {
+ if (headline != null) WriteGameLog(severity, tag + " " + LogPaths.Redact(headline));
+ if (lines != null)
+ for (int i = 0; i < lines.Count; i++)
+ if (lines[i] != null)
+ WriteGameLog(severity, tag + " " + LogPaths.Redact(lines[i]));
+ }
+ catch { }
+
+ FlightRecorder.Note(level + " " + tag + " " + Flatten(headline, lines),
+ severity != Severity.Detail);
+ }
+
+ private static void WriteGameLog(Severity severity, string line)
+ {
+ switch (severity)
+ {
+ case Severity.Warn: Mod.log.Warn(line); break;
+ case Severity.Error: Mod.log.Error(line); break;
+ default: Mod.log.Info(line); break;
+ }
+ }
+
+ private static string Flatten(string headline, IList lines)
+ {
+ var flat = new StringBuilder(headline ?? string.Empty);
if (lines != null)
for (int i = 0; i < lines.Count; i++)
{
@@ -172,6 +296,39 @@ private static string FlattenReport(string headline,
return flat.ToString();
}
+ ///
+ /// The severity marker for the flight log. The game log has a severity column of its own;
+ /// the flight log is one flat stream, so it carries the level in the line.
+ ///
+ private static string Level(Severity severity)
+ {
+ switch (severity)
+ {
+ case Severity.Warn: return "WARN";
+ case Severity.Error: return "ERROR";
+ case Severity.Event: return "EVENT";
+ default: return "detail";
+ }
+ }
+
+ ///
+ /// One short, greppable description of an exception and its causes. Deliberately not
+ /// ToString(): that is the whole stack, which belongs in the flight log, not in the
+ /// middle of a readable sentence.
+ ///
+ private static string Describe(Exception exception)
+ {
+ var text = new StringBuilder();
+ for (int depth = 0; exception != null && depth < 4; depth++)
+ {
+ if (text.Length > 0) text.Append(" <- ");
+ try { text.Append(exception.GetType().Name).Append(": ").Append(exception.Message); }
+ catch { text.Append("unreadable exception"); }
+ exception = exception.InnerException;
+ }
+ return text.ToString();
+ }
+
private static LogTopic TopicFor(SyncZone zone)
{
switch (zone)
@@ -180,14 +337,14 @@ private static LogTopic TopicFor(SyncZone zone)
case SyncZone.Commercial: return LogTopic.Commercial;
case SyncZone.Industrial: return LogTopic.Industrial;
case SyncZone.Office: return LogTopic.Office;
- default: return LogTopic.General;
+ default: return LogTopic.Pipeline;
}
}
- private static string Prefix(LogTopic topic)
+ private static string Tag(LogTopic topic)
{
int index = (int)topic;
- return index >= 0 && index < Prefixes.Length ? Prefixes[index] : Prefixes[0];
+ return "[" + (index >= 0 && index < Tags.Length ? Tags[index] : Tags[0]) + "]";
}
}
}
diff --git a/CS2MultiplayerMod/Game/Diagnostics/SyncProfiler.cs b/CS2MultiplayerMod/Game/Diagnostics/SyncProfiler.cs
index 37485ff..ab15f25 100644
--- a/CS2MultiplayerMod/Game/Diagnostics/SyncProfiler.cs
+++ b/CS2MultiplayerMod/Game/Diagnostics/SyncProfiler.cs
@@ -143,7 +143,7 @@ public static string Report(long windowMs)
double totalMs = totalTicks * MillisecondsPerTick;
var text = new StringBuilder(256);
- text.Append("[MP] SyncCost/").Append(windowMs / 1000).Append("s: total ")
+ text.Append("SyncCost/").Append(windowMs / 1000).Append("s: total ")
.Append(totalMs.ToString("F0")).Append(" ms");
if (windowMs > 0)
text.Append(" (").Append((100.0 * totalMs / windowMs).ToString("F1"))
diff --git a/CS2MultiplayerMod/Game/HelpLinks.cs b/CS2MultiplayerMod/Game/HelpLinks.cs
index 7d7de45..1948bda 100644
--- a/CS2MultiplayerMod/Game/HelpLinks.cs
+++ b/CS2MultiplayerMod/Game/HelpLinks.cs
@@ -1,4 +1,6 @@
using System;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
namespace CS2MultiplayerMod.Game
{
@@ -38,11 +40,12 @@ public static void Open(string page)
try
{
UnityEngine.Application.OpenURL(url);
- Mod.log.Info("[MP] Opened help page: " + page);
+ SyncLog.Detail(LogTopic.Ui, "Opened help page: " + page);
}
catch (Exception ex)
{
- Mod.log.Warn("[MP] Could not open the help page " + url + ": " + ex.Message);
+ SyncLog.Warn(LogTopic.Ui, "Could not open the help page " + url + ": " +
+ ex.Message);
}
}
diff --git a/CS2MultiplayerMod/Game/JoinMapLoader.cs b/CS2MultiplayerMod/Game/JoinMapLoader.cs
index 078b15a..57fb7f7 100644
--- a/CS2MultiplayerMod/Game/JoinMapLoader.cs
+++ b/CS2MultiplayerMod/Game/JoinMapLoader.cs
@@ -38,12 +38,12 @@ public static bool StageAndLoad(byte[] saveBytes, IModLogger log)
{
if (saveBytes == null || saveBytes.Length == 0)
{
- log.Warn("[MP] Received an empty host world; ignoring.");
+ log.Warn(LogTopic.WorldTransfer, "Received an empty host world; ignoring.");
return false;
}
string dir = SavesDirectory();
- if (dir == null) { log.Warn("[MP] Saves folder not found; cannot load host map."); return false; }
+ if (dir == null) { log.Warn(LogTopic.WorldTransfer, "Saves folder not found; cannot load host map."); return false; }
try
{
@@ -55,8 +55,11 @@ public static bool StageAndLoad(byte[] saveBytes, IModLogger log)
Directory.CreateDirectory(dir);
string path = Path.Combine(dir, TransientName + SaveExtension);
File.WriteAllBytes(path, saveBytes);
- log.Info("[MP] Host world staged at '" + Diagnostics.LogPaths.Redact(path) + "' (" + (saveBytes.Length / 1024) + " KB).");
- log.Info("[MP] Host world received (" + (saveBytes.Length / 1024) + " KB); loading into game...");
+ log.Detail(LogTopic.WorldTransfer, "Host world staged at '" +
+ Diagnostics.LogPaths.Redact(path) + "' (" + (saveBytes.Length / 1024) +
+ " KB).");
+ log.Event(LogTopic.WorldTransfer, "Host world received (" +
+ (saveBytes.Length / 1024) + " KB); loading into game...");
// Claim the load before starting it: the session watcher treats a world swap
// it did not ask for as the player walking out of the session. Claimed here
@@ -67,7 +70,7 @@ public static bool StageAndLoad(byte[] saveBytes, IModLogger log)
}
catch (Exception ex)
{
- log.Error("[MP] Failed to stage host map: " + ex.Message);
+ log.Error(LogTopic.WorldTransfer, "Failed to stage host map: " + ex.Message);
return false;
}
}
@@ -89,17 +92,19 @@ private static bool TryLoad(IModLogger log)
if (metadata != null)
{
GameManager.instance.Load(GameMode.Game, Purpose.LoadGame, metadata);
- log.Info("[MP] Loading host world - joining the session.");
+ log.Event(LogTopic.WorldTransfer, "Loading host world - joining the session.");
return true;
}
- log.Warn("[MP] Host world staged but could not be registered with the save index. " +
- "Run /sync to retry, or load '" + TransientName + "' from Load Game.");
+ log.Warn(LogTopic.WorldTransfer,
+ "Host world staged but could not be registered with the save index. " +
+ "Run /sync to retry, or load '" + TransientName + "' from Load Game.");
return false;
}
catch (Exception ex)
{
- log.Error("[MP] Auto-load failed: " + ex.Message + " - the world is staged as '" + TransientName + "' to load manually.");
+ log.Error(LogTopic.WorldTransfer, "Auto-load failed: " + ex.Message +
+ " - the world is staged as '" + TransientName + "' to load manually.");
return false;
}
}
@@ -136,7 +141,8 @@ private static void RegisterStagedSave(IModLogger log)
{
// Non-fatal: if the engine's watcher later notices the file (e.g. on an
// alt-tab) the lookup can still succeed; otherwise the caller recovers.
- log.Warn("[MP] Could not register the host world with the save index: " + ex.Message);
+ log.Warn(LogTopic.WorldTransfer,
+ "Could not register the host world with the save index: " + ex.Message);
}
}
@@ -175,12 +181,13 @@ public static void DeleteTransient(IModLogger log)
foreach (SaveGameMetadata md in doomed)
{
try { AssetDatabase.user.DeleteAsset(md); removedViaIndex = true; }
- catch (Exception ex) { log.Warn("[MP] Could not remove transient save entry: " + ex.Message); }
+ catch (Exception ex) { log.Warn(LogTopic.WorldTransfer, "Could not remove transient save entry: " + ex.Message); }
}
}
catch (Exception ex)
{
- log.Warn("[MP] Transient save index cleanup failed: " + ex.Message);
+ log.Warn(LogTopic.WorldTransfer, "Transient save index cleanup failed: " +
+ ex.Message);
}
// Belt and braces: if the world was staged but never indexed, remove the raw
@@ -196,11 +203,12 @@ public static void DeleteTransient(IModLogger log)
if (File.Exists(cid)) File.Delete(cid);
if (removedViaIndex || removedFile)
- log.Info("[MP] Removed transient host world (no local copy kept).");
+ log.Detail(LogTopic.WorldTransfer,
+ "Removed transient host world (no local copy kept).");
}
catch (Exception ex)
{
- log.Warn("[MP] Could not delete transient map: " + ex.Message);
+ log.Warn(LogTopic.WorldTransfer, "Could not delete transient map: " + ex.Message);
}
}
diff --git a/CS2MultiplayerMod/Game/MultiplayerService/Checks/DlcCheck.cs b/CS2MultiplayerMod/Game/MultiplayerService/Checks/DlcCheck.cs
index 96d8c4a..b26c9a5 100644
--- a/CS2MultiplayerMod/Game/MultiplayerService/Checks/DlcCheck.cs
+++ b/CS2MultiplayerMod/Game/MultiplayerService/Checks/DlcCheck.cs
@@ -71,8 +71,8 @@ public static string[] OwnedSyncRelevantDlcs(IModLogger log)
}
catch (Exception ex)
{
- log.Warn("[MP] Could not enumerate DLCs (" + ex.Message + "); " +
- "reporting no DLCs, so peers reporting DLC content will be rejected.");
+ log.Warn(LogTopic.Startup, "Could not enumerate DLCs (" + ex.Message + "); " +
+ "reporting no DLCs, so peers reporting DLC content will be rejected.");
return Array.Empty();
}
}
diff --git a/CS2MultiplayerMod/Game/MultiplayerService/Checks/ModsCheck.cs b/CS2MultiplayerMod/Game/MultiplayerService/Checks/ModsCheck.cs
index f2b262a..25c8154 100644
--- a/CS2MultiplayerMod/Game/MultiplayerService/Checks/ModsCheck.cs
+++ b/CS2MultiplayerMod/Game/MultiplayerService/Checks/ModsCheck.cs
@@ -5,6 +5,8 @@
using System.Reflection;
using Colossal.IO.AssetDatabase;
using Colossal.PSI.Common;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Localization;
using Game.Modding;
using Game.SceneFlow;
@@ -350,17 +352,17 @@ private static void LogChange(string[] previous, string[] current)
}
string source = _restartRequired ? "loaded assemblies (restart to clear)" : "active playset";
- Mod.log.Info(current.Length == 0
- ? "[MP] No other mods detected - multiplayer is available."
- : "[MP] Other mods block multiplayer, from " + source + ": " + string.Join(", ", current));
+ SyncLog.Detail(LogTopic.Startup,
+ current.Length == 0 ? "No other mods detected - multiplayer is available." : "Other mods block multiplayer, from " +
+ source + ": " + string.Join(", ", current));
}
private static void WarnOnce(string source, Exception ex)
{
if (_scanWarned) return;
_scanWarned = true;
- Mod.log.Warn("[MP] Could not read the " + source + " (" + ex.Message +
- "); other mods cannot be detected from it.");
+ SyncLog.Warn(LogTopic.Startup, "Could not read the " + source + " (" + ex.Message +
+ "); other mods cannot be detected from it.");
}
}
}
diff --git a/CS2MultiplayerMod/Game/MultiplayerService/Lifecycle/GameExit.cs b/CS2MultiplayerMod/Game/MultiplayerService/Lifecycle/GameExit.cs
index 2fbdcea..990c534 100644
--- a/CS2MultiplayerMod/Game/MultiplayerService/Lifecycle/GameExit.cs
+++ b/CS2MultiplayerMod/Game/MultiplayerService/Lifecycle/GameExit.cs
@@ -3,6 +3,7 @@
using Colossal.Serialization.Entities;
using Game;
using Game.SceneFlow;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
using CS2MultiplayerMod.Game.Diagnostics;
@@ -179,8 +180,8 @@ private void QueueClientMainMenu(string reason)
_clientMainMenuAttempts = 0;
_clientMainMenuNextAttemptMs = NowMs;
_clientMainMenuFailed = false;
- _log.Info("[MP] Client session ended (" + reason + "); returning to the main menu.");
- FlightRecorder.Note("client world exit queued: " + reason);
+ _log.Event(LogTopic.Session, "Client session ended (" + reason +
+ "); returning to the main menu.");
}
///
@@ -209,8 +210,8 @@ internal void RetryClientWorldExit()
_clientMainMenuAttempts = 0;
_clientMainMenuNextAttemptMs = NowMs;
_clientMainMenuFailed = false;
- _log.Info("[MP] Retrying the return from the disconnected host world to the main menu.");
- FlightRecorder.Note("client world exit retry requested");
+ _log.Detail(LogTopic.Session,
+ "Retrying the return from the disconnected host world to the main menu.");
}
private void ClearClientExitNotice()
@@ -311,16 +312,17 @@ private void ScheduleClientMainMenuRetry(string failure)
// beneath an open world. The UI stays blocking and offers an explicit retry.
_clientMainMenuPending = false;
_clientMainMenuFailed = true;
- _log.Error("[MP] Could not close the disconnected client's host world after " +
- ClientMainMenuMaxAttempts + " attempts: " + failure);
- FlightRecorder.Note("client world exit failed: " + failure);
+ _log.Error(LogTopic.Session,
+ "Could not close the disconnected client's host world after " +
+ ClientMainMenuMaxAttempts + " attempts: " + failure);
return;
}
_clientMainMenuNextAttemptMs = NowMs + ClientMainMenuRetryDelayMs;
- _log.Warn("[MP] Returning the disconnected client to the main menu failed (attempt " +
- _clientMainMenuAttempts + "/" + ClientMainMenuMaxAttempts + "): " +
- failure + ". Retrying.");
+ _log.Warn(LogTopic.Session,
+ "Returning the disconnected client to the main menu failed (attempt " +
+ _clientMainMenuAttempts + "/" + ClientMainMenuMaxAttempts + "): " + failure +
+ ". Retrying.");
}
private void ForgetClientHostWorld()
@@ -344,9 +346,8 @@ private void LeaveSharedSession(string logReason, string hostNotice)
try
{
bool host = _session.Role == SessionRole.Host;
- _log.Info("[MP] " + logReason + " - " +
- (host ? "closing the session for every player." : "disconnecting from the host."));
- FlightRecorder.Note("session end: " + logReason + " role=" + _session.Role);
+ _log.Event(LogTopic.Session, logReason + " - " +
+ (host ? "closing the session for every player." : "disconnecting from the host."));
// The world is being torn down or replaced: restoring the simulation speed
// into it would write to a world that is on its way out.
@@ -359,8 +360,8 @@ private void LeaveSharedSession(string logReason, string hostNotice)
}
catch (Exception ex)
{
- _log.Error("[MP] Failed to close the session while leaving the game: " + ex.Message);
- FlightRecorder.NoteException("session-leave", ex);
+ _log.Error(LogTopic.Session, "Failed to close the session while leaving the game.",
+ ex);
}
finally
{
diff --git a/CS2MultiplayerMod/Game/MultiplayerService/Lifecycle/JoinApproval.cs b/CS2MultiplayerMod/Game/MultiplayerService/Lifecycle/JoinApproval.cs
index 614a110..3e3bdfc 100644
--- a/CS2MultiplayerMod/Game/MultiplayerService/Lifecycle/JoinApproval.cs
+++ b/CS2MultiplayerMod/Game/MultiplayerService/Lifecycle/JoinApproval.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
namespace CS2MultiplayerMod.Game
@@ -64,7 +65,8 @@ private void RefreshPendingJoinsJson()
public void ApproveJoinFromUi(int playerId)
{
if (!_session.ApproveJoin(playerId, NowMs))
- _log.Warn("[MP] Ignored approve for unknown pending join #" + playerId + ".");
+ _log.Warn(LogTopic.Session, "Ignored approve for unknown pending join #" + playerId +
+ ".");
RefreshPendingJoinsJson();
}
@@ -72,7 +74,8 @@ public void ApproveJoinFromUi(int playerId)
public void DeclineJoinFromUi(int playerId)
{
if (!_session.DeclineJoin(playerId))
- _log.Warn("[MP] Ignored decline for unknown pending join #" + playerId + ".");
+ _log.Warn(LogTopic.Session, "Ignored decline for unknown pending join #" + playerId +
+ ".");
RefreshPendingJoinsJson();
}
}
diff --git a/CS2MultiplayerMod/Game/MultiplayerService/Lifecycle/Phase.cs b/CS2MultiplayerMod/Game/MultiplayerService/Lifecycle/Phase.cs
index d5aacdc..31c3c5d 100644
--- a/CS2MultiplayerMod/Game/MultiplayerService/Lifecycle/Phase.cs
+++ b/CS2MultiplayerMod/Game/MultiplayerService/Lifecycle/Phase.cs
@@ -1,8 +1,10 @@
using System;
using Game.SceneFlow;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Session;
using CS2MultiplayerMod.Core.Protocol.Messages;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Localization;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using Unity.Entities;
@@ -79,7 +81,8 @@ private void PumpWorldPhase()
if (_sawLoading)
{
SetPhase(ClientWorldPhase.WaitingForResume);
- _log.Info("[MP] Host world loaded - waiting for the epoch resume barrier.");
+ _log.Detail(LogTopic.Session,
+ "Host world loaded - waiting for the epoch resume barrier.");
_session.SendWorldSyncStage(_activeWorldSyncEpoch, WorldSyncStage.Loaded);
return;
}
@@ -91,8 +94,9 @@ private void PumpWorldPhase()
SetPhase(ClientWorldPhase.WaitingForMap);
if (_worldSyncBarrierActive && _activeWorldSyncEpoch > 0)
_session.SendWorldSyncStage(_activeWorldSyncEpoch, WorldSyncStage.Failed);
- _log.Warn("[MP] Host world never started loading. Still connected - use /sync to " +
- "request it again, or load '" + JoinMapLoader.TransientName + "' manually.");
+ _log.Warn(LogTopic.Session,
+ "Host world never started loading. Still connected - use /sync to " +
+ "request it again, or load '" + JoinMapLoader.TransientName + "' manually.");
}
}
@@ -102,8 +106,7 @@ private void SetPhase(ClientWorldPhase phase)
_phase = phase;
_phaseChangedMs = NowMs;
if (phase != ClientWorldPhase.LoadingMap) _sawLoading = false;
- _log.Info("[MP] World phase: " + phase);
- Diagnostics.FlightRecorder.Note("phase " + phase);
+ _log.Detail(LogTopic.Session, "World phase: " + phase);
// A joined client plays in the host's (transient) world: autosaving it would
// pile copies of the host's city into the local Saves folder and can collide
@@ -122,11 +125,12 @@ private void SuppressAutosave()
if (_autosaveWasEnabled) general.autoSave = false;
_autosaveSuppressed = true;
if (_autosaveWasEnabled)
- _log.Info("[MP] Autosave paused while playing in the host's session; it is restored on disconnect.");
+ _log.Detail(LogTopic.Session,
+ "Autosave paused while playing in the host's session; it is restored on disconnect.");
}
catch (Exception ex)
{
- _log.Warn("[MP] Could not pause autosave: " + ex.Message);
+ _log.Warn(LogTopic.Session, "Could not pause autosave: " + ex.Message);
}
}
@@ -138,11 +142,12 @@ private void RestoreAutosave()
try
{
GameManager.instance.settings.general.autoSave = true;
- _log.Info("[MP] Autosave restored.");
+ _log.Detail(LogTopic.Session, "Autosave restored.");
}
catch (Exception ex)
{
- _log.Warn("[MP] Could not restore autosave - re-enable it in the game options: " + ex.Message);
+ _log.Warn(LogTopic.Session,
+ "Could not restore autosave - re-enable it in the game options: " + ex.Message);
}
}
@@ -159,59 +164,54 @@ private bool RefuseForOtherMods(string action)
if (Mod.Setting != null && Mod.Setting.IgnoreModCompatibilityChecks)
{
- _log.Warn("[MP] Ignoring the other-mod compatibility check while trying to " +
- action + " at the player's own risk: " + detail + ".");
+ _log.Warn(LogTopic.Session,
+ "Ignoring the other-mod compatibility check while trying to " + action +
+ " at the player's own risk: " + detail + ".");
return false;
}
_lastFault = detail;
- _log.Warn("[MP] Cannot " + action + ": " + detail +
- ". Multiplayer runs only with CS2 Multiplayer Mod alone - disable the " +
- "others in the active playset and restart the game.");
+ _log.Warn(LogTopic.Session, "Cannot " + action + ": " + detail +
+ ". Multiplayer runs only with CS2 Multiplayer Mod alone - disable the " +
+ "others in the active playset and restart the game.");
return true;
}
public void HostFromSettings(Setting settings)
{
- if (!ModEnabled) { _log.Warn("Cannot host: the mod is disabled in settings."); return; }
- if (_session.Role != SessionRole.None) { _log.Warn("Cannot host: a session is already active."); return; }
+ if (!ModEnabled) { _log.Warn(LogTopic.Session, "Cannot host: the mod is disabled in settings."); return; }
+ if (_session.Role != SessionRole.None) { _log.Warn(LogTopic.Session, "Cannot host: a session is already active."); return; }
if (RefuseForOtherMods("host")) return;
_disconnectConfirmationRequested = false;
ClearClientExitNotice();
ResetCommandDiagnostics();
_lastFault = null;
var config = BuildConfig(settings, hosting: true);
- _log.Info("[MP] Host requested: transport=" + config.Transport +
- (config.Transport == TransportMode.SteamRelay
- ? " joinCode=" + RelayProvider.LocalJoinCode
- : " port=" + config.Port) +
- " lanOnly=" + config.LanOnly +
- " password=" + (config.Password.Length > 0 ? "SET" : "NONE") +
- " maxPlayers=" + config.MaxPlayers +
- " name='" + config.PlayerName + "'" +
- " mod=" + config.ModVersion + " game=" + config.GameVersion +
- " dlcs=[" + string.Join(", ", config.DlcList) + "]");
+ _log.Event(LogTopic.Session, "Host requested: transport=" + config.Transport +
+ (config.Transport == TransportMode.SteamRelay ? " joinCode=" + RelayProvider.LocalJoinCode : " port=" + config.Port) +
+ " lanOnly=" + config.LanOnly + " password=" +
+ (config.Password.Length > 0 ? "SET" : "NONE") + " maxPlayers=" + config.MaxPlayers +
+ " name='" + config.PlayerName + "'" + " mod=" + config.ModVersion + " game=" +
+ config.GameVersion + " dlcs=[" + string.Join(", ", config.DlcList) + "]");
_session.StartHost(config);
}
public void JoinFromSettings(Setting settings)
{
- if (!ModEnabled) { _log.Warn("Cannot join: the mod is disabled in settings."); return; }
- if (_session.Role != SessionRole.None) { _log.Warn("Cannot join: a session is already active."); return; }
+ if (!ModEnabled) { _log.Warn(LogTopic.Session, "Cannot join: the mod is disabled in settings."); return; }
+ if (_session.Role != SessionRole.None) { _log.Warn(LogTopic.Session, "Cannot join: a session is already active."); return; }
if (RefuseForOtherMods("join")) return;
_disconnectConfirmationRequested = false;
ClearClientExitNotice();
ResetCommandDiagnostics();
_lastFault = null;
var config = BuildConfig(settings, hosting: false);
- _log.Info("[MP] Join requested: transport=" + config.Transport +
- " target=" + (config.Transport == TransportMode.SteamRelay
- ? config.JoinCode
- : config.HostAddress + ":" + config.Port) +
- " password=" + (config.Password.Length > 0 ? "SET" : "NONE") +
- " name='" + config.PlayerName + "'" +
- " mod=" + config.ModVersion + " game=" + config.GameVersion +
- " dlcs=[" + string.Join(", ", config.DlcList) + "]");
+ _log.Event(LogTopic.Session, "Join requested: transport=" + config.Transport +
+ " target=" +
+ (config.Transport == TransportMode.SteamRelay ? config.JoinCode : config.HostAddress + ":" + config.Port) +
+ " password=" + (config.Password.Length > 0 ? "SET" : "NONE") + " name='" +
+ config.PlayerName + "'" + " mod=" + config.ModVersion + " game=" +
+ config.GameVersion + " dlcs=[" + string.Join(", ", config.DlcList) + "]");
SetPhase(ClientWorldPhase.Connecting);
_session.Join(config);
}
@@ -226,10 +226,8 @@ public void RequestDisconnect()
if (_session.Role == SessionRole.None) return;
if (_disconnectConfirmationRequested) return;
_disconnectConfirmationRequested = true;
- _log.Info("[MP] Waiting for confirmation before " +
- (_session.Role == SessionRole.Host
- ? "closing the hosted session."
- : "disconnecting from the session."));
+ _log.Detail(LogTopic.Session, "Waiting for confirmation before " +
+ (_session.Role == SessionRole.Host ? "closing the hosted session." : "disconnecting from the session."));
}
public void CancelDisconnectRequest()
@@ -307,8 +305,9 @@ private MultiplayerConfig BuildConfig(Setting settings, bool hosting)
// thinks they configured is exactly the kind of failure nobody can debug.
// Relay sessions carry no port at all, so there is nothing to warn about.
if (!relay)
- _log.Warn("[MP] Invalid " + (hosting ? "host" : "join") + " port '" + portText +
- "' - using default " + DefaultPort + " instead. Enter a number from 1 to 65535.");
+ _log.Warn(LogTopic.Session, "Invalid " + (hosting ? "host" : "join") + " port '" +
+ portText + "' - using default " + DefaultPort +
+ " instead. Enter a number from 1 to 65535.");
port = DefaultPort;
}
@@ -316,8 +315,8 @@ private MultiplayerConfig BuildConfig(Setting settings, bool hosting)
if (!int.TryParse((settings.MaxPlayers ?? "").Trim(), out maxPlayers) || maxPlayers < 2 || maxPlayers > 32)
{
if (hosting)
- _log.Warn("[MP] Invalid max players '" + settings.MaxPlayers +
- "' - using default " + DefaultMaxPlayers + " instead (allowed: 2-32).");
+ _log.Warn(LogTopic.Session, "Invalid max players '" + settings.MaxPlayers +
+ "' - using default " + DefaultMaxPlayers + " instead (allowed: 2-32).");
maxPlayers = DefaultMaxPlayers;
}
diff --git a/CS2MultiplayerMod/Game/MultiplayerService/MultiplayerService.cs b/CS2MultiplayerMod/Game/MultiplayerService/MultiplayerService.cs
index b0a5f28..2c216ad 100644
--- a/CS2MultiplayerMod/Game/MultiplayerService/MultiplayerService.cs
+++ b/CS2MultiplayerMod/Game/MultiplayerService/MultiplayerService.cs
@@ -8,6 +8,7 @@
using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
namespace CS2MultiplayerMod.Game
{
@@ -231,7 +232,7 @@ public void SendMapPing(string label)
try { _session.SendCommand(0, Sync.Commands.MapPingCommand.Id, command.Encode()); }
catch (Exception ex)
{
- _log.Warn("[MP] Ping not sent: " + ex.Message);
+ _log.Warn(LogTopic.Session, "Ping not sent: " + ex.Message);
return;
}
@@ -333,14 +334,10 @@ private void RecordAppliedCommand(SimulationCommandMessage command)
_lastLoggedCommandId = command.CommandId;
_lastCommandLogMs = now;
_lastCommandLoggedTotal = _appliedCommandTotal;
- Diagnostics.FlightRecorder.Note(
- "command-apply name=" + CommandName(command.CommandId) +
- " id=" + command.CommandId +
- " origin=" + command.OriginPlayerId +
- " tick=" + command.Tick +
- " bytes=" + _lastAppliedCommandBytes +
- " sinceLast=" + commandsSinceLog +
- " total=" + _appliedCommandTotal);
+ SyncLog.Trace(LogTopic.Session, "command-apply name=" + CommandName(command.CommandId) +
+ " id=" + command.CommandId + " origin=" + command.OriginPlayerId + " tick=" +
+ command.Tick + " bytes=" + _lastAppliedCommandBytes + " sinceLast=" +
+ commandsSinceLog + " total=" + _appliedCommandTotal);
}
private void ResetCommandDiagnostics()
@@ -418,7 +415,7 @@ private void PumpMapReRequest()
}
if (_session.WorldSyncSuspended) return;
_mapReRequestPending = false;
- Diagnostics.SyncLog.ProdWarn(
+ Diagnostics.SyncLog.Warn(LogTopic.Session,
"World sync: asking the host to stream this city again - the previous handover " +
"resumed before the snapshot had been installed.");
_session.RequestWorldSync("resume arrived before the snapshot finished loading");
@@ -500,17 +497,15 @@ private void RunAutomaticWorldRecovery(Diagnostics.ResyncReport report)
now - _lastAutoRecoveryMs < AutoRecoveryCooldownMs;
if (coolingDown)
{
- Diagnostics.SyncLog.ProdWarn(
+ Diagnostics.SyncLog.Warn(LogTopic.Session,
"World sync: skipped a second automatic reload within " +
- (AutoRecoveryCooldownMs / 1000) + " s (" + report.Reason +
+ (AutoRecoveryCooldownMs / 1000) + " s (" + report.Summary() +
"). The edit behind it is left un-synced; use /sync if the city looks out of step.");
- Diagnostics.FlightRecorder.Note("auto recovery suppressed (cooldown): " + report.Summary());
return;
}
_lastAutoRecoveryMs = now;
- Diagnostics.SyncLog.Prod("World sync: reloading this city from the host now (" +
- report.Reason + ").");
- Diagnostics.FlightRecorder.Note("resync requested: " + report.Summary());
+ Diagnostics.SyncLog.Event(LogTopic.Session,
+ "World sync: reloading this city from the host now (" + report.Summary() + ").");
_session.RequestWorldSync(report.Reason);
}
@@ -544,14 +539,16 @@ private void RunAutomaticWorldRecovery(Diagnostics.ResyncReport report)
public void KickPlayerFromUi(int playerId)
{
if (!_session.KickPlayer(playerId))
- _log.Warn("[MP] Ignored kick request for unavailable player #" + playerId + ".");
+ _log.Warn(LogTopic.Session, "Ignored kick request for unavailable player #" +
+ playerId + ".");
}
/// Remove a client and block its address for the current hosting session.
public void BanPlayerFromUi(int playerId)
{
if (!_session.BanPlayer(playerId))
- _log.Warn("[MP] Ignored ban request for unavailable player #" + playerId + ".");
+ _log.Warn(LogTopic.Session, "Ignored ban request for unavailable player #" +
+ playerId + ".");
}
///
@@ -690,8 +687,9 @@ public long ResyncIntervalMs
if (!_warnedResyncMinutes && minutes.ToString() != raw)
{
_warnedResyncMinutes = true;
- _log.Warn("[MP] World re-sync interval '" + raw + "' is not a whole number of minutes >= " +
- MinResyncMinutes + "; using " + minutes + " minutes instead.");
+ _log.Warn(LogTopic.Session, "World re-sync interval '" + raw +
+ "' is not a whole number of minutes >= " + MinResyncMinutes + "; using " +
+ minutes + " minutes instead.");
}
return (long)minutes * 60000L;
}
@@ -713,7 +711,7 @@ private sealed class ServiceObserver : SessionObserver
public override void OnStatusChanged(SessionStatus status, string detail)
{
- _log.Info("[MP] " + status + ": " + detail);
+ _log.Detail(LogTopic.Session, status + ": " + detail);
// Players commonly attach the flight log to a public support post. Keep
// the target IP/hostname in the private main log, but retain the port and
// transport mode needed to diagnose a connection-stage failure here.
@@ -721,8 +719,8 @@ public override void OnStatusChanged(SessionStatus status, string detail)
? "target=redacted port=" + _service._session.Port +
" encryption=" + _service._session.EncryptionActive
: detail;
- Diagnostics.FlightRecorder.Note("status " + status +
- " role=" + _service._session.Role +
+ SyncLog.Trace(LogTopic.Session, "status " + status + " role=" +
+ _service._session.Role +
(string.IsNullOrEmpty(flightDetail) ? "" : " detail=" + flightDetail));
if (status == SessionStatus.Connected &&
_service._session.Role == SessionRole.Client &&
@@ -794,22 +792,20 @@ public override void OnStatusChanged(SessionStatus status, string detail)
public override void OnPeerJoined(Peer peer)
{
- _log.Info("[MP] Peer joined: " + peer);
- Diagnostics.FlightRecorder.Note("peer joined #" + peer.PlayerId);
+ _log.Event(LogTopic.Session, "Peer joined: " + peer);
_service.RefreshPlayerListJson();
// WorldResyncSystem observes joins too and pushes the live world to the newcomer.
}
public override void OnPeerLeft(Peer peer, string reason)
{
- _log.Info("[MP] Peer left: " + peer + " (" + reason + ")");
- Diagnostics.FlightRecorder.Note("peer left #" + peer.PlayerId + " (" + reason + ")");
+ _log.Event(LogTopic.Session, "Peer left: " + peer + " (" + reason + ")");
RemotePlayer removed;
_service._remotePlayers.TryRemove(peer.PlayerId, out removed);
_service.RefreshPlayerListJson();
}
public override void OnChatReceived(string sender, string text)
{
- _log.Info("[MP] " + (sender ?? "system") + ": " + text);
+ _log.Detail(LogTopic.Session, (sender ?? "system") + ": " + text);
_service.AppendChatEntry(sender, text);
}
public override void OnCommandReceived(SimulationCommandMessage command)
@@ -829,7 +825,7 @@ public override void OnWorldSyncControl(WorldSyncStage stage, long epoch,
public override void OnError(string message)
{
_service._lastFault = message;
- _log.Error("[MP] " + message);
+ _log.Error(LogTopic.Session, message);
}
}
}
diff --git a/CS2MultiplayerMod/Game/MultiplayerService/WorldTransfer/ClientWorldSave.cs b/CS2MultiplayerMod/Game/MultiplayerService/WorldTransfer/ClientWorldSave.cs
index d544e51..3f99d28 100644
--- a/CS2MultiplayerMod/Game/MultiplayerService/WorldTransfer/ClientWorldSave.cs
+++ b/CS2MultiplayerMod/Game/MultiplayerService/WorldTransfer/ClientWorldSave.cs
@@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Colossal;
using Colossal.IO.AssetDatabase;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
using Game;
using Game.Assets;
@@ -103,14 +104,15 @@ public void SaveClientWorldFromUi(string requestedName)
SaveHelpers.kSaveLoadTaskName,
() => SaveClientWorld(world, saveName),
1);
- _log.Info("[MP] Saving a permanent local copy of the client world as '" +
- saveName + "'.");
+ _log.Detail(LogTopic.WorldTransfer,
+ "Saving a permanent local copy of the client world as '" + saveName + "'.");
}
catch (Exception ex)
{
_clientWorldSaveTask = null;
_clientWorldSaveStatus = SaveStatusFailed;
- _log.Error("[MP] Could not start the local client-world save: " + ex.Message);
+ _log.Error(LogTopic.WorldTransfer, "Could not start the local client-world save: " +
+ ex.Message);
}
}
@@ -186,8 +188,8 @@ private async Task SaveClientWorld(World world, string saveName)
UnityEngine.Object.Destroy(preview);
preview = null;
}
- _log.Warn("[MP] Could not capture a preview for the local world copy: " +
- ex.Message);
+ _log.Warn(LogTopic.WorldTransfer,
+ "Could not capture a preview for the local world copy: " + ex.Message);
}
bool completed = preview != null
@@ -215,7 +217,8 @@ private void PumpClientWorldSave()
if (task.IsCanceled)
{
_clientWorldSaveStatus = SaveStatusFailed;
- _log.Warn("[MP] Saving the local client-world copy was canceled.");
+ _log.Warn(LogTopic.WorldTransfer,
+ "Saving the local client-world copy was canceled.");
}
else if (task.IsFaulted)
{
@@ -223,14 +226,14 @@ private void PumpClientWorldSave()
? task.Exception.GetBaseException()
: null;
_clientWorldSaveStatus = _clientWorldSaveFailureStatus ?? SaveStatusFailed;
- _log.Error("[MP] Saving the local client-world copy failed" +
- (failure != null ? ": " + failure.Message : "."));
+ _log.Error(LogTopic.WorldTransfer, "Saving the local client-world copy failed" +
+ (failure != null ? ": " + failure.Message : "."));
}
else
{
_clientWorldSaveStatus = SaveStatusSaved;
- _log.Info("[MP] Permanent local client-world copy saved as '" +
- _clientWorldSaveName + "'.");
+ _log.Detail(LogTopic.WorldTransfer, "Permanent local client-world copy saved as '" +
+ _clientWorldSaveName + "'.");
}
_clientWorldSaveFailureStatus = null;
diff --git a/CS2MultiplayerMod/Game/MultiplayerService/WorldTransfer/WorldSync.cs b/CS2MultiplayerMod/Game/MultiplayerService/WorldTransfer/WorldSync.cs
index 687bbe6..cd6b69f 100644
--- a/CS2MultiplayerMod/Game/MultiplayerService/WorldTransfer/WorldSync.cs
+++ b/CS2MultiplayerMod/Game/MultiplayerService/WorldTransfer/WorldSync.cs
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Systems.Net;
using Game.Simulation;
@@ -86,10 +88,8 @@ internal bool TryBeginHostWorldSync(long epoch, out float resumeSpeed)
Diagnostics.ResyncArbiter.Reset();
MaintainWorldSyncBarrier();
resumeSpeed = _worldSyncResumeSpeed;
- _log.Info("[MP] World sync epoch " + epoch +
- " entered the local quiescence barrier (resume speed " + resumeSpeed + ").");
- Diagnostics.FlightRecorder.Note("world-sync begin epoch=" + epoch +
- " resumeSpeed=" + resumeSpeed);
+ _log.Detail(LogTopic.WorldTransfer, "World sync epoch " + epoch +
+ " entered the local quiescence barrier (resume speed " + resumeSpeed + ").");
return true;
}
@@ -98,8 +98,8 @@ internal void CompleteHostWorldSync(long epoch, float resumeSpeed)
if (!_worldSyncBarrierActive || epoch != _activeWorldSyncEpoch) return;
_worldSyncResumeSpeed = SanitizeSpeed(resumeSpeed);
ResetWorldSyncState(restoreSpeed: true);
- _log.Info("[MP] World sync epoch " + epoch + " completed; gameplay resumed.");
- Diagnostics.FlightRecorder.Note("world-sync resume epoch=" + epoch);
+ _log.Event(LogTopic.WorldTransfer, "World sync epoch " + epoch +
+ " completed; gameplay resumed.");
}
internal void AbortHostWorldSync(long epoch, float resumeSpeed)
@@ -107,9 +107,8 @@ internal void AbortHostWorldSync(long epoch, float resumeSpeed)
if (!_worldSyncBarrierActive || epoch != _activeWorldSyncEpoch) return;
_worldSyncResumeSpeed = SanitizeSpeed(resumeSpeed);
ResetWorldSyncState(restoreSpeed: true);
- _log.Warn("[MP] World sync epoch " + epoch +
- " aborted before a snapshot was installed; previous world resumed.");
- Diagnostics.FlightRecorder.Note("world-sync abort epoch=" + epoch);
+ _log.Warn(LogTopic.WorldTransfer, "World sync epoch " + epoch +
+ " aborted before a snapshot was installed; previous world resumed.");
}
private void HandleWorldSyncControl(WorldSyncStage stage, long epoch, float resumeSpeed)
@@ -131,10 +130,8 @@ private void HandleWorldSyncControl(WorldSyncStage stage, long epoch, float resu
SyncInbox.DrainAll();
Diagnostics.ResyncArbiter.Reset();
SetPhase(ClientWorldPhase.WaitingForMap);
- _log.Info("[MP] World sync epoch " + epoch +
- " began; local gameplay is paused while native transactions drain.");
- Diagnostics.FlightRecorder.Note(
- "world-sync client draining native work epoch=" + epoch);
+ _log.Detail(LogTopic.WorldTransfer, "World sync epoch " + epoch +
+ " began; local gameplay is paused while native transactions drain.");
}
MaintainWorldSyncBarrier();
if (_clientQuiescedEpoch == epoch)
@@ -149,7 +146,7 @@ private void HandleWorldSyncControl(WorldSyncStage stage, long epoch, float resu
_worldSyncResumeSpeed = SanitizeSpeed(resumeSpeed);
if (_phase != ClientWorldPhase.WaitingForResume)
{
- Diagnostics.SyncLog.ProdError(
+ Diagnostics.SyncLog.Error(LogTopic.WorldTransfer,
"World sync: the host finished epoch " + epoch +
" before this city had installed its snapshot. Asking for the world again.");
ResetWorldSyncState(restoreSpeed: false);
@@ -170,9 +167,8 @@ private void HandleWorldSyncControl(WorldSyncStage stage, long epoch, float resu
// The player watched the world reload and the simulation stop; say plainly that
// it is over, rather than leaving them to infer it from the clock moving again.
_session.NotifyChat(null, "World sync complete - your city matches the host's.");
- _log.Info("[MP] World sync epoch " + epoch +
- " resumed after the authoritative snapshot was installed.");
- Diagnostics.FlightRecorder.Note("world-sync client resumed epoch=" + epoch);
+ _log.Event(LogTopic.WorldTransfer, "World sync epoch " + epoch +
+ " resumed after the authoritative snapshot was installed.");
return;
}
@@ -182,7 +178,7 @@ private void HandleWorldSyncControl(WorldSyncStage stage, long epoch, float resu
_worldSyncResumeSpeed = SanitizeSpeed(resumeSpeed);
ResetWorldSyncState(restoreSpeed: canResumeOldWorld);
SetPhase(canResumeOldWorld ? ClientWorldPhase.InSession : ClientWorldPhase.WaitingForMap);
- _log.Warn("[MP] Host aborted world-sync epoch " + epoch + ".");
+ _log.Warn(LogTopic.WorldTransfer, "Host aborted world-sync epoch " + epoch + ".");
}
}
@@ -207,7 +203,8 @@ private void MaintainWorldSyncBarrier()
{
// Worlds are replaced between UI frames. A stale World reference is expected for
// that one boundary frame; the next MultiplayerSystem supplies the new instance.
- Mod.Verbose("[MP] Could not enforce world-sync pause on this frame: " + ex.Message);
+ SyncLog.Warn(LogTopic.WorldTransfer,
+ "Could not enforce world-sync pause on this frame: " + ex.Message);
}
}
@@ -248,9 +245,8 @@ private void PumpClientWorldSyncQuiescence()
_clientQuiescencePending = false;
_clientQuiescedEpoch = epoch;
_session.SendWorldSyncStage(epoch, WorldSyncStage.Quiesced);
- _log.Info("[MP] World sync epoch " + epoch +
- ": local native transactions drained; quiescence acknowledged.");
- Diagnostics.FlightRecorder.Note("world-sync client quiesced epoch=" + epoch);
+ _log.Detail(LogTopic.WorldTransfer, "World sync epoch " + epoch +
+ ": local native transactions drained; quiescence acknowledged.");
}
private float ReadSimulationSpeed()
@@ -289,7 +285,8 @@ private void ResetWorldSyncState(bool restoreSpeed)
}
catch (Exception ex)
{
- Mod.Verbose("[MP] Could not restore simulation speed after world sync: " + ex.Message);
+ SyncLog.Warn(LogTopic.WorldTransfer,
+ "Could not restore simulation speed after world sync: " + ex.Message);
}
}
diff --git a/CS2MultiplayerMod/Game/MultiplayerService/WorldTransfer/WorldTransfer.cs b/CS2MultiplayerMod/Game/MultiplayerService/WorldTransfer/WorldTransfer.cs
index 8b58088..2ab38d3 100644
--- a/CS2MultiplayerMod/Game/MultiplayerService/WorldTransfer/WorldTransfer.cs
+++ b/CS2MultiplayerMod/Game/MultiplayerService/WorldTransfer/WorldTransfer.cs
@@ -3,9 +3,11 @@
using System.Threading.Tasks;
using Colossal;
using Colossal.IO.AssetDatabase;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using Game;
using Game.Assets;
using Game.PSI.PdxSdk;
@@ -80,8 +82,8 @@ private async Task SaveWorldSnapshot(World world)
throw new InvalidOperationException("The game did not create the world snapshot package.");
byte[] data = ReadWorldSnapshotPackage(package);
- _log.Info("[MP] Prepared isolated recovery snapshot '" +
- WorldSnapshotFileName + "' (" + (data.Length / 1024) + " KB).");
+ _log.Detail(LogTopic.WorldTransfer, "Prepared isolated recovery snapshot '" +
+ WorldSnapshotFileName + "' (" + (data.Length / 1024) + " KB).");
return data;
}
finally
@@ -135,18 +137,18 @@ internal void StreamWorldSnapshot(ConnectionId target, long epoch, byte[] data,
if (_session.Role != SessionRole.Host || target.IsNone || data == null || epoch <= 0)
return;
_session.SendBlobTo(target, MapChannel, epoch, data);
- _log.Info("[MP] Queued recovery snapshot '" + (saveName ?? "") + "' (" +
- (data.Length / 1024) + " KB) for " + DescribeWorldTarget(target) +
- " in epoch " + epoch + ".");
+ _log.Detail(LogTopic.WorldTransfer, "Queued recovery snapshot '" +
+ (saveName ?? "") + "' (" + (data.Length / 1024) + " KB) for " +
+ DescribeWorldTarget(target) + " in epoch " + epoch + ".");
}
private void LoadReceivedMap(long transferId, byte[] data)
{
if (!_worldSyncBarrierActive || transferId <= 0 || transferId != _activeWorldSyncEpoch)
{
- _log.Warn("[MP] Ignoring map transfer " + transferId +
- ": active world-sync epoch is " +
- (_worldSyncBarrierActive ? _activeWorldSyncEpoch.ToString() : "none") + ".");
+ _log.Warn(LogTopic.WorldTransfer, "Ignoring map transfer " + transferId +
+ ": active world-sync epoch is " +
+ (_worldSyncBarrierActive ? _activeWorldSyncEpoch.ToString() : "none") + ".");
return;
}
@@ -157,8 +159,9 @@ private void LoadReceivedMap(long transferId, byte[] data)
{
_deferredMapTransferId = transferId;
_deferredMapData = data;
- _log.Info("[MP] World-sync map received while a local copy is saving; " +
- "installation will continue after that save completes.");
+ _log.Detail(LogTopic.WorldTransfer,
+ "World-sync map received while a local copy is saving; " +
+ "installation will continue after that save completes.");
return;
}
@@ -179,11 +182,13 @@ private void PumpDeferredReceivedMap()
transferId <= 0 ||
transferId != _activeWorldSyncEpoch)
{
- _log.Warn("[MP] Discarding a deferred map because its world-sync epoch is no longer active.");
+ _log.Warn(LogTopic.WorldTransfer,
+ "Discarding a deferred map because its world-sync epoch is no longer active.");
return;
}
- _log.Info("[MP] Local world copy finished; installing the deferred host map.");
+ _log.Detail(LogTopic.WorldTransfer,
+ "Local world copy finished; installing the deferred host map.");
InstallReceivedMap(transferId, data);
}
@@ -191,9 +196,8 @@ private void InstallReceivedMap(long transferId, byte[] data)
{
// The completed blob is the causal cut: commands received before it are represented by
// the save, while every later command must survive the ECS world replacement.
- _log.Info("[MP] Map blob delivered to game layer (" +
- (data != null ? data.Length / 1024 : 0) + " KB); staging and loading.");
- Diagnostics.FlightRecorder.Note("world blob received " + (data != null ? data.Length >> 10 : 0) + " KB; reloading world");
+ _log.Event(LogTopic.WorldTransfer, "Map blob delivered to game layer (" +
+ (data != null ? data.Length / 1024 : 0) + " KB); staging and loading.");
// Purge every sync inbox before the reload: queued commands describe the pre-reload
// world and would apply stale edits (or reference vanished entities) on the new one.
Sync.Infrastructure.SyncInbox.DrainAll();
@@ -204,8 +208,10 @@ private void InstallReceivedMap(long transferId, byte[] data)
// Defined, recoverable state instead of a half-connected limbo.
SetPhase(ClientWorldPhase.WaitingForMap);
_session.SendWorldSyncStage(_activeWorldSyncEpoch, WorldSyncStage.Failed);
- _log.Warn("[MP] Could not auto-load the host world. Still connected - use /sync to " +
- "request it again, or load '" + JoinMapLoader.TransientName + "' from Load Game.");
+ _log.Warn(LogTopic.WorldTransfer,
+ "Could not auto-load the host world. Still connected - use /sync to " +
+ "request it again, or load '" + JoinMapLoader.TransientName +
+ "' from Load Game.");
}
else
{
diff --git a/CS2MultiplayerMod/Game/MultiplayerSystem.cs b/CS2MultiplayerMod/Game/MultiplayerSystem.cs
index 5296760..36b642f 100644
--- a/CS2MultiplayerMod/Game/MultiplayerSystem.cs
+++ b/CS2MultiplayerMod/Game/MultiplayerSystem.cs
@@ -1,6 +1,7 @@
using Game;
using Game.SceneFlow;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
using CS2MultiplayerMod.Game.Diagnostics;
@@ -25,7 +26,7 @@ public partial class MultiplayerSystem : GameSystemBase
protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(MultiplayerSystem) + " created.");
+ SyncLog.Detail(LogTopic.Startup, nameof(MultiplayerSystem) + " created.");
// Trend counters for the flight log: live preview Temps and definition
// entities should both hover near zero between edits - either climbing
@@ -53,8 +54,8 @@ protected override void OnGamePreload(Colossal.Serialization.Entities.Purpose pu
}
catch (System.Exception ex)
{
- Mod.log.Error("[MP] Session close on world transition failed: " + ex.Message);
- FlightRecorder.NoteException("world-transition", ex);
+ SyncLog.Error(LogTopic.Startup, "Closing the session on a world transition failed.",
+ ex);
}
}
@@ -67,7 +68,8 @@ protected override void OnUpdate()
{
if (service.Session.Role != SessionRole.None)
{
- Mod.log.Info("[MP] Mod disabled in settings - closing the active session.");
+ SyncLog.Detail(LogTopic.Startup,
+ "Mod disabled in settings - closing the active session.");
service.Disconnect();
}
@@ -97,7 +99,7 @@ protected override void OnUpdate()
///
private void PumpHealth(MultiplayerService service)
{
- if (!FlightRecorder.Enabled) return;
+ if (!SyncLog.IsRecording(LogTopic.Performance)) return;
MultiplayerSession session = service.Session;
long now = service.NowMs;
bool active = session.Role != SessionRole.None ||
@@ -115,7 +117,8 @@ private void PumpHealth(MultiplayerService service)
catch (System.Exception ex)
{
// Diagnostics are never allowed to become the crash they are meant to explain.
- FlightRecorder.NoteException("health-snapshot", ex);
+ SyncLog.Error(LogTopic.Performance, "Could not write the periodic health snapshot.",
+ ex);
}
}
@@ -172,25 +175,17 @@ private void WriteHealth(MultiplayerService service, MultiplayerSession session,
? "none"
: session.IncomingBlobChannel;
- FlightRecorder.Note("health role=" + session.Role +
- " status=" + session.Status +
- " phase=" + service.WorldPhase +
- " gameLoading=" + gameLoading +
- " playerId=" + session.LocalPlayerId +
- " peers=" + peers +
- " pendingPeers=" + pendingPeers +
- " remotePlayers=" + remotePlayers +
- " latencyMS=" + latency +
- " oldestPeerAgeMS=" + oldestPeerAge +
- " entities=" + Value(entities) +
- " temps=" + Value(temps) +
- " defs=" + Value(definitions) +
- " sendKB=" + (session.PendingSendBytes >> 10) +
- " incomingBlob=" + incomingChannel +
- " incomingKB=" + (session.IncomingBlobReceived >> 10) + "/" + (session.IncomingBlobTotal >> 10) +
- " outgoingBlob=" + session.OutgoingBlobActive +
- " outgoingKB=" + (session.OutgoingBlobSent >> 10) + "/" + (session.OutgoingBlobTotal >> 10) +
- " " + service.CommandDiagnosticSnapshot(now) +
+ SyncLog.Trace(LogTopic.Performance, "health role=" + session.Role + " status=" +
+ session.Status + " phase=" + service.WorldPhase + " gameLoading=" + gameLoading +
+ " playerId=" + session.LocalPlayerId + " peers=" + peers + " pendingPeers=" +
+ pendingPeers + " remotePlayers=" + remotePlayers + " latencyMS=" + latency +
+ " oldestPeerAgeMS=" + oldestPeerAge + " entities=" + Value(entities) + " temps=" +
+ Value(temps) + " defs=" + Value(definitions) + " sendKB=" +
+ (session.PendingSendBytes >> 10) + " incomingBlob=" + incomingChannel +
+ " incomingKB=" + (session.IncomingBlobReceived >> 10) + "/" +
+ (session.IncomingBlobTotal >> 10) + " outgoingBlob=" + session.OutgoingBlobActive +
+ " outgoingKB=" + (session.OutgoingBlobSent >> 10) + "/" +
+ (session.OutgoingBlobTotal >> 10) + " " + service.CommandDiagnosticSnapshot(now) +
" " + FlightRecorder.ProcessSnapshot());
}
diff --git a/CS2MultiplayerMod/Game/MultiplayerUISystem.cs b/CS2MultiplayerMod/Game/MultiplayerUISystem.cs
index ae9c294..d05ea82 100644
--- a/CS2MultiplayerMod/Game/MultiplayerUISystem.cs
+++ b/CS2MultiplayerMod/Game/MultiplayerUISystem.cs
@@ -1,7 +1,9 @@
using Colossal.Serialization.Entities;
using Colossal.UI.Binding;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Localization;
using Game;
using Game.SceneFlow;
@@ -57,7 +59,8 @@ protected override void OnCreate()
{
if (_uiModuleReady) return;
_uiModuleReady = true;
- Mod.log.Info("UI module loaded and registered - the main-menu Multiplayer button is available.");
+ SyncLog.Detail(LogTopic.Ui,
+ "UI module loaded and registered - the main-menu Multiplayer button is available.");
}));
// Field values: polled from Setting every UI frame, pushed on change.
@@ -303,7 +306,8 @@ protected override void OnCreate()
if (Mod.Service != null) Mod.Service.RetryClientWorldExit();
}));
- Mod.log.Info(nameof(MultiplayerUISystem) + " created (binding group '" + Group + "').");
+ SyncLog.Detail(LogTopic.Ui, nameof(MultiplayerUISystem) + " created (binding group '" +
+ Group + "').");
}
///
@@ -316,7 +320,7 @@ private void OpenMultiplayerMenuScreen()
MenuUISystem menu = World.GetExistingSystemManaged();
if (menu == null)
{
- Mod.log.Error("Could not open the multiplayer menu screen.");
+ SyncLog.Error(LogTopic.Ui, "Could not open the multiplayer menu screen.");
return;
}
@@ -334,26 +338,29 @@ private void OpenHostWorldScreen(MenuUISystem.MenuScreen screen)
if (Mod.Service == null || Mod.Setting == null) return;
if (!MultiplayerService.ModEnabled)
{
- Mod.log.Warn("Cannot choose a host world: the mod is disabled in settings.");
+ SyncLog.Warn(LogTopic.Ui,
+ "Cannot choose a host world: the mod is disabled in settings.");
return;
}
if (Mod.Service.Session.Role != SessionRole.None)
{
- Mod.log.Warn("Cannot choose a host world: a multiplayer session is already active.");
+ SyncLog.Warn(LogTopic.Ui,
+ "Cannot choose a host world: a multiplayer session is already active.");
return;
}
MenuUISystem menu = World.GetExistingSystemManaged();
if (menu == null)
{
- Mod.log.Error("Could not open the game's world-selection screen.");
+ SyncLog.Error(LogTopic.Ui, "Could not open the game's world-selection screen.");
return;
}
_hostAfterWorldLoad = true;
_hostWorldLoadStarted = false;
menu.activeScreen = screen;
- Mod.log.Info("Host world selection opened through the game's " + screen + " screen.");
+ SyncLog.Detail(LogTopic.Ui, "Host world selection opened through the game's " + screen +
+ " screen.");
}
private void CancelPendingHost()
@@ -362,7 +369,7 @@ private void CancelPendingHost()
_hostAfterWorldLoad = false;
_hostWorldLoadStarted = false;
- Mod.log.Info("Host world selection cancelled.");
+ SyncLog.Detail(LogTopic.Ui, "Host world selection cancelled.");
}
private void StartHostFromSettings()
@@ -380,7 +387,7 @@ protected override void OnGamePreload(Purpose purpose, global::Game.GameMode mod
if (purpose != Purpose.NewGame && purpose != Purpose.LoadGame) return;
_hostWorldLoadStarted = true;
- Mod.log.Info("Selected host world is loading (" + purpose + ").");
+ SyncLog.Detail(LogTopic.Ui, "Selected host world is loading (" + purpose + ").");
}
protected override void OnUpdate()
@@ -398,7 +405,8 @@ protected override void OnUpdate()
// Backstop for the preload callback: UIUpdate normally observes at
// least one loading frame as the selected city enters the game.
_hostWorldLoadStarted = true;
- Mod.log.Info("Selected host world entered the game load pipeline.");
+ SyncLog.Detail(LogTopic.Ui,
+ "Selected host world entered the game load pipeline.");
}
else
{
@@ -419,7 +427,8 @@ protected override void OnUpdate()
{
_hostAfterWorldLoad = false;
_hostWorldLoadStarted = false;
- Mod.log.Info("Host world is ready - starting the multiplayer session.");
+ SyncLog.Detail(LogTopic.Ui,
+ "Host world is ready - starting the multiplayer session.");
StartHostFromSettings();
}
else
@@ -435,7 +444,7 @@ protected override void OnUpdate()
if (UnityEngine.Time.realtimeSinceStartup - _createdAt < UiReadyGraceSeconds) return;
_uiModuleWarned = true;
- Mod.log.Warn(
+ SyncLog.Warn(LogTopic.Ui,
"The multiplayer UI module never reported in - the main-menu button is most likely missing. " +
"Either CS2MultiplayerMod.mjs is not in the mod folder, or another mod's broken UI module " +
"(known offender: Gooee) crashed the game's UI-module load chain before it reached this mod. " +
diff --git a/CS2MultiplayerMod/Game/SteamRelayBootstrap.cs b/CS2MultiplayerMod/Game/SteamRelayBootstrap.cs
index 0ad31d1..6611598 100644
--- a/CS2MultiplayerMod/Game/SteamRelayBootstrap.cs
+++ b/CS2MultiplayerMod/Game/SteamRelayBootstrap.cs
@@ -35,8 +35,9 @@ public static void Register(IModLogger log, string modFolder)
{
if (!HasSteamworks())
{
- log.Info("This copy of the game ships no Steam library (Microsoft Store / Game Pass), " +
- "so multiplayer will use direct connections only.");
+ log.Event(LogTopic.Transport,
+ "This copy of the game ships no Steam library (Microsoft Store / Game Pass), " +
+ "so multiplayer will use direct connections only.");
return;
}
@@ -45,8 +46,9 @@ public static void Register(IModLogger log, string modFolder)
IRelayProvider provider = LoadProvider(modFolder);
if (provider == null)
{
- log.Warn("The Steam relay backend (" + BackendAssembly + ".dll) is not next to the mod, " +
- "so only direct connections are available. Reinstalling the mod restores it.");
+ log.Warn(LogTopic.Transport, "The Steam relay backend (" + BackendAssembly +
+ ".dll) is not next to the mod, " +
+ "so only direct connections are available. Reinstalling the mod restores it.");
return;
}
@@ -56,16 +58,20 @@ public static void Register(IModLogger log, string modFolder)
RelayProvider.Current = provider;
if (reason == null)
- log.Info("Steam relay available; the join code for this machine is " + provider.LocalJoinCode + ".");
+ log.Event(LogTopic.Transport,
+ "Steam relay available; the join code for this machine is " +
+ provider.LocalJoinCode + ".");
else
- log.Info("Steam relay not usable yet (" + reason + "). Hosting can still use a direct connection.");
+ log.Event(LogTopic.Transport, "Steam relay not usable yet (" + reason +
+ "). Hosting can still use a direct connection.");
}
catch (Exception ex)
{
RelayProvider.Current = null;
// Redacted: a file-load fault puts the mod's full path in the message.
- log.Warn("The Steam relay backend did not load (" + Diagnostics.LogPaths.Redact(ex.Message) +
- "); multiplayer will use direct connections only.");
+ log.Warn(LogTopic.Transport, "The Steam relay backend did not load (" +
+ Diagnostics.LogPaths.Redact(ex.Message) +
+ "); multiplayer will use direct connections only.");
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Channels/City/StatisticsStateChannel.cs b/CS2MultiplayerMod/Game/Sync/Channels/City/StatisticsStateChannel.cs
index 585c562..d309aa5 100644
--- a/CS2MultiplayerMod/Game/Sync/Channels/City/StatisticsStateChannel.cs
+++ b/CS2MultiplayerMod/Game/Sync/Channels/City/StatisticsStateChannel.cs
@@ -2,8 +2,9 @@
using Game.Simulation;
using Unity.Entities;
using Unity.Jobs;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
namespace CS2MultiplayerMod.Game.Sync.Channels
{
@@ -112,7 +113,8 @@ private void WarnOnce(string stage, System.Exception ex)
{
if (_warned) return;
_warned = true;
- Mod.log.Warn("[MP] Statistics channel " + stage + " failed (logged once): " + ex.Message);
+ SyncLog.Warn(LogTopic.City, "Statistics channel " + stage + " failed (logged once): " +
+ ex.Message);
}
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Channels/Economy/CompanyStatsStateChannel.cs b/CS2MultiplayerMod/Game/Sync/Channels/Economy/CompanyStatsStateChannel.cs
index 173f708..23d97ec 100644
--- a/CS2MultiplayerMod/Game/Sync/Channels/Economy/CompanyStatsStateChannel.cs
+++ b/CS2MultiplayerMod/Game/Sync/Channels/Economy/CompanyStatsStateChannel.cs
@@ -1,4 +1,6 @@
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Systems;
@@ -40,8 +42,9 @@ public bool Capture(EntityManager entityManager, NetworkWriter writer)
if (!_captureWarned)
{
_captureWarned = true;
- Mod.log.Warn("[MP] CompanyStats: host capture failed; page skipped " +
- "(logged once until world reset): " + ex.Message);
+ SyncLog.Warn(LogTopic.Commercial,
+ "CompanyStats: host capture failed; page skipped " +
+ "(logged once until world reset): " + ex.Message);
}
return false;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Channels/Economy/PropertyRentStateChannel.cs b/CS2MultiplayerMod/Game/Sync/Channels/Economy/PropertyRentStateChannel.cs
index 0a9f211..8463dc2 100644
--- a/CS2MultiplayerMod/Game/Sync/Channels/Economy/PropertyRentStateChannel.cs
+++ b/CS2MultiplayerMod/Game/Sync/Channels/Economy/PropertyRentStateChannel.cs
@@ -1,4 +1,6 @@
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Systems;
@@ -38,8 +40,9 @@ public bool Capture(EntityManager entityManager, NetworkWriter writer)
if (!_captureWarned)
{
_captureWarned = true;
- Mod.log.Warn("[MP] PropertyRent: host capture failed; rent page skipped " +
- "(logged once until world reset): " + ex.Message);
+ SyncLog.Warn(LogTopic.Residential,
+ "PropertyRent: host capture failed; rent page skipped " +
+ "(logged once until world reset): " + ex.Message);
}
return false;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Channels/Economy/ZoneDemandChannel.cs b/CS2MultiplayerMod/Game/Sync/Channels/Economy/ZoneDemandChannel.cs
index 9660de2..c234a2e 100644
--- a/CS2MultiplayerMod/Game/Sync/Channels/Economy/ZoneDemandChannel.cs
+++ b/CS2MultiplayerMod/Game/Sync/Channels/Economy/ZoneDemandChannel.cs
@@ -6,8 +6,10 @@
using Game.Simulation;
using Game.Tools;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
namespace CS2MultiplayerMod.Game.Sync.Channels
@@ -143,8 +145,9 @@ public bool Capture(EntityManager em, NetworkWriter writer)
if (!hostSpawner.Enabled)
{
hostSpawner.Enabled = true;
- Mod.log.Warn("[MP] PopulationHealth: restored the host's disabled vanilla " +
- "HouseholdSpawnSystem after withdrawing household authority.");
+ SyncLog.Warn(LogTopic.City,
+ "PopulationHealth: restored the host's disabled vanilla " +
+ "HouseholdSpawnSystem after withdrawing household authority.");
}
}
}
@@ -179,24 +182,22 @@ public bool Capture(EntityManager em, NetworkWriter writer)
float unemployment = householdData == null ? -1f : householdData.UnemploymentRate;
int workable = householdData == null ? -1 : householdData.WorkableCitizenCount;
int workers = householdData == null ? -1 : householdData.CityWorkerCount;
- Mod.log.Info("[MP] PopulationHealth/30s host: spawner=" +
- (spawner == null ? "missing" : spawner.Enabled ? "enabled" : "DISABLED") +
- ", households=" + households + " (renting=" +
- _rentingHouseholds.CalculateEntityCount() + ", seeking=" +
- _seekingHouseholds.CalculateEntityCount() + "), citizens=" + citizens +
- ", population=" + arrivedPopulation + "/" + populationWithMoveIn +
- " (arrived/withMoveIn), pets=" + pets +
- ", residentialProperties=" + residentialProperties +
- " (onMarket=" + _residentialOnMarket.CalculateEntityCount() + ")" +
- ", freeUnits=" + freeUnits.x + "/" + freeUnits.y + "/" + freeUnits.z +
- " of " + totalUnits.x + "/" + totalUnits.y + "/" + totalUnits.z +
- ", householdDemand=" + residential.householdDemand +
- ", buildingDemand=" + residentialDemand.x + "/" +
- residentialDemand.y + "/" + residentialDemand.z +
- ", unemployment=" + unemployment + "% (workable=" + workable +
- ", workers=" + workers + ")" +
- ", outsideConnections=" +
- _citizenOutsideConnections.CalculateEntityCount() + ".");
+ SyncLog.Detail(LogTopic.City, "PopulationHealth/30s host: spawner=" +
+ (spawner == null ? "missing" : spawner.Enabled ? "enabled" : "DISABLED") +
+ ", households=" + households + " (renting=" +
+ _rentingHouseholds.CalculateEntityCount() + ", seeking=" +
+ _seekingHouseholds.CalculateEntityCount() + "), citizens=" + citizens +
+ ", population=" + arrivedPopulation + "/" + populationWithMoveIn +
+ " (arrived/withMoveIn), pets=" + pets + ", residentialProperties=" +
+ residentialProperties + " (onMarket=" +
+ _residentialOnMarket.CalculateEntityCount() + ")" + ", freeUnits=" + freeUnits.x +
+ "/" + freeUnits.y + "/" + freeUnits.z + " of " + totalUnits.x + "/" +
+ totalUnits.y + "/" + totalUnits.z + ", householdDemand=" +
+ residential.householdDemand + ", buildingDemand=" + residentialDemand.x + "/" +
+ residentialDemand.y + "/" + residentialDemand.z + ", unemployment=" +
+ unemployment + "% (workable=" + workable + ", workers=" + workers + ")" +
+ ", outsideConnections=" + _citizenOutsideConnections.CalculateEntityCount() +
+ ".");
}
return true;
}
@@ -240,37 +241,37 @@ public void Apply(EntityManager em, NetworkReader reader)
bool buildingsDiverged = Diverged(localBuildings, hostBuildings);
if (buildingsDiverged)
- Mod.log.Warn("[MP] ZoneDemand: building counts have drifted - this client has " +
- localBuildings + ", the host has " + hostBuildings +
- ". Zoned-building replication is not keeping up.");
+ SyncLog.Warn(LogTopic.City,
+ "ZoneDemand: building counts have drifted - this client has " + localBuildings +
+ ", the host has " + hostBuildings +
+ ". Zoned-building replication is not keeping up.");
if (worstDemandGap >= DemandGapThreshold)
- Mod.log.Warn("[MP] ZoneDemand: demand differs from the host by up to " +
- worstDemandGap + " (res " + localResidential.x + "/" +
- localResidential.y + "/" + localResidential.z + " vs " +
- hostResidentialLow + "/" + hostResidentialMedium + "/" +
- hostResidentialHigh + ", com " + commercial.buildingDemand + " vs " +
- hostCommercial + ", ind " + industrial.industrialBuildingDemand +
- " vs " + hostIndustrial + ", off " + industrial.officeBuildingDemand +
- " vs " + hostOffice + ", sto " + industrial.storageBuildingDemand +
- " vs " + hostStorage + ").");
+ SyncLog.Warn(LogTopic.City, "ZoneDemand: demand differs from the host by up to " +
+ worstDemandGap + " (res " + localResidential.x + "/" + localResidential.y + "/" +
+ localResidential.z + " vs " + hostResidentialLow + "/" + hostResidentialMedium +
+ "/" + hostResidentialHigh + ", com " + commercial.buildingDemand + " vs " +
+ hostCommercial + ", ind " + industrial.industrialBuildingDemand + " vs " +
+ hostIndustrial + ", off " + industrial.officeBuildingDemand + " vs " +
+ hostOffice + ", sto " + industrial.storageBuildingDemand + " vs " + hostStorage +
+ ").");
int localCitizens = _citizens.CalculateEntityCount();
int localPets = _pets.CalculateEntityCount();
int localHouseholds = _households.CalculateEntityCount();
if (Diverged(localCitizens, hostCitizens) || Diverged(localPets, hostPets) ||
Diverged(localHouseholds, hostHouseholds))
- Mod.Verbose("[MP] ZoneDemand: occupancy differs - households " + localHouseholds +
- "/" + hostHouseholds + ", people " + localCitizens + "/" + hostCitizens +
- ", pets " + localPets + "/" + hostPets +
- " (local/host). Residents are simulated per machine.");
+ SyncLog.Detail(LogTopic.City, "ZoneDemand: occupancy differs - households " +
+ localHouseholds + "/" + hostHouseholds + ", people " + localCitizens + "/" +
+ hostCitizens + ", pets " + localPets + "/" + hostPets +
+ " (local/host). Residents are simulated per machine.");
- Mod.Verbose("[MP] ZoneDemand: buildings " + localBuildings + "/" + hostBuildings +
- " (local/host), properties res " + _residentialProperties.CalculateEntityCount() +
- "/" + hostResidentialProperties + ", com " +
- _commercialProperties.CalculateEntityCount() + "/" + hostCommercialProperties +
- ", ind " + _industrialProperties.CalculateEntityCount() + "/" +
- hostIndustrialProperties + ".");
+ SyncLog.Detail(LogTopic.City, "ZoneDemand: buildings " + localBuildings + "/" +
+ hostBuildings + " (local/host), properties res " +
+ _residentialProperties.CalculateEntityCount() + "/" + hostResidentialProperties +
+ ", com " + _commercialProperties.CalculateEntityCount() + "/" +
+ hostCommercialProperties + ", ind " + _industrialProperties.CalculateEntityCount() +
+ "/" + hostIndustrialProperties + ".");
}
private static int Gap(int local, int host) =>
diff --git a/CS2MultiplayerMod/Game/Sync/Channels/Population/ResidentialOccupancyChannel.cs b/CS2MultiplayerMod/Game/Sync/Channels/Population/ResidentialOccupancyChannel.cs
index 4f4c34c..8b197e2 100644
--- a/CS2MultiplayerMod/Game/Sync/Channels/Population/ResidentialOccupancyChannel.cs
+++ b/CS2MultiplayerMod/Game/Sync/Channels/Population/ResidentialOccupancyChannel.cs
@@ -1,4 +1,6 @@
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Systems;
@@ -39,8 +41,9 @@ public bool Capture(EntityManager entityManager, NetworkWriter writer)
if (!_captureWarned)
{
_captureWarned = true;
- Mod.log.Warn("[MP] Occupancy: host capture failed; page skipped " +
- "(logged once until world reset): " + ex.Message);
+ SyncLog.Warn(LogTopic.Residential,
+ "Occupancy: host capture failed; page skipped " +
+ "(logged once until world reset): " + ex.Message);
}
return false;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Channels/World/TreeStateChannel.cs b/CS2MultiplayerMod/Game/Sync/Channels/World/TreeStateChannel.cs
index eae3d66..404c765 100644
--- a/CS2MultiplayerMod/Game/Sync/Channels/World/TreeStateChannel.cs
+++ b/CS2MultiplayerMod/Game/Sync/Channels/World/TreeStateChannel.cs
@@ -7,7 +7,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -148,7 +150,8 @@ public bool Capture(EntityManager em, NetworkWriter writer)
if (!_warnedCapture)
{
_warnedCapture = true;
- Mod.log.Warn("[MP] TreeState capture failed (logged once): " + ex.Message);
+ SyncLog.Warn(LogTopic.Buildings, "TreeState capture failed (logged once): " +
+ ex.Message);
}
return false;
}
@@ -166,8 +169,8 @@ public void Apply(EntityManager em, NetworkReader reader)
_snapshots++;
if (_snapshots % 30 == 0 && (_corrected > 0 || _unmatched > 0))
{
- Mod.Verbose("[MP] TreeState/30 snapshots: corrected=" + _corrected +
- " unmatched=" + _unmatched + ".");
+ SyncLog.Detail(LogTopic.Buildings, "TreeState/30 snapshots: corrected=" + _corrected +
+ " unmatched=" + _unmatched + ".");
_corrected = 0;
_unmatched = 0;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Infrastructure/GameAccess/ConstructionCharger.cs b/CS2MultiplayerMod/Game/Sync/Infrastructure/GameAccess/ConstructionCharger.cs
index 7c8c488..5bd3dbb 100644
--- a/CS2MultiplayerMod/Game/Sync/Infrastructure/GameAccess/ConstructionCharger.cs
+++ b/CS2MultiplayerMod/Game/Sync/Infrastructure/GameAccess/ConstructionCharger.cs
@@ -2,7 +2,9 @@
using Game.Prefabs;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
namespace CS2MultiplayerMod.Game.Sync.Infrastructure
{
@@ -59,7 +61,8 @@ private static void Charge(EntityManager em, long amount, string what)
money.Subtract((int)math.min(amount, int.MaxValue));
em.SetComponentData(city, money);
- Mod.Verbose("[MP] Charged " + amount + " for remote build: " + what + ".");
+ SyncLog.Detail(LogTopic.Pipeline, "Charged " + amount + " for remote build: " + what +
+ ".");
}
finally
{
diff --git a/CS2MultiplayerMod/Game/Sync/Infrastructure/GameAccess/PrefabIndex.cs b/CS2MultiplayerMod/Game/Sync/Infrastructure/GameAccess/PrefabIndex.cs
index 744f38a..54127e6 100644
--- a/CS2MultiplayerMod/Game/Sync/Infrastructure/GameAccess/PrefabIndex.cs
+++ b/CS2MultiplayerMod/Game/Sync/Infrastructure/GameAccess/PrefabIndex.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using Game.Prefabs;
using Unity.Collections;
using Unity.Entities;
@@ -154,9 +156,10 @@ private void Build()
if (tornDownCount > 0 && !_warnedUnusable)
{
_warnedUnusable = true;
- Mod.log.Warn("[MP] PrefabIndex: " + tornDownCount + " of " + prefabs.Length +
- " catalogue entries still point at a torn-down asset (first entity " +
- firstTornDown + "); skipped. " + retired + " more were retired normally.");
+ SyncLog.Warn(LogTopic.Pipeline, "PrefabIndex: " + tornDownCount + " of " +
+ prefabs.Length +
+ " catalogue entries still point at a torn-down asset (first entity " +
+ firstTornDown + "); skipped. " + retired + " more were retired normally.");
}
}
finally
diff --git a/CS2MultiplayerMod/Game/Sync/Infrastructure/Pipeline/CommandObserver.cs b/CS2MultiplayerMod/Game/Sync/Infrastructure/Pipeline/CommandObserver.cs
index 1174ce8..d80b22a 100644
--- a/CS2MultiplayerMod/Game/Sync/Infrastructure/Pipeline/CommandObserver.cs
+++ b/CS2MultiplayerMod/Game/Sync/Infrastructure/Pipeline/CommandObserver.cs
@@ -49,7 +49,7 @@ public override void OnCommandReceived(SimulationCommandMessage command)
if (command.CommandId != _ids[i]) continue;
if (command.Body != null && command.Body.Length > MaxBodyBytes)
{
- WarnThrottled("[MP] Dropping oversized command id " + command.CommandId +
+ WarnThrottled("Dropping oversized command id " + command.CommandId +
" body=" + command.Body.Length + " > " + MaxBodyBytes + ".");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("oversized sync command rejected", "stream",
diff --git a/CS2MultiplayerMod/Game/Sync/Infrastructure/Pipeline/LocalAuthorityHold.cs b/CS2MultiplayerMod/Game/Sync/Infrastructure/Pipeline/LocalAuthorityHold.cs
index 5b6134a..9d7c98d 100644
--- a/CS2MultiplayerMod/Game/Sync/Infrastructure/Pipeline/LocalAuthorityHold.cs
+++ b/CS2MultiplayerMod/Game/Sync/Infrastructure/Pipeline/LocalAuthorityHold.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using Unity.Entities;
namespace CS2MultiplayerMod.Game.Sync.Infrastructure
@@ -71,15 +73,14 @@ public void Apply(World world, MultiplayerSession session)
// that latest native intent before holding it again so disconnect restores it on.
_wasEnabled[type] = true;
system.Enabled = false;
- Mod.Verbose("[MP] " + _label + ": " + type.Name +
- " disabled on this client; the host decides " + _decides + ".");
+ SyncLog.Detail(LogTopic.Pipeline, _label + ": " + type.Name +
+ " disabled on this client; the host decides " + _decides + ".");
}
if (_applied) return;
_applied = true;
- Mod.log.Info("[MP] " + _label + ": " + _subject + " handed to the host (" +
- _systems.Length + " simulation system(s) held).");
- CS2MultiplayerMod.Game.Diagnostics.FlightRecorder.Note(_topic + " -> host");
+ SyncLog.Detail(LogTopic.Pipeline, _label + ": " + _subject + " handed to the host (" +
+ _systems.Length + " simulation system(s) held).");
}
///
@@ -101,8 +102,8 @@ public void Restore(World world)
}
_wasEnabled.Clear();
_applied = false;
- Mod.log.Info("[MP] " + _label + ": " + _subject + " returned to the local simulation.");
- CS2MultiplayerMod.Game.Diagnostics.FlightRecorder.Note(_topic + " -> local");
+ SyncLog.Detail(LogTopic.Pipeline, _label + ": " + _subject +
+ " returned to the local simulation.");
}
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Infrastructure/Pipeline/SyncInbox.cs b/CS2MultiplayerMod/Game/Sync/Infrastructure/Pipeline/SyncInbox.cs
index 240bb16..13e47b6 100644
--- a/CS2MultiplayerMod/Game/Sync/Infrastructure/Pipeline/SyncInbox.cs
+++ b/CS2MultiplayerMod/Game/Sync/Infrastructure/Pipeline/SyncInbox.cs
@@ -54,7 +54,7 @@ public static bool Push(ConcurrentQueue queue, T item, int cap = DefaultCa
"without the command it depends on"));
Action warn = LogWarn;
if (warn != null)
- warn("[MP] Sync inbox overflowed; cleared the incomplete command suffix and " +
+ warn("Sync inbox overflowed; cleared the incomplete command suffix and " +
"requested a fresh world sync.");
return false;
}
@@ -160,7 +160,7 @@ public static void DrainAll()
catch (Exception ex)
{
Action warn = LogWarn;
- if (warn != null) warn("[MP] SyncInbox drain threw: " + ex.Message);
+ if (warn != null) warn("Sync inbox drain threw: " + ex.Message);
}
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Players/MapPingSystem.cs b/CS2MultiplayerMod/Game/Sync/Players/MapPingSystem.cs
index 5457fb4..c4011a7 100644
--- a/CS2MultiplayerMod/Game/Sync/Players/MapPingSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Players/MapPingSystem.cs
@@ -5,8 +5,10 @@
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -68,7 +70,6 @@ protected override void OnCreate()
_overlay = World.GetOrCreateSystemManaged();
_observer = SyncObserverBinding.Bind(
() => new CommandObserver(_incoming, MapPingCommand.Id), DrainQueue);
- Mod.log.Info(nameof(MapPingSystem) + " ready.");
}
protected override void OnDestroy()
@@ -140,7 +141,8 @@ private void ApplyIncoming(MultiplayerService service, long now)
try { command = MapPingCommand.Decode(message.Body); }
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] MapPing: dropping malformed ping: " + ex.Message);
+ SyncLog.Warn(LogTopic.Players, "MapPing: dropping malformed ping: " +
+ ex.Message);
continue;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Players/PlayerCursorSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Players/PlayerCursorSyncSystem.cs
index 36b66c9..4acbdfa 100644
--- a/CS2MultiplayerMod/Game/Sync/Players/PlayerCursorSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Players/PlayerCursorSyncSystem.cs
@@ -2,7 +2,9 @@
using Game;
using Game.Rendering;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
namespace CS2MultiplayerMod.Game.Sync.Players
{
@@ -72,7 +74,6 @@ public partial class PlayerCursorSyncSystem : GameSystemBase
protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(PlayerCursorSyncSystem) + " ready.");
_camera = World.GetExistingSystemManaged();
}
@@ -142,8 +143,9 @@ protected override void OnUpdate()
if (now - _lastLogMs >= 30000)
{
_lastLogMs = now;
- Mod.Verbose("[MP] Cursors: sent " + _sent + " position(s)/30s; tracking " +
- service.RemotePlayerCount + " remote player(s).");
+ SyncLog.Detail(LogTopic.Players, "Cursors: sent " + _sent +
+ " position(s)/30s; tracking " + service.RemotePlayerCount +
+ " remote player(s).");
_sent = 0;
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Players/RemotePlayerMarkerSystem.cs b/CS2MultiplayerMod/Game/Sync/Players/RemotePlayerMarkerSystem.cs
index b2ea592..0376ce4 100644
--- a/CS2MultiplayerMod/Game/Sync/Players/RemotePlayerMarkerSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Players/RemotePlayerMarkerSystem.cs
@@ -1,4 +1,6 @@
using Colossal.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using Game;
using Game.Rendering;
using Unity.Jobs;
@@ -59,7 +61,6 @@ protected override void OnCreate()
base.OnCreate();
_overlay = World.GetOrCreateSystemManaged();
_camera = World.GetExistingSystemManaged();
- Mod.log.Info(nameof(RemotePlayerMarkerSystem) + " ready.");
}
protected override void OnUpdate()
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Appearance/VisualCustomizationSyncSystem/VisualCustomizationApply.cs b/CS2MultiplayerMod/Game/Sync/Systems/Appearance/VisualCustomizationSyncSystem/VisualCustomizationApply.cs
index 3067832..ee7b875 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Appearance/VisualCustomizationSyncSystem/VisualCustomizationApply.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Appearance/VisualCustomizationSyncSystem/VisualCustomizationApply.cs
@@ -2,8 +2,10 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using Colossal.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using Game;
@@ -43,8 +45,8 @@ private void ApplyIncoming(MultiplayerSession session, long now)
}
catch (Exception ex)
{
- Mod.log.Warn("[MP] VisualCustomizationSync: dropping malformed command: " +
- ex.Message);
+ SyncLog.Warn(LogTopic.Buildings,
+ "VisualCustomizationSync: dropping malformed command: " + ex.Message);
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("malformed visual-customization command", "appearance",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.StreamLoss)
@@ -92,8 +94,8 @@ private void ApplyRetries(long now)
}
if (expiredTargets > 0)
{
- Mod.log.Warn("[MP] VisualCustomizationSync: " + expiredTargets +
- " target(s) did not appear before the retry deadline.");
+ SyncLog.Warn(LogTopic.Buildings, "VisualCustomizationSync: " + expiredTargets +
+ " target(s) did not appear before the retry deadline.");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("visual-customization target did not resolve", "appearance",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.MissingTarget)
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Appearance/VisualCustomizationSyncSystem/VisualCustomizationSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Appearance/VisualCustomizationSyncSystem/VisualCustomizationSyncSystem.cs
index 1d3639c..9b9d82a 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Appearance/VisualCustomizationSyncSystem/VisualCustomizationSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Appearance/VisualCustomizationSyncSystem/VisualCustomizationSyncSystem.cs
@@ -2,8 +2,10 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using Colossal.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using Game;
@@ -219,7 +221,6 @@ public bool SamePayload(string prefabName, VisualCustomizationFields fields,
protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(VisualCustomizationSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_prefabIndex = new PrefabIndex(_prefabSystem,
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/City/CityStateSyncSystem/CityStateSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/City/CityStateSyncSystem/CityStateSyncSystem.cs
index 5a29939..5e1335d 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/City/CityStateSyncSystem/CityStateSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/City/CityStateSyncSystem/CityStateSyncSystem.cs
@@ -4,10 +4,11 @@
using System.Threading;
using Game;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Channels;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -110,8 +111,8 @@ protected override void OnCreate()
RegisterEditable(new LoanStateChannel());
RegisterEditable(new CityNameStateChannel());
- Mod.log.Info(nameof(CityStateSyncSystem) + " ready with " + _channels.Count +
- " state channel(s), " + _editable.Count + " player-editable.");
+ SyncLog.Detail(LogTopic.City, nameof(CityStateSyncSystem) + " ready with " +
+ _channels.Count + " state channel(s), " + _editable.Count + " player-editable.");
_observer = SyncObserverBinding.Bind(
() => new Observer(_incoming, _incomingEdits, RequestOrderedPoison,
@@ -202,7 +203,12 @@ private void CaptureAndBroadcast(MultiplayerSession session)
}
// Heartbeat every ~30 s so the log shows state replication is alive without spam.
- if (now - _lastLogMs >= 30000) { _lastLogMs = now; Mod.Verbose("[MP] CityState: broadcasting " + sent + " channel(s)/snapshot to clients."); }
+ if (now - _lastLogMs >= 30000)
+ {
+ _lastLogMs = now;
+ SyncLog.Detail(LogTopic.City, "CityState: broadcasting " + sent +
+ " channel(s)/snapshot to clients.");
+ }
}
// ---- Client ------------------------------------------------------------
@@ -240,7 +246,8 @@ private void DetectLocalEdits(MultiplayerSession session)
_pendingEdits[channelId] = new PendingEdit { Payload = local, SentMs = now };
session.SendStateEdit(channelId, local);
- Mod.Verbose("[MP] CityState: local edit on channel " + channelId + " sent to host.");
+ SyncLog.Detail(LogTopic.City, "CityState: local edit on channel " + channelId +
+ " sent to host.");
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/City/CityStateSyncSystem/Realize.cs b/CS2MultiplayerMod/Game/Sync/Systems/City/CityStateSyncSystem/Realize.cs
index 94833d8..f20088e 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/City/CityStateSyncSystem/Realize.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/City/CityStateSyncSystem/Realize.cs
@@ -1,5 +1,7 @@
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol;
using CS2MultiplayerMod.Core.Protocol.Messages;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -16,8 +18,8 @@ private void ApplyIncomingEdits()
IStateChannel channel;
if (!_channels.TryGetValue(edit.ChannelId, out channel) || !_editable.Contains(edit.ChannelId))
{
- Mod.log.Warn("[MP] CityState: ignoring edit on non-editable channel " + edit.ChannelId +
- " from player " + edit.OriginPlayerId + ".");
+ SyncLog.Warn(LogTopic.City, "CityState: ignoring edit on non-editable channel " +
+ edit.ChannelId + " from player " + edit.OriginPlayerId + ".");
continue;
}
@@ -25,14 +27,15 @@ private void ApplyIncomingEdits()
{
channel.Apply(EntityManager, new NetworkReader(edit.Data));
any = true;
- Mod.Verbose("[MP] CityState: player " + edit.OriginPlayerId + " edited channel " +
- edit.ChannelId + "; applied and broadcasting.");
+ SyncLog.Detail(LogTopic.City, "CityState: player " + edit.OriginPlayerId +
+ " edited channel " + edit.ChannelId + "; applied and broadcasting.");
}
catch (System.Exception ex)
{
// Wire data must never take the host down — malformed or hostile
// edits are dropped, not crashed on.
- Mod.log.Warn("[MP] CityState: dropping bad edit on channel " + edit.ChannelId + ": " + ex.Message);
+ SyncLog.Warn(LogTopic.City, "CityState: dropping bad edit on channel " +
+ edit.ChannelId + ": " + ex.Message);
}
}
@@ -72,8 +75,9 @@ private void ApplyIncoming()
}
if (_orderedDeferred.Count >= OrderedDeferredCap)
{
- Mod.log.Warn("[MP] CityState: ordered-state deferred queue overflowed; " +
- "requesting a fresh world sync.");
+ SyncLog.Warn(LogTopic.City,
+ "CityState: ordered-state deferred queue overflowed; " +
+ "requesting a fresh world sync.");
PoisonOrderedStream("ordered state deferred overflow");
continue;
}
@@ -100,7 +104,8 @@ private void ApplyIncoming()
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] CityState: dropping bad state on channel " + newest.ChannelId + ": " + ex.Message);
+ SyncLog.Warn(LogTopic.City, "CityState: dropping bad state on channel " +
+ newest.ChannelId + ": " + ex.Message);
}
}
@@ -110,8 +115,9 @@ private void ApplyIncoming()
if (_applied > 0 && now - _lastLogMs >= 30000)
{
_lastLogMs = now;
- Mod.Verbose("[MP] CityState: applied " + _applied + " state snapshot(s) from host in last 30s" +
- (_superseded > 0 ? ", " + _superseded + " superseded before apply." : "."));
+ SyncLog.Detail(LogTopic.City, "CityState: applied " + _applied +
+ " state snapshot(s) from host in last 30s" +
+ (_superseded > 0 ? ", " + _superseded + " superseded before apply." : "."));
_applied = 0;
_superseded = 0;
}
@@ -128,8 +134,8 @@ private void ApplyOrdered(StateSnapshotMessage snapshot, ref int orderedAttempts
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] CityState: dropping bad ordered state on channel " +
- snapshot.ChannelId + ": " + ex.Message);
+ SyncLog.Warn(LogTopic.City, "CityState: dropping bad ordered state on channel " +
+ snapshot.ChannelId + ": " + ex.Message);
PoisonOrderedStream("malformed ordered household state");
}
}
@@ -142,7 +148,7 @@ private void PumpChannels()
try { _pumped[i].Pump(EntityManager); }
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] CityState: channel pump failed: " + ex.Message);
+ SyncLog.Warn(LogTopic.City, "CityState: channel pump failed: " + ex.Message);
}
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/City/DevTreeSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/City/DevTreeSyncSystem.cs
index fbf64ad..ffa5591 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/City/DevTreeSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/City/DevTreeSyncSystem.cs
@@ -6,9 +6,10 @@
using Game.Tools;
using Unity.Collections;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Channels;
@@ -40,7 +41,6 @@ public partial class DevTreeSyncSystem : GameSystemBase
protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(DevTreeSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
// Unlock events must be raised through the same barrier the game uses so
@@ -141,7 +141,8 @@ private void DetectLocalPurchases(MultiplayerSession session, long now)
var command = new DevTreePurchaseCommand { NodePrefabName = name };
session.SendCommand(0, DevTreePurchaseCommand.Id, command.Encode());
- Mod.Verbose("[MP] DevTreeSync: broadcast purchase of '" + name + "'.");
+ SyncLog.Detail(LogTopic.City, "DevTreeSync: broadcast purchase of '" + name +
+ "'.");
}
else if (!unlocked && known)
{
@@ -163,13 +164,14 @@ private void ApplyIncoming(MultiplayerSession session, long now)
DevTreePurchaseCommand command;
try { command = DevTreePurchaseCommand.Decode(message.Body); }
- catch (System.Exception ex) { Mod.log.Warn("[MP] DevTreeSync: dropping malformed command: " + ex.Message); continue; }
+ catch (System.Exception ex) { SyncLog.Warn(LogTopic.City, "DevTreeSync: dropping malformed command: " + ex.Message); continue; }
Entity node = ResolveNode(command.NodePrefabName);
if (node == Entity.Null)
{
- Mod.log.Warn("[MP] DevTreeSync: unknown node '" + command.NodePrefabName +
- "' from player " + message.OriginPlayerId + "; skipping.");
+ SyncLog.Warn(LogTopic.City, "DevTreeSync: unknown node '" +
+ command.NodePrefabName + "' from player " + message.OriginPlayerId +
+ "; skipping.");
continue;
}
if (!IsLocked(node)) continue; // already unlocked here — nothing to do
@@ -197,8 +199,8 @@ private void ApplyIncoming(MultiplayerSession session, long now)
_pointsQuery.SetSingleton(points);
}
- Mod.Verbose("[MP] DevTreeSync: applied purchase of '" + command.NodePrefabName +
- "' from player " + message.OriginPlayerId + ".");
+ SyncLog.Detail(LogTopic.City, "DevTreeSync: applied purchase of '" +
+ command.NodePrefabName + "' from player " + message.OriginPlayerId + ".");
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/City/NameSyncSystem/Capture.cs b/CS2MultiplayerMod/Game/Sync/Systems/City/NameSyncSystem/Capture.cs
index 0e91f7c..2f13da8 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/City/NameSyncSystem/Capture.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/City/NameSyncSystem/Capture.cs
@@ -3,7 +3,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -114,7 +116,8 @@ private void BaselineStreets()
{
entities.Dispose();
}
- Mod.Verbose("[MP] NameSync: baselined " + _publishedAuto.Count + " street draw(s).");
+ SyncLog.Detail(LogTopic.City, "NameSync: baselined " + _publishedAuto.Count +
+ " street draw(s).");
}
///
@@ -269,9 +272,9 @@ private void SendCustomName(MultiplayerSession session, Entity entity, string na
{
// Expected for citizens, vehicles and animals. Naming the prefab keeps the line
// useful if some other kind of entity ever turns up here.
- Mod.Verbose("[MP] NameSync: '" + name + "' is on '" +
- (LocalPrefabName(entity) ?? "?") + "', which has no cross-machine " +
- "identity; not replicated.");
+ SyncLog.Detail(LogTopic.City, "NameSync: '" + name + "' is on '" +
+ (LocalPrefabName(entity) ?? "?") + "', which has no cross-machine " +
+ "identity; not replicated.");
return;
}
@@ -296,14 +299,14 @@ private void Send(MultiplayerSession session, EntityNameCommand command, string
try { body = command.Encode(); }
catch (Exception ex)
{
- Mod.log.Warn("[MP] NameSync: could not encode " + what + " for " +
- KindName(command.TargetKind) + " '" + command.TargetPrefabName +
- "': " + ex.Message);
+ SyncLog.Warn(LogTopic.City, "NameSync: could not encode " + what + " for " +
+ KindName(command.TargetKind) + " '" + command.TargetPrefabName + "': " +
+ ex.Message);
return;
}
session.SendCommand(0, EntityNameCommand.Id, body);
- Mod.Verbose("[MP] NameSync captured " + what + " on " + KindName(command.TargetKind) +
- " '" + command.TargetPrefabName + "'.");
+ SyncLog.Detail(LogTopic.City, "NameSync captured " + what + " on " +
+ KindName(command.TargetKind) + " '" + command.TargetPrefabName + "'.");
}
/// The entity's current auto-name draw, one index per name slot its prefab has.
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/City/NameSyncSystem/NameSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/City/NameSyncSystem/NameSyncSystem.cs
index cd64a1e..770b44e 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/City/NameSyncSystem/NameSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/City/NameSyncSystem/NameSyncSystem.cs
@@ -8,8 +8,10 @@
using Unity.Entities;
using Unity.Mathematics;
using Colossal.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -118,7 +120,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(NameSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_prefabIndex = new PrefabIndex(_prefabSystem, GetEntityQuery(ComponentType.ReadOnly()));
_nameSystem = World.GetOrCreateSystemManaged();
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/City/NameSyncSystem/Realize.cs b/CS2MultiplayerMod/Game/Sync/Systems/City/NameSyncSystem/Realize.cs
index 9398fe8..32f80b8 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/City/NameSyncSystem/Realize.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/City/NameSyncSystem/Realize.cs
@@ -8,8 +8,10 @@
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -39,7 +41,8 @@ private void ApplyIncoming(MultiplayerSession session, long now)
try { command = EntityNameCommand.Decode(message.Body); }
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] NameSync: dropping malformed command: " + ex.Message);
+ SyncLog.Warn(LogTopic.City, "NameSync: dropping malformed command: " +
+ ex.Message);
continue;
}
@@ -62,9 +65,10 @@ private void RetryPending(long now)
{
// A name is cosmetic: the world stays consistent without it, so this never
// escalates to a resync the way a missing build target does.
- Mod.log.Warn("[MP] NameSync: no local " + KindName(pending.cmd.TargetKind) +
- " '" + pending.cmd.TargetPrefabName + "' appeared within " +
- (TargetRetryWindowMs / 1000) + " s; dropping its name.");
+ SyncLog.Warn(LogTopic.City, "NameSync: no local " +
+ KindName(pending.cmd.TargetKind) + " '" + pending.cmd.TargetPrefabName +
+ "' appeared within " + (TargetRetryWindowMs / 1000) +
+ " s; dropping its name.");
_targetRetry.RemoveAt(i);
continue;
}
@@ -94,8 +98,8 @@ private bool TryApplyName(EntityNameCommand command, int origin, long now)
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] NameSync: naming " + KindName(command.TargetKind) + " '" +
- command.TargetPrefabName + "' failed: " + ex.Message);
+ SyncLog.Warn(LogTopic.City, "NameSync: naming " + KindName(command.TargetKind) +
+ " '" + command.TargetPrefabName + "' failed: " + ex.Message);
return true;
}
@@ -115,11 +119,9 @@ private bool TryApplyName(EntityNameCommand command, int origin, long now)
// What makes the rendered street/district label pick the new name up; it is also what the
// game's own naming path adds.
if (refresh) EntityManager.AddComponent(target);
- Mod.Verbose("[MP] NameSync realize: " + KindName(command.TargetKind) + " '" +
- command.TargetPrefabName + "' from player " + origin +
- (command.SetsCustomName
- ? (name.Length == 0 ? " name cleared." : " named '" + name + "'.")
- : " auto-name applied."));
+ SyncLog.Detail(LogTopic.City, "NameSync realize: " + KindName(command.TargetKind) + " '" +
+ command.TargetPrefabName + "' from player " + origin +
+ (command.SetsCustomName ? (name.Length == 0 ? " name cleared." : " named '" + name + "'.") : " auto-name applied."));
return true;
}
@@ -136,7 +138,8 @@ private void QueueRetry(EntityNameCommand command, int origin, long now)
if (_targetRetry.Count >= MaxPendingTargets)
{
_targetRetry.RemoveAt(0);
- Mod.Verbose("[MP] NameSync: pending-name queue is full; dropped its oldest entry.");
+ SyncLog.Warn(LogTopic.City,
+ "NameSync: pending-name queue is full; dropped its oldest entry.");
}
_targetRetry.Add((command, origin, now + TargetRetryWindowMs));
}
@@ -200,8 +203,9 @@ private void ReassertHeldDraws(long now)
if (!ApplyRandomIndices(held.Target, held.Indices)) continue;
EntityManager.AddComponent(held.Target);
- Mod.Verbose("[MP] NameSync: restored auto-name " + Describe(held.Indices) +
- " on a " + KindName(held.Kind) + " that regrouped locally.");
+ SyncLog.Detail(LogTopic.City, "NameSync: restored auto-name " +
+ Describe(held.Indices) + " on a " + KindName(held.Kind) +
+ " that regrouped locally.");
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/City/PolicySyncSystem/Capture.cs b/CS2MultiplayerMod/Game/Sync/Systems/City/PolicySyncSystem/Capture.cs
index 6311fa7..9a5d0ef 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/City/PolicySyncSystem/Capture.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/City/PolicySyncSystem/Capture.cs
@@ -3,7 +3,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -115,8 +117,9 @@ private void Send(MultiplayerSession session, long now, byte kind, string target
Adjustment = adjustment,
};
session.SendCommand(0, EntityPolicyCommand.Id, command.Encode());
- Mod.Verbose("[MP] PolicySync captured '" + policyName + "' (" + (active ? "on" : "off") +
- ", " + adjustment + ") on " + KindName(kind) + " '" + targetName + "'.");
+ SyncLog.Detail(LogTopic.City, "PolicySync captured '" + policyName + "' (" +
+ (active ? "on" : "off") + ", " + adjustment + ") on " + KindName(kind) + " '" +
+ targetName + "'.");
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/City/PolicySyncSystem/PolicySyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/City/PolicySyncSystem/PolicySyncSystem.cs
index 7ba208c..c5a4461 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/City/PolicySyncSystem/PolicySyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/City/PolicySyncSystem/PolicySyncSystem.cs
@@ -10,9 +10,10 @@
using Game.Tools;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -64,7 +65,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(PolicySyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_prefabIndex = new PrefabIndex(_prefabSystem, GetEntityQuery(ComponentType.ReadOnly()));
_policiesUI = World.GetOrCreateSystemManaged();
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/City/PolicySyncSystem/Realize.cs b/CS2MultiplayerMod/Game/Sync/Systems/City/PolicySyncSystem/Realize.cs
index 7d260dd..7b98735 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/City/PolicySyncSystem/Realize.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/City/PolicySyncSystem/Realize.cs
@@ -6,8 +6,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -38,12 +40,10 @@ private void ApplyIncoming(MultiplayerSession session, long now)
}
if (now >= pending.deadline)
{
- Mod.log.Warn("[MP] PolicySync: no local " +
- KindName(pending.cmd.TargetKind) + " '" +
- pending.cmd.TargetPrefabName + "' appeared within " +
- (TargetRetryWindowMs / 1000) + " s for policy '" +
- pending.cmd.PolicyPrefabName +
- "'; requesting world recovery.");
+ SyncLog.Warn(LogTopic.City, "PolicySync: no local " +
+ KindName(pending.cmd.TargetKind) + " '" + pending.cmd.TargetPrefabName +
+ "' appeared within " + (TargetRetryWindowMs / 1000) + " s for policy '" +
+ pending.cmd.PolicyPrefabName + "'; requesting world recovery.");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("policy target did not resolve", "policy",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.MissingTarget)
@@ -62,7 +62,7 @@ private void ApplyIncoming(MultiplayerSession session, long now)
EntityPolicyCommand command;
try { command = EntityPolicyCommand.Decode(message.Body); }
- catch (System.Exception ex) { Mod.log.Warn("[MP] PolicySync: dropping malformed command: " + ex.Message); continue; }
+ catch (System.Exception ex) { SyncLog.Warn(LogTopic.City, "PolicySync: dropping malformed command: " + ex.Message); continue; }
if (!TryApplyPolicy(command, message.OriginPlayerId, now))
QueuePolicyRetry(command, message.OriginPlayerId, now);
@@ -78,8 +78,8 @@ private bool TryApplyPolicy(EntityPolicyCommand command, int origin, long now)
Entity policy;
if (!_prefabIndex.TryResolve(command.PolicyPrefabName, out policy))
{
- Mod.log.Warn("[MP] PolicySync: unknown policy '" +
- command.PolicyPrefabName + "'; skipping.");
+ SyncLog.Warn(LogTopic.City, "PolicySync: unknown policy '" +
+ command.PolicyPrefabName + "'; skipping.");
return true;
}
@@ -91,15 +91,14 @@ private bool TryApplyPolicy(EntityPolicyCommand command, int origin, long now)
try
{
_policiesUI.SetPolicy(target, policy, command.Active, command.Adjustment);
- Mod.Verbose("[MP] PolicySync realize: '" + command.PolicyPrefabName + "' " +
- (command.Active ? "on" : "off") + " for " +
- KindName(command.TargetKind) + " '" +
- command.TargetPrefabName + "' from player " + origin + ".");
+ SyncLog.Detail(LogTopic.City, "PolicySync realize: '" + command.PolicyPrefabName +
+ "' " + (command.Active ? "on" : "off") + " for " + KindName(command.TargetKind) +
+ " '" + command.TargetPrefabName + "' from player " + origin + ".");
}
catch (System.Exception ex)
{
- Mod.log.Error("[MP] PolicySync realize FAILED for '" +
- command.PolicyPrefabName + "': " + ex);
+ SyncLog.Error(LogTopic.City, "PolicySync realize FAILED for '" +
+ command.PolicyPrefabName + "': " + ex);
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("building policy application failed", "policy",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
@@ -122,8 +121,9 @@ private void QueuePolicyRetry(EntityPolicyCommand command, int origin, long now)
if (_targetRetry.Count >= MaxPendingTargets)
{
_targetRetry.RemoveAt(0);
- Mod.log.Warn("[MP] PolicySync: pending-target queue reached its bounded limit; " +
- "requesting world recovery.");
+ SyncLog.Warn(LogTopic.City,
+ "PolicySync: pending-target queue reached its bounded limit; " +
+ "requesting world recovery.");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("policy target retry queue overflow", "policy",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.StreamLoss)
@@ -131,9 +131,8 @@ private void QueuePolicyRetry(EntityPolicyCommand command, int origin, long now)
.Tried("nothing - the oldest queued policy was shed to stay within the bound"));
}
_targetRetry.Add((command, origin, now + TargetRetryWindowMs));
- Diagnostics.FlightRecorder.Note("policy target retrying kind=" +
- KindName(command.TargetKind) +
- " prefab=" + command.TargetPrefabName);
+ SyncLog.Trace(LogTopic.City, "policy target retrying kind=" +
+ KindName(command.TargetKind) + " prefab=" + command.TargetPrefabName);
}
private static string PendingPolicyKey(EntityPolicyCommand command) =>
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Land/AreaSyncSystem/AreaSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Land/AreaSyncSystem/AreaSyncSystem.cs
index 30d6314..aae030a 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Land/AreaSyncSystem/AreaSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Land/AreaSyncSystem/AreaSyncSystem.cs
@@ -7,9 +7,10 @@
using Game.Tools;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -49,7 +50,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(AreaSyncSystem) + " ready.");
// A specialized placement's lot must not be published ahead of its building, which
// BuildSync holds until the polygon closes (see the redraw scan).
_buildSync = World.GetOrCreateSystemManaged();
@@ -168,7 +168,7 @@ public void RealizePending()
now + OwnedAreaRetryWindowMs);
}
}
- catch (System.Exception ex) { Mod.log.Warn("[MP] AreaSync: dropping malformed command: " + ex.Message); }
+ catch (System.Exception ex) { SyncLog.Warn(LogTopic.Land, "AreaSync: dropping malformed command: " + ex.Message); }
}
if (deletes != null) RealizeDeletes(deletes, now);
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Land/AreaSyncSystem/Capture.cs b/CS2MultiplayerMod/Game/Sync/Systems/Land/AreaSyncSystem/Capture.cs
index e3d4863..4070ce3 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Land/AreaSyncSystem/Capture.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Land/AreaSyncSystem/Capture.cs
@@ -3,7 +3,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -127,9 +129,8 @@ private void ScanForEdits(MultiplayerSession session, long now,
ownedCommand.NodeZ, ownedCommand.NodeElevation);
session.SendCommand(0, OwnedAreaSnapshotCommand.Id,
ownedCommand.Encode());
- Mod.Verbose("[MP] AreaSync captured owned redraw of '" + name +
- "' on '" + ownerName + "' (" + ring.Length +
- " nodes).");
+ SyncLog.Detail(LogTopic.Land, "AreaSync captured owned redraw of '" + name +
+ "' on '" + ownerName + "' (" + ring.Length + " nodes).");
continue;
}
@@ -149,7 +150,8 @@ private void ScanForEdits(MultiplayerSession session, long now,
CopyNodes(nodes, command.NodeX, command.NodeY,
command.NodeZ, command.NodeElevation);
session.SendCommand(0, AreaUpdateCommand.Id, command.Encode());
- Mod.Verbose("[MP] AreaSync captured redraw of '" + name + "' (" + ring.Length + " nodes).");
+ SyncLog.Detail(LogTopic.Land, "AreaSync captured redraw of '" + name + "' (" +
+ ring.Length + " nodes).");
}
}
finally
@@ -204,7 +206,8 @@ private void CaptureCreated(MultiplayerSession session, long now)
command.NodeElevation[n] = nodes[n].m_Elevation;
}
session.SendCommand(0, AreaCreateCommand.Id, command.Encode());
- Mod.Verbose("[MP] AreaSync captured '" + name + "' (" + nodes.Length + " nodes).");
+ SyncLog.Detail(LogTopic.Land, "AreaSync captured '" + name + "' (" +
+ nodes.Length + " nodes).");
}
}
finally
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Land/AreaSyncSystem/Realize.cs b/CS2MultiplayerMod/Game/Sync/Systems/Land/AreaSyncSystem/Realize.cs
index 2eb2aa4..c77a1b7 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Land/AreaSyncSystem/Realize.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Land/AreaSyncSystem/Realize.cs
@@ -6,6 +6,8 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -36,12 +38,10 @@ private void RetryOwnedAreaSnapshots(long now)
// offering another snapshot. Escalating instead cost a full save-stream-reload for
// a lot outline, and did so on a fixed ten-second timer after any placement whose
// owner this machine could not match.
- Mod.log.Warn("[MP] AreaSync: owner '" +
- pending.command.OwnerPrefabName +
- "' did not appear in time for its owned area " +
- DescribeOwnedAreaOwnerSearch(pending.command) +
- "; dropping this snapshot - a later redraw will carry the polygon.");
- Diagnostics.FlightRecorder.Note("owned area owner expired; snapshot dropped");
+ SyncLog.Warn(LogTopic.Land, "AreaSync: owner '" + pending.command.OwnerPrefabName +
+ "' did not appear in time for its owned area " +
+ DescribeOwnedAreaOwnerSearch(pending.command) +
+ "; dropping this snapshot - a later redraw will carry the polygon.");
}
}
@@ -51,8 +51,7 @@ private void QueueOwnedAreaRetry(OwnedAreaSnapshotCommand command,
if (_ownedAreaRetry.Count >= MaxPendingOwnedAreas)
{
_ownedAreaRetry.Clear();
- Diagnostics.FlightRecorder.Note(
- "owned area retry queue overflow; recovery requested");
+ SyncLog.Trace(LogTopic.Land, "owned area retry queue overflow; recovery requested");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("owned area retry queue overflow", "area",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.StreamLoss)
@@ -77,9 +76,8 @@ private bool TryRealizeOwnedAreaSnapshot(OwnedAreaSnapshotCommand command,
!IsSpecializedAreaPrefab(areaPrefab) ||
!PrefabDeclaresOwnedArea(ownerPrefab, areaPrefab))
{
- Mod.log.Warn("[MP] AreaSync: rejected incompatible owned area '" +
- command.AreaPrefabName + "' on '" +
- command.OwnerPrefabName + "'.");
+ SyncLog.Warn(LogTopic.Land, "AreaSync: rejected incompatible owned area '" +
+ command.AreaPrefabName + "' on '" + command.OwnerPrefabName + "'.");
return true;
}
@@ -100,10 +98,9 @@ private bool TryRealizeOwnedAreaSnapshot(OwnedAreaSnapshotCommand command,
if (area == Entity.Null)
{
CreateMissingOwnedArea(command, areaPrefab, owner, nodeCount);
- Mod.Verbose("[MP] AreaSync: restored owned area '" +
- command.AreaPrefabName + "' on '" +
- command.OwnerPrefabName + "' from player " +
- originPlayerId + ".");
+ SyncLog.Detail(LogTopic.Land, "AreaSync: restored owned area '" +
+ command.AreaPrefabName + "' on '" + command.OwnerPrefabName + "' from player " +
+ originPlayerId + ".");
return true;
}
@@ -124,10 +121,9 @@ private bool TryRealizeOwnedAreaSnapshot(OwnedAreaSnapshotCommand command,
MarkAreaAndSubAreasUpdated(area);
EnsureOwnerSubAreaReference(owner, area);
_knownRings[area] = ring;
- Mod.Verbose("[MP] AreaSync: redrew owned area '" +
- command.AreaPrefabName + "' on '" +
- command.OwnerPrefabName + "' (" + nodeCount +
- " nodes) from player " + originPlayerId + ".");
+ SyncLog.Detail(LogTopic.Land, "AreaSync: redrew owned area '" + command.AreaPrefabName +
+ "' on '" + command.OwnerPrefabName + "' (" + nodeCount + " nodes) from player " +
+ originPlayerId + ".");
return true;
}
@@ -320,7 +316,8 @@ private void RealizeUpdate(AreaUpdateCommand command, int originPlayerId, long n
Entity prefab;
if (!_prefabIndex.TryResolve(command.PrefabName, out prefab))
{
- Mod.log.Warn("[MP] AreaSync update: unknown prefab '" + command.PrefabName + "'; skipping.");
+ SyncLog.Warn(LogTopic.Land, "AreaSync update: unknown prefab '" + command.PrefabName +
+ "'; skipping.");
return;
}
if (command.NodeX == null || command.NodeX.Length < 3) return;
@@ -351,8 +348,8 @@ private void RealizeUpdate(AreaUpdateCommand command, int originPlayerId, long n
if (best == Entity.Null)
{
- Mod.log.Warn("[MP] AreaSync update: no local '" + command.PrefabName +
- "' near the old centroid; skipping redraw.");
+ SyncLog.Warn(LogTopic.Land, "AreaSync update: no local '" + command.PrefabName +
+ "' near the old centroid; skipping redraw.");
return;
}
@@ -374,12 +371,13 @@ private void RealizeUpdate(AreaUpdateCommand command, int originPlayerId, long n
// Suppress the echo both ways: spatial guard + the scan cache itself.
_guard.Mark(AreaUpdateKey(command.PrefabName, CentroidOf(newRing)), now);
_knownRings[best] = newRing;
- Mod.Verbose("[MP] AreaSync update: redrew '" + command.PrefabName + "' (" +
- command.NodeX.Length + " nodes) from player " + originPlayerId + ".");
+ SyncLog.Detail(LogTopic.Land, "AreaSync update: redrew '" + command.PrefabName +
+ "' (" + command.NodeX.Length + " nodes) from player " + originPlayerId + ".");
}
catch (System.Exception ex)
{
- Mod.log.Error("[MP] AreaSync update FAILED for '" + command.PrefabName + "': " + ex);
+ SyncLog.Error(LogTopic.Land, "AreaSync update FAILED for '" + command.PrefabName +
+ "': " + ex);
}
}
@@ -388,7 +386,8 @@ private void RealizeCreate(AreaCreateCommand command, int originPlayerId, long n
Entity prefab;
if (!_prefabIndex.TryResolve(command.PrefabName, out prefab))
{
- Mod.log.Warn("[MP] AreaSync realize: unknown prefab '" + command.PrefabName + "'; skipping.");
+ SyncLog.Warn(LogTopic.Land, "AreaSync realize: unknown prefab '" +
+ command.PrefabName + "'; skipping.");
return;
}
if (command.NodeX == null || command.NodeX.Length < 3) return;
@@ -415,12 +414,13 @@ private void RealizeCreate(AreaCreateCommand command, int originPlayerId, long n
});
EntityManager.AddComponent(definition);
EntityManager.AddComponent(definition);
- Mod.Verbose("[MP] AreaSync realize: drew '" + command.PrefabName + "' (" +
- command.NodeX.Length + " nodes) from player " + originPlayerId + ".");
+ SyncLog.Detail(LogTopic.Land, "AreaSync realize: drew '" + command.PrefabName +
+ "' (" + command.NodeX.Length + " nodes) from player " + originPlayerId + ".");
}
catch (System.Exception ex)
{
- Mod.log.Error("[MP] AreaSync realize FAILED for '" + command.PrefabName + "': " + ex);
+ SyncLog.Error(LogTopic.Land, "AreaSync realize FAILED for '" + command.PrefabName +
+ "': " + ex);
}
}
@@ -467,8 +467,8 @@ private void RealizeDeletes(List commands, long now)
}
if (deleted > 0 || targets.Count > 0)
- Mod.Verbose("[MP] AreaSync: removed " + deleted + " area(s); " + targets.Count +
- " already gone (no local match).");
+ SyncLog.Detail(LogTopic.Land, "AreaSync: removed " + deleted + " area(s); " +
+ targets.Count + " already gone (no local match).");
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Land/TerrainSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Land/TerrainSyncSystem.cs
index 9419180..b8c11b6 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Land/TerrainSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Land/TerrainSyncSystem.cs
@@ -7,9 +7,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Systems.Net;
@@ -100,7 +101,7 @@ public partial class TerrainSyncSystem : GameSystemBase
// used to be a silent `continue`, and a terraform that goes missing is not a small error:
// one stroke is metres of height, and the roads placed near it afterwards resolve their
// endpoints against a surface the other player does not have. Counted here and reported at
- // the production level, because this is the thing a session that "keeps de-syncing" needs
+ // ungated, because this is the thing a session that "keeps de-syncing" needs
// its log to say out loud.
private int _dropSendNoToolName, _dropSendNoBrushName, _dropSendOpacity, _dropSendBadFrame;
private int _dropApplyUnknownPrefab, _dropApplyUnusablePrefab, _dropApplyCreateFailed;
@@ -111,7 +112,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(TerrainSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_prefabIndex = new PrefabIndex(_prefabSystem, GetEntityQuery(ComponentType.ReadOnly()));
_netSync = World.GetOrCreateSystemManaged();
@@ -194,14 +194,15 @@ public void CompletePendingHeightReadback()
_terrainSystem.GetHeightData(waitForPending: true);
_terrainSystem.GetHeightData(waitForPending: true);
_awaitingHeightReadback = false;
- Diagnostics.FlightRecorder.Note("terrain height readback complete");
+ SyncLog.Trace(LogTopic.Land, "terrain height readback complete");
}
catch (System.Exception ex)
{
// Do not wedge all subsequent construction forever if a future game build changes
// the readback contract. The next authoritative world sync remains the repair path.
_awaitingHeightReadback = false;
- Mod.log.Warn("[MP] TerrainSync: height readback barrier failed: " + ex.Message);
+ SyncLog.Warn(LogTopic.Land, "TerrainSync: height readback barrier failed: " +
+ ex.Message);
}
}
@@ -231,7 +232,8 @@ public void RealizePending()
catch (System.Exception ex)
{
_dropApplyMalformed++;
- Mod.log.Warn("[MP] TerrainSync: dropping malformed command: " + ex.Message);
+ SyncLog.Warn(LogTopic.Land, "TerrainSync: dropping malformed command: " +
+ ex.Message);
continue;
}
@@ -292,8 +294,9 @@ public void RealizePending()
// HasBacklog true forever, and that flag holds back every other realize in the
// mod - a lost stroke turned into a session that could apply nothing at all.
_dropApplyCreateFailed++;
- Mod.log.Warn("[MP] TerrainSync: dropping a brush sample that could not be " +
- "created: " + ex.Message);
+ SyncLog.Warn(LogTopic.Land,
+ "TerrainSync: dropping a brush sample that could not be " + "created: " +
+ ex.Message);
}
}
@@ -320,8 +323,9 @@ public void RealizePending()
if (!_commitApplyFailureLogged)
{
_commitApplyFailureLogged = true;
- Mod.log.Warn("[MP] TerrainSync: brush apply unavailable; remote samples remain queued" +
- (string.IsNullOrEmpty(commitError) ? "." : ": " + commitError));
+ SyncLog.Warn(LogTopic.Land,
+ "TerrainSync: brush apply unavailable; remote samples remain queued" +
+ (string.IsNullOrEmpty(commitError) ? "." : ": " + commitError));
}
// Bounded. An apply that never becomes available is not something to wait out: the
// queue it blocks is the one every other sync system waits behind.
@@ -338,7 +342,7 @@ public void RealizePending()
_pending.RemoveRange(0, consumed);
if (changesHeight) _awaitingHeightReadback = true;
_diagRealized += created.Count;
- Diagnostics.FlightRecorder.Note("terrain realize n=" + created.Count +
+ SyncLog.Trace(LogTopic.Land, "terrain realize n=" + created.Count +
(_pending.Count > 0 ? " held=" + _pending.Count : ""));
}
@@ -355,9 +359,9 @@ private void GiveUpOnQueuedTerrain(string commitError)
_commitApplyFailureLogged = false;
_dropApplyUnavailable += abandoned;
- Diagnostics.SyncLog.ProdWarn(
- "Terrain sync: gave up on " + abandoned + " queued terraforming sample(s) after " +
- MaxCommitFailureFrames + " frames without a usable apply pass" +
+ Diagnostics.SyncLog.Warn(LogTopic.Land, "Terrain sync: gave up on " + abandoned +
+ " queued terraforming sample(s) after " + MaxCommitFailureFrames +
+ " frames without a usable apply pass" +
(string.IsNullOrEmpty(commitError) ? "." : " (" + commitError + ").") +
" The ground here no longer matches the other player's.");
SyncInbox.RequestResync(Diagnostics.ResyncReport
@@ -515,7 +519,8 @@ private void FlushDiagnostics(long now)
if (_diagStartMs < 0) { _diagStartMs = now; return; }
if (now - _diagStartMs < 5000) return;
if (_diagCaptured > 0 || _diagRealized > 0)
- Mod.Verbose("[MP] TerrainSync/5s: captured " + _diagCaptured + " sample(s), realized " + _diagRealized + ".");
+ SyncLog.Detail(LogTopic.Land, "TerrainSync/5s: captured " + _diagCaptured +
+ " sample(s), realized " + _diagRealized + ".");
ReportDroppedTerrain();
_diagCaptured = _diagRealized = 0;
_diagStartMs = now;
@@ -537,38 +542,34 @@ private void ReportDroppedTerrain()
_dropApplyCreateFailed + _dropApplyMalformed;
if (notSent > 0)
- Diagnostics.SyncLog.ProdWarn(
- "Terrain sync: " + notSent + " terraforming sample(s) changed the ground here " +
- "but could not be sent (" + _dropSendNoToolName + " with no tool name, " +
- _dropSendNoBrushName + " with no brush name, " + _dropSendOpacity +
+ Diagnostics.SyncLog.Warn(LogTopic.Land, "Terrain sync: " + notSent +
+ " terraforming sample(s) changed the ground here " + "but could not be sent (" +
+ _dropSendNoToolName + " with no tool name, " + _dropSendNoBrushName +
+ " with no brush name, " + _dropSendOpacity +
" outside the encodable range). The other player's ground is now different here.");
if (notApplied > 0)
- Diagnostics.SyncLog.ProdWarn(
- "Terrain sync: " + notApplied + " terraforming sample(s) arrived but could not " +
- "be applied (" + _dropApplyUnknownPrefab + " naming a tool or brush this game " +
+ Diagnostics.SyncLog.Warn(LogTopic.Land, "Terrain sync: " + notApplied +
+ " terraforming sample(s) arrived but could not " + "be applied (" +
+ _dropApplyUnknownPrefab + " naming a tool or brush this game " +
"does not have, " + _dropApplyUnusablePrefab + " naming one that cannot " +
"terraform, " + _dropApplyCreateFailed + " that failed to build, " +
_dropApplyMalformed + " malformed). The ground here is now different from the " +
"other player's.");
if (_dropSendBadFrame > 0)
- Diagnostics.SyncLog.ProdWarn(
- "Terrain sync: " + _dropSendBadFrame + " terraforming sample(s) were sent with " +
+ Diagnostics.SyncLog.Warn(LogTopic.Land, "Terrain sync: " + _dropSendBadFrame +
+ " terraforming sample(s) were sent with " +
"a substitute frame time because this machine reported an implausible one. " +
"Their height change may be slightly off on the other player's map.");
if (notSent > 0 || notApplied > 0 || _dropSendBadFrame > 0 || _dropApplyUnavailable > 0)
- Diagnostics.FlightRecorder.Note(
- "terrain dropped sendNoTool=" + _dropSendNoToolName +
- " sendNoBrush=" + _dropSendNoBrushName +
- " sendOpacity=" + _dropSendOpacity +
- " sendBadFrame=" + _dropSendBadFrame +
- " applyUnknownPrefab=" + _dropApplyUnknownPrefab +
- " applyUnusablePrefab=" + _dropApplyUnusablePrefab +
- " applyCreateFailed=" + _dropApplyCreateFailed +
- " applyMalformed=" + _dropApplyMalformed +
- " applyUnavailable=" + _dropApplyUnavailable);
+ SyncLog.Trace(LogTopic.Land, "terrain dropped sendNoTool=" + _dropSendNoToolName +
+ " sendNoBrush=" + _dropSendNoBrushName + " sendOpacity=" + _dropSendOpacity +
+ " sendBadFrame=" + _dropSendBadFrame + " applyUnknownPrefab=" +
+ _dropApplyUnknownPrefab + " applyUnusablePrefab=" + _dropApplyUnusablePrefab +
+ " applyCreateFailed=" + _dropApplyCreateFailed + " applyMalformed=" +
+ _dropApplyMalformed + " applyUnavailable=" + _dropApplyUnavailable);
_dropSendNoToolName = _dropSendNoBrushName = _dropSendOpacity = _dropSendBadFrame = 0;
_dropApplyUnknownPrefab = _dropApplyUnusablePrefab = _dropApplyCreateFailed = 0;
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Land/TilePurchaseSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Land/TilePurchaseSyncSystem.cs
index c196a54..79776dd 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Land/TilePurchaseSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Land/TilePurchaseSyncSystem.cs
@@ -7,9 +7,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -36,7 +37,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(TilePurchaseSyncSystem) + " ready.");
_purchase = World.GetOrCreateSystemManaged();
// Owned-this-frame: Updated map tiles that (no longer) carry Native.
@@ -138,8 +138,8 @@ private void CapturePurchases(MultiplayerSession session, long now)
_lastSelectionCost = 0;
session.SendCommand(0, TilePurchaseCommand.Id, command.Encode());
- Mod.Verbose("[MP] TilePurchaseSync captured " + count + " tile(s), price " +
- command.TotalCost + ".");
+ SyncLog.Detail(LogTopic.Land, "TilePurchaseSync captured " + count +
+ " tile(s), price " + command.TotalCost + ".");
}
finally
{
@@ -156,7 +156,7 @@ private void RealizeIncoming(MultiplayerSession session, long now)
TilePurchaseCommand command;
try { command = TilePurchaseCommand.Decode(message.Body); }
- catch (System.Exception ex) { Mod.log.Warn("[MP] TilePurchaseSync: dropping malformed command: " + ex.Message); continue; }
+ catch (System.Exception ex) { SyncLog.Warn(LogTopic.Land, "TilePurchaseSync: dropping malformed command: " + ex.Message); continue; }
if (command.CenterX == null || command.CenterX.Length == 0) continue;
int unlocked = 0;
@@ -194,8 +194,9 @@ private void RealizeIncoming(MultiplayerSession session, long now)
(long)command.TotalCost * unlocked / command.CenterX.Length,
"map tiles x" + unlocked + " (player " + message.OriginPlayerId + ")");
- Mod.Verbose("[MP] TilePurchaseSync realize: unlocked " + unlocked + "/" +
- command.CenterX.Length + " tile(s) from player " + message.OriginPlayerId + ".");
+ SyncLog.Detail(LogTopic.Land, "TilePurchaseSync realize: unlocked " + unlocked + "/" +
+ command.CenterX.Length + " tile(s) from player " + message.OriginPlayerId +
+ ".");
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Land/ZoneSyncSystem/Capture.cs b/CS2MultiplayerMod/Game/Sync/Systems/Land/ZoneSyncSystem/Capture.cs
index 2e2d13c..e01015b 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Land/ZoneSyncSystem/Capture.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Land/ZoneSyncSystem/Capture.cs
@@ -2,7 +2,9 @@
using Game.Zones;
using Unity.Collections;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -109,8 +111,9 @@ private void CaptureUpdatedBlocks(long now)
if (!_outgoingOverflowWarned)
{
_outgoingOverflowWarned = true;
- Mod.log.Warn("[MP] ZoneSync outgoing queue reached its safety limit; " +
- "requesting a fresh world sync.");
+ SyncLog.Warn(LogTopic.Land,
+ "ZoneSync outgoing queue reached its safety limit; " +
+ "requesting a fresh world sync.");
}
continue;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Land/ZoneSyncSystem/ZoneSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Land/ZoneSyncSystem/ZoneSyncSystem.cs
index bf980ce..8ac939a 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Land/ZoneSyncSystem/ZoneSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Land/ZoneSyncSystem/ZoneSyncSystem.cs
@@ -8,9 +8,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -128,7 +129,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(ZoneSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_updatedBlocks = GetEntityQuery(new EntityQueryDesc
@@ -234,7 +234,8 @@ public void RealizePending()
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] ZoneSync: dropping malformed command: " + ex.Message);
+ SyncLog.Warn(LogTopic.Land, "ZoneSync: dropping malformed command: " +
+ ex.Message);
}
}
@@ -253,8 +254,8 @@ private void RecoverFromQueueOverflow(string reason)
.Create(reason, "zone", CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.StreamLoss)
.About("zone latest-state queue")
.Tried("nothing - the bounded queue was full and its zoning changes were shed"));
- Mod.log.Warn("[MP] ZoneSync overflowed its bounded latest-state queue; " +
- "requesting a fresh world sync.");
+ SyncLog.Warn(LogTopic.Land, "ZoneSync overflowed its bounded latest-state queue; " +
+ "requesting a fresh world sync.");
}
private void FlushDiagnostics(long now)
@@ -266,17 +267,12 @@ private void FlushDiagnostics(long now)
_diagnosticDecoded > 0 || _diagnosticApplied > 0 ||
_diagnosticDeferred > 0 || _diagnosticExpired > 0)
{
- Mod.Verbose("[MP] ZoneSync/5s: captured=" + _diagnosticCaptured +
- " sent=" + _diagnosticSent +
- " decoded=" + _diagnosticDecoded +
- " coalesced=" + _diagnosticCoalesced +
- " applied=" + _diagnosticApplied +
- " deferred=" + _diagnosticDeferred +
- " expired=" + _diagnosticExpired +
- " queues(out=" + _outgoing.Count +
- ", inbox=" + _incoming.Count +
- ", ready=" + _ready.Count +
- ", retry=" + _pending.Count + ").");
+ SyncLog.Detail(LogTopic.Land, "ZoneSync/5s: captured=" + _diagnosticCaptured +
+ " sent=" + _diagnosticSent + " decoded=" + _diagnosticDecoded + " coalesced=" +
+ _diagnosticCoalesced + " applied=" + _diagnosticApplied + " deferred=" +
+ _diagnosticDeferred + " expired=" + _diagnosticExpired + " queues(out=" +
+ _outgoing.Count + ", inbox=" + _incoming.Count + ", ready=" + _ready.Count +
+ ", retry=" + _pending.Count + ").");
}
_diagnosticCaptured = 0;
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetReplaceSyncSystem/NetReplaceSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetReplaceSyncSystem/NetReplaceSyncSystem.cs
index 3ab3e97..93add70 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetReplaceSyncSystem/NetReplaceSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetReplaceSyncSystem/NetReplaceSyncSystem.cs
@@ -9,9 +9,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Systems.Net;
@@ -122,7 +123,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(NetReplaceSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_prefabIndex = new PrefabIndex(_prefabSystem, GetEntityQuery(ComponentType.ReadOnly()));
// Replacements are committed through NetSync's ApplyTool pipeline (see Realize).
@@ -237,7 +237,8 @@ private void SeedBaseline()
entities.Dispose();
}
_seeded = true;
- Mod.Verbose("[MP] NetReplaceSync: baselined " + _edgeBaseline.Count + " edge(s).");
+ SyncLog.Detail(LogTopic.Nets, "NetReplaceSync: baselined " + _edgeBaseline.Count +
+ " edge(s).");
}
///
@@ -382,10 +383,9 @@ private void CaptureReplacements(MultiplayerSession session, long now)
OldDx = old.d.x, OldDy = old.d.y, OldDz = old.d.z,
};
session.SendCommand(0, NetReplaceCommand.Id, command.Encode());
- Mod.Verbose("[MP] NetReplaceSync captured " +
- (prefabChanged ? "replacement -> '" + name + "'" :
- reversed ? "direction flip of '" + name + "'" :
- "mixed-operation geometry update of '" + name + "'") + ".");
+ SyncLog.Detail(LogTopic.Nets, "NetReplaceSync captured " +
+ (prefabChanged ? "replacement -> '" + name + "'" : reversed ? "direction flip of '" + name + "'" : "mixed-operation geometry update of '" + name + "'") +
+ ".");
}
// Advance every touched baseline to the committed state — whether we sent or not — so a
@@ -483,8 +483,8 @@ private void TrySendExtensionPiece(MultiplayerSession session, Entity edge, Enti
End = { ElevationLeft = elevation.x, ElevationRight = elevation.y },
};
session.SendCommand(0, NetPlacementCommand.Id, command.Encode());
- Mod.Verbose("[MP] NetReplaceSync: captured merged continuation of '" + name + "' (" +
- length.ToString("F1") + " m).");
+ SyncLog.Detail(LogTopic.Nets, "NetReplaceSync: captured merged continuation of '" + name +
+ "' (" + length.ToString("F1") + " m).");
}
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetReplaceSyncSystem/Realize.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetReplaceSyncSystem/Realize.cs
index 7003673..d220de1 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetReplaceSyncSystem/Realize.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetReplaceSyncSystem/Realize.cs
@@ -7,8 +7,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -73,10 +75,10 @@ public void RealizePending()
_retry.Clear();
if (expired > 0)
{
- Mod.log.Warn("[MP] NetReplaceSync: " + expired +
- " road replacement target(s) did not resolve within " +
- (RetryWindowMs / 1000) +
- " s; dropping them and requesting authoritative world recovery.");
+ SyncLog.Warn(LogTopic.Nets, "NetReplaceSync: " + expired +
+ " road replacement target(s) did not resolve within " +
+ (RetryWindowMs / 1000) +
+ " s; dropping them and requesting authoritative world recovery.");
// One request for this expiry pass, not one per command. The expired entries were
// removed above, so they cannot request recovery again on later frames.
SyncInbox.RequestResync(Diagnostics.ResyncReport
@@ -98,7 +100,7 @@ public void RealizePending()
(work ?? (work = new List<(NetReplaceCommand, long)>()))
.Add((NetReplaceCommand.Decode(message.Body), now + RetryWindowMs));
}
- catch (System.Exception ex) { Mod.log.Warn("[MP] NetReplaceSync: dropping malformed command: " + ex.Message); }
+ catch (System.Exception ex) { SyncLog.Warn(LogTopic.Nets, "NetReplaceSync: dropping malformed command: " + ex.Message); }
}
if (work != null && work.Count > 0) Apply(work, now);
@@ -128,7 +130,8 @@ private void Apply(List<(NetReplaceCommand cmd, long deadline)> commands, long n
commands[i].cmd, commands[i].deadline));
}
else
- Mod.log.Warn("[MP] NetReplaceSync realize: unknown prefab '" + commands[i].cmd.PrefabName + "'; skipping.");
+ SyncLog.Warn(LogTopic.Nets, "NetReplaceSync realize: unknown prefab '" +
+ commands[i].cmd.PrefabName + "'; skipping.");
}
if (targets.Count == 0) return;
@@ -241,8 +244,9 @@ private void Apply(List<(NetReplaceCommand cmd, long deadline)> commands, long n
if (!found[t]) { _retry.Add((targets[t].cmd, targets[t].deadline)); retried++; }
if (replaced > 0 || retried > 0)
- Mod.Verbose("[MP] NetReplaceSync: replaced " + replaced + " road segment(s)" +
- (retried > 0 ? ", " + retried + " waiting for their segment" : "") + ".");
+ SyncLog.Detail(LogTopic.Nets, "NetReplaceSync: replaced " + replaced +
+ " road segment(s)" +
+ (retried > 0 ? ", " + retried + " waiting for their segment" : "") + ".");
}
///
@@ -341,7 +345,8 @@ private Entity CreateReplaceDefEntity(Entity edge, Entity newPrefab, bool invert
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] NetReplaceSync: failed to build replacement definition: " + ex.Message);
+ SyncLog.Warn(LogTopic.Nets,
+ "NetReplaceSync: failed to build replacement definition: " + ex.Message);
return Entity.Null;
}
finally
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Apply.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Apply.cs
index 1f999f8..2e773c8 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Apply.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Apply.cs
@@ -5,8 +5,9 @@
using Game.Tools;
using Unity.Collections;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
namespace CS2MultiplayerMod.Game.Sync.Systems.Net
{
@@ -191,7 +192,7 @@ public void RealizePending()
System.Action completed = _onCommitComplete;
_onCommitComplete = null;
if (completed != null) completed();
- Diagnostics.FlightRecorder.Note(
+ SyncLog.Trace(LogTopic.Nets,
"remote transaction drain completed after clean-frame fence");
WithdrawDrainReport("the batch drained on its own");
}
@@ -225,9 +226,8 @@ public void RealizePending()
_invalidatedDrainArmTick = System.Environment.TickCount;
_invalidatedCleanFrames = 0;
_invalidatedDrainTimedOut = false;
- Diagnostics.FlightRecorder.Note(
- "net isolated commit quarantined frames=" + _drainFrames +
- " tracked=" + trackedCount + " remaining=" + remainingTemps);
+ SyncLog.Trace(LogTopic.Nets, "net isolated commit quarantined frames=" +
+ _drainFrames + " tracked=" + trackedCount + " remaining=" + remainingTemps);
// The graph is quarantined either way - it may not be touched while native work
// is still scheduled against it. Whether that costs a world reload is a
@@ -275,11 +275,9 @@ private void ReportSlowRealizeCycle(long startTicks)
double elapsedMs = (System.Diagnostics.Stopwatch.GetTimestamp() - startTicks) * 1000d /
System.Diagnostics.Stopwatch.Frequency;
if (elapsedMs < SlowRealizeCycleMs) return;
- Mod.log.Info("[MP] NetSync realize cycle took " + elapsedMs.ToString("F0") + " ms (" +
- _rzCycleCourses + " course(s), " + _rzCyclePool + " indexed net entities).");
- Diagnostics.FlightRecorder.Note("net realize cycle ms=" + elapsedMs.ToString("F0") +
- " courses=" + _rzCycleCourses +
- " pool=" + _rzCyclePool);
+ SyncLog.Detail(LogTopic.Nets, "NetSync realize cycle took " + elapsedMs.ToString("F0") +
+ " ms (" + _rzCycleCourses + " course(s), " + _rzCyclePool +
+ " indexed net entities).");
}
///
@@ -334,7 +332,7 @@ public void PrepareDefinitionFrame()
if (_isolatedLocalTemps.Count > 0) ReleaseTrackedTemps(_isolatedLocalTemps);
DisableQueryEntities(_standingTemps, _isolatedLocalTemps);
if (_isolatedLocalTemps.Count > 0)
- Diagnostics.FlightRecorder.Note("tool preview isolated=" + _isolatedLocalTemps.Count);
+ SyncLog.Trace(LogTopic.Nets, "tool preview isolated=" + _isolatedLocalTemps.Count);
}
///
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyArm.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyArm.cs
index e861dff..b05242b 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyArm.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyArm.cs
@@ -6,6 +6,8 @@
using Unity.Collections;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
namespace CS2MultiplayerMod.Game.Sync.Systems.Net
{
@@ -22,8 +24,8 @@ private void DiscardStaleTransactionTemps(string why)
{
int cleared = ClearTempEntities(ActiveTransactionQuery());
if (cleared <= 0) return;
- Mod.log.Warn("[MP] SyncApply: discarded " + cleared + " uncommitted Temp(s) - " + why + ".");
- Diagnostics.FlightRecorder.Note("transaction temps discarded=" + cleared + " (" + why + ")");
+ SyncLog.Warn(LogTopic.Nets, "SyncApply: discarded " + cleared +
+ " uncommitted Temp(s) - " + why + ".");
}
///
@@ -58,7 +60,7 @@ public bool ArmNetCommit(System.Action onCommitLost,
_pendingNetConstructionChargeCourses = 0;
_onCommitLost = onCommitLost;
_onCommitComplete = onCommitComplete;
- Diagnostics.FlightRecorder.Note("net " + source + " batch armed");
+ SyncLog.Trace(LogTopic.Nets, "net " + source + " batch armed");
return true;
}
@@ -82,7 +84,7 @@ public bool ArmRouteCommit(System.Action onCommitLost,
_pendingNetConstructionChargeCourses = 0;
_onCommitLost = onCommitLost;
_onCommitComplete = onCommitComplete;
- Diagnostics.FlightRecorder.Note("route " + source + " operation armed");
+ SyncLog.Trace(LogTopic.Nets, "route " + source + " operation armed");
return true;
}
@@ -108,8 +110,8 @@ public bool ArmObjectCommit(System.Action onCommitLost, System.Action onCommitCo
_pendingNetConstructionChargeCourses = 0;
_onCommitLost = onCommitLost;
_onCommitComplete = onCommitComplete;
- Diagnostics.FlightRecorder.Note((rootlessAssetStamp ? "asset stamp " : "object ") +
- source + " operation armed");
+ SyncLog.Trace(LogTopic.Nets, (rootlessAssetStamp ? "asset stamp " : "object ") + source +
+ " operation armed");
return true;
}
@@ -130,7 +132,7 @@ private void ChargeCommittedNetConstruction()
{
// Charging is accounting, not geometry. Never destabilize a successfully committed
// network transaction merely because the money singleton changed unexpectedly.
- Mod.log.Warn("[MP] NetSync: remote net charge failed: " + ex.Message);
+ SyncLog.Warn(LogTopic.Nets, "NetSync: remote net charge failed: " + ex.Message);
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyCommit.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyCommit.cs
index 70adcc4..589e9d8 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyCommit.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyCommit.cs
@@ -6,6 +6,8 @@
using Unity.Collections;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
namespace CS2MultiplayerMod.Game.Sync.Systems.Net
{
@@ -73,7 +75,7 @@ private void CommitRemoteTemps(EntityQuery transactionQuery, int count)
}
catch (System.Exception ex)
{
- Diagnostics.FlightRecorder.Note("net isolated apply failed: " + ex.GetType().Name);
+ SyncLog.Trace(LogTopic.Nets, "net isolated apply failed: " + ex.GetType().Name);
InvalidateArmedBatch("isolated apply failed (" + ex.GetType().Name + ")", count);
return;
}
@@ -93,8 +95,8 @@ private void CommitRemoteTemps(EntityQuery transactionQuery, int count)
EntityManager.AddComponent(entity);
_protectedRemoteNetTemps.Add(entity);
}
- Diagnostics.FlightRecorder.Note("net commit shielded from preview clear temps=" +
- _protectedRemoteNetTemps.Count);
+ SyncLog.Trace(LogTopic.Nets, "net commit shielded from preview clear temps=" +
+ _protectedRemoteNetTemps.Count);
}
_pendingApply = false;
@@ -114,10 +116,10 @@ private void CommitRemoteTemps(EntityQuery transactionQuery, int count)
_drainRemainingTemps = int.MaxValue;
_suppressCaptureThisFrame = true;
_clearLocalNetIsolationAfterBarrier = true;
- Diagnostics.FlightRecorder.Note("remote " +
+ SyncLog.Trace(LogTopic.Nets, "remote " +
_committingTransactionKind.ToString().ToLowerInvariant() +
- " commit isolated (temps=" + count + ") validateMS=" + validateMs +
- " applyMS=" + (System.Environment.TickCount - applyStartTick));
+ " commit isolated (temps=" + count + ") validateMS=" + validateMs + " applyMS=" +
+ (System.Environment.TickCount - applyStartTick));
}
/// Members named individually before the composition line is truncated.
@@ -135,7 +137,7 @@ private void CommitRemoteTemps(EntityQuery transactionQuery, int count)
///
private void NoteTransactionComposition(RemoteToolTransactionKind kind, List members)
{
- if (!Diagnostics.FlightRecorder.Enabled) return;
+ if (!SyncLog.IsRecording(LogTopic.Nets)) return;
int edges = 0, nodes = 0, lanes = 0, aggregates = 0, objects = 0, areas = 0;
int deletedTagged = 0, missing = 0, sharedOriginals = 0;
@@ -195,11 +197,10 @@ private void NoteTransactionComposition(RemoteToolTransactionKind kind, List 0) detail.Append(" +").Append(unnamed).Append(" more");
- Diagnostics.FlightRecorder.Note("commit composition kind=" +
- kind.ToString().ToLowerInvariant() + " temps=" + members.Count +
- " edge=" + edges + " node=" + nodes + " lane=" + lanes +
- " aggr=" + aggregates + " obj=" + objects + " area=" + areas +
- " deletedTag=" + deletedTagged + " missing=" + missing +
+ SyncLog.Trace(LogTopic.Nets, "commit composition kind=" +
+ kind.ToString().ToLowerInvariant() + " temps=" + members.Count + " edge=" + edges +
+ " node=" + nodes + " lane=" + lanes + " aggr=" + aggregates + " obj=" + objects +
+ " area=" + areas + " deletedTag=" + deletedTagged + " missing=" + missing +
" sharedOriginal=" + sharedOriginals + " members=[" + detail + "]");
}
@@ -256,25 +257,16 @@ private void InvalidateArmedBatch(string reason, int count)
if (replay != null && !repeatsPreviousAttempt && _applyReplayBudget.TryConsume())
{
_replayAfterInvalidatedDrain = replay;
- Mod.log.Warn("[MP] NetApply: " + reason + "; draining rejected Temps before " +
- "re-queueing batch (attempt " +
- _applyReplayBudget.AttemptsUsed + "/" +
- _applyReplayBudget.MaximumAttempts + ").");
- Diagnostics.FlightRecorder.Note("net batch invalidated; drain then replay " +
- _applyReplayBudget.AttemptsUsed + "/" + _applyReplayBudget.MaximumAttempts);
+ SyncLog.Warn(LogTopic.Nets, "NetApply: " + reason +
+ "; draining rejected Temps before " + "re-queueing batch (attempt " +
+ _applyReplayBudget.AttemptsUsed + "/" + _applyReplayBudget.MaximumAttempts +
+ ").");
}
else
{
_replayAfterInvalidatedDrain = null;
- Mod.log.Warn("[MP] NetApply: " + reason + "; batch dropped" +
- (repeatsPreviousAttempt
- ? " - the rejection repeated unchanged, so further replays cannot " +
- "succeed."
- : replay != null
- ? " after " + _applyReplayBudget.AttemptsUsed + " replays."
- : "."));
- Diagnostics.FlightRecorder.Note("net batch invalidated; dropped" +
- (repeatsPreviousAttempt ? " (rejection is deterministic)" : string.Empty));
+ SyncLog.Warn(LogTopic.Nets, "NetApply: " + reason + "; batch dropped" +
+ (repeatsPreviousAttempt ? " - the rejection repeated unchanged, so further replays cannot " + "succeed." : replay != null ? " after " + _applyReplayBudget.AttemptsUsed + " replays." : "."));
SyncInbox.RequestResync(Diagnostics.ResyncReport
.Create(repeatsPreviousAttempt
? "remote transaction rejected deterministically"
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyDrain.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyDrain.cs
index 670f3de..87b2ed7 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyDrain.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyDrain.cs
@@ -6,6 +6,8 @@
using Unity.Collections;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
namespace CS2MultiplayerMod.Game.Sync.Systems.Net
{
@@ -119,11 +121,9 @@ private void PumpInvalidatedBatchDrain(bool allowReplay)
{
_invalidatedDrainTimedOut = true;
_replayAfterInvalidatedDrain = null;
- Diagnostics.SyncLog.ProdError(
+ Diagnostics.SyncLog.Error(LogTopic.Nets,
"Road sync: a rejected road transaction is still held by the game's own " +
"apply pass; no further road work can run until it finishes.");
- Diagnostics.FlightRecorder.Note(
- "quarantined net temps failed to drain; native work remains blocked");
NoteDrainReport("quarantined graph");
SyncInbox.RequestResync(Diagnostics.ResyncReport
.Create(DrainFailedReason, "net", Diagnostics.ResyncEvidence.Timeout)
@@ -146,7 +146,7 @@ private void PumpInvalidatedBatchDrain(bool allowReplay)
_invalidatedCleanFrames = 0;
_invalidatedDrainTimedOut = false;
_drainReleasedThisFrame = true;
- Diagnostics.FlightRecorder.Note("invalidated net transaction fully drained");
+ SyncLog.Trace(LogTopic.Nets, "invalidated net transaction fully drained");
// It drained after all. Withdraw the report before its hold matures: the window was
// too short for this batch, which is a tuning fact, not a reason to reload a world.
WithdrawDrainReport("the game's apply pass finished the batch after the window expired");
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyTemps.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyTemps.cs
index 5bff9d0..0009272 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyTemps.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyTemps.cs
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using Colossal.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using Game.Common;
using Game.Net;
using Game.Tools;
@@ -158,7 +160,7 @@ private void ProtectRemoteBatchForLocalToolOutput()
// driveway, or clear a subnet while leaving its owner behind.
ReleaseTrackedTemps(_isolatedLocalTemps);
_localToolOutputProtectedThisFrame = true;
- Diagnostics.FlightRecorder.Note("net remote batch protected for local " + tool.applyMode +
+ SyncLog.Trace(LogTopic.Nets, "net remote batch protected for local " + tool.applyMode +
" (remote=" + _protectedRemoteNetTemps.Count + ")");
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyValidateNet.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyValidateNet.cs
index d765f32..dc63d56 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyValidateNet.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyValidateNet.cs
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using Colossal.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using Game.Common;
using Game.Net;
using Game.Tools;
@@ -117,9 +119,9 @@ private bool ValidateArmedNetTransaction(out string reason)
reason = null;
if (attachedObjectRoots > 0 || areaEntities > 0 || _relinkedOwners > 0)
- Diagnostics.FlightRecorder.Note("net side-effect graph validated temps=" +
- temps.Length + " attachedRoots=" + attachedObjectRoots +
- " areas=" + areaEntities +
+ SyncLog.Trace(LogTopic.Nets, "net side-effect graph validated temps=" +
+ temps.Length + " attachedRoots=" + attachedObjectRoots + " areas=" +
+ areaEntities +
(_relinkedOwners > 0 ? " ownersRelinked=" + _relinkedOwners : string.Empty));
return true;
}
@@ -222,7 +224,7 @@ private bool ValidateTransactionOwner(Entity entity, HashSet members, ou
// One line per orphan would be hundreds on a large placement; the pass reports a
// total, and the first member is enough to identify which graph needed repair.
if (_relinkedOwners++ == 0)
- Diagnostics.FlightRecorder.Note("transaction owner re-linked " +
+ SyncLog.Trace(LogTopic.Nets, "transaction owner re-linked " +
DescribeTransactionEntity(entity) + " owner=#" + relinked.Index);
owner = relinked;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyValidateObject.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyValidateObject.cs
index 0df7f4e..5aaeccc 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyValidateObject.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/ApplyValidateObject.cs
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using Colossal.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using Game.Common;
using Game.Net;
using Game.Tools;
@@ -120,7 +122,7 @@ private bool ValidateArmedObjectTransaction(out string reason)
}
reason = null;
- Diagnostics.FlightRecorder.Note("object transaction validated temps=" + temps.Length +
+ SyncLog.Trace(LogTopic.Nets, "object transaction validated temps=" + temps.Length +
(_relinkedOwners > 0 ? " ownersRelinked=" + _relinkedOwners : string.Empty));
return true;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Capture.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Capture.cs
index eeec61f..950403b 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Capture.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Capture.cs
@@ -6,8 +6,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems.Net
@@ -33,7 +34,7 @@ private void FlushDiagnostics(long now)
if (_diagTotal > 0)
{
var sb = new StringBuilder();
- sb.Append("[MP] NetSync captured ").Append(_diagTotal)
+ sb.Append("NetSync captured ").Append(_diagTotal)
.Append(" road segment(s)/5s across ").Append(_diag.Count).Append(" prefab(s): ");
int n = 0;
foreach (var pair in _diag)
@@ -42,57 +43,47 @@ private void FlushDiagnostics(long now)
sb.Append(pair.Key).Append(" x").Append(pair.Value);
if (++n >= 12) { sb.Append(", ..."); break; }
}
- Mod.Verbose(sb.ToString());
+ SyncLog.Detail(LogTopic.Nets, sb.ToString());
}
if (_peakUpdated > 0 || _peakDeleted > 0 || _diagTotal > 0 || _capFilteredHalves > 0)
{
- Mod.Verbose("[MP] NetSync edge tags/5s peak: Created=" + _peakCreated +
- " Updated=" + _peakUpdated + " Deleted=" + _peakDeleted +
- "; dropped " + _capFilteredHalves + " split-half edge(s) (side-effects of a " +
- "mid-span tap; only the drawn edge is sent so the receiver splits locally).");
+ SyncLog.Detail(LogTopic.Nets, "NetSync edge tags/5s peak: Created=" + _peakCreated +
+ " Updated=" + _peakUpdated + " Deleted=" + _peakDeleted + "; dropped " +
+ _capFilteredHalves + " split-half edge(s) (side-effects of a " +
+ "mid-span tap; only the drawn edge is sent so the receiver splits locally).");
}
if (_rzSegments > 0)
{
- Mod.Verbose("[MP] NetSync realized " + _rzSegments + " remote segment(s)/5s; endpoints: " +
- _rzSnapEnds + " reused a node, " + _rzMergeEnds +
- " merged a shared new node, " + _rzMidEnds +
- " split an existing edge (T-junction), " +
- _rzFreeEnds + " free ground.");
+ SyncLog.Detail(LogTopic.Nets, "NetSync realized " + _rzSegments +
+ " remote segment(s)/5s; endpoints: " + _rzSnapEnds + " reused a node, " +
+ _rzMergeEnds + " merged a shared new node, " + _rzMidEnds +
+ " split an existing edge (T-junction), " + _rzFreeEnds + " free ground.");
}
if (_capPinnedSpans > 0 || _rzPinnedSpans > 0 || _rzPinRefused > 0)
{
- Mod.log.Info("[MP] NetSync water profile/5s: captured " + _capPinnedSpans +
- " span(s) over water as " + _capPinnedPieces +
- " pinned piece(s); realized " + _rzPinnedSpans +
- " pinned span(s), " + _rzPinRefused +
- " refused (those rebuild their deck from local water).");
- Diagnostics.FlightRecorder.Note("net water pin captured=" + _capPinnedSpans +
- " pieces=" + _capPinnedPieces +
- " realized=" + _rzPinnedSpans +
- " refused=" + _rzPinRefused);
+ SyncLog.Detail(LogTopic.Nets, "NetSync water profile/5s: captured " +
+ _capPinnedSpans + " span(s) over water as " + _capPinnedPieces +
+ " pinned piece(s); realized " + _rzPinnedSpans + " pinned span(s), " +
+ _rzPinRefused + " refused (those rebuild their deck from local water).");
}
if (_rzSurfaceCorrections > 0)
{
- Mod.log.Info("[MP] NetSync: " + _rzSurfaceCorrections + " remote endpoint(s)/5s needed " +
- "an elevation correction (up to " +
- _rzSurfaceCorrectionMax.ToString("F1") + " m) because the surface under " +
- "them differs from the source's. The height is reproduced; a large or " +
- "growing figure means terrain or water is out of step.");
- Diagnostics.FlightRecorder.Note("net surface correction ends=" + _rzSurfaceCorrections +
- " maxM=" + _rzSurfaceCorrectionMax.ToString("F1"));
+ SyncLog.Detail(LogTopic.Nets, "NetSync: " + _rzSurfaceCorrections +
+ " remote endpoint(s)/5s needed " + "an elevation correction (up to " +
+ _rzSurfaceCorrectionMax.ToString("F1") + " m) because the surface under " +
+ "them differs from the source's. The height is reproduced; a large or " +
+ "growing figure means terrain or water is out of step.");
}
if (_rzLocalSurfaceMatches > 0)
{
- Mod.log.Info("[MP] NetSync: " + _rzLocalSurfaceMatches +
- " utility endpoint(s)/5s reused connectivity through local-surface " +
- "height projection instead of creating an overlapping free node.");
- Diagnostics.FlightRecorder.Note("net utility local-surface matches=" +
- _rzLocalSurfaceMatches);
+ SyncLog.Detail(LogTopic.Nets, "NetSync: " + _rzLocalSurfaceMatches +
+ " utility endpoint(s)/5s reused connectivity through local-surface " +
+ "height projection instead of creating an overlapping free node.");
}
_diag.Clear();
@@ -280,8 +271,8 @@ private void CaptureNewEdges(MultiplayerSession session, long now)
}
if (stubs > 0)
- Diagnostics.FlightRecorder.Note("net per-edge fallback sent=" + stubs +
- " (no native operation covered this apply)");
+ SyncLog.Trace(LogTopic.Nets, "net per-edge fallback sent=" + stubs +
+ " (no native operation covered this apply)");
}
///
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Intent.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Intent.cs
index 5207cba..44ead9a 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Intent.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Intent.cs
@@ -7,6 +7,8 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems.Net
@@ -194,8 +196,8 @@ public void ObserveLocalNetDefinitions(NativeArray definitions)
_cachedFallbackOriginalEdges.Clear();
_cachedNeedsFinalEdgeFallback = false;
_cachedLocalMixedOperation.AddRange(mixed);
- Diagnostics.FlightRecorder.Note("net atomic mixed capture cached items=" +
- mixed.Count + " placements=" + next.Count + " mutations=" + mutations +
+ SyncLog.Trace(LogTopic.Nets, "net atomic mixed capture cached items=" + mixed.Count +
+ " placements=" + next.Count + " mutations=" + mutations +
(hiddenSubNets > 0 ? " hiddenSubNets=" + hiddenSubNets : string.Empty));
return;
}
@@ -207,14 +209,12 @@ public void ObserveLocalNetDefinitions(NativeArray definitions)
_cachedFallbackOriginalEdges.AddRange(rejectedOriginalEdges);
_cachedNeedsFinalEdgeFallback = true;
- Mod.log.Warn("[MP] NetSync: local mixed net operation cannot be encoded atomically (" +
- rejected + " of " + (rejected + next.Count) + " courses " + rejection +
- "; " + hiddenSubNets + " generated connector(s) skipped); the legacy " +
- "fragmented fallback is disabled and world recovery will be requested " +
- "after Apply.");
- Diagnostics.FlightRecorder.Note("net native capture voided rejected=" + rejected + "/" +
- (rejected + next.Count) +
- " hiddenSubNets=" + hiddenSubNets);
+ SyncLog.Warn(LogTopic.Nets,
+ "NetSync: local mixed net operation cannot be encoded atomically (" + rejected +
+ " of " + (rejected + next.Count) + " courses " + rejection + "; " + hiddenSubNets +
+ " generated connector(s) skipped); the legacy " +
+ "fragmented fallback is disabled and world recovery will be requested " +
+ "after Apply.");
}
///
@@ -284,7 +284,7 @@ private void CaptureLocalNetApply(bool refreshStandingDefinitions, bool barrierR
_atomicMixedOriginals.Add(_cachedFallbackOriginalEdges[i]);
service.RequestAutomaticWorldRecovery(
"mixed road operation could not be encoded atomically");
- Diagnostics.FlightRecorder.Note("net mixed capture rejected; recovery requested" +
+ SyncLog.Trace(LogTopic.Nets, "net mixed capture rejected; recovery requested" +
(barrierRecovery ? " source=barrier" : string.Empty));
_cachedFallbackOriginalEdges.Clear();
_cachedNeedsFinalEdgeFallback = false;
@@ -325,8 +325,8 @@ private void CaptureLocalNetApply(bool refreshStandingDefinitions, bool barrierR
catch (System.Exception ex)
{
_cachedLocalCourses.Clear();
- Mod.log.Warn("[MP] NetSync intent capture could not encode operation; " +
- "using final-edge capture: " + ex.Message);
+ SyncLog.Warn(LogTopic.Nets, "NetSync intent capture could not encode operation; " +
+ "using final-edge capture: " + ex.Message);
return;
}
@@ -342,8 +342,8 @@ private void CaptureLocalNetApply(bool refreshStandingDefinitions, bool barrierR
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] NetSync intent capture dropped course " + i + "/" + count +
- ": " + ex.Message);
+ SyncLog.Warn(LogTopic.Nets, "NetSync intent capture dropped course " + i + "/" +
+ count + ": " + ex.Message);
}
}
@@ -353,9 +353,8 @@ private void CaptureLocalNetApply(bool refreshStandingDefinitions, bool barrierR
// enabled to provide a complete geometry fallback for this local apply.
if (sent == count) _nativeApplyCapturedFrame = _realizeFrame;
if (sent > 0)
- Diagnostics.FlightRecorder.Note("net intent apply op=" + operationId + " courses=" +
- sent + "/" + count +
- (barrierRecovery ? " source=barrier" : string.Empty));
+ SyncLog.Trace(LogTopic.Nets, "net intent apply op=" + operationId + " courses=" +
+ sent + "/" + count + (barrierRecovery ? " source=barrier" : string.Empty));
}
///
@@ -447,7 +446,8 @@ private void CaptureAtomicMixedNetApply(MultiplayerService service, bool barrier
// A fragmented fallback is exactly the failure this envelope prevents. Suppress
// every legacy echo even when encoding/sending fails, and repair the peer from a
// world snapshot instead of racing delete/replace/place streams again.
- Mod.log.Warn("[MP] NetSync atomic mixed apply could not be sent: " + ex.Message);
+ SyncLog.Warn(LogTopic.Nets, "NetSync atomic mixed apply could not be sent: " +
+ ex.Message);
service.RequestAutomaticWorldRecovery("atomic mixed road operation could not be sent");
}
finally
@@ -460,10 +460,9 @@ private void CaptureAtomicMixedNetApply(MultiplayerService service, bool barrier
_atomicMixedApplyCapturedFrame = _realizeFrame;
}
- Diagnostics.FlightRecorder.Note("net atomic mixed apply op=" + operationId +
- " items=" + itemCount +
- " status=" + (sent ? "sent" : "recovery") +
- (barrierRecovery ? " source=barrier" : string.Empty));
+ SyncLog.Trace(LogTopic.Nets, "net atomic mixed apply op=" + operationId + " items=" +
+ itemCount + " status=" + (sent ? "sent" : "recovery") +
+ (barrierRecovery ? " source=barrier" : string.Empty));
}
///
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Lifecycle.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Lifecycle.cs
index 84f8142..052dd1f 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Lifecycle.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Lifecycle.cs
@@ -6,9 +6,10 @@
using Game.Prefabs;
using Game.Tools;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems.Net
@@ -21,7 +22,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(NetSyncSystem) + " ready.");
// An owned connector re-cut beside an already-standing building names an owner that is
// live, not part of the transaction. Owner resolution only ever matches a Temp to a
// Temp, so that link has to be found by asking what stands at the described point.
@@ -297,7 +297,7 @@ protected override void OnUpdate()
// ModificationEnd where the one-frame Created/Updated/Deleted tags are still alive.
// Each count walks every matching chunk, and the only thing they feed is a verbose
// line - so they are not paid at all unless someone is reading it.
- if (Mod.VerboseEnabled)
+ if (SyncLog.IsEnabled(LogTopic.Nets))
{
_peakCreated = System.Math.Max(_peakCreated, _createdEdges.CalculateEntityCount());
_peakUpdated = System.Math.Max(_peakUpdated, _updatedEdges.CalculateEntityCount());
@@ -326,8 +326,9 @@ public override void OnCommandReceived(SimulationCommandMessage command)
if (command.Body == null || command.Body.Length > cap) return;
if (mixed && _sink.Count >= MixedNetInboxAdmissionCap)
{
- Mod.log.Warn("[MP] NetSync: mixed-operation inbox admission cap reached; " +
- "requesting recovery instead of dropping an atomic edit silently.");
+ SyncLog.Warn(LogTopic.Nets,
+ "NetSync: mixed-operation inbox admission cap reached; " +
+ "requesting recovery instead of dropping an atomic edit silently.");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("mixed net operation inbox overflow", "net",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.StreamLoss)
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/MixedOperation.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/MixedOperation.cs
index 7b93ecb..4685bf2 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/MixedOperation.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/MixedOperation.cs
@@ -8,8 +8,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -102,8 +104,8 @@ private void RealizeMixedNetOperation(MultiplayerSession session,
};
if (_completedNetOperations.Contains(key, now))
{
- Diagnostics.FlightRecorder.Note("net mixed operation duplicate suppressed op=" +
- operation.OperationId);
+ SyncLog.Trace(LogTopic.Nets, "net mixed operation duplicate suppressed op=" +
+ operation.OperationId);
return;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/MixedOperationBuild.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/MixedOperationBuild.cs
index 087c0a9..5e9f234 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/MixedOperationBuild.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/MixedOperationBuild.cs
@@ -8,7 +8,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -186,8 +188,8 @@ private bool BuildAndArmMixedOperation(SimulationCommandMessage source,
{
CancelPreparedDefinitionFrame();
_completedNetOperations.Remember(key, now, 60000);
- Diagnostics.FlightRecorder.Note("net mixed operation already present op=" +
- operation.OperationId);
+ SyncLog.Trace(LogTopic.Nets, "net mixed operation already present op=" +
+ operation.OperationId);
return false;
}
@@ -204,8 +206,8 @@ private bool BuildAndArmMixedOperation(SimulationCommandMessage source,
{
long completedNow = Mod.Service != null ? Mod.Service.NowMs : now;
_completedNetOperations.Remember(key, completedNow, 60000);
- Diagnostics.FlightRecorder.Note("net mixed operation committed/drained op=" +
- operation.OperationId);
+ SyncLog.Trace(LogTopic.Nets, "net mixed operation committed/drained op=" +
+ operation.OperationId);
}, "mixed operation n=" + created.Count);
if (!armed)
throw new System.InvalidOperationException(
@@ -242,13 +244,9 @@ private bool BuildAndArmMixedOperation(SimulationCommandMessage source,
CancelPreparedDefinitionFrame();
}
_operationBuildFailures.Remove(key);
- Mod.log.Warn("[MP] NetSync: mixed operation " + operation.OperationId +
- (commitArmed ? " failed after its atomic commit was armed: " :
- " rolled back before generation: ") + ex.Message +
- "; requesting world recovery.");
- Diagnostics.FlightRecorder.Note((commitArmed ?
- "net mixed operation post-arm failure/resync op=" :
- "net mixed operation rollback/resync op=") + operation.OperationId);
+ SyncLog.Warn(LogTopic.Nets, "NetSync: mixed operation " + operation.OperationId +
+ (commitArmed ? " failed after its atomic commit was armed: " : " rolled back before generation: ") +
+ ex.Message + "; requesting world recovery.");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("mixed net operation could not be generated atomically", "net",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
@@ -299,8 +297,8 @@ private void HandleMixedPreflightFailure(SimulationCommandMessage source,
}
_nativeOperationHolds.Remove(key);
- Diagnostics.FlightRecorder.Note("net mixed operation rejected/resync op=" +
- operation.OperationId);
+ SyncLog.Trace(LogTopic.Nets, "net mixed operation rejected/resync op=" +
+ operation.OperationId);
}
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Realize.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Realize.cs
index 859bf6e..cb55cbd 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Realize.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/Realize.cs
@@ -9,9 +9,10 @@
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems.Net
@@ -188,8 +189,8 @@ private void RealizeIncoming(MultiplayerSession session, long now)
};
if (_completedNetOperations.Contains(completedKey, now))
{
- Diagnostics.FlightRecorder.Note("net operation duplicate suppressed op=" +
- completedHeader.OperationId);
+ SyncLog.Trace(LogTopic.Nets, "net operation duplicate suppressed op=" +
+ completedHeader.OperationId);
return;
}
hasCompletedKey = true;
@@ -280,8 +281,9 @@ private void RealizeIncoming(MultiplayerSession session, long now)
try { command = NetPlacementCommand.Decode(work[i].Body); }
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] NetSync: native operation became malformed during preflight: " +
- ex.Message + "; dropping whole operation.");
+ SyncLog.Warn(LogTopic.Nets,
+ "NetSync: native operation became malformed during preflight: " +
+ ex.Message + "; dropping whole operation.");
return;
}
@@ -290,8 +292,9 @@ private void RealizeIncoming(MultiplayerSession session, long now)
!EntityManager.HasComponent(prefab) ||
!EntityManager.HasComponent(prefab))
{
- Mod.log.Warn("[MP] NetSync: native operation references unavailable net prefab '" +
- command.PrefabName + "'; dropping whole operation.");
+ SyncLog.Warn(LogTopic.Nets,
+ "NetSync: native operation references unavailable net prefab '" +
+ command.PrefabName + "'; dropping whole operation.");
return;
}
if (!string.IsNullOrEmpty(command.SubPrefabName))
@@ -300,8 +303,9 @@ private void RealizeIncoming(MultiplayerSession session, long now)
if (!_prefabIndex.TryResolve(command.SubPrefabName, out subPrefab) ||
!EntityManager.HasComponent(subPrefab))
{
- Mod.log.Warn("[MP] NetSync: native operation references unavailable lane prefab '" +
- command.SubPrefabName + "'; dropping whole operation.");
+ SyncLog.Warn(LogTopic.Nets,
+ "NetSync: native operation references unavailable lane prefab '" +
+ command.SubPrefabName + "'; dropping whole operation.");
return;
}
}
@@ -322,8 +326,9 @@ private void RealizeIncoming(MultiplayerSession session, long now)
if (!math.isfinite(measuredLength) ||
(measuredLength < NetPlacementCommand.MinCourseLength && !nativePoint))
{
- Mod.log.Warn("[MP] NetSync: native operation " + command.OperationId +
- " contains a degenerate course; dropping the whole operation.");
+ SyncLog.Warn(LogTopic.Nets, "NetSync: native operation " +
+ command.OperationId +
+ " contains a degenerate course; dropping the whole operation.");
return;
}
@@ -332,8 +337,9 @@ private void RealizeIncoming(MultiplayerSession session, long now)
float lengthTolerance = math.max(0.05f, measuredLength * 0.01f);
if (math.abs(command.Length - measuredLength) > lengthTolerance)
{
- Mod.log.Warn("[MP] NetSync: native operation " + command.OperationId +
- " has an inconsistent course length; dropping the whole operation.");
+ SyncLog.Warn(LogTopic.Nets, "NetSync: native operation " +
+ command.OperationId +
+ " has an inconsistent course length; dropping the whole operation.");
return;
}
@@ -343,8 +349,9 @@ private void RealizeIncoming(MultiplayerSession session, long now)
CreationFlags.Construction | CreationFlags.SubElevation;
if ((((CreationFlags)command.CreationFlags) & ~allowedNativeFlags) != 0)
{
- Mod.log.Warn("[MP] NetSync: native operation " + command.OperationId +
- " contains an unsafe creation mode; dropping the whole operation.");
+ SyncLog.Warn(LogTopic.Nets, "NetSync: native operation " +
+ command.OperationId +
+ " contains an unsafe creation mode; dropping the whole operation.");
SyncInbox.RequestResync(Diagnostics.ResyncReport
.Create("unsafe native net creation flags", "net",
Diagnostics.ResyncEvidence.StreamLoss)
@@ -433,7 +440,7 @@ private void RealizeIncoming(MultiplayerSession session, long now)
}
if (geometryAlreadyBuilt && topologyNeedsReplay)
- Diagnostics.FlightRecorder.Note("net native topology replay op=" +
+ SyncLog.Trace(LogTopic.Nets, "net native topology replay op=" +
command.OperationId + " course=" + command.CourseIndex);
// A course whose geometry and endpoint topology are already present is this
@@ -449,15 +456,14 @@ private void RealizeIncoming(MultiplayerSession session, long now)
operationRetryKey.Origin),
"the operation turned out to be already present");
_operationBuildFailures.Remove(operationRetryKey);
- Diagnostics.FlightRecorder.Note("net native op already present=" +
- operationHeader.OperationId +
- " courses=" + work.Count);
+ SyncLog.Trace(LogTopic.Nets, "net native op already present=" +
+ operationHeader.OperationId + " courses=" + work.Count);
_completedNetOperations.Remember(operationRetryKey, now, 60000);
return;
}
if (alreadyBuiltCourses > 0)
- Diagnostics.FlightRecorder.Note("net native op reconcile existing=" +
- alreadyBuiltCourses + "/" + work.Count);
+ SyncLog.Trace(LogTopic.Nets, "net native op reconcile existing=" +
+ alreadyBuiltCourses + "/" + work.Count);
if (unresolvedOperationTarget)
{
@@ -509,14 +515,13 @@ private void RealizeIncoming(MultiplayerSession session, long now)
}
_nativeOperationHolds.Remove(operationRetryKey);
- Diagnostics.FlightRecorder.Note("net native operation rejected/resync op=" +
- operationHeader.OperationId + " " +
- unresolvedDetail);
+ SyncLog.Trace(LogTopic.Nets, "net native operation rejected/resync op=" +
+ operationHeader.OperationId + " " + unresolvedDetail);
return;
}
if (allowMergedNodeSplit)
- Diagnostics.FlightRecorder.Note("net native node target recovered op=" +
- operationHeader.OperationId);
+ SyncLog.Trace(LogTopic.Nets, "net native node target recovered op=" +
+ operationHeader.OperationId);
ClearOperationHold(operationRetryKey, UnresolvedNativeTargetReason,
NativeOperationSubject(operationHeader.OperationId, operationRetryKey.Origin),
"every endpoint resolved on a later attempt");
@@ -527,9 +532,8 @@ private void RealizeIncoming(MultiplayerSession session, long now)
if (aliasedSplitTarget)
{
_operationBuildFailures.Remove(operationRetryKey);
- Diagnostics.FlightRecorder.Note("net native op aliased split target op=" +
- operationHeader.OperationId +
- " courses=" + work.Count);
+ SyncLog.Trace(LogTopic.Nets, "net native op aliased split target op=" +
+ operationHeader.OperationId + " courses=" + work.Count);
SyncInbox.RequestResync(Diagnostics.ResyncReport
.Create("net split target aliased by local divergence", "net",
Diagnostics.ResyncEvidence.Contradiction)
@@ -550,9 +554,9 @@ private void RealizeIncoming(MultiplayerSession session, long now)
// through, but record it: the source applied a different course set than this
// batch will, and CourseSplitSystem resolves intersections from what it is given.
if (alreadyBuiltCourses > 0 && !_armedNetOperations.Contains(operationRetryKey, now))
- Diagnostics.FlightRecorder.Note("net native op partial on first sight op=" +
- operationHeader.OperationId + " present=" +
- alreadyBuiltCourses + "/" + work.Count);
+ SyncLog.Trace(LogTopic.Nets, "net native op partial on first sight op=" +
+ operationHeader.OperationId + " present=" + alreadyBuiltCourses + "/" +
+ work.Count);
_armedNetOperations.Remember(operationRetryKey, now, ArmedOperationWindowMs);
}
@@ -584,7 +588,8 @@ private void RealizeIncoming(MultiplayerSession session, long now)
try { command = NetPlacementCommand.Decode(message.Body); }
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] NetSync: dropping malformed command: " + ex.Message);
+ SyncLog.Warn(LogTopic.Nets, "NetSync: dropping malformed command: " +
+ ex.Message);
continue;
}
@@ -592,9 +597,9 @@ private void RealizeIncoming(MultiplayerSession session, long now)
!EntityManager.HasComponent(prefab) ||
!EntityManager.HasComponent(prefab))
{
- Mod.log.Warn("[MP] NetSync realize: unavailable net prefab '" +
- command.PrefabName + "' from player " +
- message.OriginPlayerId + "; skipping.");
+ SyncLog.Warn(LogTopic.Nets, "NetSync realize: unavailable net prefab '" +
+ command.PrefabName + "' from player " + message.OriginPlayerId +
+ "; skipping.");
continue;
}
@@ -610,8 +615,9 @@ private void RealizeIncoming(MultiplayerSession session, long now)
if (!math.isfinite(measuredLength) ||
measuredLength < NetPlacementCommand.MinCourseLength)
{
- Mod.log.Warn("[MP] NetSync realize: degenerate fallback course for '" +
- command.PrefabName + "'; skipping.");
+ SyncLog.Warn(LogTopic.Nets,
+ "NetSync realize: degenerate fallback course for '" +
+ command.PrefabName + "'; skipping.");
continue;
}
// Geometry-only fallback has no exact native length, so derive it locally.
@@ -816,8 +822,8 @@ private void RealizeIncoming(MultiplayerSession session, long now)
ex.GetType().Name + ")";
break;
}
- Mod.log.Error("[MP] NetSync realize FAILED for '" + command.PrefabName +
- "': " + ex);
+ SyncLog.Error(LogTopic.Nets, "NetSync realize FAILED for '" +
+ command.PrefabName + "': " + ex);
}
}
@@ -862,11 +868,8 @@ private void RealizeIncoming(MultiplayerSession session, long now)
if (retry) outcome = "; retrying the whole operation (" + failures + "/3).";
else if (abortAliasedSplit) outcome = "; dropped and requested world recovery.";
else outcome = "; dropped after 3 retries.";
- Mod.log.Warn("[MP] NetSync: native operation rolled back before generation - " +
- abortReason + outcome);
- Diagnostics.FlightRecorder.Note(abortAliasedSplit
- ? "net native op aliased split target op=" + header.OperationId
- : "net native op rollback before generation retry=" + (retry ? failures : 0));
+ SyncLog.Warn(LogTopic.Nets, "NetSync: native operation " + header.OperationId +
+ " rolled back before generation - " + abortReason + outcome);
return;
}
@@ -898,7 +901,8 @@ private void RealizeIncoming(MultiplayerSession session, long now)
{
constructionCost = 0;
chargedCourses = 0;
- Mod.log.Warn("[MP] NetSync: could not calculate remote net charge: " + ex.Message);
+ SyncLog.Warn(LogTopic.Nets, "NetSync: could not calculate remote net charge: " +
+ ex.Message);
}
// Publish echo guards and diagnostics only after every definition selected for this
// operation exists. A failed later course therefore cannot leave a phantom realized
@@ -961,11 +965,12 @@ private void RealizeIncoming(MultiplayerSession session, long now)
{
long completedNow = Mod.Service != null ? Mod.Service.NowMs : now;
_completedNetOperations.Remember(completionKey, completedNow, 60000);
- Diagnostics.FlightRecorder.Note("net operation committed/drained op=" +
- completionKey.Operation);
+ SyncLog.Trace(LogTopic.Nets, "net operation committed/drained op=" +
+ completionKey.Operation);
};
}
- Diagnostics.FlightRecorder.Note("net build batch armed n=" + built + (splitUsed ? " +split" : ""));
+ SyncLog.Trace(LogTopic.Nets, "net build batch armed n=" + built +
+ (splitUsed ? " +split" : ""));
}
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/RealizeHold.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/RealizeHold.cs
index dff4dc5..4d557eb 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/RealizeHold.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/RealizeHold.cs
@@ -9,8 +9,9 @@
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems.Net
@@ -80,8 +81,8 @@ private bool HoldUnresolvedOperation(NetOperationKey key, long now, long operati
Windows = 1,
};
_nativeOperationHolds[key] = hold;
- Diagnostics.FlightRecorder.Note("net native target retry op=" + operationId +
- " " + detail);
+ SyncLog.Trace(LogTopic.Nets, "net native target retry op=" + operationId + " " +
+ detail);
}
windows = hold.Windows;
return now < hold.DeadlineMs;
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/RealizeOperation.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/RealizeOperation.cs
index 81deb75..6cd9d13 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/RealizeOperation.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetSyncSystem/RealizeOperation.cs
@@ -9,9 +9,10 @@
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems.Net
@@ -108,8 +109,8 @@ private bool TryTakeCompleteOperation(MultiplayerSession session, long now,
try { mixedOperation = NetToolOperationCommand.Decode(message.Body); }
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] NetSync: dropping malformed mixed net operation: " +
- ex.Message);
+ SyncLog.Warn(LogTopic.Nets,
+ "NetSync: dropping malformed mixed net operation: " + ex.Message);
SyncInbox.RequestResync(Diagnostics.ResyncReport
.Create("malformed mixed net operation", "net",
Diagnostics.ResyncEvidence.StreamLoss)
@@ -130,8 +131,8 @@ private bool TryTakeCompleteOperation(MultiplayerSession session, long now,
}
if (message.CommandId != NetPlacementCommand.Id)
{
- Mod.log.Warn("[MP] NetSync: dropping unsupported queued command " +
- message.CommandId + ".");
+ SyncLog.Warn(LogTopic.Nets, "NetSync: dropping unsupported queued command " +
+ message.CommandId + ".");
continue;
}
@@ -139,7 +140,8 @@ private bool TryTakeCompleteOperation(MultiplayerSession session, long now,
try { command = NetPlacementCommand.Decode(message.Body); }
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] NetSync: dropping malformed command: " + ex.Message);
+ SyncLog.Warn(LogTopic.Nets, "NetSync: dropping malformed command: " +
+ ex.Message);
continue;
}
@@ -160,8 +162,9 @@ private bool TryTakeCompleteOperation(MultiplayerSession session, long now,
continue;
if (command.CourseCount != expected)
{
- Mod.log.Warn("[MP] NetSync: dropping inconsistent course count for op=" +
- key.Operation + " from player " + key.Origin + ".");
+ SyncLog.Warn(LogTopic.Nets,
+ "NetSync: dropping inconsistent course count for op=" + key.Operation +
+ " from player " + key.Origin + ".");
continue;
}
@@ -205,7 +208,7 @@ private bool TryTakeCompleteOperation(MultiplayerSession session, long now,
later.Add(scanned[i]);
}
RequeueAtFront(later);
- Diagnostics.FlightRecorder.Note("net incomplete op dropped=" + key.Operation +
+ SyncLog.Trace(LogTopic.Nets, "net incomplete op dropped=" + key.Operation +
" courses=" + received + "/" + expected);
SyncInbox.RequestResync(Diagnostics.ResyncReport
.Create("incomplete net operation expired", "net",
@@ -254,8 +257,8 @@ private bool TryTakeCompleteOperation(MultiplayerSession session, long now,
// from smuggling a partially native operation into per-course fallback realization.
if ((hasNativeCourse && hasGeometryOnlyCourse) || (expected > 1 && !nativeOperation))
{
- Diagnostics.FlightRecorder.Note("net incompatible multi-course op dropped=" +
- key.Operation);
+ SyncLog.Trace(LogTopic.Nets, "net incompatible multi-course op dropped=" +
+ key.Operation);
SyncInbox.RequestResync(Diagnostics.ResyncReport
.Create("incompatible net operation rejected", "net",
Diagnostics.ResyncEvidence.StreamLoss)
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetUpgradeSyncSystem/Apply.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetUpgradeSyncSystem/Apply.cs
index 042fb67..837b6e3 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetUpgradeSyncSystem/Apply.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetUpgradeSyncSystem/Apply.cs
@@ -11,6 +11,8 @@
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
{
@@ -58,10 +60,10 @@ private void Apply(List commands, long now)
_retry.Add((nodeTargets[t].cmd, now + RetryWindowMs));
if (applied > 0)
- Mod.Verbose("[MP] NetUpgradeSync: applied " + applied + " road upgrade(s)" +
- (edgeTargets.Count + nodeTargets.Count > 0
- ? ", " + (edgeTargets.Count + nodeTargets.Count) + " waiting for their segment"
- : "") + ".");
+ SyncLog.Detail(LogTopic.Nets, "NetUpgradeSync: applied " + applied +
+ " road upgrade(s)" +
+ (edgeTargets.Count + nodeTargets.Count > 0 ? ", " + (edgeTargets.Count + nodeTargets.Count) + " waiting for their segment" : "") +
+ ".");
}
private int ApplyEdges(List<(Entity prefab, float3 a, float3 d, NetUpgradeCommand cmd)> targets)
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetUpgradeSyncSystem/Capture.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetUpgradeSyncSystem/Capture.cs
index a1c1e06..b7cbb74 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetUpgradeSyncSystem/Capture.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetUpgradeSyncSystem/Capture.cs
@@ -10,8 +10,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
{
@@ -63,7 +64,8 @@ private void CaptureEdgeUpgrades(MultiplayerSession session)
SubReps = subs,
};
session.SendCommand(0, NetUpgradeCommand.Id, command.Encode());
- Mod.Verbose("[MP] NetUpgradeSync captured upgrade on '" + name + "'.");
+ SyncLog.Detail(LogTopic.Nets, "NetUpgradeSync captured upgrade on '" + name +
+ "'.");
}
}
finally
@@ -98,7 +100,8 @@ private void CaptureEdgeClears(MultiplayerSession session)
Dx = b.d.x, Dy = b.d.y, Dz = b.d.z,
};
session.SendCommand(0, NetUpgradeCommand.Id, command.Encode());
- Mod.Verbose("[MP] NetUpgradeSync captured upgrade REMOVAL on '" + name + "'.");
+ SyncLog.Detail(LogTopic.Nets, "NetUpgradeSync captured upgrade REMOVAL on '" +
+ name + "'.");
}
}
finally
@@ -144,7 +147,8 @@ private void CaptureNodeUpgrades(MultiplayerSession session)
IsNode = true,
};
session.SendCommand(0, NetUpgradeCommand.Id, command.Encode());
- Mod.Verbose("[MP] NetUpgradeSync captured node upgrade at '" + name + "'.");
+ SyncLog.Detail(LogTopic.Nets, "NetUpgradeSync captured node upgrade at '" + name +
+ "'.");
}
}
finally
@@ -180,7 +184,8 @@ private void CaptureNodeClears(MultiplayerSession session)
IsNode = true,
};
session.SendCommand(0, NetUpgradeCommand.Id, command.Encode());
- Mod.Verbose("[MP] NetUpgradeSync captured node upgrade REMOVAL at '" + name + "'.");
+ SyncLog.Detail(LogTopic.Nets,
+ "NetUpgradeSync captured node upgrade REMOVAL at '" + name + "'.");
}
}
finally
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetUpgradeSyncSystem/NetUpgradeSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetUpgradeSyncSystem/NetUpgradeSyncSystem.cs
index dc9f33d..9feaab0 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetUpgradeSyncSystem/NetUpgradeSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Nets/NetUpgradeSyncSystem/NetUpgradeSyncSystem.cs
@@ -10,9 +10,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -95,7 +96,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(NetUpgradeSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_prefabIndex = new PrefabIndex(_prefabSystem, GetEntityQuery(ComponentType.ReadOnly()));
@@ -219,7 +219,8 @@ private void SeedLastSeen()
};
}
if (entities.Length > 0)
- Mod.Verbose("[MP] NetUpgradeSync: seeded " + entities.Length + " existing upgrade(s).");
+ SyncLog.Detail(LogTopic.Nets, "NetUpgradeSync: seeded " + entities.Length +
+ " existing upgrade(s).");
}
finally
{
@@ -253,7 +254,7 @@ public void RealizePending()
{
if (message.OriginPlayerId == session.LocalPlayerId) continue;
try { (work ?? (work = new List())).Add(NetUpgradeCommand.Decode(message.Body)); }
- catch (System.Exception ex) { Mod.log.Warn("[MP] NetUpgradeSync: dropping malformed command: " + ex.Message); }
+ catch (System.Exception ex) { SyncLog.Warn(LogTopic.Nets, "NetUpgradeSync: dropping malformed command: " + ex.Message); }
}
if (work != null && work.Count > 0) Apply(work, now);
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/BuildSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/BuildSyncSystem.cs
index 64a4572..3016954 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/BuildSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/BuildSyncSystem.cs
@@ -10,9 +10,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Systems.Net;
@@ -119,7 +120,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(BuildSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_cityStateSync = World.GetOrCreateSystemManaged();
_toolSystem = World.GetOrCreateSystemManaged();
@@ -215,11 +215,10 @@ private void DrainQueue()
// specialized placement went missing on one machine with nothing in the log.
if (_pendingSpecializedObjectOperation != null)
{
- Mod.log.Warn("[MP] BuildSync: discarding a held specialized placement (" +
- _pendingSpecializedObjectOperation.Definitions.Length +
- " definitions) that was still waiting for its polygon.");
- Diagnostics.FlightRecorder.Note("specialized handoff discarded by reset defs=" +
- _pendingSpecializedObjectOperation.Definitions.Length);
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: discarding a held specialized placement (" +
+ _pendingSpecializedObjectOperation.Definitions.Length +
+ " definitions) that was still waiting for its polygon.");
}
ClearSpecializedAreaCapture();
_nativeLifecycleCapturedThisFrame = false;
@@ -246,9 +245,9 @@ protected override void OnUpdate()
bool ready = service.GameplaySyncReady;
_hbUpdates++;
- // These probes walk broad Created queries. They are troubleshooting-only work,
- // so keep them off the normal frame path unless their verbose summary is enabled.
- if (ready && Mod.Setting != null && Mod.Setting.VerboseLogging)
+ // These probes walk broad Created queries. They are troubleshooting-only work, so
+ // keep them off the normal frame path unless the summary they feed is switched on.
+ if (ready && SyncLog.IsEnabled(LogTopic.Buildings))
{
_hbAnyCreated = System.Math.Max(_hbAnyCreated, _diagAnyCreated.CalculateEntityCount());
_hbCreatedPrefab = System.Math.Max(_hbCreatedPrefab, _diagCreatedPrefab.CalculateEntityCount());
@@ -370,7 +369,7 @@ active is ObjectToolSystem &&
_localLifecycleApplyThisFrame = _localObjectApplyThisFrame;
if (objectToOwnedAreaHandoff && applying)
- Diagnostics.FlightRecorder.Note(
+ SyncLog.Trace(LogTopic.Buildings,
"object lifecycle apply retained across owned-area handoff");
}
@@ -428,7 +427,7 @@ private void FlushDiagnostics(long now, bool connected)
if (connected || _hbAnyCreated > 0 || _diagTotal > 0)
{
var sb = new StringBuilder();
- sb.Append("[MP] BuildSync/5s: updates=").Append(_hbUpdates)
+ sb.Append("BuildSync/5s: updates=").Append(_hbUpdates)
.Append(" created[any/+prefab/+transform/filtered]=")
.Append(_hbAnyCreated).Append('/').Append(_hbCreatedPrefab).Append('/')
.Append(_hbCreatedTransform).Append('/').Append(_hbFiltered)
@@ -445,13 +444,13 @@ private void FlushDiagnostics(long now, bool connected)
}
sb.Append(']');
}
- Mod.Verbose(sb.ToString());
+ SyncLog.Detail(LogTopic.Buildings, sb.ToString());
}
if (_refusedTotal > 0)
{
var sb = new StringBuilder();
- sb.Append("[MP] BuildSync realize: refused ").Append(_refusedTotal)
+ sb.Append("BuildSync realize: refused ").Append(_refusedTotal)
.Append(" simulation-only placement(s) in the last 5s [");
int n = 0;
foreach (KeyValuePair pair in _refused)
@@ -461,7 +460,7 @@ private void FlushDiagnostics(long now, bool connected)
if (++n >= 10) { sb.Append(", ..."); break; }
}
sb.Append(']');
- Mod.log.Warn(sb.ToString());
+ SyncLog.Warn(LogTopic.Buildings, sb.ToString());
_refused.Clear();
_refusedTotal = 0;
}
@@ -531,10 +530,11 @@ private void CaptureNewObjects(MultiplayerSession session, long now)
if (!_partialPlacementRecoveryRequested)
{
_partialPlacementRecoveryRequested = true;
- Mod.log.Error("[MP] BuildSync: complete lifecycle capture was missed for '" +
- name + "'; requesting (debounced) world recovery instead of " +
- "sending a partial object graph. " +
- (_lastObjectGraphMissDetail ?? "no correlation detail"));
+ SyncLog.Error(LogTopic.Buildings,
+ "BuildSync: complete lifecycle capture was missed for '" + name +
+ "'; requesting (debounced) world recovery instead of " +
+ "sending a partial object graph. " +
+ (_lastObjectGraphMissDetail ?? "no correlation detail"));
Mod.Service.RequestAutomaticWorldRecovery("building placement capture missed");
}
continue;
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCapture.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCapture.cs
index 5951bc8..0883727 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCapture.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCapture.cs
@@ -7,6 +7,8 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -131,7 +133,7 @@ private void ObserveLocalObjectToolStateAfterOutput()
_cachedLocalObjectOperation != null &&
TryBeginSpecializedAreaCapture(recreate))
{
- Diagnostics.FlightRecorder.Note("specialized object/area handoff tracked");
+ SyncLog.Trace(LogTopic.Buildings, "specialized object/area handoff tracked");
}
if (_pendingSpecializedObjectOperation != null)
@@ -377,7 +379,8 @@ private void CaptureObjectToolOperation(NativeArray definitions)
if (string.IsNullOrEmpty(stampPrefabName))
{
_cachedLocalObjectOperation = null;
- Diagnostics.FlightRecorder.Note("asset stamp definitions lacked selected prefab");
+ SyncLog.Trace(LogTopic.Buildings,
+ "asset stamp definitions lacked selected prefab");
return;
}
// Any ObjectDefinitions in this output are independently placed stamp subobjects,
@@ -404,23 +407,22 @@ private void CaptureObjectToolOperation(NativeArray definitions)
AttachPlacementInput(undivided);
_cachedLocalObjectOperation = undivided;
RememberRecentLocalObjectOperation(undivided);
- Diagnostics.FlightRecorder.Note("fixed-element net kept undivided defs=" +
+ SyncLog.Trace(LogTopic.Buildings, "fixed-element net kept undivided defs=" +
undivided.Definitions.Length + " divided=" + captured.Count);
return;
}
if (hasFixedElementCut)
- Diagnostics.FlightRecorder.Note(
+ SyncLog.Trace(LogTopic.Buildings,
"fixed-element net has no undivided graph; publishing divided defs=" +
captured.Count);
_cachedLocalObjectOperation = operation;
RememberRecentLocalObjectOperation(_cachedLocalObjectOperation);
- Diagnostics.FlightRecorder.Note(hasStampingNet
- ? "asset stamp native definitions captured=" + captured.Count +
- " prefab=" + stampPrefabName
- : "object native definitions captured=" + captured.Count +
- " root=" + captured[root].PrefabName +
- " seed=" + unchecked((ushort)captured[root].RandomSeed));
+ SyncLog.Trace(LogTopic.Buildings,
+ hasStampingNet ? "asset stamp native definitions captured=" + captured.Count +
+ " prefab=" + stampPrefabName : "object native definitions captured=" +
+ captured.Count + " root=" + captured[root].PrefabName + " seed=" +
+ unchecked((ushort)captured[root].RandomSeed));
}
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureArea.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureArea.cs
index 0a3ee0c..fdaa4a5 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureArea.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureArea.cs
@@ -7,6 +7,8 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -102,7 +104,8 @@ private void PublishSpecializedAreaOperation()
if (rootIndex < 0 || definitions.Count > ObjectToolOperationCommand.MaxDefinitions)
{
- Mod.log.Warn("[MP] BuildSync: specialized object/area operation was incomplete; not sent.");
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: specialized object/area operation was incomplete; not sent.");
if (Mod.Service != null)
Mod.Service.RequestAutomaticWorldRecovery(
"specialized building capture was incomplete");
@@ -125,18 +128,17 @@ private void PublishSpecializedAreaOperation()
{
if (TryPublishLocalObjectOperation(operation))
{
- Diagnostics.FlightRecorder.Note("specialized object/area operation captured op=" +
- operation.OperationId + " defs=" + operation.Definitions.Length +
- " areaNodes=" + _pendingSpecializedAreaDefinition.AreaNodes.Length);
+ SyncLog.Trace(LogTopic.Buildings,
+ "specialized object/area operation captured op=" + operation.OperationId +
+ " defs=" + operation.Definitions.Length + " areaNodes=" +
+ _pendingSpecializedAreaDefinition.AreaNodes.Length);
PublishOwnedAreaSnapshot(root, _pendingSpecializedAreaDefinition);
}
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] BuildSync: specialized object/area operation was not sent: " +
- ex.Message);
- Diagnostics.FlightRecorder.Note("specialized object/area capture rejected=" +
- ex.GetType().Name);
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: specialized object/area operation was not sent: " + ex.Message);
if (Mod.Service != null)
Mod.Service.RequestAutomaticWorldRecovery(
"specialized building capture failed");
@@ -185,13 +187,13 @@ private void PublishOwnedAreaSnapshot(ObjectToolDefinitionIntent root,
{
service.Session.SendCommand(0, OwnedAreaSnapshotCommand.Id,
command.Encode());
- Diagnostics.FlightRecorder.Note("specialized owned-area safeguard sent nodes=" +
- count);
+ SyncLog.Trace(LogTopic.Buildings, "specialized owned-area safeguard sent nodes=" +
+ count);
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] BuildSync: owned-area safeguard was not sent: " +
- ex.Message);
+ SyncLog.Warn(LogTopic.Buildings, "BuildSync: owned-area safeguard was not sent: " +
+ ex.Message);
if (Mod.Service != null)
Mod.Service.RequestAutomaticWorldRecovery(
"specialized owned-area safeguard failed");
@@ -228,7 +230,7 @@ private void FinishSpecializedAreaCaptureWithoutPolygon()
if (!SpecializedPlacementStillCommitted(operation))
{
- Diagnostics.FlightRecorder.Note(
+ SyncLog.Trace(LogTopic.Buildings,
"specialized object/area handoff ended with no committed building");
ClearSpecializedAreaCapture();
return;
@@ -237,15 +239,13 @@ private void FinishSpecializedAreaCaptureWithoutPolygon()
try
{
if (TryPublishLocalObjectOperation(operation))
- Diagnostics.FlightRecorder.Note("specialized object without area captured op=" +
+ SyncLog.Trace(LogTopic.Buildings, "specialized object without area captured op=" +
operation.OperationId + " defs=" + operation.Definitions.Length);
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] BuildSync: specialized object without area was not sent: " +
- ex.Message);
- Diagnostics.FlightRecorder.Note("specialized object without area rejected=" +
- ex.GetType().Name);
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: specialized object without area was not sent: " + ex.Message);
if (Mod.Service != null)
Mod.Service.RequestAutomaticWorldRecovery(
"specialized building capture failed");
@@ -304,7 +304,7 @@ private void CaptureCompletedSpecializedArea()
ObjectToolDefinitionIntent completed;
if (!TryCaptureCompletedSpecializedArea(out completed))
{
- Diagnostics.FlightRecorder.Note("specialized object/area apply not observed");
+ SyncLog.Trace(LogTopic.Buildings, "specialized object/area apply not observed");
FinishSpecializedAreaCaptureWithoutPolygon();
return;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureCommit.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureCommit.cs
index c89e07e..41870ab 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureCommit.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureCommit.cs
@@ -7,6 +7,8 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -82,9 +84,9 @@ private bool TryPublishMatchingRecentLocalObjectOperation(List created,
if (!TryPublishLocalObjectOperation(operation)) return false;
if (object.ReferenceEquals(_cachedLocalObjectOperation, operation))
_cachedLocalObjectOperation = null;
- Diagnostics.FlightRecorder.Note("object graph matched committed root op=" +
- operation.OperationId + " defs=" + definitionCount +
- " prefab=" + prefabName + " seed=" + randomSeed);
+ SyncLog.Trace(LogTopic.Buildings, "object graph matched committed root op=" +
+ operation.OperationId + " defs=" + definitionCount + " prefab=" +
+ prefabName + " seed=" + randomSeed);
return true;
}
catch (System.Exception ex)
@@ -92,10 +94,8 @@ private bool TryPublishMatchingRecentLocalObjectOperation(List created,
ForgetRecentLocalObjectOperation(operation);
if (object.ReferenceEquals(_cachedLocalObjectOperation, operation))
_cachedLocalObjectOperation = null;
- Mod.log.Warn("[MP] BuildSync: committed object graph was not sent: " +
- ex.Message);
- Diagnostics.FlightRecorder.Note("committed object graph rejected=" +
- ex.GetType().Name);
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: committed object graph was not sent: " + ex.Message);
if (Mod.Service != null)
Mod.Service.RequestAutomaticWorldRecovery(
"committed building graph could not be sent");
@@ -211,7 +211,8 @@ private void NoteCommittedObjectGraphMiss(List created)
_lastObjectGraphMissDetail = "prefab=" + prefabName + " seed=" + seed +
" recent=" + _recentLocalObjectOperations.Count + " newest=" + newest +
matchingIdentity;
- Diagnostics.FlightRecorder.Note("object graph match missed " + _lastObjectGraphMissDetail);
+ SyncLog.Trace(LogTopic.Buildings, "object graph match missed " +
+ _lastObjectGraphMissDetail);
}
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureInput.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureInput.cs
index 444360c..b893793 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureInput.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureInput.cs
@@ -7,6 +7,8 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -90,7 +92,7 @@ private void AttachPlacementInput(ObjectToolOperationCommand operation)
{
if (PlacementSnapTargetReachesGenerator(point.m_OriginalEntity))
{
- Diagnostics.FlightRecorder.Note(
+ SyncLog.Trace(LogTopic.Buildings,
"building placement snap target was not portable");
return;
}
@@ -101,8 +103,8 @@ private void AttachPlacementInput(ObjectToolOperationCommand operation)
operation.HasPlacementInput = true;
operation.ToolRandomSeed = AppliedLifecycleToolSeed;
operation.PlacementTarget = target;
- Diagnostics.FlightRecorder.Note("building placement inputs captured prefab=" +
- root.PrefabName + " target=" + target.Kind);
+ SyncLog.Trace(LogTopic.Buildings, "building placement inputs captured prefab=" +
+ root.PrefabName + " target=" + target.Kind);
}
///
@@ -181,15 +183,14 @@ private bool TryPublishLocalAssetStamp(string prefabName)
catch (System.Exception ex)
{
// Fall back to the definition batch rather than losing the placement entirely.
- Mod.log.Warn("[MP] BuildSync: asset-stamp inputs were not sent: " + ex.Message);
- Diagnostics.FlightRecorder.Note("asset stamp inputs rejected=" + ex.GetType().Name);
+ SyncLog.Warn(LogTopic.Buildings, "BuildSync: asset-stamp inputs were not sent: " +
+ ex.Message);
return false;
}
_nativeLifecycleCapturedThisFrame = true;
- Diagnostics.FlightRecorder.Note("asset stamp inputs published op=" +
- command.OperationId + " prefab=" + prefabName +
- " seed=" + command.ToolRandomSeed);
+ SyncLog.Trace(LogTopic.Buildings, "asset stamp inputs published op=" +
+ command.OperationId + " prefab=" + prefabName + " seed=" + command.ToolRandomSeed);
return true;
}
@@ -254,7 +255,8 @@ private void CaptureLocalRelocationForApply(NativeArray definitions)
{
// The final-entity detector remains available later in the frame. Do not send a
// compact move without knowing whether the tool snapped it to a road.
- Diagnostics.FlightRecorder.Note("relocation control point unavailable; final-entity fallback");
+ SyncLog.Trace(LogTopic.Buildings,
+ "relocation control point unavailable; final-entity fallback");
return;
}
@@ -335,10 +337,9 @@ public void CaptureLocalObjectApplyBeforeToolOutput()
_localObjectApplyThisFrame = true;
_localLifecycleApplyThisFrame = true;
- Diagnostics.FlightRecorder.Note((operation.IsAssetStamp
- ? "asset stamp"
- : "object lifecycle") + " apply captured from standing definitions=" +
- operation.Definitions.Length);
+ SyncLog.Trace(LogTopic.Buildings,
+ (operation.IsAssetStamp ? "asset stamp" : "object lifecycle") +
+ " apply captured from standing definitions=" + operation.Definitions.Length);
PublishCachedLocalObjectOperation();
}
finally
@@ -371,15 +372,14 @@ private void PublishCachedLocalObjectOperation()
try
{
if (TryPublishLocalObjectOperation(_cachedLocalObjectOperation))
- Diagnostics.FlightRecorder.Note("object operation captured op=" +
+ SyncLog.Trace(LogTopic.Buildings, "object operation captured op=" +
_cachedLocalObjectOperation.OperationId + " defs=" +
_cachedLocalObjectOperation.Definitions.Length);
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] BuildSync: native object operation was not sent: " + ex.Message);
- Diagnostics.FlightRecorder.Note("object operation capture rejected=" +
- ex.GetType().Name);
+ SyncLog.Warn(LogTopic.Buildings, "BuildSync: native object operation was not sent: " +
+ ex.Message);
if (Mod.Service != null)
Mod.Service.RequestAutomaticWorldRecovery(
"native object operation could not be sent");
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureSpawnables.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureSpawnables.cs
index 81c8c71..dc42c33 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureSpawnables.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeCaptureSpawnables.cs
@@ -7,6 +7,8 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -66,7 +68,7 @@ internal bool ConsumePlayerPlacedSpawnable(Entity entity, long now)
if (!transformMatches) continue;
_playerPlacedSpawnableCreations.RemoveAt(i);
- Diagnostics.FlightRecorder.Note("player-placed spawnable guard consumed");
+ SyncLog.Trace(LogTopic.Buildings, "player-placed spawnable guard consumed");
return true;
}
@@ -126,8 +128,8 @@ private void RememberPlayerPlacedSpawnables(ObjectToolOperationCommand operation
}
if (remembered > 0)
- Diagnostics.FlightRecorder.Note("player-placed spawnable guard armed=" +
- remembered);
+ SyncLog.Trace(LogTopic.Buildings, "player-placed spawnable guard armed=" +
+ remembered);
}
private void PrunePlayerPlacedSpawnables(long now)
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeDerive.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeDerive.cs
index 1c1da32..9c0f6e1 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeDerive.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeDerive.cs
@@ -5,6 +5,8 @@
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -103,8 +105,9 @@ private static bool ResolveDeriveReflection()
BindingFlags.Instance | BindingFlags.NonPublic);
if (_createDefinitionsMethod == null || _randomSeedValueField == null)
- Mod.log.Warn("[MP] BuildSync: the game's object definition generator is not " +
- "reachable; upgrades and building moves fall back to reduced replication.");
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: the game's object definition generator is not " +
+ "reachable; upgrades and building moves fall back to reduced replication.");
return _createDefinitionsMethod != null && _randomSeedValueField != null;
}
@@ -225,9 +228,9 @@ internal NativeDeriveResult TryDeriveObjectTransaction(Entity objectPrefab, Enti
_nativeNetCoordinator.CancelPreparedDefinitionFrame();
// Reflection wraps whatever the generator threw; the inner one is the useful message.
System.Exception cause = ex.InnerException ?? ex;
- Mod.log.Warn("[MP] BuildSync: the game's definition generator rejected " + source +
- "; dropping this edit: " + cause.Message);
- Diagnostics.FlightRecorder.Note("native derive rejected=" + cause.GetType().Name);
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: the game's definition generator rejected " + source +
+ "; dropping this edit: " + cause.Message);
return NativeDeriveResult.Failed;
}
finally
@@ -242,7 +245,8 @@ internal NativeDeriveResult TryDeriveObjectTransaction(Entity objectPrefab, Enti
if (derived == 0)
{
_nativeNetCoordinator.CancelPreparedDefinitionFrame();
- Diagnostics.FlightRecorder.Note("native derive produced no definitions (" + source + ")");
+ SyncLog.Trace(LogTopic.Buildings, "native derive produced no definitions (" + source +
+ ")");
return NativeDeriveResult.Failed;
}
@@ -256,7 +260,7 @@ internal NativeDeriveResult TryDeriveObjectTransaction(Entity objectPrefab, Enti
return NativeDeriveResult.Busy;
}
- Diagnostics.FlightRecorder.Note("native derive " + source + " defs=" + derived +
+ SyncLog.Trace(LogTopic.Buildings, "native derive " + source + " defs=" + derived +
" seed=" + toolSeed + " deriveMS=" + (System.Environment.TickCount - startTick));
return NativeDeriveResult.Armed;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeRealize.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeRealize.cs
index 96fa51e..1435da2 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeRealize.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeRealize.cs
@@ -7,7 +7,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -229,21 +231,19 @@ private bool TryRealizeBlockedNativeObject(long now)
// incompatible. In both cases silently dropping it leaves known world divergence.
if (compactPlacement)
{
- Mod.log.Warn("[MP] BuildSync: building placement '" + placementPrefab +
- "' could not resolve its snapped target within the retry window (" +
- (_lastUnresolvedObjectReason ?? "unknown target") +
- "); requesting an automatic world sync.");
- Diagnostics.FlightRecorder.Note(
- "building placement target expired; world sync requested");
+ SyncLog.Warn(LogTopic.Buildings, "BuildSync: building placement '" +
+ placementPrefab +
+ "' could not resolve its snapped target within the retry window (" +
+ (_lastUnresolvedObjectReason ?? "unknown target") +
+ "); requesting an automatic world sync.");
}
else
{
- Mod.log.Warn("[MP] BuildSync: native object operation target did not resolve " +
- "within the retry window (" +
- (_lastUnresolvedObjectReason ?? "unknown target") +
- "); requesting an automatic world sync.");
- Diagnostics.FlightRecorder.Note(
- "object operation target expired; world sync requested");
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: native object operation target did not resolve " +
+ "within the retry window (" +
+ (_lastUnresolvedObjectReason ?? "unknown target") +
+ "); requesting an automatic world sync.");
}
// Read before the reset below clears it - it is the whole point of the report.
string unresolvedDetail = _lastUnresolvedObjectReason ?? "unknown target";
@@ -294,7 +294,7 @@ private void BlockNativeObject(SimulationCommandMessage message, long now)
_blockedNativeObjectDeadline = now + NativeObjectTargetRetryMs;
_blockedNativeObjectNextAttemptMs = now + NativeObjectRetryIntervalMs;
_hasBlockedNativeObject = true;
- Diagnostics.FlightRecorder.Note("object operation target retrying");
+ SyncLog.Trace(LogTopic.Buildings, "object operation target retrying");
}
///
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeRealizeOperation.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeRealizeOperation.cs
index 91323b5..24d2cee 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeRealizeOperation.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeRealizeOperation.cs
@@ -7,7 +7,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -29,8 +31,8 @@ private NativeObjectResult TryRealizeAssetStamp(SimulationCommandMessage message
try { command = AssetStampCommand.Decode(message.Body); }
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] BuildSync: dropping malformed asset-stamp command: " + ex.Message);
- Diagnostics.FlightRecorder.Note("asset stamp dropped malformed");
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: dropping malformed asset-stamp command: " + ex.Message);
return NativeObjectResult.Rejected;
}
@@ -41,8 +43,8 @@ private NativeObjectResult TryRealizeAssetStamp(SimulationCommandMessage message
};
if (_recentNativeObjectOperations.Contains(key, now))
{
- Diagnostics.FlightRecorder.Note("asset stamp duplicate suppressed op=" +
- command.OperationId);
+ SyncLog.Trace(LogTopic.Buildings, "asset stamp duplicate suppressed op=" +
+ command.OperationId);
return NativeObjectResult.Completed;
}
@@ -55,9 +57,8 @@ private NativeObjectResult TryRealizeAssetStamp(SimulationCommandMessage message
// A peer with content we lack. Nothing local will make this resolve, so do not hold
// the ordered queue for it.
RecordRefused(command.PrefabName);
- Mod.log.Warn("[MP] BuildSync: asset stamp '" + command.PrefabName +
- "' is unavailable here; skipping.");
- Diagnostics.FlightRecorder.Note("asset stamp prefab unavailable");
+ SyncLog.Warn(LogTopic.Buildings, "BuildSync: asset stamp '" + command.PrefabName +
+ "' is unavailable here; skipping.");
return NativeObjectResult.Rejected;
}
@@ -75,7 +76,7 @@ private NativeObjectResult TryRealizeAssetStamp(SimulationCommandMessage message
switch (derived)
{
case NativeDeriveResult.Armed:
- Diagnostics.FlightRecorder.Note("asset stamp derived op=" +
+ SyncLog.Trace(LogTopic.Buildings, "asset stamp derived op=" +
command.OperationId + " prefab=" + prefabName);
return NativeObjectResult.Armed;
case NativeDeriveResult.Busy:
@@ -83,10 +84,10 @@ private NativeObjectResult TryRealizeAssetStamp(SimulationCommandMessage message
case NativeDeriveResult.Unsupported:
// This build cannot reach the generator, and a stamp has no reduced form that
// preserves its topology. A world reload is the only complete fallback.
- Mod.log.Warn("[MP] BuildSync: the game's definition generator is not reachable; " +
- "the remote stamp '" + prefabName +
- "' was skipped and world recovery was requested.");
- Diagnostics.FlightRecorder.Note("asset stamp unsupported; recovery requested");
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: the game's definition generator is not reachable; " +
+ "the remote stamp '" + prefabName +
+ "' was skipped and world recovery was requested.");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("asset stamp generator unavailable", "object",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
@@ -94,7 +95,8 @@ private NativeObjectResult TryRealizeAssetStamp(SimulationCommandMessage message
.Tried("nothing - this build cannot reach the generator and a stamp has no reduced form"));
return NativeObjectResult.Rejected;
case NativeDeriveResult.Failed:
- Diagnostics.FlightRecorder.Note("asset stamp derive failed; recovery requested");
+ SyncLog.Trace(LogTopic.Buildings,
+ "asset stamp derive failed; recovery requested");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("asset stamp generation failed", "object",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
@@ -117,10 +119,11 @@ private void CompleteAssetStamp(NativeObjectOperationKey key, Entity prefab,
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] BuildSync: committed stamp charge failed: " + ex.Message);
+ SyncLog.Warn(LogTopic.Buildings, "BuildSync: committed stamp charge failed: " +
+ ex.Message);
}
- Diagnostics.FlightRecorder.Note("asset stamp transaction committed/drained op=" +
- key.Operation);
+ SyncLog.Trace(LogTopic.Buildings, "asset stamp transaction committed/drained op=" +
+ key.Operation);
}
private NativeObjectResult TryRealizeNativeObject(SimulationCommandMessage message, long now)
@@ -131,8 +134,8 @@ private NativeObjectResult TryRealizeNativeObject(SimulationCommandMessage messa
{
// A malformed command from a peer is a protocol/peer problem, not local world
// corruption. The decode guard already protected us; drop it, do not resync.
- Mod.log.Warn("[MP] BuildSync: dropping malformed native object operation: " + ex.Message);
- Diagnostics.FlightRecorder.Note("object operation dropped malformed");
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: dropping malformed native object operation: " + ex.Message);
return NativeObjectResult.Rejected;
}
@@ -142,14 +145,15 @@ private NativeObjectResult TryRealizeNativeObject(SimulationCommandMessage messa
// into an impossible ten-second retry.
int normalizedPermanentFlags = NormalizeRemoteObjectCreationFlags(command);
if (normalizedPermanentFlags > 0)
- Diagnostics.FlightRecorder.Note("object operation normalized permanent flags=" +
- normalizedPermanentFlags);
+ SyncLog.Trace(LogTopic.Buildings, "object operation normalized permanent flags=" +
+ normalizedPermanentFlags);
string unsafePrefab;
if (TryFindUnsafeSimulationReference(command, out unsafePrefab))
{
RecordRefused(unsafePrefab);
- Diagnostics.FlightRecorder.Note("object operation dropped (simulation-only prefab)");
+ SyncLog.Trace(LogTopic.Buildings,
+ "object operation dropped (simulation-only prefab)");
return NativeObjectResult.Rejected;
}
@@ -160,8 +164,8 @@ private NativeObjectResult TryRealizeNativeObject(SimulationCommandMessage messa
};
if (_recentNativeObjectOperations.Contains(key, now))
{
- Diagnostics.FlightRecorder.Note("object operation duplicate suppressed op=" +
- command.OperationId);
+ SyncLog.Trace(LogTopic.Buildings, "object operation duplicate suppressed op=" +
+ command.OperationId);
return NativeObjectResult.Completed;
}
@@ -183,8 +187,8 @@ private NativeObjectResult TryRealizeNativeObject(SimulationCommandMessage messa
if (reason != _lastUnresolvedObjectReason)
{
_lastUnresolvedObjectReason = reason;
- Diagnostics.FlightRecorder.Note("object operation unresolved op=" +
- command.OperationId + " (" + reason + ")");
+ SyncLog.Trace(LogTopic.Buildings, "object operation unresolved op=" +
+ command.OperationId + " (" + reason + ")");
}
return NativeObjectResult.Retry;
}
@@ -196,8 +200,8 @@ private NativeObjectResult TryRealizeNativeObject(SimulationCommandMessage messa
if (equivalentExists)
{
_recentNativeObjectOperations.Remember(key, now, NativeObjectReplayRememberMs);
- Diagnostics.FlightRecorder.Note("object equivalent placement suppressed op=" +
- command.OperationId);
+ SyncLog.Trace(LogTopic.Buildings, "object equivalent placement suppressed op=" +
+ command.OperationId);
return NativeObjectResult.Completed;
}
@@ -217,10 +221,9 @@ private NativeObjectResult TryRealizeNativeObject(SimulationCommandMessage messa
// The partial definitions are torn down here, so nothing inconsistent was committed.
// The operation nevertheless exists on the sender, so repair the known divergence.
DestroyDefinitions(created);
- Mod.log.Warn("[MP] BuildSync: native object definitions could not be generated; " +
- "requesting world recovery: " + ex.Message);
- Diagnostics.FlightRecorder.Note("object definitions failed=" + ex.GetType().Name +
- "; recovery requested");
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: native object definitions could not be generated; " +
+ "requesting world recovery: " + ex.Message);
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("native object definitions could not be generated", "object",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
@@ -246,11 +249,11 @@ private NativeObjectResult TryRealizeNativeObject(SimulationCommandMessage messa
// Per-phase cost of one native operation. A big relocation is inherently a large
// transaction; these numbers say which phase is actually spiking rather than leaving it
// to guesswork.
- Diagnostics.FlightRecorder.Note("object definitions generated op=" + command.OperationId +
- " defs=" + created.Count +
- " resolveMS=" + (isolateStartTick - resolveStartTick) +
- " isolateMS=" + (generateStartTick - isolateStartTick) +
- " generateMS=" + (System.Environment.TickCount - generateStartTick));
+ SyncLog.Trace(LogTopic.Buildings, "object definitions generated op=" +
+ command.OperationId + " defs=" + created.Count + " resolveMS=" +
+ (isolateStartTick - resolveStartTick) + " isolateMS=" +
+ (generateStartTick - isolateStartTick) + " generateMS=" +
+ (System.Environment.TickCount - generateStartTick));
return NativeObjectResult.Armed;
}
@@ -336,8 +339,8 @@ private bool TryRealizePlacementInput(SimulationCommandMessage message,
if (EquivalentObjectOperationAlreadyExists(command, resolved))
{
_recentNativeObjectOperations.Remember(key, now, NativeObjectReplayRememberMs);
- Diagnostics.FlightRecorder.Note("derived placement equivalent suppressed op=" +
- command.OperationId);
+ SyncLog.Trace(LogTopic.Buildings, "derived placement equivalent suppressed op=" +
+ command.OperationId);
result = NativeObjectResult.Completed;
return true;
}
@@ -357,22 +360,21 @@ private bool TryRealizePlacementInput(SimulationCommandMessage message,
switch (derived)
{
case NativeDeriveResult.Armed:
- Diagnostics.FlightRecorder.Note("building placement regenerated op=" +
- command.OperationId + " prefab=" +
- root.PrefabName);
+ SyncLog.Trace(LogTopic.Buildings, "building placement regenerated op=" +
+ command.OperationId + " prefab=" + root.PrefabName);
result = NativeObjectResult.Armed;
return true;
case NativeDeriveResult.Busy:
result = NativeObjectResult.Retry;
return true;
case NativeDeriveResult.Unsupported:
- Diagnostics.FlightRecorder.Note(
+ SyncLog.Trace(LogTopic.Buildings,
"building placement generator unavailable; using exact graph fallback");
return false;
case NativeDeriveResult.Failed:
// The complete captured graph is still present in the command. A transient
// local generator rejection must not discard the building before trying it.
- Diagnostics.FlightRecorder.Note(
+ SyncLog.Trace(LogTopic.Buildings,
"building placement regeneration failed; using exact graph fallback");
return false;
default:
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeRealizeResolve.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeRealizeResolve.cs
index f86653b..ab3a927 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeRealizeResolve.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/NativeRealizeResolve.cs
@@ -7,7 +7,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -21,9 +23,9 @@ private void ReplayNativeObject(SimulationCommandMessage message)
{
if (_nativeObjectReplayPrefix.Count >= MaxNativeObjectReplayPrefix)
{
- Mod.log.Warn("[MP] BuildSync: native object replay prefix overflowed; requesting " +
- "world recovery instead of losing an operation.");
- Diagnostics.FlightRecorder.Note("object replay prefix overflow; recovery requested");
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: native object replay prefix overflowed; requesting " +
+ "world recovery instead of losing an operation.");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("native object replay prefix overflow", "object",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.StreamLoss)
@@ -32,7 +34,7 @@ private void ReplayNativeObject(SimulationCommandMessage message)
return;
}
_nativeObjectReplayPrefix.Add(message);
- Diagnostics.FlightRecorder.Note("object transaction rejected; replay prioritized");
+ SyncLog.Trace(LogTopic.Buildings, "object transaction rejected; replay prioritized");
}
private void CompleteNativeObject(NativeObjectOperationKey key,
@@ -73,11 +75,12 @@ private void CompleteNativeObject(NativeObjectOperationKey key,
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] BuildSync: committed object charge failed: " + ex.Message);
+ SyncLog.Warn(LogTopic.Buildings, "BuildSync: committed object charge failed: " +
+ ex.Message);
}
- Diagnostics.FlightRecorder.Note((command.IsAssetStamp
- ? "asset stamp transaction committed/drained op="
- : "object transaction committed/drained op=") + command.OperationId);
+ SyncLog.Trace(LogTopic.Buildings,
+ (command.IsAssetStamp ? "asset stamp transaction committed/drained op=" : "object transaction committed/drained op=") +
+ command.OperationId);
}
///
@@ -238,7 +241,8 @@ private bool TryResolveObjectOperation(ObjectToolOperationCommand command,
resolved[i] = target;
}
reason = null;
- Diagnostics.FlightRecorder.Note("object operation targets resolved defs=" + resolved.Length);
+ SyncLog.Trace(LogTopic.Buildings, "object operation targets resolved defs=" +
+ resolved.Length);
return true;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/Realize.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/Realize.cs
index 15a0431..d797a43 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/Realize.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/Realize.cs
@@ -7,8 +7,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
@@ -102,7 +104,7 @@ private void RealizeIncoming(MultiplayerSession session, long now)
int held = _incoming.Count + _nativeObjectReplayPrefix.Count;
if (held > 0) note.Append(" held=").Append(held);
AppendRealizedNames(note);
- Diagnostics.FlightRecorder.Note(note.ToString());
+ SyncLog.Trace(LogTopic.Buildings, note.ToString());
}
}
finally
@@ -128,8 +130,8 @@ private void DrainIncoming(MultiplayerSession session, long now)
if (message.CommandId == ObjectToolOperationCommand.Id ||
message.CommandId == AssetStampCommand.Id)
{
- Diagnostics.FlightRecorder.Note("object command received origin=" +
- message.OriginPlayerId);
+ SyncLog.Trace(LogTopic.Buildings, "object command received origin=" +
+ message.OriginPlayerId);
NativeObjectResult result = TryRealizeRemoteObjectMessage(message, now);
if (result == NativeObjectResult.Retry)
{
@@ -142,15 +144,16 @@ private void DrainIncoming(MultiplayerSession session, long now)
ObjectPlacementCommand command;
try { command = ObjectPlacementCommand.Decode(message.Body); }
- catch (System.Exception ex) { Mod.log.Warn("[MP] BuildSync: dropping malformed command: " + ex.Message); continue; }
+ catch (System.Exception ex) { SyncLog.Warn(LogTopic.Buildings, "BuildSync: dropping malformed command: " + ex.Message); continue; }
Entity prefab;
if (!_prefabIndex.TryResolve(command.PrefabName,
candidate => EntityManager.HasComponent(candidate),
out prefab))
{
- Mod.log.Warn("[MP] BuildSync realize: unknown prefab '" + command.PrefabName +
- "' from player " + message.OriginPlayerId + "; skipping.");
+ SyncLog.Warn(LogTopic.Buildings, "BuildSync realize: unknown prefab '" +
+ command.PrefabName + "' from player " + message.OriginPlayerId +
+ "; skipping.");
continue;
}
@@ -167,9 +170,9 @@ private void DrainIncoming(MultiplayerSession session, long now)
// A reduced command can't represent a building's owned graph; the native
// object-tool path owns those. This should not be emitted by v38 senders; if it
// arrives, recover rather than silently accepting a missing building.
- Mod.log.Warn("[MP] BuildSync realize: reduced placement for spatial object '" +
- command.PrefabName +
- "' was rejected; requesting world recovery.");
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync realize: reduced placement for spatial object '" +
+ command.PrefabName + "' was rejected; requesting world recovery.");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("reduced spatial object placement rejected", "object",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
@@ -185,10 +188,9 @@ private void DrainIncoming(MultiplayerSession session, long now)
if (_attachRetry.Count >= MaxPendingAttachments)
{
_attachRetry.Clear();
- Mod.log.Warn("[MP] BuildSync: attachment retry queue overflowed; dropping the " +
- "incomplete backlog and requesting world recovery.");
- Diagnostics.FlightRecorder.Note(
- "attachment retry queue overflow; recovery requested");
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync: attachment retry queue overflowed; dropping the " +
+ "incomplete backlog and requesting world recovery.");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("object attachment retry queue overflow", "object",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.StreamLoss)
@@ -233,11 +235,9 @@ private void RetryPendingAttachments(long now)
// The parent road never reached us. The prop cannot safely be created without
// it, but silently dropping it leaves known divergence.
_attachRetry.RemoveAt(i);
- Mod.log.Warn("[MP] BuildSync realize: no local road for '" + pending.command.PrefabName +
- "' after " + (AttachRetryWindowMs / 1000) +
- " s; requesting world recovery.");
- Diagnostics.FlightRecorder.Note(
- "attachment target expired; recovery requested");
+ SyncLog.Warn(LogTopic.Buildings, "BuildSync realize: no local road for '" +
+ pending.command.PrefabName + "' after " + (AttachRetryWindowMs / 1000) +
+ " s; requesting world recovery.");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("object attachment target did not resolve", "object",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.MissingTarget)
@@ -275,15 +275,14 @@ private void RealizeCommand(ObjectPlacementCommand command, Entity prefab, int o
_rzFrameSpawned++;
_rzRealizedThisFrame.Add((prefab, position, command.RandomSeed, rotation,
command.AttachKind));
- Mod.Verbose("[MP] BuildSync realize: spawned '" + command.PrefabName + "' from player " +
- originPlayerId + " at (" + position.x.ToString("F1") + "," +
- position.z.ToString("F1") + ").");
+ SyncLog.Detail(LogTopic.Buildings, "BuildSync realize: spawned '" +
+ command.PrefabName + "' from player " + originPlayerId + " at (" +
+ position.x.ToString("F1") + "," + position.z.ToString("F1") + ").");
}
catch (System.Exception ex)
{
- Mod.log.Error("[MP] BuildSync realize FAILED for '" + command.PrefabName + "': " + ex);
- Diagnostics.FlightRecorder.Note("build realize FAILED '" + command.PrefabName + "': "
- + ex.GetType().Name + "; recovery requested");
+ SyncLog.Error(LogTopic.Buildings, "BuildSync realize FAILED for '" +
+ command.PrefabName + "': " + ex);
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("object placement realization failed", "object",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/RealizeOwned.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/RealizeOwned.cs
index 7ce20ed..e126e88 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/RealizeOwned.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/RealizeOwned.cs
@@ -7,6 +7,8 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -158,7 +160,8 @@ private void RealizeSubAreas(Entity prefab, OwnerDefinition owner, Entity ownerE
// a candidate missing it is a hard (native) crash, not a catchable exception. Guard.
if (!AllHaveSpawnableData(placeholders))
{
- Mod.log.Warn("[MP] BuildSync realize: a placeholder sub-area of '" +
+ SyncLog.Warn(LogTopic.Buildings,
+ "BuildSync realize: a placeholder sub-area of '" +
_prefabSystem.GetPrefabName(prefab) +
"' has a candidate without SpawnableObjectData; skipping that area.");
continue;
@@ -179,8 +182,9 @@ private void RealizeSubAreas(Entity prefab, OwnerDefinition owner, Entity ownerE
// prefab here hard-crashes the game. Only emit a definition for a real area prefab.
if (!EntityManager.HasComponent(areaPrefab))
{
- Mod.log.Warn("[MP] BuildSync realize: sub-area prefab '" +
- _prefabSystem.GetPrefabName(areaPrefab) + "' of '" + _prefabSystem.GetPrefabName(prefab) +
+ SyncLog.Warn(LogTopic.Buildings, "BuildSync realize: sub-area prefab '" +
+ _prefabSystem.GetPrefabName(areaPrefab) + "' of '" +
+ _prefabSystem.GetPrefabName(prefab) +
"' has no AreaData; skipping that area.");
continue;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/RealizeSubNets.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/RealizeSubNets.cs
index 974a25b..6773194 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/RealizeSubNets.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/BuildSyncSystem/RealizeSubNets.cs
@@ -1,5 +1,7 @@
using System.Text;
using Colossal.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using Game.Common;
using Game.Prefabs;
using Game.Simulation;
@@ -74,8 +76,9 @@ private void RealizeSubNets(Entity prefab, OwnerDefinition owner, Entity ownerEn
if (!EntityManager.HasComponent(subNet.m_Prefab) ||
!EntityManager.HasComponent(subNet.m_Prefab))
{
- Mod.log.Warn("[MP] BuildSync realize: sub-net prefab '" +
- _prefabSystem.GetPrefabName(subNet.m_Prefab) + "' of '" + _prefabSystem.GetPrefabName(prefab) +
+ SyncLog.Warn(LogTopic.Buildings, "BuildSync realize: sub-net prefab '" +
+ _prefabSystem.GetPrefabName(subNet.m_Prefab) + "' of '" +
+ _prefabSystem.GetPrefabName(prefab) +
"' lacks NetData/NetGeometryData; skipping that driveway.");
continue;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/MoveSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/MoveSyncSystem.cs
index eeb19ee..c76f52c 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/MoveSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/MoveSyncSystem.cs
@@ -8,9 +8,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -41,7 +42,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(MoveSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_prefabIndex = new PrefabIndex(_prefabSystem, GetEntityQuery(ComponentType.ReadOnly()));
@@ -154,15 +154,14 @@ private void CaptureMoves(MultiplayerSession session, long now)
if (HasOwnedLifecycle(entity, prefab) &&
!command.DestinationAttachmentKnown)
{
- Mod.log.Warn("[MP] MoveSync: final-entity fallback for '" + name +
- "' could not recover the applied road attachment; skipping " +
- "the unsafe partial move.");
- Diagnostics.FlightRecorder.Note("relocation fallback lacked attachment prefab=" +
- name);
+ SyncLog.Warn(LogTopic.Buildings, "MoveSync: final-entity fallback for '" +
+ name + "' could not recover the applied road attachment; skipping " +
+ "the unsafe partial move.");
continue;
}
session.SendCommand(0, ObjectMoveCommand.Id, command.Encode());
- Mod.Verbose("[MP] MoveSync captured relocation of '" + name + "'.");
+ SyncLog.Detail(LogTopic.Buildings, "MoveSync captured relocation of '" + name +
+ "'.");
}
}
finally
@@ -202,25 +201,24 @@ public void PublishLocalRelocation(Entity prefab, Entity original, float3 oldPos
{
// An owned upgrade is found on the peer through its host. Without that identity the
// move would name an object the peer cannot look up, so drop it here instead.
- Mod.log.Warn("[MP] MoveSync: relocation of owned '" + name +
- "' could not describe its host building; skipping this move.");
- Diagnostics.FlightRecorder.Note("relocation host identity unavailable prefab=" + name);
+ SyncLog.Warn(LogTopic.Buildings, "MoveSync: relocation of owned '" + name +
+ "' could not describe its host building; skipping this move.");
return;
}
CaptureSourceAttachment(command, original);
if (!CaptureDestinationAttachment(command, destinationParent, newPosition,
destinationAttachmentKnown))
{
- Diagnostics.FlightRecorder.Note("relocation destination attachment could not be encoded");
+ SyncLog.Trace(LogTopic.Buildings,
+ "relocation destination attachment could not be encoded");
return;
}
// Also stops the MovedLocation sweep below from sending this same move again. Mark only
// once encoding succeeded so the final-entity fallback remains available on failure.
_guard.Mark(MoveKey(name, newPosition), service.NowMs);
service.Session.SendCommand(0, ObjectMoveCommand.Id, command.Encode());
- Mod.Verbose("[MP] MoveSync captured relocation of '" + name + "' from the tool definition.");
- Diagnostics.FlightRecorder.Note("relocation captured prefab=" + name +
- " seed=" + toolSeed);
+ SyncLog.Detail(LogTopic.Buildings, "MoveSync captured relocation of '" + name +
+ "' from the tool definition (seed " + toolSeed + ").");
}
private void RealizeIncoming(MultiplayerSession session, long now)
@@ -232,9 +230,9 @@ private void RealizeIncoming(MultiplayerSession session, long now)
if (now < _blockedMoveDeadline) return;
// The object to relocate never arrived here. Drop the relocation rather than
// loop the whole world through recovery (which re-failed every reload).
- Mod.log.Warn("[MP] MoveSync: relocation target did not resolve within the retry " +
- "window; dropping this move (use /sync if the city drifts).");
- Diagnostics.FlightRecorder.Note("move dropped after retry window");
+ SyncLog.Warn(LogTopic.Buildings,
+ "MoveSync: relocation target did not resolve within the retry " +
+ "window; dropping this move (use /sync if the city drifts).");
_hasBlockedMove = false;
_blockedMove = null;
return;
@@ -252,7 +250,7 @@ private void RealizeIncoming(MultiplayerSession session, long now)
_hasBlockedMove = true;
_blockedMove = message;
_blockedMoveDeadline = now + MoveRetryWindowMs;
- Diagnostics.FlightRecorder.Note("move target retrying");
+ SyncLog.Trace(LogTopic.Buildings, "move target retrying");
return;
}
}
@@ -264,7 +262,8 @@ private bool TryRealizeMove(SimulationCommandMessage message, long now)
catch (System.Exception ex)
{
// A malformed peer command is not local corruption; drop it, do not resync.
- Mod.log.Warn("[MP] MoveSync: dropping malformed command: " + ex.Message);
+ SyncLog.Warn(LogTopic.Buildings, "MoveSync: dropping malformed command: " +
+ ex.Message);
return true;
}
@@ -312,9 +311,9 @@ private bool TryRealizeMove(SimulationCommandMessage message, long now)
bool requiresCompleteLifecycle = RequiresCompleteLifecycle(original, prefab, command);
if (requiresCompleteLifecycle && !command.DestinationAttachmentKnown)
{
- Mod.log.Warn("[MP] MoveSync: relocation of '" + command.PrefabName +
- "' lacks an authoritative destination attachment; dropping it instead " +
- "of detaching its owned/roadside graph.");
+ SyncLog.Warn(LogTopic.Buildings, "MoveSync: relocation of '" + command.PrefabName +
+ "' lacks an authoritative destination attachment; dropping it instead " +
+ "of detaching its owned/roadside graph.");
return true;
}
@@ -331,17 +330,16 @@ private bool TryRealizeMove(SimulationCommandMessage message, long now)
if (derived == BuildSyncSystem.NativeDeriveResult.Armed)
{
_guard.Mark(MoveKey(command.PrefabName, newPos), now);
- Mod.Verbose("[MP] MoveSync realize: derived relocation of '" +
- command.PrefabName + "' from player " +
- message.OriginPlayerId + ".");
+ SyncLog.Detail(LogTopic.Buildings, "MoveSync realize: derived relocation of '" +
+ command.PrefabName + "' from player " + message.OriginPlayerId + ".");
return true;
}
if (derived == BuildSyncSystem.NativeDeriveResult.Failed) return true;
// A root-only compatibility move would strand an owned graph or bypass attachment /
// transport-stop lifecycle events, so unsupported native derivation is a hard stop.
- Mod.log.Warn("[MP] MoveSync: relocation of '" + command.PrefabName +
- "' needs the game's object lifecycle generator; dropping this move.");
+ SyncLog.Warn(LogTopic.Buildings, "MoveSync: relocation of '" + command.PrefabName +
+ "' needs the game's object lifecycle generator; dropping this move.");
return true;
}
@@ -376,17 +374,16 @@ private bool TryRealizeMove(SimulationCommandMessage message, long now)
});
EntityManager.AddComponent(definition);
EntityManager.AddComponent(definition);
- Mod.Verbose("[MP] MoveSync realize: moved '" + command.PrefabName + "' from player " +
- message.OriginPlayerId + " to (" + newPos.x.ToString("F1") + "," +
- newPos.z.ToString("F1") + ").");
+ SyncLog.Detail(LogTopic.Buildings, "MoveSync realize: moved '" + command.PrefabName +
+ "' from player " + message.OriginPlayerId + " to (" + newPos.x.ToString("F1") +
+ "," + newPos.z.ToString("F1") + ").");
}
catch (System.Exception ex)
{
// The definition was rejected before commit; drop this move rather than freeze
// the world (the placer can /sync if the object looks out of place).
- Mod.log.Error("[MP] MoveSync realize FAILED for '" + command.PrefabName +
- "'; dropping this move: " + ex);
- Diagnostics.FlightRecorder.Note("move realize failed; dropped");
+ SyncLog.Error(LogTopic.Buildings, "MoveSync realize FAILED for '" +
+ command.PrefabName + "'; dropping this move: " + ex);
}
return true;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Objects/UpgradeSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Objects/UpgradeSyncSystem.cs
index 9f50ebe..4c8f907 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Objects/UpgradeSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Objects/UpgradeSyncSystem.cs
@@ -8,9 +8,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -48,7 +49,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(UpgradeSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_prefabIndex = new PrefabIndex(_prefabSystem, GetEntityQuery(ComponentType.ReadOnly()));
_buildSync = World.GetOrCreateSystemManaged();
@@ -193,7 +193,8 @@ private void CaptureNewUpgrades(MultiplayerSession session, long now)
ToolRandomSeed = _buildSync.AppliedLifecycleToolSeed,
};
session.SendCommand(0, UpgradePlacementCommand.Id, command.Encode());
- Mod.Verbose("[MP] UpgradeSync captured '" + name + "' on '" + ownerName + "'.");
+ SyncLog.Detail(LogTopic.Buildings, "UpgradeSync captured '" + name + "' on '" +
+ ownerName + "'.");
}
}
finally
@@ -212,9 +213,9 @@ private void RealizeIncoming(MultiplayerSession session, long now)
if (now >= pending.deadline)
{
_ownerRetry.RemoveAt(i);
- Mod.log.Warn("[MP] UpgradeSync realize: no local '" + pending.cmd.OwnerPrefabName +
- "' after " + (OwnerRetryWindowMs / 1000) + " s to attach '" +
- pending.cmd.PrefabName + "'; dropping.");
+ SyncLog.Warn(LogTopic.Buildings, "UpgradeSync realize: no local '" +
+ pending.cmd.OwnerPrefabName + "' after " + (OwnerRetryWindowMs / 1000) +
+ " s to attach '" + pending.cmd.PrefabName + "'; dropping.");
}
}
@@ -225,7 +226,7 @@ private void RealizeIncoming(MultiplayerSession session, long now)
UpgradePlacementCommand command;
try { command = UpgradePlacementCommand.Decode(message.Body); }
- catch (System.Exception ex) { Mod.log.Warn("[MP] UpgradeSync: dropping malformed command: " + ex.Message); continue; }
+ catch (System.Exception ex) { SyncLog.Warn(LogTopic.Buildings, "UpgradeSync: dropping malformed command: " + ex.Message); continue; }
if (TryRealize(command, message.OriginPlayerId, now)) continue;
@@ -250,8 +251,8 @@ private bool TryRealize(UpgradePlacementCommand command, int origin, long now)
if (!_prefabIndex.TryResolve(command.PrefabName, out prefab) ||
!_prefabIndex.TryResolve(command.OwnerPrefabName, out ownerPrefab))
{
- Mod.log.Warn("[MP] UpgradeSync realize: unknown prefab '" + command.PrefabName +
- "'/'" + command.OwnerPrefabName + "'; skipping.");
+ SyncLog.Warn(LogTopic.Buildings, "UpgradeSync realize: unknown prefab '" +
+ command.PrefabName + "'/'" + command.OwnerPrefabName + "'; skipping.");
return true;
}
@@ -261,8 +262,8 @@ private bool TryRealize(UpgradePlacementCommand command, int origin, long now)
// without the links it needs. One test refuses the whole class.
if (!EntityManager.HasComponent(prefab))
{
- Mod.log.Warn("[MP] UpgradeSync realize: '" + command.PrefabName +
- "' is not a service upgrade; skipping.");
+ SyncLog.Warn(LogTopic.Buildings, "UpgradeSync realize: '" + command.PrefabName +
+ "' is not a service upgrade; skipping.");
return true;
}
@@ -296,8 +297,9 @@ private bool TryRealize(UpgradePlacementCommand command, int origin, long now)
{
_guard.Mark(UpgradeKey(command.PrefabName, position), now);
ConstructionCharger.ChargeUpgrade(EntityManager, prefab, command.PrefabName);
- Mod.Verbose("[MP] UpgradeSync realize: derived '" + command.PrefabName + "' on '" +
- command.OwnerPrefabName + "' from player " + origin + ".");
+ SyncLog.Detail(LogTopic.Buildings, "UpgradeSync realize: derived '" +
+ command.PrefabName + "' on '" + command.OwnerPrefabName + "' from player " +
+ origin + ".");
return true;
}
if (derived == BuildSyncSystem.NativeDeriveResult.Failed) return true;
@@ -308,12 +310,14 @@ private bool TryRealize(UpgradePlacementCommand command, int origin, long now)
RealizeUpgrade(prefab, owner, position, rotation,
EntityManager.GetComponentData(owner), command.RandomSeed);
ConstructionCharger.ChargeUpgrade(EntityManager, prefab, command.PrefabName);
- Mod.Verbose("[MP] UpgradeSync realize: attached '" + command.PrefabName + "' to '" +
- command.OwnerPrefabName + "' from player " + origin + ".");
+ SyncLog.Detail(LogTopic.Buildings, "UpgradeSync realize: attached '" +
+ command.PrefabName + "' to '" + command.OwnerPrefabName + "' from player " +
+ origin + ".");
}
catch (System.Exception ex)
{
- Mod.log.Error("[MP] UpgradeSync realize FAILED for '" + command.PrefabName + "': " + ex);
+ SyncLog.Error(LogTopic.Buildings, "UpgradeSync realize FAILED for '" +
+ command.PrefabName + "': " + ex);
}
return true;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/DefinitionGateSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/DefinitionGateSystem.cs
index 6858717..a87803d 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/DefinitionGateSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/DefinitionGateSystem.cs
@@ -4,6 +4,8 @@
using Unity.Collections;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Systems.Net;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -27,7 +29,6 @@ public partial class DefinitionGateSystem : GameSystemBase
protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(DefinitionGateSystem) + " ready.");
_netSync = World.GetOrCreateSystemManaged();
_buildSync = World.GetOrCreateSystemManaged();
_toolSystem = World.GetOrCreateSystemManaged();
@@ -115,7 +116,7 @@ protected override void OnUpdate()
if (killed > 0)
{
_netSync.ForceActiveToolUpdate();
- Diagnostics.FlightRecorder.Note("def gate wiped defs=" + killed);
+ SyncLog.Trace(LogTopic.Pipeline, "def gate wiped defs=" + killed);
}
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/ObjectToolApplyCaptureSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/ObjectToolApplyCaptureSystem.cs
index 53d1fdc..901936f 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/ObjectToolApplyCaptureSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/ObjectToolApplyCaptureSystem.cs
@@ -1,5 +1,7 @@
using Game;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Systems.Net;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -19,7 +21,6 @@ protected override void OnCreate()
base.OnCreate();
_buildSync = World.GetOrCreateSystemManaged();
_netSync = World.GetOrCreateSystemManaged();
- Mod.log.Info(nameof(ObjectToolApplyCaptureSystem) + " ready.");
}
protected override void OnUpdate()
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/OwnerDefinitionSnapshotSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/OwnerDefinitionSnapshotSystem.cs
index 29ee379..d60f8a8 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/OwnerDefinitionSnapshotSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/OwnerDefinitionSnapshotSystem.cs
@@ -4,6 +4,8 @@
using Unity.Collections;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Systems.Net;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -24,7 +26,6 @@ public partial class OwnerDefinitionSnapshotSystem : GameSystemBase
protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(OwnerDefinitionSnapshotSystem) + " ready.");
_netSync = World.GetOrCreateSystemManaged();
_describedTemps = GetEntityQuery(new EntityQueryDesc
{
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/SyncRealizeSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/SyncRealizeSystem.cs
index a2114a5..73ee040 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/SyncRealizeSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Pipeline/SyncRealizeSystem.cs
@@ -2,6 +2,8 @@
using System.Collections.Generic;
using Game;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Systems.Net;
namespace CS2MultiplayerMod.Game.Sync.Systems
{
@@ -71,10 +73,8 @@ private void Step(string stage, Action work)
unchecked(now - last) < FaultReportThrottleMs) return;
_lastFaultTick[stage] = now;
- Mod.log.Warn("[MP] " + stage + " failed this frame and was skipped: " +
- ex.GetType().Name + ": " + ex.Message);
- CS2MultiplayerMod.Game.Diagnostics.FlightRecorder.NoteException(
- "realize stage " + stage, ex);
+ SyncLog.Error(LogTopic.Pipeline, "Realize stage '" + stage +
+ "' failed this frame and was skipped.", ex);
}
}
@@ -109,9 +109,8 @@ protected override void OnUpdate()
if (deferTerrain != _wasDeferringTerrain)
{
_wasDeferringTerrain = deferTerrain;
- CS2MultiplayerMod.Game.Diagnostics.FlightRecorder.Note(deferTerrain
- ? "net/build realize deferred (terrain backlog)"
- : "terrain drained; net/build realize resumed");
+ SyncLog.Trace(LogTopic.Pipeline,
+ deferTerrain ? "net/build realize deferred (terrain backlog)" : "terrain drained; net/build realize resumed");
}
Step("BuildSync", _buildSync.RealizePending);
@@ -129,9 +128,8 @@ protected override void OnUpdate()
if (netMutationHeld != _wasHoldingNetMutations)
{
_wasHoldingNetMutations = netMutationHeld;
- CS2MultiplayerMod.Game.Diagnostics.FlightRecorder.Note(netMutationHeld
- ? "net delete/replace held behind a stalled placement"
- : "net delete/replace resumed");
+ SyncLog.Trace(LogTopic.Pipeline,
+ netMutationHeld ? "net delete/replace held behind a stalled placement" : "net delete/replace resumed");
}
// DeleteSync BEFORE NetSync: a remote bulldoze applied this frame tags its edge Deleted,
// and NetSync's split-target query excludes Deleted edges — so NetSync never resolves a
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/Capture.cs b/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/Capture.cs
index 070afb5..55c2e63 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/Capture.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/Capture.cs
@@ -6,7 +6,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -44,8 +46,8 @@ private void BaselineLiveRoutes()
}
if (Mod.Service != null) _lastEditScanMs = Mod.Service.NowMs;
- Diagnostics.FlightRecorder.Note("route baseline live=" + _knownRoutes.Count +
- " pending=" + _baselinePendingRoutes.Count);
+ SyncLog.Trace(LogTopic.Routes, "route baseline live=" + _knownRoutes.Count + " pending=" +
+ _baselinePendingRoutes.Count);
}
private bool TryCaptureSnapshot(Entity route, out RouteSnapshot snapshot)
@@ -304,9 +306,8 @@ private void PublishCreate(MultiplayerSession session, Entity entity, string nam
Waypoints = snapshot.Waypoints,
};
session.SendCommand(0, RouteCreateCommand.Id, command.Encode());
- Mod.Verbose("[MP] RouteSync captured line '" + name + "' (" +
- DescribeShape(snapshot.Waypoints) + ", number " +
- snapshot.RouteNumber + ").");
+ SyncLog.Detail(LogTopic.Routes, "RouteSync captured line '" + name + "' (" +
+ DescribeShape(snapshot.Waypoints) + ", number " + snapshot.RouteNumber + ").");
}
_knownRoutes[entity] = snapshot;
}
@@ -526,9 +527,9 @@ private void ScanForEdits(MultiplayerSession session, long now)
Waypoints = snapshot.Waypoints,
};
session.SendCommand(0, RouteUpdateCommand.Id, command.Encode());
- Mod.Verbose("[MP] RouteSync captured edit of line '" + name + "' (" +
- DescribeShape(snapshot.Waypoints) + ", number " +
- snapshot.RouteNumber + ").");
+ SyncLog.Detail(LogTopic.Routes, "RouteSync captured edit of line '" + name +
+ "' (" + DescribeShape(snapshot.Waypoints) + ", number " +
+ snapshot.RouteNumber + ").");
}
}
finally
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/Realize.cs b/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/Realize.cs
index cb13cbc..94b97cd 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/Realize.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/Realize.cs
@@ -7,6 +7,8 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -51,8 +53,8 @@ private RealizeResult RealizeCreate(RouteCreateCommand command, int originPlayer
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.StreamLoss)
.About("route prefab on creation")
.Tried("nothing - this game does not have the transport line prefab the other player used"));
- Mod.log.Warn("[MP] RouteSync create: unknown prefab '" +
- command.PrefabName + "'; skipping.");
+ SyncLog.Warn(LogTopic.Routes, "RouteSync create: unknown prefab '" +
+ command.PrefabName + "'; skipping.");
return RealizeResult.Rejected;
}
if (!ValidateRouteContract(prefab, command.Waypoints, command.PrefabName))
@@ -76,8 +78,9 @@ private RealizeResult RealizeCreate(RouteCreateCommand command, int originPlayer
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
.About("pending line number")
.Tried("nothing - another line being created in this batch already claimed that number"));
- Mod.log.Warn("[MP] RouteSync create: two different pending lines claim number " +
- command.RouteNumber + " for '" + command.PrefabName + "'.");
+ SyncLog.Warn(LogTopic.Routes,
+ "RouteSync create: two different pending lines claim number " +
+ command.RouteNumber + " for '" + command.PrefabName + "'.");
return RealizeResult.Rejected;
}
// Two distinct lines may legitimately use the same stops. Serialize that shape so
@@ -95,9 +98,9 @@ private RealizeResult RealizeCreate(RouteCreateCommand command, int originPlayer
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
.About("line number on creation")
.Tried("nothing - an established line here already uses that number"));
- Mod.log.Warn("[MP] RouteSync create: route number " + command.RouteNumber +
- " for '" + command.PrefabName +
- "' already belongs to a different line; requested a fresh world sync.");
+ SyncLog.Warn(LogTopic.Routes, "RouteSync create: route number " +
+ command.RouteNumber + " for '" + command.PrefabName +
+ "' already belongs to a different line; requested a fresh world sync.");
return RealizeResult.Rejected;
}
if (existing != Entity.Null)
@@ -179,12 +182,9 @@ private RealizeResult RealizeCreate(RouteCreateCommand command, int originPlayer
}
MarkCreateGuards(command, now);
- Diagnostics.FlightRecorder.Note("route create definition armed " +
- DescribeShape(command.Waypoints));
- Mod.Verbose("[MP] RouteSync create: submitted line '" +
- command.PrefabName + "' (" + DescribeShape(command.Waypoints) +
- ", number " + command.RouteNumber + ") from player " +
- originPlayerId + ".");
+ SyncLog.Detail(LogTopic.Routes, "RouteSync create: submitted line '" +
+ command.PrefabName + "' (" + DescribeShape(command.Waypoints) + ", number " +
+ command.RouteNumber + ") from player " + originPlayerId + ".");
return RealizeResult.Applied;
}
catch (Exception ex)
@@ -201,8 +201,8 @@ private RealizeResult RealizeCreate(RouteCreateCommand command, int originPlayer
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
.About("line creation")
.Tried("nothing - creation threw and was rolled back"));
- Mod.log.Error("[MP] RouteSync create FAILED for '" +
- command.PrefabName + "': " + ex);
+ SyncLog.Error(LogTopic.Routes, "RouteSync create FAILED for '" + command.PrefabName +
+ "': " + ex);
return RealizeResult.Rejected;
}
}
@@ -217,8 +217,8 @@ private RealizeResult RealizeUpdate(RouteUpdateCommand command, int originPlayer
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.StreamLoss)
.About("route prefab on update")
.Tried("nothing - this game does not have the transport line prefab the other player used"));
- Mod.log.Warn("[MP] RouteSync update: unknown prefab '" +
- command.PrefabName + "'; skipping.");
+ SyncLog.Warn(LogTopic.Routes, "RouteSync update: unknown prefab '" +
+ command.PrefabName + "'; skipping.");
return RealizeResult.Rejected;
}
if (!ValidateRouteContract(prefab, command.Waypoints, command.PrefabName))
@@ -231,10 +231,10 @@ private RealizeResult RealizeUpdate(RouteUpdateCommand command, int originPlayer
if (route == Entity.Null)
{
if (ambiguous)
- Mod.Verbose("[MP] RouteSync update: multiple local candidates for '" +
- command.PrefabName + "' number " +
- command.AnchorRouteNumber +
- "; waiting instead of editing the wrong line.");
+ SyncLog.Detail(LogTopic.Routes,
+ "RouteSync update: multiple local candidates for '" + command.PrefabName +
+ "' number " + command.AnchorRouteNumber +
+ "; waiting instead of editing the wrong line.");
return RealizeResult.Retry;
}
if (_mutatedRoutesThisFrame.Contains(route))
@@ -257,9 +257,8 @@ private RealizeResult RealizeUpdate(RouteUpdateCommand command, int originPlayer
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
.About("line number on update")
.Tried("nothing - another line here already uses the number this update assigns"));
- Mod.log.Warn("[MP] RouteSync update: requested number " +
- command.RouteNumber + " is already in use for '" +
- command.PrefabName + "'.");
+ SyncLog.Warn(LogTopic.Routes, "RouteSync update: requested number " +
+ command.RouteNumber + " is already in use for '" + command.PrefabName + "'.");
return RealizeResult.Rejected;
}
_mutatedRoutesThisFrame.Add(route);
@@ -328,9 +327,9 @@ private RealizeResult RealizeUpdate(RouteUpdateCommand command, int originPlayer
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
.About("line number on update")
.Tried("nothing - another line here already uses the number this update assigns"));
- Mod.log.Warn("[MP] RouteSync update: requested number " +
- command.RouteNumber + " is already in use for '" +
- command.PrefabName + "'.");
+ SyncLog.Warn(LogTopic.Routes, "RouteSync update: requested number " +
+ command.RouteNumber + " is already in use for '" + command.PrefabName +
+ "'.");
return RealizeResult.Rejected;
}
@@ -357,13 +356,12 @@ private RealizeResult RealizeUpdate(RouteUpdateCommand command, int originPlayer
}
else
{
- Diagnostics.FlightRecorder.Note("route update definition armed " +
- DescribeShape(command.Waypoints));
+ SyncLog.Trace(LogTopic.Routes, "route update definition armed " +
+ DescribeShape(command.Waypoints));
}
- Mod.Verbose("[MP] RouteSync update: applied line '" +
- command.PrefabName + "' (" + DescribeShape(command.Waypoints) +
- ", number " + command.RouteNumber + ") from player " +
- originPlayerId + ".");
+ SyncLog.Detail(LogTopic.Routes, "RouteSync update: applied line '" +
+ command.PrefabName + "' (" + DescribeShape(command.Waypoints) + ", number " +
+ command.RouteNumber + ") from player " + originPlayerId + ".");
return RealizeResult.Applied;
}
catch (Exception ex)
@@ -382,8 +380,8 @@ private RealizeResult RealizeUpdate(RouteUpdateCommand command, int originPlayer
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
.About("line update")
.Tried("nothing - the update threw and was rolled back"));
- Mod.log.Error("[MP] RouteSync update FAILED for '" +
- command.PrefabName + "': " + ex);
+ SyncLog.Error(LogTopic.Routes, "RouteSync update FAILED for '" + command.PrefabName +
+ "': " + ex);
return RealizeResult.Rejected;
}
}
@@ -398,8 +396,8 @@ private RealizeResult RealizeDelete(RouteDeleteCommand command, long now)
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.StreamLoss)
.About("route prefab on deletion")
.Tried("nothing - this game does not have the transport line prefab the other player used"));
- Mod.log.Warn("[MP] RouteSync delete: unknown prefab '" +
- command.PrefabName + "'; skipping.");
+ SyncLog.Warn(LogTopic.Routes, "RouteSync delete: unknown prefab '" +
+ command.PrefabName + "'; skipping.");
return RealizeResult.Rejected;
}
@@ -411,9 +409,10 @@ private RealizeResult RealizeDelete(RouteDeleteCommand command, long now)
if (route == Entity.Null)
{
if (ambiguous)
- Mod.Verbose("[MP] RouteSync delete: multiple local candidates for '" +
- command.PrefabName + "' number " + command.RouteNumber +
- "; waiting instead of deleting the wrong line.");
+ SyncLog.Detail(LogTopic.Routes,
+ "RouteSync delete: multiple local candidates for '" + command.PrefabName +
+ "' number " + command.RouteNumber +
+ "; waiting instead of deleting the wrong line.");
return RealizeResult.Retry;
}
if (_mutatedRoutesThisFrame.Contains(route))
@@ -426,8 +425,8 @@ private RealizeResult RealizeDelete(RouteDeleteCommand command, long now)
if (!EntityManager.HasComponent(route))
EntityManager.AddComponent(route);
_knownRoutes.Remove(route);
- Mod.Verbose("[MP] RouteSync deleted line '" + command.PrefabName +
- "' number " + command.RouteNumber + ".");
+ SyncLog.Detail(LogTopic.Routes, "RouteSync deleted line '" + command.PrefabName +
+ "' number " + command.RouteNumber + ".");
return RealizeResult.Applied;
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/RealizeCommit.cs b/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/RealizeCommit.cs
index 33acdfc..f6b98b9 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/RealizeCommit.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/RealizeCommit.cs
@@ -7,6 +7,8 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -46,9 +48,9 @@ private void FinalizeCreatedRoutes(long now)
: "waited for the line the game builds from this definition, not counting " +
"time route realization was held back")
.Fact("line number", pending.RouteNumber));
- Mod.log.Warn("[MP] RouteSync could not finalize created line '" +
- pending.PrefabName + "' number " + pending.RouteNumber +
- "; requested a fresh world sync.");
+ SyncLog.Warn(LogTopic.Routes, "RouteSync could not finalize created line '" +
+ pending.PrefabName + "' number " + pending.RouteNumber +
+ "; requested a fresh world sync.");
}
// The game's initializer may temporarily give several routes created in one batch the
@@ -68,21 +70,17 @@ private void FinalizeCreatedRoutes(long now)
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
.About("line number after creation")
.Tried("assigned every line finalized in this batch together before rejecting the conflict"));
- Mod.log.Warn("[MP] RouteSync could not assign number " +
- pending.RouteNumber + " to '" + pending.PrefabName +
- "'; requested a fresh world sync.");
+ SyncLog.Warn(LogTopic.Routes, "RouteSync could not assign number " +
+ pending.RouteNumber + " to '" + pending.PrefabName +
+ "'; requested a fresh world sync.");
}
else
{
RouteSnapshot snapshot;
if (TryCaptureSnapshot(route, out snapshot))
_knownRoutes[route] = snapshot;
- Diagnostics.FlightRecorder.Note("route create finalized number=" +
- pending.RouteNumber + " stops=" +
- pending.Waypoints.Length);
- Mod.Verbose("[MP] RouteSync finalized line '" +
- pending.PrefabName + "' number " +
- pending.RouteNumber + ".");
+ SyncLog.Detail(LogTopic.Routes, "RouteSync finalized line '" +
+ pending.PrefabName + "' number " + pending.RouteNumber + ".");
}
_pendingCreateMetadata.Remove(pending);
}
@@ -141,7 +139,7 @@ private void CompleteCreateCommit(PendingCreateMetadata pending)
pending.GraphCommitted = true;
if (Mod.Service != null)
pending.DeadlineMs = Mod.Service.NowMs + RetryWindowMs;
- Diagnostics.FlightRecorder.Note("route create graph committed; awaiting identity");
+ SyncLog.Trace(LogTopic.Routes, "route create graph committed; awaiting identity");
}
private void ReplayCreateAfterCommitLoss(PendingCreateMetadata pending)
@@ -166,7 +164,7 @@ private void CompleteUpdateCommit(PendingUpdateCommit pending)
_knownRoutes[pending.Route] = snapshot;
else
_knownRoutes[pending.Route] = pending.Desired;
- Diagnostics.FlightRecorder.Note("route update graph committed");
+ SyncLog.Trace(LogTopic.Routes, "route update graph committed");
}
private void ReplayUpdateAfterCommitLoss(PendingUpdateCommit pending)
@@ -190,9 +188,9 @@ private void QueueCommitReplay(PendingRouteCommand command, string operation)
// reload BECAUSE a world reload is under way is how a session gets into a loop.
if (service == null || !service.GameplaySyncReady)
{
- Mod.log.Warn("[MP] RouteSync " + operation +
- " commit was lost while the world was being replaced; the incoming " +
- "world supersedes it.");
+ SyncLog.Warn(LogTopic.Routes, "RouteSync " + operation +
+ " commit was lost while the world was being replaced; the incoming " +
+ "world supersedes it.");
return;
}
if (now >= command.DeadlineMs ||
@@ -204,16 +202,15 @@ private void QueueCommitReplay(PendingRouteCommand command, string operation)
.About("route " + operation + " commit")
.Tried("nothing - the armed commit was wiped and its window had already closed")
.Fact("route commands still queued", _pendingCommands.Count));
- Mod.log.Warn("[MP] RouteSync " + operation +
- " commit was lost and could not be replayed safely.");
+ SyncLog.Warn(LogTopic.Routes, "RouteSync " + operation +
+ " commit was lost and could not be replayed safely.");
return;
}
command.NextAttemptMs = now;
command.RetryDelayMs = InitialRetryDelayMs;
_pendingCommands.Insert(0, command);
- Diagnostics.FlightRecorder.Note("route " + operation +
- " commit re-queued");
+ SyncLog.Trace(LogTopic.Routes, "route " + operation + " commit re-queued");
}
private void MarkCreateGuards(RouteCreateCommand command, long now)
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/RealizeConnections.cs b/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/RealizeConnections.cs
index b9a15ea..a87416a 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/RealizeConnections.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/RealizeConnections.cs
@@ -7,6 +7,8 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -45,8 +47,8 @@ private bool ValidateRouteContract(Entity routePrefab,
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
.About("line with no stops")
.Tried("nothing - a public transport line with no stops cannot be created"));
- Mod.log.Warn("[MP] RouteSync rejected public-transport line '" + prefabName +
- "' because none of its waypoints is connected to a stop.");
+ SyncLog.Warn(LogTopic.Routes, "RouteSync rejected public-transport line '" + prefabName +
+ "' because none of its waypoints is connected to a stop.");
return false;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/RouteSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/RouteSyncSystem.cs
index 1379fe8..7e609d5 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/RouteSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Routes/RouteSyncSystem/RouteSyncSystem.cs
@@ -7,8 +7,10 @@
using Game.Tools;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Systems.Net;
@@ -111,7 +113,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(RouteSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_prefabIndex = new PrefabIndex(_prefabSystem,
GetEntityQuery(ComponentType.ReadOnly()));
@@ -334,7 +335,8 @@ public void RealizePending()
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.StreamLoss)
.About("malformed route command")
.Tried("nothing - the command could not be decoded"));
- Mod.log.Warn("[MP] RouteSync: dropping malformed command: " + ex.Message);
+ SyncLog.Warn(LogTopic.Routes, "RouteSync: dropping malformed command: " +
+ ex.Message);
}
}
}
@@ -360,8 +362,8 @@ private RealizeResult TryRealize(PendingRouteCommand pending, long now)
// attributable from the log instead of only visible as a missing line.
if (_lastRealizeFailure == null) return result;
if (pending.LastFailure == null)
- Diagnostics.FlightRecorder.Note("route dependency unresolved: " +
- _lastRealizeFailure);
+ SyncLog.Trace(LogTopic.Routes, "route dependency unresolved: " +
+ _lastRealizeFailure);
pending.LastFailure = _lastRealizeFailure;
return result;
}
@@ -385,7 +387,8 @@ private void QueueRetry(PendingRouteCommand pending, long now)
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.StreamLoss)
.About("route retry queue")
.Tried("nothing - the retry queue was full and was cleared"));
- Mod.log.Warn("[MP] RouteSync retry queue overflowed; cleared it and requested a fresh world sync.");
+ SyncLog.Warn(LogTopic.Routes,
+ "RouteSync retry queue overflowed; cleared it and requested a fresh world sync.");
}
private void ExpirePending(PendingRouteCommand pending)
@@ -405,12 +408,10 @@ private void ExpirePending(PendingRouteCommand pending)
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.MissingTarget)
.About("route dependency")
.Tried("retried with backoff for 30 s of attempts, not counting time this system was held back"));
- Mod.log.Warn("[MP] RouteSync " + operation + " for '" + prefabName +
- "' did not resolve within " + (RetryWindowMs / 1000) + " s" +
- (pending.LastFailure != null ? " (" + pending.LastFailure + ")" : string.Empty) +
- (needsRecovery
- ? "; requested a fresh world sync."
- : "; line is already absent."));
+ SyncLog.Warn(LogTopic.Routes, "RouteSync " + operation + " for '" + prefabName +
+ "' did not resolve within " + (RetryWindowMs / 1000) + " s" +
+ (pending.LastFailure != null ? " (" + pending.LastFailure + ")" : string.Empty) +
+ (needsRecovery ? "; requested a fresh world sync." : "; line is already absent."));
}
private void DrainQueue()
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Routes/TransitFareSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Routes/TransitFareSyncSystem.cs
index f9d11c8..e64954f 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Routes/TransitFareSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Routes/TransitFareSyncSystem.cs
@@ -7,8 +7,10 @@
using Game.Tools;
using Unity.Collections;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -62,7 +64,6 @@ protected override void OnCreate()
_observer = SyncObserverBinding.Bind(
() => new CommandObserver(_incoming, TransitFareCommand.Id), DrainQueue);
- Mod.log.Info(nameof(TransitFareSyncSystem) + " ready.");
}
protected override void OnDestroy()
@@ -142,7 +143,8 @@ private void Scan(MultiplayerSession session, long now)
TicketPrice = price,
};
session.SendCommand(0, TransitFareCommand.Id, command.Encode());
- Mod.Verbose("[MP] TransitFare: broadcast line " + number + " at " + price + ".");
+ SyncLog.Detail(LogTopic.Routes, "TransitFare: broadcast line " + number + " at " +
+ price + ".");
}
// A line deleted while we were not looking would otherwise keep its last price in
@@ -181,7 +183,8 @@ private void ApplyIncoming(MultiplayerSession session, long now)
try { command = TransitFareCommand.Decode(message.Body); }
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] TransitFare: dropping malformed command: " + ex.Message);
+ SyncLog.Warn(LogTopic.Routes, "TransitFare: dropping malformed command: " +
+ ex.Message);
continue;
}
@@ -189,16 +192,16 @@ private void ApplyIncoming(MultiplayerSession session, long now)
{
// Not a reason to resync: the line is on its way through the route pipeline,
// and its price will be picked up by the sender's next scan once it lands.
- Mod.Verbose("[MP] TransitFare: line " + command.RouteNumber +
- " not here yet; ignoring its fare.");
+ SyncLog.Detail(LogTopic.Routes, "TransitFare: line " + command.RouteNumber +
+ " not here yet; ignoring its fare.");
continue;
}
_guard.Mark(FareKey(command.RouteNumber, command.TicketPrice), now);
_known[command.RouteNumber] = command.TicketPrice;
- Mod.Verbose("[MP] TransitFare: line " + command.RouteNumber +
- " set to " + command.TicketPrice + " by player " +
- message.OriginPlayerId + ".");
+ SyncLog.Detail(LogTopic.Routes, "TransitFare: line " + command.RouteNumber +
+ " set to " + command.TicketPrice + " by player " + message.OriginPlayerId +
+ ".");
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/CompanyStatsSyncSystem/CompanyStatsSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/CompanyStatsSyncSystem/CompanyStatsSyncSystem.cs
index 1917614..956094f 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/CompanyStatsSyncSystem/CompanyStatsSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/CompanyStatsSyncSystem/CompanyStatsSyncSystem.cs
@@ -1,8 +1,10 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using Game;
@@ -12,7 +14,6 @@
using Game.Prefabs;
using Game.Simulation;
using Game.Tools;
-using CS2MultiplayerMod.Game.Diagnostics;
using Unity.Collections;
using Unity.Entities;
@@ -235,8 +236,6 @@ protected override void OnCreate()
});
SyncInbox.RegisterDrain(DrainForWorldChange);
- Mod.log.Info(nameof(CompanyStatsSyncSystem) +
- " ready (host-authoritative workplace tenancy and figures).");
}
protected override void OnDestroy()
@@ -470,16 +469,16 @@ private void WriteToWorkplaceTopics(string body)
bool office = SyncLog.IsZoneEnabled(SyncZone.Office);
if (!commercial && !industrial && !office) return;
string line = "CompanyStats/30s: " + body;
- if (commercial) SyncLog.WriteZone(SyncZone.Commercial, line);
- if (industrial) SyncLog.WriteZone(SyncZone.Industrial, line);
- if (office) SyncLog.WriteZone(SyncZone.Office, line);
+ if (commercial) SyncLog.DetailZone(SyncZone.Commercial, line);
+ if (industrial) SyncLog.DetailZone(SyncZone.Industrial, line);
+ if (office) SyncLog.DetailZone(SyncZone.Office, line);
}
private void ReportZone(SyncZone zone)
{
if (!SyncLog.IsZoneEnabled(zone)) return;
int index = (int)zone;
- SyncLog.WriteZone(zone, "corrected=" + _zoneApplied[index] + ", opened=" +
+ SyncLog.DetailZone(zone, "corrected=" + _zoneApplied[index] + ", opened=" +
_zoneOpened[index] + ", closed=" + _zoneClosed[index] + ".");
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/DisasterSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/DisasterSyncSystem.cs
index f1e288d..8621cda 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/DisasterSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/DisasterSyncSystem.cs
@@ -9,9 +9,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -63,7 +63,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(DisasterSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_prefabIndex = new PrefabIndex(_prefabSystem, GetEntityQuery(ComponentType.ReadOnly()));
_simulation = World.GetOrCreateSystemManaged();
@@ -152,7 +151,8 @@ public void RealizePending()
try { command = DisasterEventCommand.Decode(message.Body); }
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] DisasterSync: dropping malformed command: " + ex.Message);
+ SyncLog.Warn(LogTopic.City, "DisasterSync: dropping malformed command: " +
+ ex.Message);
continue;
}
@@ -214,12 +214,11 @@ private void CapturePhenomena(MultiplayerSession session)
command.DurationFrames;
if (IsDamaging(prefab))
{
- Mod.log.Info("[MP] DisasterSync sent " + detail + ".");
- FlightRecorder.Note("disaster sent " + detail);
+ SyncLog.Detail(LogTopic.City, "DisasterSync sent " + detail + ".");
}
else
{
- Mod.Verbose("[MP] DisasterSync sent weather " + detail + ".");
+ SyncLog.Detail(LogTopic.City, "DisasterSync sent weather " + detail + ".");
}
}
}
@@ -272,8 +271,7 @@ private void CaptureSurges(MultiplayerSession session)
string detail = "water surge '" + prefabName + "', intensity " +
command.MaxIntensity + ", lasting " + command.DurationFrames +
" frame(s)";
- Mod.log.Info("[MP] DisasterSync sent " + detail + ".");
- FlightRecorder.Note("disaster sent " + detail);
+ SyncLog.Detail(LogTopic.City, "DisasterSync sent " + detail + ".");
}
}
finally
@@ -305,8 +303,8 @@ private bool Send(MultiplayerSession session, DisasterEventCommand command, stri
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] DisasterSync: refusing to send " + label + " '" +
- command.PrefabName + "': " + ex.Message);
+ SyncLog.Warn(LogTopic.City, "DisasterSync: refusing to send " + label + " '" +
+ command.PrefabName + "': " + ex.Message);
return false;
}
}
@@ -318,14 +316,14 @@ private bool Realize(DisasterEventCommand command, int originPlayerId)
Entity prefab;
if (!_prefabIndex.TryResolve(command.PrefabName, out prefab))
{
- Mod.log.Warn("[MP] DisasterSync: no local event prefab named '" +
- command.PrefabName + "'; ignoring the disaster.");
+ SyncLog.Warn(LogTopic.City, "DisasterSync: no local event prefab named '" +
+ command.PrefabName + "'; ignoring the disaster.");
return false;
}
if (!EntityManager.HasComponent(prefab) || !MatchesKind(prefab, command.Kind))
{
- Mod.log.Warn("[MP] DisasterSync: prefab '" + command.PrefabName + "' is not a " +
- command.Kind + " event here; ignoring.");
+ SyncLog.Warn(LogTopic.City, "DisasterSync: prefab '" + command.PrefabName +
+ "' is not a " + command.Kind + " event here; ignoring.");
return false;
}
@@ -333,8 +331,9 @@ private bool Realize(DisasterEventCommand command, int originPlayerId)
// switched off never starts a damaging event, so it must not accept one either.
if (IsDamaging(prefab) && !_cityConfiguration.naturalDisasters)
{
- Mod.Verbose("[MP] DisasterSync: natural disasters are off in this city; ignoring '" +
- command.PrefabName + "' from player " + originPlayerId + ".");
+ SyncLog.Detail(LogTopic.City,
+ "DisasterSync: natural disasters are off in this city; ignoring '" +
+ command.PrefabName + "' from player " + originPlayerId + ".");
return false;
}
@@ -352,8 +351,9 @@ private bool Realize(DisasterEventCommand command, int originPlayerId)
!HasKindComponent(entity, command.Kind))
{
EntityManager.DestroyEntity(entity);
- Mod.log.Warn("[MP] DisasterSync: the event archetype for '" + command.PrefabName +
- "' is missing what a " + command.Kind + " needs; ignoring.");
+ SyncLog.Warn(LogTopic.City, "DisasterSync: the event archetype for '" +
+ command.PrefabName + "' is missing what a " + command.Kind +
+ " needs; ignoring.");
return false;
}
@@ -372,12 +372,11 @@ private bool Realize(DisasterEventCommand command, int originPlayerId)
originPlayerId + ", starting in " + command.StartDelayFrames + " frame(s)";
if (IsDamaging(prefab))
{
- Mod.log.Info("[MP] DisasterSync realized " + detail + ".");
- FlightRecorder.Note("disaster realized " + detail);
+ SyncLog.Detail(LogTopic.City, "DisasterSync realized " + detail + ".");
}
else
{
- Mod.Verbose("[MP] DisasterSync realized weather " + detail + ".");
+ SyncLog.Detail(LogTopic.City, "DisasterSync realized weather " + detail + ".");
}
return true;
}
@@ -467,9 +466,8 @@ private void SuppressLocalRolls(bool suppress)
spawner.Enabled = !suppress;
_rollsSuppressed = suppress;
- Mod.log.Info("[MP] DisasterSync: local weather-hazard rolls " +
- (suppress ? "stopped; the host's disasters are replicated instead."
- : "restored."));
+ SyncLog.Detail(LogTopic.City, "DisasterSync: local weather-hazard rolls " +
+ (suppress ? "stopped; the host's disasters are replicated instead." : "restored."));
}
// ---- Helpers ------------------------------------------------------------
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/Capture.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/Capture.cs
index 6916223..af07476 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/Capture.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/Capture.cs
@@ -6,7 +6,9 @@
using Game.Simulation;
using Unity.Collections;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -86,9 +88,9 @@ private void CaptureCreated(MultiplayerSession session, long now)
};
}
_sentSpawn++;
- Mod.Verbose("[MP] GrowableSync capture: grew '" + name + "' at " +
- Format(transform.m_Position) + " seed=" + seed + " seq=" +
- command.Sequence + ".");
+ SyncLog.Detail(LogTopic.Buildings, "GrowableSync capture: grew '" + name +
+ "' at " + Format(transform.m_Position) + " seed=" + seed + " seq=" +
+ command.Sequence + ".");
}
}
finally
@@ -136,8 +138,8 @@ private void CaptureRemoved(MultiplayerSession session, long now)
};
Send(session, command);
_sentRemove++;
- Mod.Verbose("[MP] GrowableSync capture: retired '" + name + "' at " +
- Format(transform.m_Position) + " seq=" + command.Sequence + ".");
+ SyncLog.Detail(LogTopic.Buildings, "GrowableSync capture: retired '" + name +
+ "' at " + Format(transform.m_Position) + " seq=" + command.Sequence + ".");
}
}
finally
@@ -187,9 +189,9 @@ private void CaptureLevelChanges(MultiplayerSession session, long now)
// Only reachable if buildings are levelling faster than they finish. Drop
// the memory rather than the cap: a repeat announcement is idempotent on
// the receiver, an unbounded dictionary is not recoverable.
- Mod.log.Warn("[MP] GrowableSync: level-change memory hit " +
- MaxTrackedLevelChanges + " entries and was cleared; " +
- "some level changes may be announced twice.");
+ SyncLog.Warn(LogTopic.Buildings, "GrowableSync: level-change memory hit " +
+ MaxTrackedLevelChanges + " entries and was cleared; " +
+ "some level changes may be announced twice.");
_announcedLevelChange.Clear();
}
_announcedLevelChange[entity] = newPrefab;
@@ -219,8 +221,9 @@ private void CaptureLevelChanges(MultiplayerSession session, long now)
Speed = command.ConstructionSpeed,
};
_sentLevel++;
- Mod.Verbose("[MP] GrowableSync capture: level change to '" + name + "' at " +
- Format(transform.m_Position) + " seq=" + command.Sequence + ".");
+ SyncLog.Detail(LogTopic.Buildings, "GrowableSync capture: level change to '" +
+ name + "' at " + Format(transform.m_Position) + " seq=" + command.Sequence +
+ ".");
}
}
finally
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/GrowableSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/GrowableSyncSystem.cs
index fc02a94..6cfb73f 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/GrowableSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/GrowableSyncSystem.cs
@@ -9,9 +9,11 @@
using Game.Tools;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
using CS2MultiplayerMod.Core.Sync;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -180,7 +182,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(GrowableSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_prefabIndex = new PrefabIndex(_prefabSystem, GetEntityQuery(ComponentType.ReadOnly()));
_objectSearch = new ObjectSearch(
@@ -335,9 +336,9 @@ private bool IsAutonomousGrowable(Entity entity, long now)
if (_buildSync != null && _buildSync.ConsumePlayerPlacedSpawnable(entity, now))
{
_playerPlacedGrowables.Add(entity);
- Mod.Verbose("[MP] GrowableSync: excluded player-placed spawnable '" +
- PrefabIndexSafeName(prefab) + "' from autonomous lifecycle sync.");
- Diagnostics.FlightRecorder.Note("player-placed spawnable excluded from growables");
+ SyncLog.Detail(LogTopic.Buildings,
+ "GrowableSync: excluded player-placed spawnable '" + PrefabIndexSafeName(prefab) +
+ "' from autonomous lifecycle sync.");
return false;
}
return true;
@@ -390,8 +391,8 @@ private void ReportStats(MultiplayerSession session, long now)
_lastStatsMs = now;
if (_sentSpawn + _sentLevel + _sentRemove + _sentState == 0) return;
- Mod.Verbose("[MP] GrowableSync/30s host: spawn=" + _sentSpawn + " level=" + _sentLevel +
- " remove=" + _sentRemove + " state=" + _sentState + ".");
+ SyncLog.Detail(LogTopic.Buildings, "GrowableSync/30s host: spawn=" + _sentSpawn +
+ " level=" + _sentLevel + " remove=" + _sentRemove + " state=" + _sentState + ".");
_sentSpawn = _sentLevel = _sentRemove = _sentState = 0;
}
@@ -403,11 +404,10 @@ private void ReportClientStats(long now)
if (_gotSpawn + _gotLevel + _gotRemove + _gotState + _duplicates + _conflicts +
_unmatched + _unknownPrefab + _rejectedLocal == 0) return;
- Mod.Verbose("[MP] GrowableSync/30s client: spawn=" + _gotSpawn + " level=" + _gotLevel +
- " remove=" + _gotRemove + " state=" + _gotState +
- " duplicate=" + _duplicates + " conflict=" + _conflicts +
- " unmatched=" + _unmatched + " unknownPrefab=" + _unknownPrefab +
- " rejectedLocal=" + _rejectedLocal + ".");
+ SyncLog.Detail(LogTopic.Buildings, "GrowableSync/30s client: spawn=" + _gotSpawn +
+ " level=" + _gotLevel + " remove=" + _gotRemove + " state=" + _gotState +
+ " duplicate=" + _duplicates + " conflict=" + _conflicts + " unmatched=" + _unmatched +
+ " unknownPrefab=" + _unknownPrefab + " rejectedLocal=" + _rejectedLocal + ".");
_gotSpawn = _gotLevel = _gotRemove = _gotState = 0;
_duplicates = _conflicts = _unmatched = _unknownPrefab = _rejectedLocal = 0;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/Realize.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/Realize.cs
index d49c6e7..fe17838 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/Realize.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/Realize.cs
@@ -6,8 +6,10 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -98,17 +100,18 @@ public void RealizePending()
try { command = GrowableLifecycleCommand.Decode(message.Body); }
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] GrowableSync: dropping malformed command from player " +
- message.OriginPlayerId + ": " + ex.Message);
+ SyncLog.Warn(LogTopic.Buildings,
+ "GrowableSync: dropping malformed command from player " +
+ message.OriginPlayerId + ": " + ex.Message);
continue;
}
if (_applied.Contains(command.Sequence, now))
{
_duplicates++;
- Mod.Verbose("[MP] GrowableSync: ignoring duplicate " +
- GrowableLifecycleCommand.OpName(command.Op) + " seq=" +
- command.Sequence + " (already applied).");
+ SyncLog.Detail(LogTopic.Buildings, "GrowableSync: ignoring duplicate " +
+ GrowableLifecycleCommand.OpName(command.Op) + " seq=" + command.Sequence +
+ " (already applied).");
continue;
}
@@ -148,8 +151,8 @@ private bool ApplySpawn(GrowableLifecycleCommand command, long now)
// is not a zoned building at all. Neither is retryable.
_unknownPrefab++;
_applied.Remember(command.Sequence, now, ReplayWindowMs);
- Mod.log.Warn("[MP] GrowableSync: unknown zoned-building prefab '" +
- command.PrefabName + "' at " + Format(position) + "; spawn dropped.");
+ SyncLog.Warn(LogTopic.Buildings, "GrowableSync: unknown zoned-building prefab '" +
+ command.PrefabName + "' at " + Format(position) + "; spawn dropped.");
return true;
}
@@ -175,8 +178,9 @@ private bool ApplySpawn(GrowableLifecycleCommand command, long now)
}
_duplicates++;
_applied.Remember(command.Sequence, now, ReplayWindowMs);
- Mod.Verbose("[MP] GrowableSync: '" + command.PrefabName + "' already stands at " +
- Format(position) + "; spawn seq=" + command.Sequence + " ignored.");
+ SyncLog.Detail(LogTopic.Buildings, "GrowableSync: '" + command.PrefabName +
+ "' already stands at " + Format(position) + "; spawn seq=" +
+ command.Sequence + " ignored.");
return true;
}
@@ -188,11 +192,10 @@ private bool ApplySpawn(GrowableLifecycleCommand command, long now)
// the two cities agreeing about the building that was deliberately placed.
_conflicts++;
_applied.Remember(command.Sequence, now, ReplayWindowMs);
- Mod.log.Warn("[MP] GrowableSync conflict: '" + command.PrefabName + "' at " +
- Format(position) + " overlaps " +
- DescribeBlocker(placedBlocker, now) +
- "; spawn refused (seq=" + command.Sequence + ").");
- Diagnostics.FlightRecorder.Note("growable spawn refused (placed building)");
+ SyncLog.Warn(LogTopic.Buildings, "GrowableSync conflict: '" + command.PrefabName +
+ "' at " + Format(position) + " overlaps " +
+ DescribeBlocker(placedBlocker, now) + "; spawn refused (seq=" +
+ command.Sequence + ").");
return true;
}
@@ -202,11 +205,12 @@ private bool ApplySpawn(GrowableLifecycleCommand command, long now)
for (int i = 0; i < blockers.Length; i++)
{
_conflicts++;
- Mod.log.Warn("[MP] GrowableSync conflict: evicting locally grown " +
- DescribeBlocker(blockers[i], now) + " for the host's '" +
- command.PrefabName + "' at " + Format(position) + ".");
+ SyncLog.Warn(LogTopic.Buildings,
+ "GrowableSync conflict: evicting locally grown " +
+ DescribeBlocker(blockers[i], now) + " for the host's '" + command.PrefabName +
+ "' at " + Format(position) + ".");
EntityManager.AddComponent(blockers[i]);
- Diagnostics.FlightRecorder.Note("growable evicted for host spawn");
+ SyncLog.Trace(LogTopic.Buildings, "growable evicted for host spawn");
}
}
finally
@@ -219,9 +223,9 @@ private bool ApplySpawn(GrowableLifecycleCommand command, long now)
NoteSelfRealized(prefab, position, command, now);
_applied.Remember(command.Sequence, now, ReplayWindowMs);
_gotSpawn++;
- Mod.Verbose("[MP] GrowableSync realize: built '" + command.PrefabName + "' at " +
- Format(position) + " seed=" + command.RandomSeed + " seq=" +
- command.Sequence + ".");
+ SyncLog.Detail(LogTopic.Buildings, "GrowableSync realize: built '" + command.PrefabName +
+ "' at " + Format(position) + " seed=" + command.RandomSeed + " seq=" +
+ command.Sequence + ".");
return true;
}
@@ -241,8 +245,8 @@ private bool ApplyLevel(GrowableLifecycleCommand command, long now)
{
_unknownPrefab++;
_applied.Remember(command.Sequence, now, ReplayWindowMs);
- Mod.log.Warn("[MP] GrowableSync: unknown level-change prefab '" +
- command.PrefabName + "' at " + Format(position) + "; skipped.");
+ SyncLog.Warn(LogTopic.Buildings, "GrowableSync: unknown level-change prefab '" +
+ command.PrefabName + "' at " + Format(position) + "; skipped.");
return true;
}
@@ -251,8 +255,8 @@ private bool ApplyLevel(GrowableLifecycleCommand command, long now)
{
_unmatched++;
_applied.Remember(command.Sequence, now, ReplayWindowMs);
- Mod.Verbose("[MP] GrowableSync: no building at " + Format(position) +
- " to level to '" + command.PrefabName + "'; skipped.");
+ SyncLog.Detail(LogTopic.Buildings, "GrowableSync: no building at " +
+ Format(position) + " to level to '" + command.PrefabName + "'; skipped.");
return true;
}
@@ -270,9 +274,9 @@ private bool ApplyLevel(GrowableLifecycleCommand command, long now)
return true;
}
if (current.m_NewPrefab != Entity.Null)
- Mod.Verbose("[MP] GrowableSync: replacing this machine's own level-change target " +
- "at " + Format(position) + " with the host's '" +
- command.PrefabName + "'.");
+ SyncLog.Detail(LogTopic.Buildings,
+ "GrowableSync: replacing this machine's own level-change target " + "at " +
+ Format(position) + " with the host's '" + command.PrefabName + "'.");
current.m_NewPrefab = prefab;
current.m_Progress = command.ConstructionProgress;
current.m_Speed = command.ConstructionSpeed;
@@ -292,8 +296,8 @@ private bool ApplyLevel(GrowableLifecycleCommand command, long now)
EntityManager.AddComponent(building);
_applied.Remember(command.Sequence, now, ReplayWindowMs);
_gotLevel++;
- Mod.Verbose("[MP] GrowableSync realize: level change to '" + command.PrefabName +
- "' at " + Format(position) + " seq=" + command.Sequence + ".");
+ SyncLog.Detail(LogTopic.Buildings, "GrowableSync realize: level change to '" +
+ command.PrefabName + "' at " + Format(position) + " seq=" + command.Sequence + ".");
return true;
}
@@ -310,16 +314,16 @@ private bool ApplyRemove(GrowableLifecycleCommand command, long now)
// built here (its spawn was refused), or a player already bulldozed it.
_unmatched++;
_applied.Remember(command.Sequence, now, ReplayWindowMs);
- Mod.Verbose("[MP] GrowableSync: no building at " + Format(position) +
- " to remove ('" + command.PrefabName + "'); already gone.");
+ SyncLog.Detail(LogTopic.Buildings, "GrowableSync: no building at " +
+ Format(position) + " to remove ('" + command.PrefabName + "'); already gone.");
return true;
}
EntityManager.AddComponent(building);
_applied.Remember(command.Sequence, now, ReplayWindowMs);
_gotRemove++;
- Mod.Verbose("[MP] GrowableSync realize: removed '" + command.PrefabName + "' at " +
- Format(position) + " seq=" + command.Sequence + ".");
+ SyncLog.Detail(LogTopic.Buildings, "GrowableSync realize: removed '" +
+ command.PrefabName + "' at " + Format(position) + " seq=" + command.Sequence + ".");
return true;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/RealizeGuard.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/RealizeGuard.cs
index a6da9e2..06a04fc 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/RealizeGuard.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/GrowableSyncSystem/RealizeGuard.cs
@@ -6,7 +6,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -104,10 +106,9 @@ private void RejectLocallyGrownBuildings(long now)
EntityManager.AddComponent(entity);
_rejectedLocal++;
- Mod.log.Warn("[MP] GrowableSync: this client grew '" +
- PrefabIndexSafeName(prefab) + "' at " + Format(position) +
- " on its own; removed (the host decides zoned buildings).");
- Diagnostics.FlightRecorder.Note("locally grown building rejected");
+ SyncLog.Warn(LogTopic.Buildings, "GrowableSync: this client grew '" +
+ PrefabIndexSafeName(prefab) + "' at " + Format(position) +
+ " on its own; removed (the host decides zoned buildings).");
}
}
finally
@@ -166,11 +167,9 @@ private void ValidateRealizedBuildings(long now)
if (pending.Expiry <= now)
{
_realizationValidations.RemoveAt(i);
- Mod.log.Warn("[MP] GrowableSync: generated building '" +
- PrefabIndexSafeName(pending.Prefab) + "' at " +
- Format(pending.Position) + " did not join its road/service graph; " +
- "requesting world repair.");
- Diagnostics.FlightRecorder.Note("growable realization invalid/resync");
+ SyncLog.Warn(LogTopic.Buildings, "GrowableSync: generated building '" +
+ PrefabIndexSafeName(pending.Prefab) + "' at " + Format(pending.Position) +
+ " did not join its road/service graph; " + "requesting world repair.");
SyncInbox.RequestResync(CS2MultiplayerMod.Game.Diagnostics.ResyncReport
.Create("growable building failed road/service realization", "growable",
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.MissingTarget)
@@ -230,8 +229,8 @@ private void SyncInboxDrop(int localPlayerId)
while (_incoming.TryDequeue(out message))
if (message.OriginPlayerId != localPlayerId) foreign++;
if (foreign == 0) return;
- Mod.log.Warn("[MP] GrowableSync: host discarded " + foreign +
- " zoned-building command(s) from another player; only a host may author them.");
+ SyncLog.Warn(LogTopic.Buildings, "GrowableSync: host discarded " + foreign +
+ " zoned-building command(s) from another player; only a host may author them.");
}
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/PropertyRentSyncSystem/Capture.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/PropertyRentSyncSystem/Capture.cs
index 3cd8220..30ad178 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/PropertyRentSyncSystem/Capture.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/PropertyRentSyncSystem/Capture.cs
@@ -1,7 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using Game;
@@ -167,8 +169,8 @@ private bool SeedClientBaseline(long installGeneration)
// the old generation so the next UI pump retries.
_lastSeededWorldInstallGeneration = installGeneration;
_clientBaselineWarned = false;
- Mod.Verbose("[MP] PropertyRent: seeded " + seeded +
- " loaded property rent(s) before rolling host correction.");
+ SyncLog.Detail(LogTopic.Residential, "PropertyRent: seeded " + seeded +
+ " loaded property rent(s) before rolling host correction.");
return true;
}
catch (Exception ex)
@@ -178,8 +180,9 @@ private bool SeedClientBaseline(long installGeneration)
if (!_clientBaselineWarned)
{
_clientBaselineWarned = true;
- Mod.log.Warn("[MP] PropertyRent: loaded-world baseline seed failed; " +
- "will retry (logged once): " + ex.Message);
+ SyncLog.Warn(LogTopic.Residential,
+ "PropertyRent: loaded-world baseline seed failed; " +
+ "will retry (logged once): " + ex.Message);
}
return false;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/PropertyRentSyncSystem/PropertyRentSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/PropertyRentSyncSystem/PropertyRentSyncSystem.cs
index 9fd1095..30e30de 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/PropertyRentSyncSystem/PropertyRentSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/PropertyRentSyncSystem/PropertyRentSyncSystem.cs
@@ -1,7 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using Game;
@@ -179,8 +181,6 @@ protected override void OnCreate()
None = SyncQuery.ReadOnly(),
});
SyncInbox.RegisterDrain(DrainForWorldChange);
- Mod.log.Info(nameof(PropertyRentSyncSystem) +
- " ready (market and non-household rent authority). ");
}
protected override void OnDestroy()
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/PropertyRentSyncSystem/Realize.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/PropertyRentSyncSystem/Realize.cs
index 9eea8fa..7837faa 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/PropertyRentSyncSystem/Realize.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/PropertyRentSyncSystem/Realize.cs
@@ -1,7 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using Game;
@@ -333,27 +335,23 @@ private void ReportStats(MultiplayerSession session, long now)
foreach (Peer peer in session.Peers)
if (peer.Handshaked) clients++;
long estimatedFanoutBytes = _sentBytes * clients;
- Mod.Verbose("[MP] PropertyRent/30s host: pages=" + _sentPages +
- ", entries=" + _sentEntries + ", bytes=" + _sentBytes +
- ", clients=" + clients + ", estimatedFanoutBytes=" +
- estimatedFanoutBytes + ", transportPendingBytes=" +
- session.PendingSendBytes +
- ", changedPriority=" + _priorityChanges +
- ", priorityQueued=" + _priority.Count +
- ", priorityDropped=" + _priorityDrops +
- ", captureSkipped=" + _localCaptureSkips +
- ", identityCollision=" + _localIdentityCollisions + ".");
+ SyncLog.Detail(LogTopic.Residential, "PropertyRent/30s host: pages=" + _sentPages +
+ ", entries=" + _sentEntries + ", bytes=" + _sentBytes + ", clients=" + clients +
+ ", estimatedFanoutBytes=" + estimatedFanoutBytes + ", transportPendingBytes=" +
+ session.PendingSendBytes + ", changedPriority=" + _priorityChanges +
+ ", priorityQueued=" + _priority.Count + ", priorityDropped=" + _priorityDrops +
+ ", captureSkipped=" + _localCaptureSkips + ", identityCollision=" +
+ _localIdentityCollisions + ".");
}
else
{
- Mod.Verbose("[MP] PropertyRent/30s client: pages=" + _receivedPages +
- ", queueDropped=" + _droppedPages + ", cached=" + _cache.Count +
- ", pending=" + _pending.Count + ", resolved=" + _resolved +
- ", unresolved=" + _unresolved + ", ambiguous=" + _ambiguous +
- ", expired=" + _expired + ", cacheDropped=" + _cacheDrops +
- ", pruned=" + _pruned + ", appliedProperties=" +
- _appliedProperties + ", renterWrites=" + _appliedRenters +
- ", marketWrites=" + _appliedMarkets + ".");
+ SyncLog.Detail(LogTopic.Residential, "PropertyRent/30s client: pages=" +
+ _receivedPages + ", queueDropped=" + _droppedPages + ", cached=" + _cache.Count +
+ ", pending=" + _pending.Count + ", resolved=" + _resolved + ", unresolved=" +
+ _unresolved + ", ambiguous=" + _ambiguous + ", expired=" + _expired +
+ ", cacheDropped=" + _cacheDrops + ", pruned=" + _pruned + ", appliedProperties=" +
+ _appliedProperties + ", renterWrites=" + _appliedRenters + ", marketWrites=" +
+ _appliedMarkets + ".");
}
_sentPages = _sentEntries = _priorityChanges = _priorityDrops = 0;
_localCaptureSkips = _localIdentityCollisions = 0;
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/CaptureHash.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/CaptureHash.cs
index 8d9982a..bec80c2 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/CaptureHash.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/CaptureHash.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using Game.Buildings;
@@ -164,11 +165,11 @@ private static void LogRosterTrace(string stage, OccupancyProperty property)
.Append("/money=").Append(household.Money);
if (household.Departing) roster.Append("/departing");
}
- Mod.log.Info("[MP][OCC-DEV] " + stage + " house='" + property.PrefabName +
- "' anchor=(" + property.AnchorX.ToString("F2") + ", " +
- property.AnchorY.ToString("F2") + ", " +
- property.AnchorZ.ToString("F2") + ") rev=" + property.Revision +
- " families=" + property.Households.Length + " roster=[" + roster + "].");
+ SyncLog.Detail(LogTopic.Residential, stage + " house='" + property.PrefabName +
+ "' anchor=(" + property.AnchorX.ToString("F2") + ", " +
+ property.AnchorY.ToString("F2") + ", " + property.AnchorZ.ToString("F2") + ") rev=" +
+ property.Revision + " families=" + property.Households.Length + " roster=[" + roster +
+ "].");
}
private static int Clamp(int value, int min, int max) =>
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/Cycle.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/Cycle.cs
index f57573a..c1c580a 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/Cycle.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/Cycle.cs
@@ -1,7 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using Game;
@@ -307,38 +309,33 @@ private void ReportStats(MultiplayerSession session, long now)
{
int clients = 0;
foreach (Peer peer in session.Peers) if (peer.Handshaked) clients++;
- Diagnostics.SyncLog.Write(Diagnostics.LogTopic.Residential, "Occupancy/30s host: pages=" + _sentPages + ", properties=" +
- _sentProperties + ", bytes=" + _sentBytes + ", clients=" + clients +
- ", estimatedFanoutBytes=" + _sentBytes * clients +
- ", transportPendingBytes=" + session.PendingSendBytes +
- ", changedPriority=" + _priorityChanges + ", priorityQueued=" +
- _priority.Count + ", priorityDropped=" + _priorityDrops +
- ", departuresTracked=" + _hostDepartures.Count +
- ", citizenDeparturesTracked=" + _hostCitizenDepartures.Count +
- ", captureSkipped=" + _captureSkips + ", observed=" +
- _observedProperties + ".");
+ Diagnostics.SyncLog.Detail(LogTopic.Residential, "Occupancy/30s host: pages=" +
+ _sentPages + ", properties=" + _sentProperties + ", bytes=" + _sentBytes +
+ ", clients=" + clients + ", estimatedFanoutBytes=" + _sentBytes * clients +
+ ", transportPendingBytes=" + session.PendingSendBytes + ", changedPriority=" +
+ _priorityChanges + ", priorityQueued=" + _priority.Count + ", priorityDropped=" +
+ _priorityDrops + ", departuresTracked=" + _hostDepartures.Count +
+ ", citizenDeparturesTracked=" + _hostCitizenDepartures.Count +
+ ", captureSkipped=" + _captureSkips + ", observed=" + _observedProperties +
+ ".");
}
else
{
- Diagnostics.SyncLog.Write(Diagnostics.LogTopic.Residential, "Occupancy/30s client: pages=" + _receivedPages +
- ", queueDropped=" + _droppedPages + ", cached=" + _cache.Count +
- ", pending=" + _pending.Count + ", resolved=" + _resolved +
- ", unresolved=" + _unresolved + ", ambiguous=" + _ambiguous +
- ", expired=" + _expired + ", stale=" + _stalePages +
- ", pruned=" + _pruned + ", cacheDropped=" + _cacheDrops +
- ", appliedProperties=" + _appliedProperties + ", households +" +
- _createdHouseholds + "/-" + _retiredHouseholds + ", citizens +" +
- _createdCitizens + "/-" + _removedCitizens + "/~" +
- _rewrittenCitizens + ", pets +" + _createdPets + ", renamed=" +
- _renamedEntities + ", vehicles +" + _createdVehicles +
- ", rentActions=" + _rentActions +
- ", refusedMoveIns=" + _refusedMoveIns + ", buildRatesAligned=" +
- _alignedBuildRates + ", forcedCompletions=" + _forcedCompletions +
- ", deferredForConstruction=" + _deferredForConstruction +
- ", economyCorrections=" + _economyCorrections +
- "/deferred " + _economyDeferred +
- ", pendingMoveIns=" + _pendingMoveIns.Count + ", dirty=" +
- _dirty.Count + ".");
+ Diagnostics.SyncLog.Detail(LogTopic.Residential, "Occupancy/30s client: pages=" +
+ _receivedPages + ", queueDropped=" + _droppedPages + ", cached=" + _cache.Count +
+ ", pending=" + _pending.Count + ", resolved=" + _resolved + ", unresolved=" +
+ _unresolved + ", ambiguous=" + _ambiguous + ", expired=" + _expired + ", stale=" +
+ _stalePages + ", pruned=" + _pruned + ", cacheDropped=" + _cacheDrops +
+ ", appliedProperties=" + _appliedProperties + ", households +" +
+ _createdHouseholds + "/-" + _retiredHouseholds + ", citizens +" +
+ _createdCitizens + "/-" + _removedCitizens + "/~" + _rewrittenCitizens +
+ ", pets +" + _createdPets + ", renamed=" + _renamedEntities + ", vehicles +" +
+ _createdVehicles + ", rentActions=" + _rentActions + ", refusedMoveIns=" +
+ _refusedMoveIns + ", buildRatesAligned=" + _alignedBuildRates +
+ ", forcedCompletions=" + _forcedCompletions + ", deferredForConstruction=" +
+ _deferredForConstruction + ", economyCorrections=" + _economyCorrections +
+ "/deferred " + _economyDeferred + ", pendingMoveIns=" + _pendingMoveIns.Count +
+ ", dirty=" + _dirty.Count + ".");
}
_sentPages = _sentProperties = _priorityChanges = _priorityDrops = _captureSkips = 0;
_observedProperties = 0;
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeCreate.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeCreate.cs
index ee5dc03..161291d 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeCreate.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeCreate.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using Game.Agents;
using Game.Buildings;
@@ -77,8 +79,9 @@ private Entity CreateHousehold(Entity property, OccupancyHousehold wanted)
if (!_arrivalSourceWarned)
{
_arrivalSourceWarned = true;
- Mod.Verbose("[MP] Occupancy: no live road outside connection was " +
- "available; new families will start at home.");
+ SyncLog.Detail(LogTopic.Residential,
+ "Occupancy: no live road outside connection was " +
+ "available; new families will start at home.");
}
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeMoveIn.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeMoveIn.cs
index 6aae10c..9608fe5 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeMoveIn.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeMoveIn.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
@@ -169,14 +170,12 @@ private void TracePlacedHousehold(CachedProperty property, OccupancyHousehold ho
if (_tracePlacedHouseholds.TryGetValue(household.HouseholdId, out previous) &&
previous.Equals(property.Identity)) return;
_tracePlacedHouseholds[household.HouseholdId] = property.Identity;
- Mod.log.Info("[MP][OCC-DEV] PLACED house='" + property.Identity.PrefabName +
- "' anchor=(" + property.Identity.AnchorX.ToString("F2") + ", " +
- property.Identity.AnchorY.ToString("F2") + ", " +
- property.Identity.AnchorZ.ToString("F2") + ") rev=" +
- property.Revision + " family=0x" +
- household.HouseholdId.ToString("X16") + " people=" + localPeople +
- "/" + wantedPeople + " vehicles=" + localVehicles + "/" +
- wantedVehicles + ".");
+ SyncLog.Detail(LogTopic.Residential, "PLACED house='" + property.Identity.PrefabName +
+ "' anchor=(" + property.Identity.AnchorX.ToString("F2") + ", " +
+ property.Identity.AnchorY.ToString("F2") + ", " +
+ property.Identity.AnchorZ.ToString("F2") + ") rev=" + property.Revision +
+ " family=0x" + household.HouseholdId.ToString("X16") + " people=" + localPeople +
+ "/" + wantedPeople + " vehicles=" + localVehicles + "/" + wantedVehicles + ".");
}
private int CountRealizedCitizens(Entity household, OccupancyHousehold wanted)
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeProperty.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeProperty.cs
index 13d98e7..93b4264 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeProperty.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeProperty.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using Game.Agents;
using Game.Buildings;
@@ -115,8 +117,9 @@ private void ApplyOne(Entity property)
if (!_applyWarned)
{
_applyWarned = true;
- Mod.log.Warn("[MP] Occupancy: reconcile failed for one property; dropped it " +
- "until the next page (logged once): " + ex.Message);
+ SyncLog.Warn(LogTopic.Residential,
+ "Occupancy: reconcile failed for one property; dropped it " +
+ "until the next page (logged once): " + ex.Message);
}
}
if (applied && cached.RemoveAfterApply)
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeVehicles.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeVehicles.cs
index f3778f5..6885e54 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeVehicles.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RealizeVehicles.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using Game.Agents;
@@ -147,10 +148,10 @@ private void LinkOwnedVehicle(Entity household, Entity vehicle)
private void TraceVehicleSpawn(ulong householdId, string prefabName, Entity vehicle,
Entity property, Entity source, bool initial)
{
- Mod.log.Info("[MP][OCC-DEV] CAR-SPAWN family=0x" +
- householdId.ToString("X16") + " vehicle='" + prefabName +
- "' local=" + vehicle + " house='" + SafePrefabName(property) +
- "' origin='" + SafePrefabName(source) + "' initial=" + initial + ".");
+ SyncLog.Detail(LogTopic.Residential, "CAR-SPAWN family=0x" + householdId.ToString("X16") +
+ " vehicle='" + prefabName + "' local=" + vehicle + " house='" +
+ SafePrefabName(property) + "' origin='" + SafePrefabName(source) + "' initial=" +
+ initial + ".");
}
private void TraceVehicleSpawnFailure(ulong householdId, string prefabName,
@@ -158,9 +159,9 @@ private void TraceVehicleSpawnFailure(ulong householdId, string prefabName,
{
string warningKey = householdId.ToString("X16") + "|" + prefabName;
if (!_vehicleSpawnWarnings.Add(warningKey)) return;
- Mod.Verbose("[MP] Occupancy: could not spawn owned vehicle '" + prefabName +
- "' for family 0x" + householdId.ToString("X16") + " at '" +
- SafePrefabName(property) + "' (from '" + SafePrefabName(source) + "').");
+ SyncLog.Warn(LogTopic.Residential, "Occupancy: could not spawn owned vehicle '" +
+ prefabName + "' for family 0x" + householdId.ToString("X16") + " at '" +
+ SafePrefabName(property) + "' (from '" + SafePrefabName(source) + "').");
}
///
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RentAuthority.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RentAuthority.cs
index 5f91dfc..319ab49 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RentAuthority.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/RentAuthority.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using Game.Buildings;
using Game.Citizens;
@@ -69,8 +71,9 @@ internal void SeedLoadedWorldHouseholdRents()
_loadedWorldRentSeedGeneration = installGeneration;
_loadedWorldRentSeeded = true;
_loadedWorldRentSeedWarned = false;
- Mod.Verbose("[MP] Occupancy: seeded " + _loadedWorldHouseholdRents.Count +
- " loaded household rent contract(s) before local RentAdjust.");
+ SyncLog.Detail(LogTopic.Residential, "Occupancy: seeded " +
+ _loadedWorldHouseholdRents.Count +
+ " loaded household rent contract(s) before local RentAdjust.");
}
catch (Exception ex)
{
@@ -79,8 +82,9 @@ internal void SeedLoadedWorldHouseholdRents()
if (!_loadedWorldRentSeedWarned)
{
_loadedWorldRentSeedWarned = true;
- Mod.log.Warn("[MP] Occupancy: loaded-world household rent seed failed; " +
- "will retry (logged once): " + ex.Message);
+ SyncLog.Warn(LogTopic.Residential,
+ "Occupancy: loaded-world household rent seed failed; " +
+ "will retry (logged once): " + ex.Message);
}
}
finally
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/ResidentialOccupancySyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/ResidentialOccupancySyncSystem.cs
index 1b29d49..e3784de 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/ResidentialOccupancySyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/Simulation/ResidentialOccupancySyncSystem/ResidentialOccupancySyncSystem.cs
@@ -1,8 +1,10 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using Game;
@@ -400,8 +402,6 @@ protected override void OnCreate()
ComponentType.ReadOnly(),
ComponentType.ReadOnly());
SyncInbox.RegisterDrain(DrainForWorldChange);
- Mod.log.Info(nameof(ResidentialOccupancySyncSystem) +
- " ready (host-authoritative residential occupancy).");
}
protected override void OnDestroy()
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/World/DeleteSyncSystem/Capture.cs b/CS2MultiplayerMod/Game/Sync/Systems/World/DeleteSyncSystem/Capture.cs
index a31ce39..22cd2ba 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/World/DeleteSyncSystem/Capture.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/World/DeleteSyncSystem/Capture.cs
@@ -7,7 +7,9 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -56,8 +58,9 @@ private void SendObjectDeletes(MultiplayerSession session, long now, EntityQuery
if (!ownedUpgrades && IsSimulationOwnedLifecycle(prefab) &&
!_toolDeleteOriginals.Contains(entity))
{
- Mod.Verbose("[MP] DeleteSync: not replicating simulation-owned removal of '" +
- name + "'.");
+ SyncLog.Detail(LogTopic.Buildings,
+ "DeleteSync: not replicating simulation-owned removal of '" + name +
+ "'.");
continue;
}
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/World/DeleteSyncSystem/DeleteSyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/World/DeleteSyncSystem/DeleteSyncSystem.cs
index 92e6560..1621612 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/World/DeleteSyncSystem/DeleteSyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/World/DeleteSyncSystem/DeleteSyncSystem.cs
@@ -8,9 +8,10 @@
using Game.Tools;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
-
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Systems.Net;
@@ -88,7 +89,6 @@ protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(DeleteSyncSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
_prefabIndex = new PrefabIndex(_prefabSystem, GetEntityQuery(ComponentType.ReadOnly()));
// Edge deletes are committed through NetSync's ApplyTool pipeline (see RealizeEdgeDeletes).
@@ -273,7 +273,7 @@ public void RealizePending()
.Add((NetDeleteCommand.Decode(message.Body), freshDeadline));
}
}
- catch (System.Exception ex) { Mod.log.Warn("[MP] DeleteSync: dropping malformed command: " + ex.Message); }
+ catch (System.Exception ex) { SyncLog.Warn(LogTopic.Buildings, "DeleteSync: dropping malformed command: " + ex.Message); }
}
// Re-queue edge deletes that arrived while the net pipeline was mid-commit (the drain loop
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/World/DeleteSyncSystem/Realize.cs b/CS2MultiplayerMod/Game/Sync/Systems/World/DeleteSyncSystem/Realize.cs
index 022eb0b..09dd0d9 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/World/DeleteSyncSystem/Realize.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/World/DeleteSyncSystem/Realize.cs
@@ -9,6 +9,8 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Commands;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
@@ -111,8 +113,9 @@ private void RealizeObjectDeletes(List<(ObjectDeleteCommand cmd, long deadline)>
CS2MultiplayerMod.Game.Diagnostics.ResyncEvidence.Contradiction)
.About("object delete graph")
.Tried("nothing - the ownership graph under this object cannot be torn down safely"));
- Mod.log.Warn("[MP] DeleteSync: rejected stale building graph: " +
- invalidReason + ".");
+ SyncLog.Warn(LogTopic.Buildings,
+ "DeleteSync: rejected stale building graph: " + invalidReason +
+ ".");
}
continue;
}
@@ -152,11 +155,12 @@ private void RealizeObjectDeletes(List<(ObjectDeleteCommand cmd, long deadline)>
expired++;
// Name the target: a delete that never finds a victim means the two cities
// disagree about what stands here, and the prefab says which kind.
- Mod.log.Warn("[MP] DeleteSync: no local match for '" + targets[t].name +
- "' at " + targets[t].pos + " within " + ObjectMatchRadius +
- "m (" + candidates.Length + " object(s) in range, prefab " +
- (targets[t].prefab == Entity.Null ? "unknown here" : "resolved") +
- "); dropping this delete.");
+ SyncLog.Warn(LogTopic.Buildings, "DeleteSync: no local match for '" +
+ targets[t].name + "' at " + targets[t].pos + " within " +
+ ObjectMatchRadius + "m (" + candidates.Length +
+ " object(s) in range, prefab " +
+ (targets[t].prefab == Entity.Null ? "unknown here" : "resolved") +
+ "); dropping this delete.");
}
}
}
@@ -166,15 +170,16 @@ private void RealizeObjectDeletes(List<(ObjectDeleteCommand cmd, long deadline)>
}
if (deleted > 0 || waiting > 0 || expired > 0)
- Mod.Verbose("[MP] DeleteSync: removed " + deleted + " object root(s) and " +
- deletedOwned + " owned upgrade/subobject(s); " + waiting +
- " awaiting a local match, " + expired + " gave up (already gone, or geometry diverged).");
+ SyncLog.Detail(LogTopic.Buildings, "DeleteSync: removed " + deleted +
+ " object root(s) and " + deletedOwned + " owned upgrade/subobject(s); " +
+ waiting + " awaiting a local match, " + expired +
+ " gave up (already gone, or geometry diverged).");
// Same reasoning as the road case: a demolition that found nothing to demolish leaves
// this city holding a building the other player has already removed.
if (expired > 0)
- Diagnostics.SyncLog.ProdWarn(
- "Build sync: " + expired + " demolished object(s) had no match here and were " +
- "dropped after " + (DeleteRetryWindowMs / 1000) + " s. Those objects still " +
+ Diagnostics.SyncLog.Warn(LogTopic.Buildings, "Build sync: " + expired +
+ " demolished object(s) had no match here and were " + "dropped after " +
+ (DeleteRetryWindowMs / 1000) + " s. Those objects still " +
"stand in this city and no longer stand in the other player's.");
}
@@ -386,9 +391,9 @@ private void RealizeEdgeDeletes(List<(NetDeleteCommand cmd, long deadline)> comm
}
if (deleted > 0 || waiting > 0 || expired > 0)
{
- Mod.Verbose("[MP] DeleteSync: bulldozing " + deleted + " road segment(s); " + waiting +
- " awaiting a local match, " + expired +
- " gave up (already gone, or geometry diverged).");
+ SyncLog.Detail(LogTopic.Buildings, "DeleteSync: bulldozing " + deleted +
+ " road segment(s); " + waiting + " awaiting a local match, " + expired +
+ " gave up (already gone, or geometry diverged).");
}
// A bulldoze that never found its road is a road the other player no longer has and
// this one still does - a silent divergence, and one that surfaces later as somebody
@@ -396,9 +401,9 @@ private void RealizeEdgeDeletes(List<(NetDeleteCommand cmd, long deadline)> comm
// which is exactly the switch nobody has set during the session that needs explaining.
// Production level, always.
if (expired > 0)
- Diagnostics.SyncLog.ProdWarn(
- "Road sync: " + expired + " bulldozed road segment(s) had no match here and " +
- "were dropped after " + (DeleteRetryWindowMs / 1000) + " s. Those roads still " +
+ Diagnostics.SyncLog.Warn(LogTopic.Buildings, "Road sync: " + expired +
+ " bulldozed road segment(s) had no match here and " + "were dropped after " +
+ (DeleteRetryWindowMs / 1000) + " s. Those roads still " +
"stand in this city and no longer stand in the other player's.");
}
@@ -473,7 +478,8 @@ private Entity CreateEdgeDeleteDefEntity(Entity edge)
}
catch (System.Exception ex)
{
- Mod.log.Warn("[MP] DeleteSync: failed to build edge delete-definition: " + ex.Message);
+ SyncLog.Warn(LogTopic.Buildings,
+ "DeleteSync: failed to build edge delete-definition: " + ex.Message);
return Entity.Null;
}
finally
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/World/WorldRepairSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/World/WorldRepairSystem.cs
index 65bf8cb..eb94bec 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/World/WorldRepairSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/World/WorldRepairSystem.cs
@@ -7,6 +7,8 @@
using Game.Tools;
using Unity.Collections;
using Unity.Entities;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
namespace CS2MultiplayerMod.Game.Sync.Systems
@@ -36,7 +38,6 @@ public partial class WorldRepairSystem : GameSystemBase
protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(WorldRepairSystem) + " ready.");
_prefabSystem = World.GetOrCreateSystemManaged();
// Top-level mover instances. Simulation-owned vehicles normally carry Owner;
@@ -182,8 +183,8 @@ private void FinishSweep()
_sweepIndex = 0;
_sweeping = false;
- Diagnostics.FlightRecorder.Note("world repair scanned=" + scanned +
- " removed=" + _sweepRemoved);
+ SyncLog.Trace(LogTopic.Resync, "world repair scanned=" + scanned + " removed=" +
+ _sweepRemoved);
if (_sweepRemoved == 0) return;
var detail = new StringBuilder();
@@ -198,9 +199,8 @@ private void FinishSweep()
break;
}
}
- Mod.log.Info("[MP] World repair: removed " + _sweepRemoved +
- " stranded mover instance(s) left by an earlier session [" +
- detail + "].");
+ SyncLog.Event(LogTopic.Resync, "World repair: removed " + _sweepRemoved +
+ " stranded mover instance(s) left by an earlier session [" + detail + "].");
}
private void CancelSweep()
diff --git a/CS2MultiplayerMod/Game/Sync/Systems/World/WorldResyncSystem.cs b/CS2MultiplayerMod/Game/Sync/Systems/World/WorldResyncSystem.cs
index eaa9082..ac63a33 100644
--- a/CS2MultiplayerMod/Game/Sync/Systems/World/WorldResyncSystem.cs
+++ b/CS2MultiplayerMod/Game/Sync/Systems/World/WorldResyncSystem.cs
@@ -2,9 +2,11 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Protocol.Messages;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Game.Sync.Infrastructure;
using CS2MultiplayerMod.Game.Sync.Systems.Net;
using Game;
@@ -73,7 +75,6 @@ private struct RecoveryRequest
protected override void OnCreate()
{
base.OnCreate();
- Mod.log.Info(nameof(WorldResyncSystem) + " ready (atomic epoch barrier).");
_netSync = World.GetOrCreateSystemManaged();
_observer = SyncObserverBinding.Bind(
@@ -202,9 +203,9 @@ private void DrainObserverEvents(MultiplayerSession session)
else if (evt.Stage == WorldSyncStage.Failed &&
_state == RecoveryState.WaitingForLoaded)
{
- Mod.log.Error("[MP] " + DescribePeer(session, evt.Connection) +
- " could not install world-sync epoch " + _epoch +
- "; disconnecting it rather than resuming divergent worlds.");
+ SyncLog.Error(LogTopic.Resync, DescribePeer(session, evt.Connection) +
+ " could not install world-sync epoch " + _epoch +
+ "; disconnecting it rather than resuming divergent worlds.");
session.DisconnectPeer(evt.Connection);
RemoveParticipant(evt.Connection);
}
@@ -228,14 +229,14 @@ private void StartEpoch(MultiplayerService service, MultiplayerSession session,
_epoch = ++_epochCounter;
if (!service.TryBeginHostWorldSync(_epoch, out _resumeSpeed))
{
- Mod.log.Error("[MP] Could not enter the local world-sync barrier.");
+ SyncLog.Error(LogTopic.Resync, "Could not enter the local world-sync barrier.");
ResetEpoch(now);
return;
}
if (!session.BeginWorldSync(_epoch, _resumeSpeed, _participants))
{
service.AbortHostWorldSync(_epoch, _resumeSpeed);
- Mod.log.Error("[MP] Could not open world-sync epoch " + _epoch + ".");
+ SyncLog.Error(LogTopic.Resync, "Could not open world-sync epoch " + _epoch + ".");
ResetEpoch(now);
return;
}
@@ -247,10 +248,8 @@ private void StartEpoch(MultiplayerService service, MultiplayerSession session,
_cleanFrames = 0;
_deadlineMs = now + QuiesceTimeoutMs;
_state = RecoveryState.WaitingForQuiescence;
- Mod.log.Info("[MP] World sync epoch " + _epoch + " waiting for " +
- _participants.Count + " client quiescence acknowledgement(s).");
- CS2MultiplayerMod.Game.Diagnostics.FlightRecorder.Note(
- "resync epoch=" + _epoch + " barrier opened participants=" + _participants.Count);
+ SyncLog.Event(LogTopic.Resync, "World sync epoch " + _epoch + " waiting for " +
+ _participants.Count + " client quiescence acknowledgement(s).");
}
private void PumpQuiescence(MultiplayerService service, MultiplayerSession session, long now)
@@ -292,10 +291,8 @@ private void StartSave(MultiplayerService service, long now)
_saveStartMs = now;
_state = RecoveryState.Saving;
service.SetHostWorldSyncUiStage(HostWorldSyncUiStage.Saving);
- Mod.log.Info("[MP] World sync epoch " + _epoch +
- ": barrier closed; saving the authoritative world.");
- CS2MultiplayerMod.Game.Diagnostics.FlightRecorder.Note(
- "resync epoch=" + _epoch + " save started");
+ SyncLog.Event(LogTopic.Resync, "World sync epoch " + _epoch +
+ ": barrier closed; saving the authoritative world.");
}
catch (Exception ex)
{
@@ -331,12 +328,10 @@ private void PumpSave(MultiplayerService service, MultiplayerSession session, lo
_deadlineMs = now + LoadTimeoutMs;
_state = RecoveryState.WaitingForLoaded;
service.SetHostWorldSyncUiStage(HostWorldSyncUiStage.WaitingForLoaded);
- Mod.log.Info("[MP] World sync epoch " + _epoch + ": queued one " +
- (snapshot.Length / 1024) + " KB snapshot for " + _participants.Count +
- " participant(s); waiting for load acknowledgement(s). Save took " +
- (now - _saveStartMs) + " ms.");
- CS2MultiplayerMod.Game.Diagnostics.FlightRecorder.Note(
- "resync epoch=" + _epoch + " snapshot queued KB=" + (snapshot.Length >> 10));
+ SyncLog.Event(LogTopic.Resync, "World sync epoch " + _epoch + ": queued one " +
+ (snapshot.Length / 1024) + " KB snapshot for " + _participants.Count +
+ " participant(s); waiting for load acknowledgement(s). Save took " +
+ (now - _saveStartMs) + " ms.");
}
private void PumpLoaded(MultiplayerService service, MultiplayerSession session, long now)
@@ -367,8 +362,8 @@ private void CompleteEpoch(MultiplayerService service, MultiplayerSession sessio
// clients each get their own completion notice as they install the snapshot.
session.NotifyChat(null, "World sync complete - " + targets.Count +
(targets.Count == 1 ? " player is" : " players are") + " in sync.");
- Mod.log.Info("[MP] World sync epoch " + _epoch + " completed for " +
- targets.Count + " participant(s).");
+ SyncLog.Event(LogTopic.Resync, "World sync epoch " + _epoch + " completed for " +
+ targets.Count + " participant(s).");
ResetEpoch(now);
// A peer that joined after this snapshot was queued needs another snapshot. Open the
@@ -391,9 +386,8 @@ private void AbortEpoch(MultiplayerService service, string reason)
session.AbortWorldSync(_epoch, _resumeSpeed, targets);
service.AbortHostWorldSync(_epoch, _resumeSpeed);
}
- Mod.log.Error("[MP] World sync epoch " + _epoch + " aborted: " + reason + ".");
- CS2MultiplayerMod.Game.Diagnostics.FlightRecorder.Note(
- "resync epoch=" + _epoch + " ABORTED reason=" + reason);
+ SyncLog.Error(LogTopic.Resync, "World sync epoch " + _epoch + " aborted: " + reason +
+ ".");
ResetEpoch(service.NowMs);
}
@@ -418,8 +412,9 @@ private void DisconnectMissing(MultiplayerSession session, HashSet acknowle
{
ConnectionId connection = _participants[i];
if (acknowledgements.Contains(connection.Value)) continue;
- Mod.log.Error("[MP] " + DescribePeer(session, connection) + " timed out waiting for " +
- expected + " in epoch " + _epoch + "; disconnecting it.");
+ SyncLog.Error(LogTopic.Resync, DescribePeer(session, connection) +
+ " timed out waiting for " + expected + " in epoch " + _epoch +
+ "; disconnecting it.");
session.DisconnectPeer(connection);
RemoveParticipant(connection);
}
@@ -499,7 +494,8 @@ public override void OnPeerJoined(Peer peer)
Connection = peer.Connection,
IsJoin = true,
});
- Mod.log.Info("[MP] Queued atomic initial world sync for " + peer + ".");
+ SyncLog.Event(LogTopic.Resync, "Queued atomic initial world sync for " + peer +
+ ".");
}
public override void OnPeerLeft(Peer peer, string reason) =>
@@ -512,7 +508,8 @@ public override void OnResyncRequested(int playerId, ConnectionId connection)
Connection = connection,
IsJoin = false,
});
- Mod.log.Info("[MP] Queued atomic world-sync request from player #" + playerId + ".");
+ SyncLog.Event(LogTopic.Resync, "Queued atomic world-sync request from player #" +
+ playerId + ".");
}
public override void OnWorldSyncControl(WorldSyncStage stage, long epoch,
diff --git a/CS2MultiplayerMod/Localization/locales/de.properties b/CS2MultiplayerMod/Localization/locales/de.properties
index 58bc68c..942a8ce 100644
--- a/CS2MultiplayerMod/Localization/locales/de.properties
+++ b/CS2MultiplayerMod/Localization/locales/de.properties
@@ -6,6 +6,7 @@
@tab.General = Allgemein
@tab.Join = Spiel beitreten
@tab.Host = Spiel hosten
+@tab.Logging = Protokollierung
@group.General = Allgemein
@group.Status = Status
@@ -28,13 +29,41 @@
@label.IgnoreModCompatibilityChecks = Mod-Kompatibilitätsprüfungen ignorieren (eigenes Risiko)
@desc.IgnoreModCompatibilityChecks = Erlaubt andere aktive Mods und beim Hosten abweichende Versionen von CS2 Multiplayer Mod. Dies kann Desyncs, beschädigte Städte oder Abstürze verursachen. Protokoll-, Spielversions- und DLC-Prüfungen bleiben aktiv. Nur offline ändern.
-@label.VerboseLogging = Ausführliches Logging
-@desc.VerboseLogging = Protokolliert zusätzliche Details zur Fehlersuche (jede Sync-Aktion und die periodische Diagnose). Aus für ruhigere Logs; ein, wenn du ein detailliertes Log zum Teilen brauchst, während du Hilfe bekommst.
+# -- Logging tab --
+# One switch per feature. None of them has to be on for a bug report to be worth
+# reading: connects, disconnects, world transfers, resyncs, dropped commands and
+# every fault are logged whatever is set here. These add the detail underneath.
+@group.LogAll = Gesamte Protokollierung
+@group.LogConnection = Verbindung
+@group.LogWorld = Welt & Sync
+@group.LogEconomy = Stadtwirtschaft
+@group.LogClient = Dieser Client
+
+@label.VerboseLogging = Alles protokollieren
+@desc.VerboseLogging = Schaltet alle Schalter unten auf einmal ein. Verbindungen, Trennungen, Weltübertragungen, Resyncs, verworfene Aktionen und alle Fehler werden unabhängig von diesen Einstellungen protokolliert - die Schalter ergänzen nur die Details darunter. Aktiviere dies, wenn du um ein vollständiges Log gebeten wurdest.
+
+@label.LogSession = Sitzung protokollieren
+@desc.LogSession = Verbinden, Trennen, den Handshake sowie beitretende und verlassende Spieler.
+@label.LogTransport = Verbindung protokollieren
+@desc.LogTransport = Die Leitung unter der Sitzung: Sockets, das Steam-Relay, Portweiterleitung und Senderaten. Aktiviere dies, wenn gar keine Verbindung zustande kommt.
+@label.LogWorldTransfer = Weltübertragung protokollieren
+@desc.LogWorldTransfer = Senden, Empfangen, Speichern und Laden der Stadt, die ein beitretender Spieler herunterlädt. Aktiviere dies, wenn der Beitritt hängen bleibt.
+
+@label.LogResync = Resync protokollieren
+@desc.LogResync = Welche Unterschiede zwischen den beiden Städten gefunden wurden, wie der Mod entschieden hat und was die Reparatur getan hat. Aktiviere dies, wenn die Welt ständig neu geladen wird.
+@label.LogPipeline = Befehls-Pipeline protokollieren
+@desc.LogPipeline = Der Weg jeder Aktion: eingereiht, gesendet, empfangen, angewendet oder verworfen. Aktiviere dies, wenn manche Aktionen ankommen und andere stillschweigend nicht.
+@label.LogNets = Straßen & Netze protokollieren
+@desc.LogNets = Straßen, Gleise, Rohre und Leitungen: Bau, Ausbau, Ersetzen und ihre Verbindungen.
+@label.LogBuildings = Gebäude & Objekte protokollieren
+@desc.LogBuildings = Platzierte Dinge: Gebäude, Objekte und Bäume - Bauen, Verschieben, Ausbauen und Abreißen.
+@label.LogLand = Land & Gelände protokollieren
+@desc.LogLand = Zonen, Gebiete und Bezirke, Geländeänderungen und Kachelkäufe.
+@label.LogCity = Stadtzustand protokollieren
+@desc.LogCity = Stadtweiter Zustand: Namen, Verordnungen, Geld, Meilensteine und der Entwicklungsbaum.
+@label.LogRoutes = Verkehrslinien protokollieren
+@desc.LogRoutes = Verkehrslinien, ihre Haltestellen und Fahrzeuge sowie Ticketpreise.
-# Diagnostic logging: each topic is a separate switch so a log stays readable.
-@group.Diagnostics = Diagnose-Protokollierung
-@label.LogPerformance = Leistung protokollieren
-@desc.LogPerformance = Protokolliert alle 30 Sekunden die Bildzeiten und wie viel Hauptthread-Zeit der Mod selbst verbraucht hat, aufgeteilt nach Wohn-, Gewerbe-, Industrie- und Bürogebieten. Damit lässt sich der Aufwand des Mods vom Aufwand der Stadt unterscheiden.
@label.LogResidential = Wohngebiets-Sync protokollieren
@desc.LogResidential = Protokolliert Details zu Haushalten, Bewohnern und ihren Wohnorten.
@label.LogCommercial = Gewerbe-Sync protokollieren
@@ -44,6 +73,15 @@
@label.LogOffice = Büro-Sync protokollieren
@desc.LogOffice = Protokolliert Details zu Bürogebäuden: welches Unternehmen ein Gebäude mietet, seine Kennzahlen und sein Warenbestand.
+@label.LogPlayers = Andere Spieler protokollieren
+@desc.LogPlayers = Die anderen Spieler: ihre Cursor, Markierungen, Kartenpings und der Chat.
+@label.LogUi = Mod-Fenster protokollieren
+@desc.LogUi = Die eigenen Fenster des Mods: die Hauptmenü-Schaltfläche, der Beitrittsdialog und die Optionsseite.
+@label.LogStartup = Start protokollieren
+@desc.LogStartup = Laden des Mods, Registrieren seiner Systeme sowie die Mod- und DLC-Prüfungen.
+@label.LogPerformance = Leistung protokollieren
+@desc.LogPerformance = Protokolliert alle 30 Sekunden die Bildzeiten und wie viel Hauptthread-Zeit der Mod selbst verbraucht hat, aufgeteilt nach Wohn-, Gewerbe-, Industrie- und Bürogebieten. Damit lässt sich der Aufwand des Mods vom Aufwand der Stadt unterscheiden.
+
@label.StatusRole = Rolle
@desc.StatusRole = Ob diese Instanz offline ist, hostet oder als Client beigetreten ist.
diff --git a/CS2MultiplayerMod/Localization/locales/en.properties b/CS2MultiplayerMod/Localization/locales/en.properties
index 05fe7f2..5ed8d7b 100644
--- a/CS2MultiplayerMod/Localization/locales/en.properties
+++ b/CS2MultiplayerMod/Localization/locales/en.properties
@@ -20,6 +20,7 @@
@tab.General = General
@tab.Join = Join Game
@tab.Host = Host Game
+@tab.Logging = Logging
@group.General = General
@group.Status = Status
@@ -42,13 +43,41 @@
@label.IgnoreModCompatibilityChecks = Ignore Mod Compatibility Checks (Own Risk)
@desc.IgnoreModCompatibilityChecks = Allow other active mods and, when you host, different CS2 Multiplayer Mod builds. This can cause desyncs, broken cities or crashes. Protocol, game-version and DLC checks are still enforced. Change only while offline.
-@label.VerboseLogging = Verbose Logging
-@desc.VerboseLogging = Log extra detail for troubleshooting (every sync action and the periodic diagnostics). Leave off for quieter logs; turn it on when you want a detailed log to share while getting help.
+# -- Logging tab --
+# One switch per feature. None of them has to be on for a bug report to be worth
+# reading: connects, disconnects, world transfers, resyncs, dropped commands and
+# every fault are logged whatever is set here. These add the detail underneath.
+@group.LogAll = All Logging
+@group.LogConnection = Connection
+@group.LogWorld = World & Sync
+@group.LogEconomy = City Economy
+@group.LogClient = This Client
+
+@label.VerboseLogging = Log Everything
+@desc.VerboseLogging = Turn on every switch below at once. Connects, disconnects, world transfers, resyncs, dropped commands and all faults are logged whatever you set here - these switches only add the detail underneath them. Turn this on when you have been asked for a full log.
+
+@label.LogSession = Log Session
+@desc.LogSession = Connecting, disconnecting, the handshake, and players joining or leaving.
+@label.LogTransport = Log Connection
+@desc.LogTransport = The wire underneath a session: sockets, the Steam relay, port forwarding and send rates. Turn this on if you cannot connect at all.
+@label.LogWorldTransfer = Log World Transfer
+@desc.LogWorldTransfer = Sending, receiving, saving and loading the city a joining player downloads. Turn this on if joining hangs or never finishes.
+
+@label.LogResync = Log Resync
+@desc.LogResync = What was found to differ between the two cities, what the mod decided about it, and what the repair did. Turn this on if the world keeps reloading.
+@label.LogPipeline = Log Command Pipeline
+@desc.LogPipeline = How each action travels: queued, sent, received, applied or dropped. Turn this on if some actions arrive and others silently do not.
+@label.LogNets = Log Roads & Networks
+@desc.LogNets = Roads, tracks, pipes and wires: placement, upgrades, replacement and how they connect.
+@label.LogBuildings = Log Buildings & Objects
+@desc.LogBuildings = Placed things: buildings, props and trees - placing, moving, upgrading and bulldozing.
+@label.LogLand = Log Land & Terrain
+@desc.LogLand = Zoning, areas and districts, terrain edits and tile purchases.
+@label.LogCity = Log City State
+@desc.LogCity = City-wide state: names, policies, money, milestones and the development tree.
+@label.LogRoutes = Log Transit Routes
+@desc.LogRoutes = Transit lines, their stops and vehicles, and ticket prices.
-# Diagnostic logging: each topic is a separate switch so a log stays readable.
-@group.Diagnostics = Diagnostic Logging
-@label.LogPerformance = Log Performance
-@desc.LogPerformance = Every 30 seconds, log frame times and how much main-thread time the mod itself used, split by residential, commercial, industrial and office. This is what tells the mod's cost apart from the city's own.
@label.LogResidential = Log Residential Sync
@desc.LogResidential = Log detail about households, residents and who lives where.
@label.LogCommercial = Log Commercial Sync
@@ -58,6 +87,15 @@
@label.LogOffice = Log Office Sync
@desc.LogOffice = Log detail about offices: which business rents each building, its figures and its stock.
+@label.LogPlayers = Log Other Players
+@desc.LogPlayers = The other players: their cursors, markers, map pings and chat.
+@label.LogUi = Log Mod Screens
+@desc.LogUi = The mod's own screens: the main-menu button, the join dialog and the options page.
+@label.LogStartup = Log Startup
+@desc.LogStartup = Loading the mod, registering its systems, and the mod and DLC compatibility checks.
+@label.LogPerformance = Log Performance
+@desc.LogPerformance = Every 30 seconds, log frame times and how much main-thread time the mod itself used, split by residential, commercial, industrial and office. This is what tells the mod's cost apart from the city's own.
+
@label.StatusRole = Role
@desc.StatusRole = Whether this instance is offline, hosting, or joined as a client.
diff --git a/CS2MultiplayerMod/Localization/locales/es.properties b/CS2MultiplayerMod/Localization/locales/es.properties
index 6ce5b36..c88b90e 100644
--- a/CS2MultiplayerMod/Localization/locales/es.properties
+++ b/CS2MultiplayerMod/Localization/locales/es.properties
@@ -6,6 +6,7 @@
@tab.General = General
@tab.Join = Unirse a partida
@tab.Host = Alojar partida
+@tab.Logging = Registro
@group.General = General
@group.Status = Estado
@@ -28,13 +29,41 @@
@label.IgnoreModCompatibilityChecks = Ignorar comprobaciones de compatibilidad de mods (bajo tu responsabilidad)
@desc.IgnoreModCompatibilityChecks = Permite otros mods activos y, cuando alojas, versiones distintas de CS2 Multiplayer Mod. Esto puede provocar desincronizaciones, ciudades dañadas o cierres inesperados. Las comprobaciones de protocolo, versión del juego y DLC se siguen aplicando. Cámbialo solo estando sin conexión.
-@label.VerboseLogging = Registro detallado
-@desc.VerboseLogging = Registra más detalles para solucionar problemas (cada acción de sincronización y los diagnósticos periódicos). Déjalo desactivado para registros más limpios; actívalo cuando quieras compartir un registro detallado al pedir ayuda.
+# -- Logging tab --
+# One switch per feature. None of them has to be on for a bug report to be worth
+# reading: connects, disconnects, world transfers, resyncs, dropped commands and
+# every fault are logged whatever is set here. These add the detail underneath.
+@group.LogAll = Registro completo
+@group.LogConnection = Conexión
+@group.LogWorld = Mundo y sincronización
+@group.LogEconomy = Economía de la ciudad
+@group.LogClient = Este cliente
+
+@label.VerboseLogging = Registrarlo todo
+@desc.VerboseLogging = Activa de una vez todas las opciones de abajo. Las conexiones, desconexiones, transferencias de mundo, resincronizaciones, acciones descartadas y todos los fallos se registran en cualquier caso; estas opciones solo añaden el detalle. Actívala cuando te pidan un registro completo.
+
+@label.LogSession = Registrar sesión
+@desc.LogSession = Conexión, desconexión, el saludo inicial y los jugadores que entran o salen.
+@label.LogTransport = Registrar conexión
+@desc.LogTransport = El enlace bajo la sesión: sockets, el relé de Steam, la redirección de puertos y las tasas de envío. Actívala si no consigues conectar.
+@label.LogWorldTransfer = Registrar transferencia del mundo
+@desc.LogWorldTransfer = Envío, recepción, guardado y carga de la ciudad que descarga quien se une. Actívala si unirse se queda bloqueado.
+
+@label.LogResync = Registrar resincronización
+@desc.LogResync = Qué se encontró distinto entre las dos ciudades, qué decidió el mod y qué hizo la reparación. Actívala si el mundo se recarga sin parar.
+@label.LogPipeline = Registrar canal de comandos
+@desc.LogPipeline = El recorrido de cada acción: en cola, enviada, recibida, aplicada o descartada. Actívala si unas acciones llegan y otras no.
+@label.LogNets = Registrar carreteras y redes
+@desc.LogNets = Carreteras, vías, tuberías y cables: construcción, mejora, reemplazo y sus conexiones.
+@label.LogBuildings = Registrar edificios y objetos
+@desc.LogBuildings = Lo que se coloca: edificios, objetos y árboles: colocar, mover, mejorar y demoler.
+@label.LogLand = Registrar terreno y suelo
+@desc.LogLand = Zonificación, áreas y distritos, ediciones del terreno y compras de parcelas.
+@label.LogCity = Registrar estado de la ciudad
+@desc.LogCity = Estado global de la ciudad: nombres, políticas, dinero, hitos y el árbol de desarrollo.
+@label.LogRoutes = Registrar líneas de transporte
+@desc.LogRoutes = Líneas de transporte, sus paradas y vehículos, y el precio de los billetes.
-# Diagnostic logging: each topic is a separate switch so a log stays readable.
-@group.Diagnostics = Registro de diagnóstico
-@label.LogPerformance = Registrar rendimiento
-@desc.LogPerformance = Cada 30 segundos registra los tiempos de fotograma y cuánto tiempo del hilo principal ha usado el propio mod, desglosado en residencial, comercial, industrial y oficinas. Es lo que permite distinguir el coste del mod del de la ciudad.
@label.LogResidential = Registrar sincronización residencial
@desc.LogResidential = Registra detalles sobre hogares, residentes y dónde vive cada uno.
@label.LogCommercial = Registrar sincronización comercial
@@ -44,6 +73,15 @@
@label.LogOffice = Registrar sincronización de oficinas
@desc.LogOffice = Registra detalles sobre las oficinas: qué empresa alquila cada edificio, sus cifras y sus existencias.
+@label.LogPlayers = Registrar otros jugadores
+@desc.LogPlayers = Los demás jugadores: sus cursores, marcadores, señales en el mapa y el chat.
+@label.LogUi = Registrar pantallas del mod
+@desc.LogUi = Las pantallas propias del mod: el botón del menú principal, el diálogo para unirse y la página de opciones.
+@label.LogStartup = Registrar inicio
+@desc.LogStartup = Carga del mod, registro de sus sistemas y las comprobaciones de mods y DLC.
+@label.LogPerformance = Registrar rendimiento
+@desc.LogPerformance = Cada 30 segundos registra los tiempos de fotograma y cuánto tiempo del hilo principal ha usado el propio mod, desglosado en residencial, comercial, industrial y oficinas. Es lo que permite distinguir el coste del mod del de la ciudad.
+
@label.StatusRole = Rol
@desc.StatusRole = Si esta instancia está sin conexión, alojando o unida como cliente.
diff --git a/CS2MultiplayerMod/Localization/locales/fr.properties b/CS2MultiplayerMod/Localization/locales/fr.properties
index 7f8b744..529e3e1 100644
--- a/CS2MultiplayerMod/Localization/locales/fr.properties
+++ b/CS2MultiplayerMod/Localization/locales/fr.properties
@@ -6,6 +6,7 @@
@tab.General = Général
@tab.Join = Rejoindre une partie
@tab.Host = Héberger une partie
+@tab.Logging = Journalisation
@group.General = Général
@group.Status = État
@@ -28,13 +29,41 @@
@label.IgnoreModCompatibilityChecks = Ignorer les vérifications de compatibilité des mods (à vos risques)
@desc.IgnoreModCompatibilityChecks = Autorise d'autres mods actifs et, lorsque vous hébergez, des versions différentes de CS2 Multiplayer Mod. Cela peut provoquer des désynchronisations, des villes corrompues ou des plantages. Les vérifications du protocole, de la version du jeu et des DLC restent appliquées. À modifier uniquement hors ligne.
-@label.VerboseLogging = Journalisation détaillée
-@desc.VerboseLogging = Enregistre plus de détails pour le dépannage (chaque action de synchronisation et les diagnostics périodiques). Laissez désactivé pour des journaux plus légers ; activez-le quand vous voulez partager un journal détaillé pour obtenir de l'aide.
+# -- Logging tab --
+# One switch per feature. None of them has to be on for a bug report to be worth
+# reading: connects, disconnects, world transfers, resyncs, dropped commands and
+# every fault are logged whatever is set here. These add the detail underneath.
+@group.LogAll = Journalisation complète
+@group.LogConnection = Connexion
+@group.LogWorld = Monde et synchro
+@group.LogEconomy = Économie de la ville
+@group.LogClient = Ce client
+
+@label.VerboseLogging = Tout journaliser
+@desc.VerboseLogging = Active d'un coup toutes les options ci-dessous. Les connexions, déconnexions, transferts de monde, resynchronisations, actions perdues et toutes les erreurs sont journalisées quoi qu'il arrive - ces options n'ajoutent que le détail en dessous. Activez-la si l'on vous a demandé un journal complet.
+
+@label.LogSession = Journaliser la session
+@desc.LogSession = Connexion, déconnexion, la poignée de main, et les joueurs qui arrivent ou partent.
+@label.LogTransport = Journaliser la connexion
+@desc.LogTransport = Le lien sous la session : sockets, relais Steam, redirection de port et débits d'envoi. Activez-la si la connexion échoue totalement.
+@label.LogWorldTransfer = Journaliser le transfert du monde
+@desc.LogWorldTransfer = Envoi, réception, sauvegarde et chargement de la ville téléchargée par un joueur qui rejoint. Activez-la si la connexion reste bloquée.
+
+@label.LogResync = Journaliser la resynchronisation
+@desc.LogResync = Ce qui différait entre les deux villes, la décision prise par le mod et ce que la réparation a fait. Activez-la si le monde se recharge sans arrêt.
+@label.LogPipeline = Journaliser le pipeline de commandes
+@desc.LogPipeline = Le trajet de chaque action : mise en file, envoi, réception, application ou abandon. Activez-la si certaines actions arrivent et d'autres non.
+@label.LogNets = Journaliser routes et réseaux
+@desc.LogNets = Routes, voies, tuyaux et câbles : construction, amélioration, remplacement et raccordements.
+@label.LogBuildings = Journaliser bâtiments et objets
+@desc.LogBuildings = Les objets posés : bâtiments, accessoires et arbres - poser, déplacer, améliorer et démolir.
+@label.LogLand = Journaliser terrain et terres
+@desc.LogLand = Zonage, zones et quartiers, modifications du terrain et achats de parcelles.
+@label.LogCity = Journaliser l'état de la ville
+@desc.LogCity = État global de la ville : noms, décrets, argent, jalons et arbre de développement.
+@label.LogRoutes = Journaliser les lignes de transport
+@desc.LogRoutes = Lignes de transport, leurs arrêts et véhicules, et le prix des billets.
-# Diagnostic logging: each topic is a separate switch so a log stays readable.
-@group.Diagnostics = Journalisation de diagnostic
-@label.LogPerformance = Journaliser les performances
-@desc.LogPerformance = Toutes les 30 secondes, journalise les temps d'image et le temps de thread principal consommé par le mod, réparti entre résidentiel, commercial, industriel et bureaux. C'est ce qui permet de distinguer le coût du mod de celui de la ville.
@label.LogResidential = Journaliser la synchro résidentielle
@desc.LogResidential = Journalise les détails sur les ménages, les habitants et leur logement.
@label.LogCommercial = Journaliser la synchro commerciale
@@ -44,6 +73,15 @@
@label.LogOffice = Journaliser la synchro des bureaux
@desc.LogOffice = Journalise les détails sur les bureaux : quelle entreprise loue chaque bâtiment, ses chiffres et son stock.
+@label.LogPlayers = Journaliser les autres joueurs
+@desc.LogPlayers = Les autres joueurs : leurs curseurs, marqueurs, pings sur la carte et le chat.
+@label.LogUi = Journaliser les écrans du mod
+@desc.LogUi = Les écrans propres au mod : le bouton du menu principal, la fenêtre pour rejoindre et la page d'options.
+@label.LogStartup = Journaliser le démarrage
+@desc.LogStartup = Chargement du mod, enregistrement de ses systèmes, et les vérifications de mods et de DLC.
+@label.LogPerformance = Journaliser les performances
+@desc.LogPerformance = Toutes les 30 secondes, journalise les temps d'image et le temps de thread principal consommé par le mod, réparti entre résidentiel, commercial, industriel et bureaux. C'est ce qui permet de distinguer le coût du mod de celui de la ville.
+
@label.StatusRole = Rôle
@desc.StatusRole = Indique si cette instance est hors ligne, héberge, ou a rejoint en tant que client.
diff --git a/CS2MultiplayerMod/Localization/locales/it.properties b/CS2MultiplayerMod/Localization/locales/it.properties
index e12ca61..bb510c4 100644
--- a/CS2MultiplayerMod/Localization/locales/it.properties
+++ b/CS2MultiplayerMod/Localization/locales/it.properties
@@ -6,6 +6,7 @@
@tab.General = Generale
@tab.Join = Unisciti a una partita
@tab.Host = Ospita una partita
+@tab.Logging = Registrazione
@group.General = Generale
@group.Status = Stato
@@ -28,13 +29,41 @@
@label.IgnoreModCompatibilityChecks = Ignora i controlli di compatibilità delle mod (a tuo rischio)
@desc.IgnoreModCompatibilityChecks = Consente altre mod attive e, quando ospiti, versioni diverse di CS2 Multiplayer Mod. Questo può causare desincronizzazioni, città danneggiate o crash. I controlli su protocollo, versione del gioco e DLC restano attivi. Modificalo solo quando sei offline.
-@label.VerboseLogging = Log dettagliato
-@desc.VerboseLogging = Registra più dettagli per la risoluzione dei problemi (ogni azione di sincronizzazione e la diagnostica periodica). Lascialo disattivato per log più snelli; attivalo quando vuoi condividere un log dettagliato mentre chiedi aiuto.
+# -- Logging tab --
+# One switch per feature. None of them has to be on for a bug report to be worth
+# reading: connects, disconnects, world transfers, resyncs, dropped commands and
+# every fault are logged whatever is set here. These add the detail underneath.
+@group.LogAll = Registrazione completa
+@group.LogConnection = Connessione
+@group.LogWorld = Mondo e sincronizzazione
+@group.LogEconomy = Economia cittadina
+@group.LogClient = Questo client
+
+@label.VerboseLogging = Registra tutto
+@desc.VerboseLogging = Attiva in una volta tutte le opzioni sottostanti. Connessioni, disconnessioni, trasferimenti del mondo, risincronizzazioni, azioni scartate e tutti gli errori vengono registrati comunque: queste opzioni aggiungono solo il dettaglio. Attivala quando ti viene chiesto un log completo.
+
+@label.LogSession = Registra sessione
+@desc.LogSession = Connessione, disconnessione, handshake e giocatori che entrano o escono.
+@label.LogTransport = Registra connessione
+@desc.LogTransport = Il collegamento sotto la sessione: socket, relay Steam, inoltro delle porte e frequenze di invio. Attivala se non riesci proprio a connetterti.
+@label.LogWorldTransfer = Registra trasferimento del mondo
+@desc.LogWorldTransfer = Invio, ricezione, salvataggio e caricamento della città scaricata da chi si unisce. Attivala se entrare si blocca.
+
+@label.LogResync = Registra risincronizzazione
+@desc.LogResync = Cosa risultava diverso tra le due città, cosa ha deciso la mod e cosa ha fatto la riparazione. Attivala se il mondo continua a ricaricarsi.
+@label.LogPipeline = Registra pipeline dei comandi
+@desc.LogPipeline = Il percorso di ogni azione: in coda, inviata, ricevuta, applicata o scartata. Attivala se alcune azioni arrivano e altre no.
+@label.LogNets = Registra strade e reti
+@desc.LogNets = Strade, binari, tubature e cavi: costruzione, potenziamento, sostituzione e collegamenti.
+@label.LogBuildings = Registra edifici e oggetti
+@desc.LogBuildings = Le cose posizionate: edifici, elementi e alberi - posizionare, spostare, potenziare e demolire.
+@label.LogLand = Registra terreno e suolo
+@desc.LogLand = Zonizzazione, aree e distretti, modifiche al terreno e acquisti di lotti.
+@label.LogCity = Registra stato della città
+@desc.LogCity = Stato generale della città: nomi, politiche, denaro, traguardi e albero di sviluppo.
+@label.LogRoutes = Registra linee di trasporto
+@desc.LogRoutes = Linee di trasporto, fermate e veicoli, e il prezzo dei biglietti.
-# Diagnostic logging: each topic is a separate switch so a log stays readable.
-@group.Diagnostics = Registrazione diagnostica
-@label.LogPerformance = Registra le prestazioni
-@desc.LogPerformance = Ogni 30 secondi registra i tempi di fotogramma e quanto tempo del thread principale ha usato la mod stessa, suddiviso tra residenziale, commerciale, industriale e uffici. È ciò che distingue il costo della mod da quello della città.
@label.LogResidential = Registra sincronizzazione residenziale
@desc.LogResidential = Registra i dettagli su famiglie, residenti e dove abitano.
@label.LogCommercial = Registra sincronizzazione commerciale
@@ -44,6 +73,15 @@
@label.LogOffice = Registra sincronizzazione uffici
@desc.LogOffice = Registra i dettagli sugli uffici: quale azienda affitta ogni edificio, i suoi numeri e le sue scorte.
+@label.LogPlayers = Registra altri giocatori
+@desc.LogPlayers = Gli altri giocatori: cursori, indicatori, segnali sulla mappa e chat.
+@label.LogUi = Registra schermate della mod
+@desc.LogUi = Le schermate della mod: il pulsante nel menu principale, la finestra per unirsi e la pagina delle opzioni.
+@label.LogStartup = Registra avvio
+@desc.LogStartup = Caricamento della mod, registrazione dei suoi sistemi e i controlli su mod e DLC.
+@label.LogPerformance = Registra le prestazioni
+@desc.LogPerformance = Ogni 30 secondi registra i tempi di fotogramma e quanto tempo del thread principale ha usato la mod stessa, suddiviso tra residenziale, commerciale, industriale e uffici. È ciò che distingue il costo della mod da quello della città.
+
@label.StatusRole = Ruolo
@desc.StatusRole = Indica se questa istanza è offline, sta ospitando o si è unita come client.
diff --git a/CS2MultiplayerMod/Localization/locales/ja.properties b/CS2MultiplayerMod/Localization/locales/ja.properties
index 170c18b..00ea3c1 100644
--- a/CS2MultiplayerMod/Localization/locales/ja.properties
+++ b/CS2MultiplayerMod/Localization/locales/ja.properties
@@ -6,6 +6,7 @@
@tab.General = 全般
@tab.Join = ゲームに参加
@tab.Host = ゲームをホスト
+@tab.Logging = ログ
@group.General = 全般
@group.Status = ステータス
@@ -28,13 +29,41 @@
@label.IgnoreModCompatibilityChecks = MOD互換性チェックを無視(自己責任)
@desc.IgnoreModCompatibilityChecks = 他のMODの併用を許可し、ホスト時には CS2 Multiplayer Mod のバージョン違いも許可します。同期ずれ、都市の破損、クラッシュの原因になることがあります。プロトコル・ゲームバージョン・DLCのチェックは引き続き適用されます。変更はオフラインのときだけにしてください。
-@label.VerboseLogging = 詳細ログ
-@desc.VerboseLogging = トラブルシューティング用に詳細を記録します(すべての同期処理と定期診断)。ログを軽くしたいときはオフのままに、サポートを受ける際に詳細なログを共有したいときはオンにしてください。
+# -- Logging tab --
+# One switch per feature. None of them has to be on for a bug report to be worth
+# reading: connects, disconnects, world transfers, resyncs, dropped commands and
+# every fault are logged whatever is set here. These add the detail underneath.
+@group.LogAll = すべてのログ
+@group.LogConnection = 接続
+@group.LogWorld = ワールドと同期
+@group.LogEconomy = 都市経済
+@group.LogClient = このクライアント
+
+@label.VerboseLogging = すべて記録
+@desc.VerboseLogging = 下のスイッチをすべて一度に有効にします。接続、切断、ワールド転送、再同期、破棄された操作、すべての障害は、この設定に関わらず記録されます。スイッチはその下の詳細を追加するだけです。完全なログを求められたときに有効にしてください。
+
+@label.LogSession = セッションを記録
+@desc.LogSession = 接続、切断、ハンドシェイク、プレイヤーの参加と退出。
+@label.LogTransport = 接続を記録
+@desc.LogTransport = セッションの下層: ソケット、Steam リレー、ポート転送、送信レート。まったく接続できない場合に有効にしてください。
+@label.LogWorldTransfer = ワールド転送を記録
+@desc.LogWorldTransfer = 参加するプレイヤーがダウンロードする都市の送受信・保存・読み込み。参加が止まる場合に有効にしてください。
+
+@label.LogResync = 再同期を記録
+@desc.LogResync = 2 つの都市で何が食い違ったか、Mod がどう判断したか、修復が何をしたか。ワールドが繰り返し再読み込みされる場合に有効にしてください。
+@label.LogPipeline = コマンド経路を記録
+@desc.LogPipeline = 各操作の経路: 待機、送信、受信、適用、破棄。一部の操作だけが届かない場合に有効にしてください。
+@label.LogNets = 道路とネットワークを記録
+@desc.LogNets = 道路、線路、配管、電線: 敷設、アップグレード、置き換え、接続。
+@label.LogBuildings = 建物とオブジェクトを記録
+@desc.LogBuildings = 設置物: 建物、装飾、樹木の設置・移動・アップグレード・撤去。
+@label.LogLand = 土地と地形を記録
+@desc.LogLand = 区画設定、エリアと地区、地形の編集、タイルの購入。
+@label.LogCity = 都市の状態を記録
+@desc.LogCity = 都市全体の状態: 名称、条例、資金、マイルストーン、開発ツリー。
+@label.LogRoutes = 交通路線を記録
+@desc.LogRoutes = 交通路線、停留所と車両、運賃。
-# Diagnostic logging: each topic is a separate switch so a log stays readable.
-@group.Diagnostics = 診断ログ
-@label.LogPerformance = パフォーマンスを記録
-@desc.LogPerformance = 30秒ごとにフレーム時間と、MOD自身が使用したメインスレッド時間を、住宅・商業・工業・オフィス別に記録します。MODの負荷と都市そのものの負荷を切り分けられます。
@label.LogResidential = 住宅の同期を記録
@desc.LogResidential = 世帯・住民と、誰がどこに住んでいるかの詳細を記録します。
@label.LogCommercial = 商業の同期を記録
@@ -44,6 +73,15 @@
@label.LogOffice = オフィスの同期を記録
@desc.LogOffice = オフィスの詳細を記録します。どの企業が建物を借りているか、その収支と在庫。
+@label.LogPlayers = 他のプレイヤーを記録
+@desc.LogPlayers = 他のプレイヤー: カーソル、マーカー、マップ ping、チャット。
+@label.LogUi = Mod の画面を記録
+@desc.LogUi = Mod 自身の画面: メインメニューのボタン、参加ダイアログ、オプションページ。
+@label.LogStartup = 起動を記録
+@desc.LogStartup = Mod の読み込み、システムの登録、Mod と DLC の互換性チェック。
+@label.LogPerformance = パフォーマンスを記録
+@desc.LogPerformance = 30秒ごとにフレーム時間と、MOD自身が使用したメインスレッド時間を、住宅・商業・工業・オフィス別に記録します。MODの負荷と都市そのものの負荷を切り分けられます。
+
@label.StatusRole = 役割
@desc.StatusRole = このインスタンスがオフラインか、ホスト中か、クライアントとして参加中かを示します。
diff --git a/CS2MultiplayerMod/Localization/locales/pl.properties b/CS2MultiplayerMod/Localization/locales/pl.properties
index 451f6dd..9e82b28 100644
--- a/CS2MultiplayerMod/Localization/locales/pl.properties
+++ b/CS2MultiplayerMod/Localization/locales/pl.properties
@@ -6,6 +6,7 @@
@tab.General = Ogólne
@tab.Join = Dołącz do gry
@tab.Host = Hostuj grę
+@tab.Logging = Rejestrowanie
@group.General = Ogólne
@group.Status = Stan
@@ -28,13 +29,41 @@
@label.IgnoreModCompatibilityChecks = Ignoruj sprawdzanie zgodności modów (na własne ryzyko)
@desc.IgnoreModCompatibilityChecks = Zezwala na inne aktywne mody, a podczas hostowania także na inne wersje CS2 Multiplayer Mod. Może to powodować desynchronizacje, uszkodzone miasta lub awarie gry. Kontrole protokołu, wersji gry i DLC nadal obowiązują. Zmieniaj tylko poza sesją.
-@label.VerboseLogging = Szczegółowe logowanie
-@desc.VerboseLogging = Zapisuje więcej szczegółów na potrzeby diagnostyki (każdą akcję synchronizacji i okresową diagnostykę). Zostaw wyłączone dla czytelniejszych logów; włącz, gdy chcesz udostępnić szczegółowy log przy proszeniu o pomoc.
+# -- Logging tab --
+# One switch per feature. None of them has to be on for a bug report to be worth
+# reading: connects, disconnects, world transfers, resyncs, dropped commands and
+# every fault are logged whatever is set here. These add the detail underneath.
+@group.LogAll = Pełne rejestrowanie
+@group.LogConnection = Połączenie
+@group.LogWorld = Świat i synchronizacja
+@group.LogEconomy = Gospodarka miasta
+@group.LogClient = Ten klient
+
+@label.VerboseLogging = Rejestruj wszystko
+@desc.VerboseLogging = Włącza naraz wszystkie przełączniki poniżej. Połączenia, rozłączenia, przesyłanie świata, resynchronizacje, porzucone działania i wszystkie błędy są rejestrowane niezależnie od tych ustawień - przełączniki dodają tylko szczegóły. Włącz to, gdy poproszono cię o pełny dziennik.
+
+@label.LogSession = Rejestruj sesję
+@desc.LogSession = Łączenie, rozłączanie, uzgadnianie oraz gracze dołączający i wychodzący.
+@label.LogTransport = Rejestruj połączenie
+@desc.LogTransport = Łącze pod sesją: gniazda, przekaźnik Steam, przekierowanie portów i tempo wysyłania. Włącz, jeśli w ogóle nie możesz się połączyć.
+@label.LogWorldTransfer = Rejestruj przesyłanie świata
+@desc.LogWorldTransfer = Wysyłanie, odbieranie, zapis i wczytywanie miasta pobieranego przez dołączającego gracza. Włącz, jeśli dołączanie się zawiesza.
+
+@label.LogResync = Rejestruj resynchronizację
+@desc.LogResync = Co różniło się między miastami, co zdecydował mod i co zrobiła naprawa. Włącz, jeśli świat wciąż się przeładowuje.
+@label.LogPipeline = Rejestruj potok poleceń
+@desc.LogPipeline = Droga każdego działania: w kolejce, wysłane, odebrane, zastosowane lub porzucone. Włącz, jeśli część działań dociera, a część nie.
+@label.LogNets = Rejestruj drogi i sieci
+@desc.LogNets = Drogi, tory, rury i przewody: budowa, ulepszanie, wymiana i połączenia.
+@label.LogBuildings = Rejestruj budynki i obiekty
+@desc.LogBuildings = Postawione rzeczy: budynki, obiekty i drzewa - stawianie, przenoszenie, ulepszanie i wyburzanie.
+@label.LogLand = Rejestruj teren i grunty
+@desc.LogLand = Strefowanie, obszary i dzielnice, zmiany terenu i zakup kafelków.
+@label.LogCity = Rejestruj stan miasta
+@desc.LogCity = Stan całego miasta: nazwy, ustawy, pieniądze, kamienie milowe i drzewo rozwoju.
+@label.LogRoutes = Rejestruj linie transportu
+@desc.LogRoutes = Linie transportu, ich przystanki i pojazdy oraz ceny biletów.
-# Diagnostic logging: each topic is a separate switch so a log stays readable.
-@group.Diagnostics = Rejestrowanie diagnostyczne
-@label.LogPerformance = Rejestruj wydajność
-@desc.LogPerformance = Co 30 sekund zapisuje czasy klatek oraz ile czasu głównego wątku zużyła sama modyfikacja, w podziale na mieszkaniowe, handlowe, przemysłowe i biurowe. To pozwala odróżnić koszt modyfikacji od kosztu samego miasta.
@label.LogResidential = Rejestruj synchronizację mieszkaniową
@desc.LogResidential = Zapisuje szczegóły o gospodarstwach domowych, mieszkańcach i tym, kto gdzie mieszka.
@label.LogCommercial = Rejestruj synchronizację handlową
@@ -44,6 +73,15 @@
@label.LogOffice = Rejestruj synchronizację biurową
@desc.LogOffice = Zapisuje szczegóły o biurach: która firma wynajmuje dany budynek, jej wyniki i zapasy.
+@label.LogPlayers = Rejestruj innych graczy
+@desc.LogPlayers = Pozostali gracze: kursory, znaczniki, pingi na mapie i czat.
+@label.LogUi = Rejestruj ekrany moda
+@desc.LogUi = Własne ekrany moda: przycisk w menu głównym, okno dołączania i strona opcji.
+@label.LogStartup = Rejestruj uruchamianie
+@desc.LogStartup = Wczytywanie moda, rejestrowanie jego systemów oraz kontrole modów i DLC.
+@label.LogPerformance = Rejestruj wydajność
+@desc.LogPerformance = Co 30 sekund zapisuje czasy klatek oraz ile czasu głównego wątku zużyła sama modyfikacja, w podziale na mieszkaniowe, handlowe, przemysłowe i biurowe. To pozwala odróżnić koszt modyfikacji od kosztu samego miasta.
+
@label.StatusRole = Rola
@desc.StatusRole = Czy ta instancja jest offline, hostuje, czy dołączyła jako klient.
diff --git a/CS2MultiplayerMod/Localization/locales/ru.properties b/CS2MultiplayerMod/Localization/locales/ru.properties
index 3e92ee4..3562d82 100644
--- a/CS2MultiplayerMod/Localization/locales/ru.properties
+++ b/CS2MultiplayerMod/Localization/locales/ru.properties
@@ -6,6 +6,7 @@
@tab.General = Общее
@tab.Join = Присоединиться к игре
@tab.Host = Создать игру
+@tab.Logging = Журналирование
@group.General = Общее
@group.Status = Состояние
@@ -28,13 +29,41 @@
@label.IgnoreModCompatibilityChecks = Игнорировать проверки совместимости модов (на свой риск)
@desc.IgnoreModCompatibilityChecks = Разрешает другие активные моды, а при хостинге - и другие версии CS2 Multiplayer Mod. Это может привести к рассинхронизации, повреждённым городам или вылетам. Проверки протокола, версии игры и DLC по-прежнему выполняются. Меняйте только вне сессии.
-@label.VerboseLogging = Подробное журналирование
-@desc.VerboseLogging = Записывает больше подробностей для диагностики (каждое действие синхронизации и периодическую диагностику). Оставьте выключенным ради более чистых журналов; включите, когда нужен подробный журнал, чтобы приложить его к обращению за помощью.
+# -- Logging tab --
+# One switch per feature. None of them has to be on for a bug report to be worth
+# reading: connects, disconnects, world transfers, resyncs, dropped commands and
+# every fault are logged whatever is set here. These add the detail underneath.
+@group.LogAll = Полное журналирование
+@group.LogConnection = Соединение
+@group.LogWorld = Мир и синхронизация
+@group.LogEconomy = Экономика города
+@group.LogClient = Этот клиент
+
+@label.VerboseLogging = Записывать всё
+@desc.VerboseLogging = Включает сразу все переключатели ниже. Подключения, отключения, передачи мира, ресинхронизации, отброшенные действия и все сбои записываются в любом случае - переключатели лишь добавляют подробности. Включите, когда у вас попросили полный журнал.
+
+@label.LogSession = Записывать сессию
+@desc.LogSession = Подключение, отключение, рукопожатие, а также вход и выход игроков.
+@label.LogTransport = Записывать соединение
+@desc.LogTransport = Канал под сессией: сокеты, ретранслятор Steam, проброс портов и скорость отправки. Включите, если подключиться вообще не удаётся.
+@label.LogWorldTransfer = Записывать передачу мира
+@desc.LogWorldTransfer = Отправка, приём, сохранение и загрузка города, который скачивает присоединяющийся игрок. Включите, если вход зависает.
+
+@label.LogResync = Записывать ресинхронизацию
+@desc.LogResync = Что разошлось между двумя городами, что решил мод и что сделало восстановление. Включите, если мир постоянно перезагружается.
+@label.LogPipeline = Записывать конвейер команд
+@desc.LogPipeline = Путь каждого действия: в очереди, отправлено, получено, применено или отброшено. Включите, если одни действия доходят, а другие нет.
+@label.LogNets = Записывать дороги и сети
+@desc.LogNets = Дороги, пути, трубы и провода: строительство, улучшение, замена и соединения.
+@label.LogBuildings = Записывать здания и объекты
+@desc.LogBuildings = Размещённое: здания, объекты и деревья - размещение, перенос, улучшение и снос.
+@label.LogLand = Записывать землю и ландшафт
+@desc.LogLand = Зонирование, области и районы, правки ландшафта и покупка клеток.
+@label.LogCity = Записывать состояние города
+@desc.LogCity = Общегородское состояние: названия, указы, деньги, вехи и дерево развития.
+@label.LogRoutes = Записывать транспортные линии
+@desc.LogRoutes = Транспортные линии, их остановки и транспорт, а также цены на билеты.
-# Diagnostic logging: each topic is a separate switch so a log stays readable.
-@group.Diagnostics = Диагностический журнал
-@label.LogPerformance = Записывать производительность
-@desc.LogPerformance = Каждые 30 секунд записывает время кадров и то, сколько времени основного потока использовал сам мод, с разбивкой на жилую, коммерческую, промышленную и офисную застройку. Именно это позволяет отличить затраты мода от затрат самого города.
@label.LogResidential = Записывать синхронизацию жилья
@desc.LogResidential = Записывает подробности о домохозяйствах, жителях и том, кто где живёт.
@label.LogCommercial = Записывать синхронизацию коммерции
@@ -44,6 +73,15 @@
@label.LogOffice = Записывать синхронизацию офисов
@desc.LogOffice = Записывает подробности об офисах: какая компания арендует здание, её показатели и запасы.
+@label.LogPlayers = Записывать других игроков
+@desc.LogPlayers = Другие игроки: их курсоры, метки, сигналы на карте и чат.
+@label.LogUi = Записывать экраны мода
+@desc.LogUi = Собственные экраны мода: кнопка в главном меню, окно подключения и страница настроек.
+@label.LogStartup = Записывать запуск
+@desc.LogStartup = Загрузка мода, регистрация его систем и проверки модов и DLC.
+@label.LogPerformance = Записывать производительность
+@desc.LogPerformance = Каждые 30 секунд записывает время кадров и то, сколько времени основного потока использовал сам мод, с разбивкой на жилую, коммерческую, промышленную и офисную застройку. Именно это позволяет отличить затраты мода от затрат самого города.
+
@label.StatusRole = Роль
@desc.StatusRole = Работает ли эта копия автономно, как хост или подключена как клиент.
diff --git a/CS2MultiplayerMod/Localization/locales/zh-HANS.properties b/CS2MultiplayerMod/Localization/locales/zh-HANS.properties
index 39ac2d2..000c9a0 100644
--- a/CS2MultiplayerMod/Localization/locales/zh-HANS.properties
+++ b/CS2MultiplayerMod/Localization/locales/zh-HANS.properties
@@ -6,6 +6,7 @@
@tab.General = 常规
@tab.Join = 加入游戏
@tab.Host = 创建游戏
+@tab.Logging = 日志
@group.General = 常规
@group.Status = 状态
@@ -28,13 +29,41 @@
@label.IgnoreModCompatibilityChecks = 忽略模组兼容性检查(风险自负)
@desc.IgnoreModCompatibilityChecks = 允许启用其他模组,并且在你主持游戏时允许不同版本的 CS2 Multiplayer Mod。这可能导致不同步、城市损坏或游戏崩溃。协议、游戏版本和 DLC 检查仍会执行。请仅在离线时更改。
-@label.VerboseLogging = 详细日志
-@desc.VerboseLogging = 记录更多排查问题所需的细节(每一次同步操作以及定期诊断)。想让日志更清爽就保持关闭;需要提供详细日志以便获得帮助时再打开。
+# -- Logging tab --
+# One switch per feature. None of them has to be on for a bug report to be worth
+# reading: connects, disconnects, world transfers, resyncs, dropped commands and
+# every fault are logged whatever is set here. These add the detail underneath.
+@group.LogAll = 全部日志
+@group.LogConnection = 连接
+@group.LogWorld = 世界与同步
+@group.LogEconomy = 城市经济
+@group.LogClient = 本客户端
+
+@label.VerboseLogging = 记录全部
+@desc.VerboseLogging = 一次性打开下面所有开关。无论这里如何设置,连接、断开、世界传输、重新同步、被丢弃的操作以及所有故障都会被记录;这些开关只是补充其下的细节。当别人向你索要完整日志时打开它。
+
+@label.LogSession = 记录会话
+@desc.LogSession = 连接、断开、握手,以及玩家的加入和离开。
+@label.LogTransport = 记录连接
+@desc.LogTransport = 会话之下的链路:套接字、Steam 中继、端口转发和发送速率。完全无法连接时打开。
+@label.LogWorldTransfer = 记录世界传输
+@desc.LogWorldTransfer = 加入的玩家所下载城市的发送、接收、保存与载入。加入卡住时打开。
+
+@label.LogResync = 记录重新同步
+@desc.LogResync = 两座城市有何差异、模组如何判断、修复做了什么。世界反复重新载入时打开。
+@label.LogPipeline = 记录指令流水线
+@desc.LogPipeline = 每个操作的去向:排队、发送、接收、应用或丢弃。部分操作到达而另一些没有时打开。
+@label.LogNets = 记录道路与管网
+@desc.LogNets = 道路、轨道、管道和电线:铺设、升级、替换及其连接。
+@label.LogBuildings = 记录建筑与物件
+@desc.LogBuildings = 放置的东西:建筑、装饰和树木的放置、移动、升级与拆除。
+@label.LogLand = 记录土地与地形
+@desc.LogLand = 分区、区域与街区、地形编辑和地块购买。
+@label.LogCity = 记录城市状态
+@desc.LogCity = 全城状态:名称、政策、资金、里程碑和发展树。
+@label.LogRoutes = 记录交通线路
+@desc.LogRoutes = 交通线路、站点与车辆,以及票价。
-# Diagnostic logging: each topic is a separate switch so a log stays readable.
-@group.Diagnostics = 诊断日志
-@label.LogPerformance = 记录性能
-@desc.LogPerformance = 每 30 秒记录一次帧时间,以及模组自身占用的主线程时间,并按住宅、商业、工业和办公分别列出。这样才能把模组的开销与城市本身的开销区分开。
@label.LogResidential = 记录住宅同步
@desc.LogResidential = 记录家庭、居民以及谁住在哪里的详细信息。
@label.LogCommercial = 记录商业同步
@@ -44,6 +73,15 @@
@label.LogOffice = 记录办公同步
@desc.LogOffice = 记录办公楼的详细信息:哪家企业租用该建筑、其财务数据和库存。
+@label.LogPlayers = 记录其他玩家
+@desc.LogPlayers = 其他玩家:他们的光标、标记、地图标点和聊天。
+@label.LogUi = 记录模组界面
+@desc.LogUi = 模组自己的界面:主菜单按钮、加入对话框和选项页面。
+@label.LogStartup = 记录启动
+@desc.LogStartup = 模组载入、系统注册,以及模组和 DLC 兼容性检查。
+@label.LogPerformance = 记录性能
+@desc.LogPerformance = 每 30 秒记录一次帧时间,以及模组自身占用的主线程时间,并按住宅、商业、工业和办公分别列出。这样才能把模组的开销与城市本身的开销区分开。
+
@label.StatusRole = 角色
@desc.StatusRole = 此实例当前处于离线、主持中,还是以客户端身份加入。
diff --git a/CS2MultiplayerMod/Mod.cs b/CS2MultiplayerMod/Mod.cs
index 86fb6e1..11465e2 100644
--- a/CS2MultiplayerMod/Mod.cs
+++ b/CS2MultiplayerMod/Mod.cs
@@ -1,6 +1,8 @@
using System.Collections.Generic;
using Colossal.IO.AssetDatabase;
using Colossal.Logging;
+using CS2MultiplayerMod.Core.Diagnostics;
+using CS2MultiplayerMod.Core.Protocol;
using CS2MultiplayerMod.Game;
using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Localization;
@@ -14,6 +16,11 @@ public class Mod : IMod
{
public const string Name = "CS2MultiplayerMod";
+ ///
+ /// The game's logger, and the destination writes to.
+ /// Not a front door: log through SyncLog so the line gets its topic, its switch and its
+ /// copy in the flight log.
+ ///
public static ILog log = LogManager.GetLogger(Name).SetShowsErrorsInUI(false);
public static Setting Setting;
@@ -36,22 +43,6 @@ public class Mod : IMod
new KeyValuePair("zh-HANS", "zh-HANS"),
};
- ///
- /// Log a chatty, troubleshooting-only line - the per-action sync notices and the
- /// periodic diagnostics. Silent unless "Verbose Logging" is enabled in settings, so
- /// the default log stays quiet and only the important lifecycle/fault lines remain.
- ///
- public static void Verbose(string message)
- {
- if (VerboseEnabled) log.Info(message);
- }
-
- ///
- /// Whether anything would come of a call. Ask before *computing* a
- /// diagnostic, not just before logging one: a counter nobody reads must not cost a frame.
- ///
- public static bool VerboseEnabled => Setting != null && Setting.VerboseLogging;
-
///
/// The live multiplayer bridge. Created here and pumped each tick by
/// ; the settings screen drives it via
@@ -59,22 +50,27 @@ public static void Verbose(string message)
///
public static MultiplayerService Service;
+ ///
+ /// The version this build reports - to the log, and to a peer during the handshake.
+ ///
+ private static string Version => typeof(Mod).Assembly.GetName().Version.ToString();
+
public void OnLoad(UpdateSystem updateSystem)
{
- log.Info(nameof(OnLoad));
-
// Crash forensics first: the flight log must be recording before anything
// else of ours can fail (see FlightRecorder).
- FlightRecorder.Start(typeof(Mod).Assembly.GetName().Version.ToString());
+ FlightRecorder.Start(Version);
- // Route the sync inbox's rare backpressure/drain warnings to the mod log.
- Game.Sync.Infrastructure.SyncInbox.LogWarn = log.Warn;
+ // Route the sync inbox's rare backpressure/drain warnings through the one logger.
+ // They are pipeline faults, so they are never gated by a switch.
+ Game.Sync.Infrastructure.SyncInbox.LogWarn =
+ delegate(string message) { SyncLog.Warn(LogTopic.Pipeline, message); };
// Also where the Steam relay backend sits, when this copy of the game has one.
string modFolder = null;
if (GameManager.instance.modManager.TryGetExecutableAsset(this, out var asset))
{
- log.Info($"Current mod asset at {Game.Diagnostics.LogPaths.Redact(asset.path)}");
+ SyncLog.Detail(LogTopic.Startup, "Loaded from " + asset.path + ".");
modFolder = System.IO.Path.GetDirectoryName(asset.path);
}
@@ -94,9 +90,9 @@ public void OnLoad(UpdateSystem updateSystem)
// Persist / load settings to the standard mod settings store.
AssetDatabase.global.LoadSettings(Name, Setting, new Setting(this));
- // Stand up the multiplayer core (portable session + game logger adapter) and
- // register the ECS system that pumps it once per simulation tick.
- var coreLog = new ColossalModLogger(log);
+ // Stand up the multiplayer core. The portable half logs through the same logger as
+ // the rest of the mod; ColossalModLogger is the seam (see there).
+ IModLogger coreLog = ColossalModLogger.Instance;
// Offer Steam's relay as a hosting backend. Availability is decided here once;
// when Steam is absent the mod simply keeps to direct connections.
@@ -111,11 +107,6 @@ public void OnLoad(UpdateSystem updateSystem)
// service, which owns the clock, the in-flight-recovery state and the arbiter.
Game.Sync.Infrastructure.SyncInbox.Arbitrate = Service.SettleResyncReport;
- FlightRecorder.Note("startup-stage service-created");
- log.Info("Multiplayer core initialised. Protocol v" +
- CS2MultiplayerMod.Core.Protocol.ProtocolConstants.ProtocolVersion +
- ". Registering sync systems...");
-
// UIUpdate, not GameSimulation: the session pump must also run in the main
// menu (joining from there) and while the game is paused - the options
// screen pauses the simulation, which previously froze all connection
@@ -285,12 +276,19 @@ public void OnLoad(UpdateSystem updateSystem)
// was never processed while the host sat in the (paused) menu, leaving
// the client stuck in WaitingForMap forever.
updateSystem.UpdateAt(SystemUpdatePhase.UIUpdate);
- FlightRecorder.Note("startup-complete systems-registered");
+
+ // One line, at the end, rather than a "ready" line per registered system: the thirty
+ // of those said nothing a reader could act on, and the only question they answered -
+ // "did the mod actually come up?" - is answered better here, with the numbers that
+ // decide whether two players can even play together.
+ SyncLog.Event(LogTopic.Startup, "Loaded: mod v" + Version + ", protocol v" +
+ ProtocolConstants.ProtocolVersion + ", game v" + UnityEngine.Application.version +
+ ", sync systems registered.");
}
public void OnDispose()
{
- log.Info(nameof(OnDispose));
+ SyncLog.Event(LogTopic.Startup, "Unloading.");
Game.Sync.Infrastructure.SyncInbox.Arbitrate = null;
ResyncArbiter.Reset();
diff --git a/CS2MultiplayerMod/Setting.cs b/CS2MultiplayerMod/Setting.cs
index 59a405d..f6f6665 100644
--- a/CS2MultiplayerMod/Setting.cs
+++ b/CS2MultiplayerMod/Setting.cs
@@ -1,6 +1,8 @@
using Colossal.IO.AssetDatabase;
+using CS2MultiplayerMod.Core.Diagnostics;
using CS2MultiplayerMod.Core.Networking;
using CS2MultiplayerMod.Core.Session;
+using CS2MultiplayerMod.Game.Diagnostics;
using CS2MultiplayerMod.Localization;
using Game;
using Game.Modding;
@@ -12,9 +14,13 @@
namespace CS2MultiplayerMod
{
[FileLocation(nameof(CS2MultiplayerMod))]
- [SettingsUITabOrder(GeneralTab, JoinTab, HostTab)]
- [SettingsUIGroupOrder(GeneralGroup, DiagnosticsGroup, StatusGroup, SessionGroup, JoinSetupGroup, JoinActionGroup, HostSetupGroup, HostActionGroup)]
- [SettingsUIShowGroupName(GeneralGroup, DiagnosticsGroup, StatusGroup, SessionGroup, JoinSetupGroup, JoinActionGroup, HostSetupGroup, HostActionGroup)]
+ [SettingsUITabOrder(GeneralTab, JoinTab, HostTab, LoggingTab)]
+ [SettingsUIGroupOrder(GeneralGroup, StatusGroup, SessionGroup, JoinSetupGroup, JoinActionGroup,
+ HostSetupGroup, HostActionGroup,
+ LogAllGroup, LogConnectionGroup, LogWorldGroup, LogEconomyGroup, LogClientGroup)]
+ [SettingsUIShowGroupName(GeneralGroup, StatusGroup, SessionGroup, JoinSetupGroup, JoinActionGroup,
+ HostSetupGroup, HostActionGroup,
+ LogAllGroup, LogConnectionGroup, LogWorldGroup, LogEconomyGroup, LogClientGroup)]
public class Setting : ModSetting
{
// The options UI exposes general/session state plus join and host setup.
@@ -25,8 +31,12 @@ public class Setting : ModSetting
public const string JoinTab = "Join";
public const string HostTab = "Host";
+ // Its own tab, not a group on General: there is one switch per feature (see LogTopic),
+ // which is the point of them - but eighteen checkboxes wedged under the player-name field
+ // would be the first thing anyone sees when they open the mod's options.
+ public const string LoggingTab = "Logging";
+
public const string GeneralGroup = "General";
- public const string DiagnosticsGroup = "Diagnostics";
public const string StatusGroup = "Status";
public const string SessionGroup = "Session";
public const string JoinSetupGroup = "JoinSetup";
@@ -34,6 +44,14 @@ public class Setting : ModSetting
public const string HostSetupGroup = "HostSetup";
public const string HostActionGroup = "HostAction";
+ // The logging switches, grouped the way a player narrows a problem down: first "can I get
+ // in", then "is the city the same", then "are the numbers right", then "is it my client".
+ public const string LogAllGroup = "LogAll";
+ public const string LogConnectionGroup = "LogConnection";
+ public const string LogWorldGroup = "LogWorld";
+ public const string LogEconomyGroup = "LogEconomy";
+ public const string LogClientGroup = "LogClient";
+
/// Values of . Stored as strings so the UI binding is one plain value.
public const string ConnectionRelay = "relay";
public const string ConnectionDirect = "direct";
@@ -163,36 +181,139 @@ public void ApplyPlatformNamePreset()
PlayerName = preset;
PlayerNamePresetApplied = true;
ApplyAndSave();
- Mod.log.Info("Player name preset from the platform account: '" + preset + "'.");
+ SyncLog.Detail(LogTopic.Startup, "Player name preset from the platform account: '" +
+ preset + "'.");
}
+ // ---- Logging tab --------------------------------------------------------
+ // One switch per feature rather than one "extra logging" switch, because the log that
+ // answers a question is the one about the thing that broke: a player chasing missing
+ // roads should get roads, not twenty thousand lines of everything else.
+ //
+ // None of them has to be on for a bug report to be worth reading. Connects,
+ // disconnects, world transfers, resyncs, dropped commands and every fault are written
+ // whatever is set here (see SyncLog); these only add the per-action detail underneath
+ // them. VerboseLogging below is the "I do not know which one" shortcut, not a
+ // different kind of logging.
+
///
- /// Off: only the important lines (connect/disconnect, world transfer, faults).
- /// On: also the per-action sync notices and periodic diagnostics. See .
+ /// The master switch: turns every topic below on at once, without disturbing which
+ /// individual ones the player had ticked.
+ ///
+ /// Safe to leave on - it makes the log longer, not the game slower, because the detail
+ /// lines sit behind a field read and the flight log only flushes them in batches. Turn it
+ /// on when you have been asked for a full log and do not want to guess which switch
+ /// covers the problem.
///
- [SettingsUISection(GeneralTab, GeneralGroup)]
+ [SettingsUISection(LoggingTab, LogAllGroup)]
public bool VerboseLogging { get; set; } = false;
- ///
- /// Frame times and the mod's own main-thread cost, reported every 30 s together with a
- /// per-zone split. Cheap enough to leave on: the measurement itself is two timestamp reads
- /// per pass, and it is the only thing that can tell the mod's cost apart from the city's.
- ///
- [SettingsUISection(GeneralTab, DiagnosticsGroup)]
- public bool LogPerformance { get; set; } = false;
+ /// Connecting, disconnecting, the handshake, and players joining or leaving.
+ [SettingsUISection(LoggingTab, LogConnectionGroup)]
+ public bool LogSession { get; set; } = false;
+
+ /// The wire underneath a session: sockets, the Steam relay, port forwarding, rates.
+ [SettingsUISection(LoggingTab, LogConnectionGroup)]
+ public bool LogTransport { get; set; } = false;
+
+ /// Sending, receiving, staging and loading the world a joining player downloads.
+ [SettingsUISection(LoggingTab, LogConnectionGroup)]
+ public bool LogWorldTransfer { get; set; } = false;
+
+ /// What diverged, what the arbiter decided about it, and what the repair did.
+ [SettingsUISection(LoggingTab, LogWorldGroup)]
+ public bool LogResync { get; set; } = false;
+
+ /// The command pipeline: inbox, observers, authority holds, realization.
+ [SettingsUISection(LoggingTab, LogWorldGroup)]
+ public bool LogPipeline { get; set; } = false;
+
+ /// Roads, tracks, pipes and wires.
+ [SettingsUISection(LoggingTab, LogWorldGroup)]
+ public bool LogNets { get; set; } = false;
+
+ /// Placed objects: buildings, props and trees.
+ [SettingsUISection(LoggingTab, LogWorldGroup)]
+ public bool LogBuildings { get; set; } = false;
+
+ /// Zoning, areas and districts, terrain, tile purchases.
+ [SettingsUISection(LoggingTab, LogWorldGroup)]
+ public bool LogLand { get; set; } = false;
+
+ /// City-wide state: names, policies, money, milestones, the development tree.
+ [SettingsUISection(LoggingTab, LogWorldGroup)]
+ public bool LogCity { get; set; } = false;
- [SettingsUISection(GeneralTab, DiagnosticsGroup)]
+ /// Transit lines, stops, vehicles and fares.
+ [SettingsUISection(LoggingTab, LogWorldGroup)]
+ public bool LogRoutes { get; set; } = false;
+
+ [SettingsUISection(LoggingTab, LogEconomyGroup)]
public bool LogResidential { get; set; } = false;
- [SettingsUISection(GeneralTab, DiagnosticsGroup)]
+ [SettingsUISection(LoggingTab, LogEconomyGroup)]
public bool LogCommercial { get; set; } = false;
- [SettingsUISection(GeneralTab, DiagnosticsGroup)]
+ [SettingsUISection(LoggingTab, LogEconomyGroup)]
public bool LogIndustrial { get; set; } = false;
- [SettingsUISection(GeneralTab, DiagnosticsGroup)]
+ [SettingsUISection(LoggingTab, LogEconomyGroup)]
public bool LogOffice { get; set; } = false;
+ /// The other players: their cursors, markers, map pings and chat.
+ [SettingsUISection(LoggingTab, LogClientGroup)]
+ public bool LogPlayers { get; set; } = false;
+
+ /// The mod's own screens: the main-menu button, the join dialog, the options page.
+ [SettingsUISection(LoggingTab, LogClientGroup)]
+ public bool LogUi { get; set; } = false;
+
+ /// Mod load, system registration, and the compatibility and DLC checks.
+ [SettingsUISection(LoggingTab, LogClientGroup)]
+ public bool LogStartup { get; set; } = false;
+
+ ///
+ /// Frame times and the mod's own main-thread cost, reported every 30 s together with a
+ /// per-zone split. Cheap enough to leave on: the measurement itself is two timestamp reads
+ /// per pass, and it is the only thing that can tell the mod's cost apart from the city's.
+ ///
+ [SettingsUISection(LoggingTab, LogClientGroup)]
+ public bool LogPerformance { get; set; } = false;
+
+ ///
+ /// Whether the player asked for detail about this topic. is
+ /// applied by the caller (), so this stays
+ /// a plain per-topic answer.
+ ///
+ /// Unknown topics answer false: a topic added without a switch should be silent by
+ /// default rather than quietly chatty in every player's log.
+ ///
+ public bool IsTopicEnabled(LogTopic topic)
+ {
+ switch (topic)
+ {
+ case LogTopic.Startup: return LogStartup;
+ case LogTopic.Session: return LogSession;
+ case LogTopic.Transport: return LogTransport;
+ case LogTopic.WorldTransfer: return LogWorldTransfer;
+ case LogTopic.Resync: return LogResync;
+ case LogTopic.Pipeline: return LogPipeline;
+ case LogTopic.Nets: return LogNets;
+ case LogTopic.Buildings: return LogBuildings;
+ case LogTopic.Land: return LogLand;
+ case LogTopic.City: return LogCity;
+ case LogTopic.Routes: return LogRoutes;
+ case LogTopic.Residential: return LogResidential;
+ case LogTopic.Commercial: return LogCommercial;
+ case LogTopic.Industrial: return LogIndustrial;
+ case LogTopic.Office: return LogOffice;
+ case LogTopic.Players: return LogPlayers;
+ case LogTopic.Ui: return LogUi;
+ case LogTopic.Performance: return LogPerformance;
+ default: return false;
+ }
+ }
+
///
/// The partner markers are the only thing this mod draws every rendered frame, so they are
/// the one part of it whose cost scales with screen resolution rather than with city size.
@@ -467,6 +588,24 @@ public override void SetDefaults()
{
EnableMod = true;
VerboseLogging = false;
+ LogSession = false;
+ LogTransport = false;
+ LogWorldTransfer = false;
+ LogResync = false;
+ LogPipeline = false;
+ LogNets = false;
+ LogBuildings = false;
+ LogLand = false;
+ LogCity = false;
+ LogRoutes = false;
+ LogResidential = false;
+ LogCommercial = false;
+ LogIndustrial = false;
+ LogOffice = false;
+ LogPlayers = false;
+ LogUi = false;
+ LogStartup = false;
+ LogPerformance = false;
ShowPartnerMarkers = true;
IgnoreModCompatibilityChecks = false;
PlayerName = DefaultPlayerName;
diff --git a/help/errors-and-warnings.md b/help/errors-and-warnings.md
index d6682d0..5618db5 100644
--- a/help/errors-and-warnings.md
+++ b/help/errors-and-warnings.md
@@ -283,7 +283,13 @@ Include:
2. Whether each player used Steam Relay or Direct Connection.
3. Whether Ignore Mod Compatibility Checks (Own Risk) was enabled and which other mods were active.
4. The game and CS2 Multiplayer Mod versions from both computers.
-5. `%USERPROFILE%\AppData\LocalLow\Colossal Order\Cities Skylines II\Logs\Player.log` and `CS2MP-flight.log` from the affected computers. Send the files after the problem occurs and before repeatedly restarting, because later runs can rotate diagnostic history.
+5. `CS2MP-flight.log` from `%USERPROFILE%\AppData\LocalLow\Colossal Order\Cities Skylines II\Logs\` on the affected computers. Send it after the problem occurs and before repeatedly restarting, because later runs can rotate diagnostic history.
+
+`CS2MP-flight.log` is the one file to send. It carries every line the mod writes plus the crash detail the readable log cannot keep, and unlike the game's own log it is not wiped when the game restarts. `Player.log` and `CS2MultiplayerMod.log` are the readable versions of the same events, and are worth adding when someone asks for them.
+
+You do not have to switch anything on first. Connects, disconnects, world transfers, resyncs, dropped actions and every fault are logged whatever your settings are.
+
+If you are asked for more detail, the mod's **Logging** options tab has one switch per feature - Session, Connection, World Transfer, Resync, Roads & Networks, Buildings & Objects and so on. Turn on the one that matches the problem, or **Log Everything** if you are not sure, then reproduce the problem and send the file again.
Never post a session password. Network addresses and profile paths are redacted by the mod where it controls the log line, but review files before sharing them publicly.