Skip to content

Commit e26bb39

Browse files
authored
Merge pull request #14 from bonsaibauer/2.0.0.0
v2.0.0.0
2 parents bb323a1 + 9cba70a commit e26bb39

112 files changed

Lines changed: 6660 additions & 428 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22

33
In-game changelog source: `src/CSM.TmpeSync/Services/UI/ChangelogPanel.cs` (`ChangelogService.LoadEntries()`).
44

5+
## [2.0.0.0] - 2026-04-03
6+
7+
### Current State
8+
- **Timed Traffic Lights synchronization**: Active and fully integrated as a host-authoritative sync feature.
9+
- **Manual Traffic Lights synchronization**: Active as a stable node-snapshot sync feature for multiplayer sessions.
10+
11+
### Runtime & Data Flow
12+
- **Timed Traffic Lights**: Uses an event-driven definition/runtime pipeline with per-frame batching and synchronization-readiness guards.
13+
- **Manual Traffic Lights**: Uses a lean queued listener-dispatch flow with low runtime overhead.
14+
15+
### Logging
16+
- **Synchronization logs**: Use consistent tags, contextual fields, and host/client diagnostic output.
17+
518
## [1.2.0.0] - 2026-03-31
619

720
### Compatibility & Health Check

CSM.TmpeSync.sln

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSM.TmpeSync.ParkingRestric
1414
EndProject
1515
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSM.TmpeSync.JunctionRestrictions", "src\CSM.TmpeSync.JunctionRestrictions\CSM.TmpeSync.JunctionRestrictions.csproj", "{7B887B69-C358-44F6-97F8-9970252154DE}"
1616
EndProject
17+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSM.TmpeSync.ManualTrafficLights", "src\CSM.TmpeSync.ManualTrafficLights\CSM.TmpeSync.ManualTrafficLights.csproj", "{DA7480A1-9A52-4FE1-A277-3A20A4A9BFD0}"
18+
EndProject
1719
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSM.TmpeSync.SpeedLimits", "src\CSM.TmpeSync.SpeedLimits\CSM.TmpeSync.SpeedLimits.csproj", "{EDE0BEE5-E8E9-4163-8284-3BB79A6716BD}"
1820
EndProject
1921
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSM.TmpeSync.VehicleRestrictions", "src\CSM.TmpeSync.VehicleRestrictions\CSM.TmpeSync.VehicleRestrictions.csproj", "{3C2E294B-D561-4FAF-976E-6D85B42C4405}"
2022
EndProject
2123
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSM.TmpeSync.ToggleTrafficLights", "src\CSM.TmpeSync.ToggleTrafficLights\CSM.TmpeSync.ToggleTrafficLights.csproj", "{5E3C3361-03F5-4BB5-849C-C8E6528D2C15}"
2224
EndProject
25+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSM.TmpeSync.TimedTrafficLights", "src\CSM.TmpeSync.TimedTrafficLights\CSM.TmpeSync.TimedTrafficLights.csproj", "{033C0B69-9FBD-47A3-97EA-8261D3C8A4B3}"
26+
EndProject
2327
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSM.TmpeSync.ClearTraffic", "src\CSM.TmpeSync.ClearTraffic\CSM.TmpeSync.ClearTraffic.csproj", "{BBAA00D7-39C0-4094-A35B-5BD43C88B3E0}"
2428
EndProject
2529
Global
@@ -52,6 +56,10 @@ Global
5256
{7B887B69-C358-44F6-97F8-9970252154DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
5357
{7B887B69-C358-44F6-97F8-9970252154DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
5458
{7B887B69-C358-44F6-97F8-9970252154DE}.Release|Any CPU.Build.0 = Release|Any CPU
59+
{DA7480A1-9A52-4FE1-A277-3A20A4A9BFD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
60+
{DA7480A1-9A52-4FE1-A277-3A20A4A9BFD0}.Debug|Any CPU.Build.0 = Debug|Any CPU
61+
{DA7480A1-9A52-4FE1-A277-3A20A4A9BFD0}.Release|Any CPU.ActiveCfg = Release|Any CPU
62+
{DA7480A1-9A52-4FE1-A277-3A20A4A9BFD0}.Release|Any CPU.Build.0 = Release|Any CPU
5563
{EDE0BEE5-E8E9-4163-8284-3BB79A6716BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
5664
{EDE0BEE5-E8E9-4163-8284-3BB79A6716BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
5765
{EDE0BEE5-E8E9-4163-8284-3BB79A6716BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -64,6 +72,10 @@ Global
6472
{5E3C3361-03F5-4BB5-849C-C8E6528D2C15}.Debug|Any CPU.Build.0 = Debug|Any CPU
6573
{5E3C3361-03F5-4BB5-849C-C8E6528D2C15}.Release|Any CPU.ActiveCfg = Release|Any CPU
6674
{5E3C3361-03F5-4BB5-849C-C8E6528D2C15}.Release|Any CPU.Build.0 = Release|Any CPU
75+
{033C0B69-9FBD-47A3-97EA-8261D3C8A4B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
76+
{033C0B69-9FBD-47A3-97EA-8261D3C8A4B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
77+
{033C0B69-9FBD-47A3-97EA-8261D3C8A4B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
78+
{033C0B69-9FBD-47A3-97EA-8261D3C8A4B3}.Release|Any CPU.Build.0 = Release|Any CPU
6779
{BBAA00D7-39C0-4094-A35B-5BD43C88B3E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
6880
{BBAA00D7-39C0-4094-A35B-5BD43C88B3E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
6981
{BBAA00D7-39C0-4094-A35B-5BD43C88B3E0}.Release|Any CPU.ActiveCfg = Release|Any CPU

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ Detailed docs live alongside the code. Every feature description is bilingual (G
2525
| Junction Restrictions | [docs/JunctionRestrictions.md](docs/JunctionRestrictions.md) ||
2626
| Lane Arrows | [docs/LaneArrows.md](docs/LaneArrows.md) ||
2727
| Lane Connector | [docs/LaneConnector.md](docs/LaneConnector.md) ||
28+
| Manual Traffic Lights | [docs/ManualTrafficLights.md](docs/ManualTrafficLights.md) ||
2829
| Parking Restrictions | [docs/ParkingRestrictions.md](docs/ParkingRestrictions.md) ||
2930
| Priority Signs | [docs/PrioritySigns.md](docs/PrioritySigns.md) ||
3031
| Speed Limits | [docs/SpeedLimits.md](docs/SpeedLimits.md) ||
31-
| Timed Traffic Lights | [docs/TimedTrafficLights.md](docs/TimedTrafficLights.md) | |
32+
| Timed Traffic Lights | [docs/TimedTrafficLights.md](docs/TimedTrafficLights.md) | |
3233
| Toggle Traffic Lights | [docs/ToggleTrafficLights.md](docs/ToggleTrafficLights.md) ||
3334
| Vehicle Restrictions | [docs/VehicleRestrictions.md](docs/VehicleRestrictions.md) ||
3435

docs/ManualTrafficLights.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# CSM TM:PE Sync - Manual Traffic Lights / Manuelle Ampeln
2+
3+
This document is bilingual. German (DE) first, English (EN) follows.
4+
5+
---
6+
7+
## DE - Deutsch
8+
9+
### 1) Kurzbeschreibung
10+
- Synchronisiert TM:PE Manual Traffic Lights pro Knoten zwischen Host und Clients in CSM-Sitzungen.
11+
- Verwendet ein Node-Snapshot-Modell: pro betroffenem Knoten wird der komplette manuelle Ampelzustand uebertragen.
12+
- Host ist autoritativ: Clients senden Aenderungswuensche, der Host wendet den Zustand in TM:PE an und broadcastet den effektiv angewandten Snapshot.
13+
- Event-getriebener Versand: lokale TM:PE-Events werden knotenweise gebuendelt und als Simulation-Flush verarbeitet.
14+
- Retry-/Backoff-Apply sorgt fuer robuste Anwendung bei temporaer nicht bereiten TM:PE-Komponenten.
15+
16+
### 2) Dateistruktur
17+
- `src/CSM.TmpeSync.ManualTrafficLights/`
18+
- `ManualTrafficLightsSyncFeature.cs` - Feature-Bootstrap (aktiviert Listener).
19+
- `Handlers/` - CSM-Command-Handler (Server/Client-Verarbeitung).
20+
- `Messages/` - Netzwerkbefehle (ProtoBuf-Vertraege) fuer Node-Snapshots.
21+
- `Services/` - Harmony-Listener, Synchronisationslogik, TM:PE-Adapter, Apply-Koordinator, State-Cache.
22+
23+
### 3) Dateiuebersicht
24+
| Datei | Zweck |
25+
| --- | --- |
26+
| `ManualTrafficLightsSyncFeature.cs` | Registriert/aktiviert das Feature und den TM:PE-Listener. |
27+
| `Handlers/ManualTrafficLightsUpdateRequestHandler.cs` | Server: validiert Requests, fuehrt Apply unter Node-Lock aus und triggert Host-Broadcast. |
28+
| `Handlers/ManualTrafficLightsAppliedCommandHandler.cs` | Client: verarbeitet Host-Broadcasts und wendet den Snapshot lokal an; `OnClientConnect` triggert Host-Resync. |
29+
| `Messages/ManualTrafficLightsUpdateRequest.cs` | Client -> Server: Aenderungswunsch (`NodeId`, `State`). |
30+
| `Messages/ManualTrafficLightsAppliedCommand.cs` | Server -> Alle: final angewandter Snapshot (`NodeId`, `State`). |
31+
| `Messages/ManualTrafficLightsNodeState.cs` | Wire-State fuer manuelle Ampeln: Node, Segmente, Fussgaengerzustand, Vehicle-Lights. |
32+
| `Services/ManualTrafficLightsEventListener.cs` | Harmony-Hooks auf relevante TM:PE-Methoden; sammelt lokale Aenderungen pro Knoten und flush't gebuendelt. |
33+
| `Services/ManualTrafficLightsSynchronization.cs` | Gemeinsamer Versandweg, Host/Client-Dispatch, Retry/Backoff-Apply, Reconnect-Resync. |
34+
| `Services/ManualTrafficLightsTmpeAdapter.cs` | Lesen/Anwenden des Snapshot-Zustands in TM:PE (SetUp/Remove Manual-Simulation, Segment-/Vehicle-Lights). |
35+
| `Services/ManualTrafficLightsStateCache.cs` | Host-Cache fuer zuletzt angewandte Snapshots inkl. Hashing, Clone und Prune. |
36+
37+
### 4) Workflow (Server/Host und Client)
38+
- **Host aendert manuelle Ampeln in TM:PE**
39+
- Harmony-Postfix erkennt den betroffenen Knoten (z. B. `ChangeMainLight`, `SetLightMode`, `set_CurrentMode`).
40+
- Listener queued den Knoten und fuehrt genau einen Flush auf der Simulation aus.
41+
- Der Host liest den kompletten Node-Snapshot und broadcastet `ManualTrafficLightsAppliedCommand`.
42+
43+
- **Client aendert manuelle Ampeln in TM:PE**
44+
- Harmony-Postfix queued den betroffenen Knoten und erzeugt beim Flush einen Node-Snapshot.
45+
- Client sendet `ManualTrafficLightsUpdateRequest` an den Host.
46+
- Host validiert den Knoten, lockt ihn via `EntityLocks.AcquireNode`, wendet den Snapshot an und broadcastet danach den effektiv angewandten Zustand.
47+
48+
- **Apply-Verhalten**
49+
- Apply laeuft ueber einen Koordinator mit bis zu 6 Versuchen und Frame-Backoff `5/15/30/60/120/240`.
50+
- Bei temporaeren TM:PE-Problemen (z. B. uninitialisierte Manager, `NullReference`) wird automatisch erneut versucht.
51+
- Bei finalem Fehler wird server-/clientseitig geloggt.
52+
53+
- **Reconnect-Resync**
54+
- Beim Client-Reconnect sendet der Host alle gueltigen gecachten `ManualTrafficLightsAppliedCommand`-Eintraege.
55+
- Vor dem Versand wird der Cache per `Prune` auf gueltige Knoten bereinigt.
56+
57+
- **Ablehnungen**
58+
- Dieses Feature verwendet keinen dedizierten `RequestRejected`-Kanal; fehlerhafte Requests werden serverseitig verworfen und geloggt.
59+
60+
### 5) Datenaustausch (Nachrichten/Felder)
61+
| Nachricht | Richtung | Feld | Typ | Beschreibung |
62+
| --- | --- | --- | --- | --- |
63+
| `ManualTrafficLightsUpdateRequest` | Client -> Server | `NodeId` | `ushort` | Knoten-ID der manuellen Ampel. |
64+
| | | `State` | `ManualTrafficLightsNodeState` | Gewuenschter kompletter Node-Snapshot. |
65+
| `ManualTrafficLightsAppliedCommand` | Server -> Alle | `NodeId` | `ushort` | Knoten-ID der manuellen Ampel. |
66+
| | | `State` | `ManualTrafficLightsNodeState` | Effektiv angewandter kompletter Node-Snapshot. |
67+
| `ManualTrafficLightsNodeState` | (payload) | `NodeId` | `ushort` | Zielknoten. |
68+
| | | `IsManualEnabled` | `bool` | Manual-Simulation aktiv/inaktiv. |
69+
| | | `Segments[]` | `List<SegmentState>` | Segmentende-Snapshots am Knoten. |
70+
| `SegmentState` | (payload) | `SegmentId` | `ushort` | Betroffenes Segment. |
71+
| | | `StartNode` | `bool` | `true` = Startknoten-Ende, `false` = Endknoten-Ende. |
72+
| | | `ManualPedestrianMode` | `bool` | Manueller Fussgaenger-Modus aktiv/inaktiv. |
73+
| | | `HasPedestrianLightState` | `bool` | Kennzeichnet expliziten Fussgaenger-Lichtzustand. |
74+
| | | `PedestrianLightState` | `int` | Serialisierter TM:PE-Lichtzustand fuer Fussgaenger. |
75+
| | | `VehicleLights[]` | `List<VehicleLightState>` | Fahrzeug-Lichtzustaende pro VehicleType. |
76+
| `VehicleLightState` | (payload) | `VehicleType` | `int` | Serialisierter TM:PE-VehicleType. |
77+
| | | `LightMode` | `int` | Serialisierter TM:PE-LightMode. |
78+
| | | `MainLightState` | `int` | Hauptsignal-Zustand. |
79+
| | | `LeftLightState` | `int` | Links-Signal-Zustand. |
80+
| | | `RightLightState` | `int` | Rechts-Signal-Zustand. |
81+
82+
Hinweise
83+
- Enum-Werte werden als `int` uebertragen, um API-Drift zwischen TM:PE-Versionen robust zu behandeln.
84+
- Client-Dispatch verwendet pro Knoten Hash-Dedupe, damit identische Snapshots nicht erneut gesendet werden.
85+
- Host-Cache verwendet Hash-Dedupe fuer Broadcast/Resync und haelt nur den letzten effektiven Snapshot je Knoten.
86+
- `NetworkUtil.IsSynchronizationReady()` gate't Queue/Flush waehrend der Ladephase.
87+
- Ignore- und Local-Apply-Scopes verhindern Echo-Schleifen zwischen lokalem Apply und Harmony-Listener.
88+
89+
---
90+
91+
## EN - English
92+
93+
### 1) Summary
94+
- Synchronizes TM:PE manual traffic lights per node between host and clients in CSM sessions.
95+
- Uses a node snapshot model: the full manual light state is transferred for each affected node.
96+
- Host is authoritative: clients send update requests, the host applies in TM:PE, then broadcasts the effective snapshot.
97+
- Event-driven dispatch: local TM:PE events are queued per node and processed in a single simulation flush.
98+
- Retry/backoff apply keeps synchronization stable when TM:PE components are temporarily unavailable.
99+
100+
### 2) Directory Layout
101+
- `src/CSM.TmpeSync.ManualTrafficLights/`
102+
- `ManualTrafficLightsSyncFeature.cs` - feature bootstrap (enables listener).
103+
- `Handlers/` - CSM command handlers (server/client processing).
104+
- `Messages/` - network commands (ProtoBuf contracts) for node snapshots.
105+
- `Services/` - Harmony listener, synchronization logic, TM:PE adapter, apply coordinator, state cache.
106+
107+
### 3) File Overview
108+
| File | Purpose |
109+
| --- | --- |
110+
| `ManualTrafficLightsSyncFeature.cs` | Registers/enables the feature and TM:PE listener. |
111+
| `Handlers/ManualTrafficLightsUpdateRequestHandler.cs` | Server: validates requests, applies under node lock, and triggers host broadcast. |
112+
| `Handlers/ManualTrafficLightsAppliedCommandHandler.cs` | Client: processes host broadcasts and applies snapshots locally; `OnClientConnect` triggers host resync. |
113+
| `Messages/ManualTrafficLightsUpdateRequest.cs` | Client -> Server: change request (`NodeId`, `State`). |
114+
| `Messages/ManualTrafficLightsAppliedCommand.cs` | Server -> All: final applied snapshot (`NodeId`, `State`). |
115+
| `Messages/ManualTrafficLightsNodeState.cs` | Wire state for manual lights: node, segments, pedestrian state, vehicle lights. |
116+
| `Services/ManualTrafficLightsEventListener.cs` | Harmony hooks on relevant TM:PE methods; queues local changes per node and flushes in batch. |
117+
| `Services/ManualTrafficLightsSynchronization.cs` | Common dispatch path, host/client routing, retry/backoff apply, reconnect resync. |
118+
| `Services/ManualTrafficLightsTmpeAdapter.cs` | Reads/applies snapshot state in TM:PE (set up/remove manual simulation, segment/vehicle lights). |
119+
| `Services/ManualTrafficLightsStateCache.cs` | Host cache for latest applied snapshots with hashing, cloning, and prune support. |
120+
121+
### 4) Workflow (Server/Host and Client)
122+
- **Host edits manual lights in TM:PE**
123+
- Harmony postfix detects the affected node (for example `ChangeMainLight`, `SetLightMode`, `set_CurrentMode`).
124+
- Listener queues the node and executes exactly one simulation flush.
125+
- Host reads the full node snapshot and broadcasts `ManualTrafficLightsAppliedCommand`.
126+
127+
- **Client edits manual lights in TM:PE**
128+
- Harmony postfix queues the affected node and builds a node snapshot during flush.
129+
- Client sends `ManualTrafficLightsUpdateRequest` to the host.
130+
- Host validates the node, acquires `EntityLocks.AcquireNode`, applies the snapshot, and then broadcasts the effective state.
131+
132+
- **Apply behavior**
133+
- Apply runs through a coordinator with up to 6 attempts and frame backoff `5/15/30/60/120/240`.
134+
- Temporary TM:PE failures (for example uninitialized managers, `NullReference`) automatically trigger retries.
135+
- Final failures are logged on host/client side.
136+
137+
- **Reconnect resync**
138+
- On reconnect, the host sends all valid cached `ManualTrafficLightsAppliedCommand` entries to the client.
139+
- The cache is pruned against current node validity before sending.
140+
141+
- **Rejections**
142+
- This feature does not use a dedicated `RequestRejected` channel; invalid requests are dropped and logged on the server.
143+
144+
### 5) Data Exchange (messages/fields)
145+
| Message | Direction | Field | Type | Description |
146+
| --- | --- | --- | --- | --- |
147+
| `ManualTrafficLightsUpdateRequest` | Client -> Server | `NodeId` | `ushort` | Node ID of the manual traffic light. |
148+
| | | `State` | `ManualTrafficLightsNodeState` | Desired full node snapshot. |
149+
| `ManualTrafficLightsAppliedCommand` | Server -> All | `NodeId` | `ushort` | Node ID of the manual traffic light. |
150+
| | | `State` | `ManualTrafficLightsNodeState` | Effective full node snapshot after host apply. |
151+
| `ManualTrafficLightsNodeState` | (payload) | `NodeId` | `ushort` | Target node. |
152+
| | | `IsManualEnabled` | `bool` | Manual simulation enabled/disabled. |
153+
| | | `Segments[]` | `List<SegmentState>` | Segment-end snapshots at the node. |
154+
| `SegmentState` | (payload) | `SegmentId` | `ushort` | Target segment. |
155+
| | | `StartNode` | `bool` | `true` = start-node end, `false` = end-node end. |
156+
| | | `ManualPedestrianMode` | `bool` | Manual pedestrian mode enabled/disabled. |
157+
| | | `HasPedestrianLightState` | `bool` | Indicates explicit pedestrian light state presence. |
158+
| | | `PedestrianLightState` | `int` | Serialized TM:PE pedestrian light state. |
159+
| | | `VehicleLights[]` | `List<VehicleLightState>` | Vehicle light states per vehicle type. |
160+
| `VehicleLightState` | (payload) | `VehicleType` | `int` | Serialized TM:PE vehicle type. |
161+
| | | `LightMode` | `int` | Serialized TM:PE light mode. |
162+
| | | `MainLightState` | `int` | Main light state. |
163+
| | | `LeftLightState` | `int` | Left light state. |
164+
| | | `RightLightState` | `int` | Right light state. |
165+
166+
Notes
167+
- Enum values are transferred as `int` to keep compatibility resilient across TM:PE enum drift.
168+
- Client dispatch uses per-node hash deduplication to suppress identical snapshots.
169+
- Host cache uses hash deduplication for broadcast/resync and stores only the latest effective snapshot per node.
170+
- `NetworkUtil.IsSynchronizationReady()` gates queue/flush during loading.
171+
- Ignore scopes and local-apply scopes prevent feedback loops between local apply and Harmony listeners.

0 commit comments

Comments
 (0)