Skip to content

Commit 0ffe1cd

Browse files
committed
fix compatibility system for csm and add changelog news
1 parent a79b14d commit 0ffe1cd

7 files changed

Lines changed: 869 additions & 191 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ In-game changelog source: `src/CSM.TmpeSync/Services/UI/ChangelogPanel.cs` (`Cha
77
### Compatibility & Health Check
88
- **Compatibility management rework**: Added a dedicated Health Check flow in Mod Options with live Host/Client and dependency diagnostics.
99
- **Cities: Skylines compatibility validation**: Added explicit CS version-line checks and integrated them into diagnostics and notifier flows.
10+
- [Fixed] Manual Host/Client compatibility checks: stabilized probe/response delivery to prevent false "No Response" timeouts during in-session checks.
11+
- [Fixed] Mod Options UX: selecting the Compatibility tab now triggers the same immediate refresh as the Refresh button.
1012
- **Runtime UX behavior**: Improved Host/Client session handling and status rendering in Mod Options and popup panels.
1113

1214
### UI & Messaging

src/CSM.TmpeSync/Handlers/System/VersionCheckHandlers.cs

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,31 @@ protected override void Handle(VersionCheckRequest command)
3737

3838
var clientVersion = command?.Version;
3939
var serverVersion = CompatibilityChecker.LocalVersion;
40-
var versionsMatch = CompatibilityChecker.CompareVersions(clientVersion, serverVersion);
41-
var status = versionsMatch ? "Match" : "Mismatch";
40+
string comparisonStatus;
41+
string comparisonSeverity;
42+
var versionsMatch = CompatibilityChecker.CompareVersions(
43+
clientVersion,
44+
serverVersion,
45+
out comparisonStatus,
46+
out comparisonSeverity);
4247

4348
Log.Info(
4449
LogCategory.Network,
45-
"Client version comparison | senderId={0} clientVersion={1} serverVersion={2} status={3}",
50+
"Client version comparison | senderId={0} clientVersion={1} serverVersion={2} status={3} severity={4}",
4651
senderId,
4752
clientVersion ?? "<null>",
4853
serverVersion ?? "<null>",
49-
status);
54+
comparisonStatus,
55+
comparisonSeverity ?? "<null>");
56+
57+
CompatibilityChecker.HandleAutomaticServerHandshakeObservation(
58+
senderId,
59+
clientVersion,
60+
serverVersion);
5061

5162
if (!versionsMatch)
5263
{
5364
VersionMismatchNotifier.NotifyServerMismatch(senderId, clientVersion, serverVersion);
54-
FeatureBootstrapper.SuspendForVersionMismatch(clientVersion);
5565
var shouldBroadcast = command == null || !command.IsManualCheck;
5666
if (shouldBroadcast)
5767
{
@@ -79,23 +89,23 @@ protected override void Handle(VersionCheckRequest command)
7989

8090
try
8191
{
82-
CsmBridge.SendToClient(senderId, new VersionCheckResponse
92+
CsmBridge.SendToAll(new VersionCheckResponse
8393
{
8494
Version = serverVersion,
8595
IsManualCheck = command != null && command.IsManualCheck,
8696
RequestId = command?.RequestId
8797
});
8898
Log.Info(
8999
LogCategory.Network,
90-
"Version check response sent | targetId={0} version={1} manual={2} requestId={3}",
100+
"Version check response broadcast sent | targetId={0} version={1} manual={2} requestId={3}",
91101
senderId,
92102
serverVersion ?? "<null>",
93103
command != null && command.IsManualCheck ? "Yes" : "No",
94104
command?.RequestId ?? "<null>");
95105
}
96106
catch (Exception ex)
97107
{
98-
Log.Warn(LogCategory.Network, LogRole.General, "Failed to send version check response | targetId={0} error={1}", senderId, ex);
108+
Log.Warn(LogCategory.Network, LogRole.General, "Failed to broadcast version check response | targetId={0} error={1}", senderId, ex);
99109
}
100110
}
101111
}
@@ -121,15 +131,23 @@ protected override void Handle(VersionCheckResponse command)
121131

122132
var serverVersion = command?.Version;
123133
var localVersion = CompatibilityChecker.LocalVersion;
124-
var versionsMatch = CompatibilityChecker.CompareVersions(serverVersion, localVersion);
125-
var status = versionsMatch ? "Match" : "Mismatch";
134+
string comparisonStatus;
135+
string comparisonSeverity;
136+
var versionsMatch = CompatibilityChecker.CompareVersions(
137+
serverVersion,
138+
localVersion,
139+
out comparisonStatus,
140+
out comparisonSeverity);
126141

127142
Log.Info(
128143
LogCategory.Network,
129-
"Server version comparison | serverVersion={0} localVersion={1} status={2}",
144+
"Server version comparison | serverVersion={0} localVersion={1} status={2} severity={3}",
130145
serverVersion ?? "<null>",
131146
localVersion ?? "<null>",
132-
status);
147+
comparisonStatus,
148+
comparisonSeverity ?? "<null>");
149+
150+
CompatibilityChecker.HandleAutomaticClientHandshakeResult(localVersion, serverVersion);
133151

134152
if (!versionsMatch)
135153
{
@@ -145,7 +163,6 @@ protected override void Handle(VersionCheckResponse command)
145163
VersionMismatchNotifier.NotifyClientMismatch(serverVersion, localVersion);
146164
}
147165

148-
FeatureBootstrapper.SuspendForVersionMismatch(serverVersion);
149166
return;
150167
}
151168

@@ -164,6 +181,12 @@ public class VersionProbeRequestHandler : CommandHandler<VersionProbeRequest>
164181
protected override void Handle(VersionProbeRequest command)
165182
{
166183
var senderId = CsmBridge.GetSenderId(command);
184+
Log.Info(
185+
LogCategory.Network,
186+
"Version probe request received | senderId={0} requestId={1} hostVersion={2}",
187+
senderId,
188+
command?.RequestId ?? "<null>",
189+
command?.HostVersion ?? "<null>");
167190
if (CsmBridge.IsServerInstance())
168191
{
169192
Log.Debug(LogCategory.Network, LogRole.General, "Version probe request ignored | reason=server_instance senderId={0}", senderId);
@@ -182,6 +205,14 @@ protected override void Handle(VersionProbeRequest command)
182205
ClientVersion = localVersion,
183206
MatchesHost = matches
184207
});
208+
Log.Info(
209+
LogCategory.Network,
210+
LogRole.Client,
211+
"Version probe response sent | requestId={0} clientVersion={1} hostVersion={2} matchesHost={3}",
212+
command?.RequestId ?? "<null>",
213+
localVersion ?? "<null>",
214+
hostVersion ?? "<null>",
215+
matches ? "Yes" : "No");
185216
}
186217
catch (Exception ex)
187218
{
@@ -207,6 +238,14 @@ protected override void Handle(VersionProbeResponse command)
207238
}
208239

209240
var senderId = CsmBridge.GetSenderId(command);
241+
Log.Info(
242+
LogCategory.Network,
243+
LogRole.Host,
244+
"Version probe response received | senderId={0} requestId={1} clientVersion={2} matchesHost={3}",
245+
senderId,
246+
command?.RequestId ?? "<null>",
247+
command?.ClientVersion ?? "<null>",
248+
command != null && command.MatchesHost ? "Yes" : "No");
210249
CompatibilityChecker.HandleManualHostProbeResponse(
211250
senderId,
212251
command?.RequestId,

src/CSM.TmpeSync/Handlers/System/VersionMismatchBroadcastHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ protected override void Handle(VersionMismatchBroadcast command)
2525
var reportedClientVersion = command?.ReportedClientVersion;
2626
var localVersion = CompatibilityChecker.LocalVersion;
2727
var matches = CompatibilityChecker.CompareVersions(serverVersion, localVersion);
28+
CompatibilityChecker.HandleAutomaticClientHandshakeResult(localVersion, serverVersion);
2829

2930
Log.Info(
3031
LogCategory.Network,
@@ -39,7 +40,6 @@ protected override void Handle(VersionMismatchBroadcast command)
3940
if (!matches)
4041
{
4142
VersionMismatchNotifier.NotifyClientMismatch(serverVersion, localVersion);
42-
FeatureBootstrapper.SuspendForVersionMismatch(serverVersion);
4343
}
4444
}
4545
}

src/CSM.TmpeSync/Mod/FeatureBootstrapper.cs

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,7 @@ internal static void Register()
5252
_registered = true;
5353
}
5454

55-
foreach (var feature in Features)
56-
EnableFeature(feature);
57-
58-
Log.Info(LogCategory.Synchronization, GetCurrentRole(), "Synchronization features enabled.");
55+
EnableAllFeatures();
5956
}
6057

6158
internal static void Unregister()
@@ -83,26 +80,55 @@ internal static void SuspendForVersionMismatch(string remoteVersion)
8380
? "mod version mismatch"
8481
: string.Format("mod version mismatch (remote={0})", remoteVersion);
8582

83+
ApplyCompatibilityGate(enabled: false, reason: reason);
84+
}
85+
86+
internal static void ApplyCompatibilityGate(bool enabled, string reason)
87+
{
8688
var shouldDisable = false;
89+
var shouldEnable = false;
90+
var effectiveReason = string.IsNullOrEmpty(reason) ? "compatibility gate" : reason;
8791
lock (SyncRoot)
8892
{
89-
if (_suspended)
90-
return;
91-
92-
_suspended = true;
93-
_suspendReason = reason;
94-
95-
if (_registered)
93+
if (enabled)
9694
{
97-
_registered = false;
98-
shouldDisable = true;
95+
if (!_suspended)
96+
return;
97+
98+
_suspended = false;
99+
_suspendReason = string.Empty;
100+
if (!_registered)
101+
{
102+
_registered = true;
103+
shouldEnable = true;
104+
}
105+
}
106+
else
107+
{
108+
if (_suspended && !_registered && string.Equals(_suspendReason, effectiveReason, StringComparison.Ordinal))
109+
return;
110+
111+
_suspended = true;
112+
_suspendReason = effectiveReason;
113+
if (_registered)
114+
{
115+
_registered = false;
116+
shouldDisable = true;
117+
}
99118
}
100119
}
101120

102121
if (shouldDisable)
122+
{
103123
DisableAllFeatures();
124+
Log.Warn(LogCategory.Synchronization, GetCurrentRole(), "Synchronization suspended | reason={0}", effectiveReason);
125+
}
104126

105-
Log.Warn(LogCategory.Synchronization, GetCurrentRole(), "Synchronization suspended | reason={0}", reason);
127+
if (shouldEnable)
128+
{
129+
EnableAllFeatures();
130+
Log.Info(LogCategory.Synchronization, GetCurrentRole(), "Synchronization resumed | reason={0}", effectiveReason);
131+
}
106132
}
107133

108134
internal static void GetRuntimeState(out bool isRegistered, out bool isSuspended, out string suspendReason)
@@ -121,6 +147,14 @@ private static void DisableAllFeatures()
121147
DisableFeature(feature);
122148
}
123149

150+
private static void EnableAllFeatures()
151+
{
152+
foreach (var feature in Features)
153+
EnableFeature(feature);
154+
155+
Log.Info(LogCategory.Synchronization, GetCurrentRole(), "Synchronization features enabled.");
156+
}
157+
124158
private static void EnableFeature(FeatureToggle feature)
125159
{
126160
if (!feature.Enabled)

src/CSM.TmpeSync/Mod/MyUserMod.cs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,25 +47,37 @@ public void OnSettingsUI(UIHelperBase helper)
4747
generalGroup.AddButton("Show What's New", ChangelogService.ShowLatestNow);
4848

4949
var compatibilityTab = tabStrip.AddTabPage("Compatibility");
50+
var compatibilityTabIndex = tabStrip.tabCount - 1;
5051
var compatibilityGroup = compatibilityTab.AddGroup("Compatibility");
5152
var compatibilityGroupHelper = compatibilityGroup as UIHelper;
5253
var compatibilityContainer = compatibilityGroupHelper?.self as UIPanel;
53-
if (compatibilityContainer != null)
54+
55+
OnButtonClicked refreshCompatibilityUi = () =>
5456
{
57+
if (compatibilityContainer == null)
58+
return;
59+
5560
BuildCompatibilityUi(compatibilityContainer);
61+
compatibilityContainer.Invalidate();
62+
};
63+
64+
if (compatibilityContainer != null)
65+
{
66+
refreshCompatibilityUi();
5667
}
5768

69+
tabStrip.eventSelectedIndexChanged += (_, index) =>
70+
{
71+
if (index == compatibilityTabIndex)
72+
{
73+
refreshCompatibilityUi();
74+
}
75+
};
76+
5877
var compatibilityActions = compatibilityTab.AddGroup("Actions");
5978
compatibilityActions.AddButton(
6079
"Refresh",
61-
() =>
62-
{
63-
if (compatibilityContainer == null)
64-
return;
65-
66-
BuildCompatibilityUi(compatibilityContainer);
67-
compatibilityContainer.Invalidate();
68-
});
80+
refreshCompatibilityUi);
6981
compatibilityActions.AddButton(
7082
"Copy diagnostics",
7183
() =>

0 commit comments

Comments
 (0)