Skip to content

Commit 7e65df3

Browse files
11EJDE11Metadorius
andauthored
Port CnCNet YR fixes to mainline: desyncs, open-top cloak, DisableChat (#68)
* Move from separate tree to ifdef * Make desync fixes available to all instead of just YR. * Replace DWORD(-1) with names consts * fix editorconfig * Add credits --------- Co-authored-by: Metadorius <crabiter@vivaldi.net>
1 parent 9d86e77 commit 7e65df3

7 files changed

Lines changed: 514 additions & 0 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,16 @@ Credits
3434
- Beacon crash fix for multiplayer save/load
3535
- Game speed slider toggle
3636
- Fake multiplayer flag
37+
- **[EJ](https://github.com/11EJDE11)**
38+
- Whole lot of desync fixes
3739
- **[ZivDero](https://github.com/ZivDero)**
3840
- Handicaps (difficulty & credits) support
3941
- **[Starkku](https://github.com/Starkku)**
4042
- Allow customizing whether or not special house is ally to all players via spawn.ini option (#51)
4143
- **[RAZER](https://github.com/CnCRAZER)**
4244
- Game speed slider toggle
45+
- Revert of Battle Fortress cloak changes for CnCNet YR
46+
- Ability to disable ingame chat
4347
- **[TaranDahl](https://github.com/TaranDahl)**
4448
- Porting of multiplayer save/load
4549
- Porting of autosaves

Spawner.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
<!-- Misc -->
3535
<ClCompile Include="$(ThisDir)\src\Misc\Bugfixes.cpp" />
3636
<ClCompile Include="$(ThisDir)\src\Misc\Bugfixes.Blowfish.cpp" />
37+
<ClCompile Include="$(ThisDir)\src\Misc\Bugfixes.Desyncs.cpp" />
3738
<ClCompile Include="$(ThisDir)\src\Misc\Bugfixes.ClampTacticalPos.cpp" />
39+
<ClCompile Include="$(ThisDir)\src\Misc\Bugfixes.OpenTopCloak.cpp" />
3840
<ClCompile Include="$(ThisDir)\src\Misc\Bugfixes.CommonCrashes.cpp" />
3941
<ClCompile Include="$(ThisDir)\src\Misc\Bugfixes.ExceptionCatch.cpp" />
4042
<ClCompile Include="$(ThisDir)\src\Misc\Bugfixes.IsoMapPack5Limit.cpp" />

src/Misc/Bugfixes.Desyncs.cpp

Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
/**
2+
* yrpp-spawner
3+
*
4+
* Copyright(C) 2022-present CnCNet
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program.If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
// Multiplayer desync fixes for missing CellClass::RecalcAttributes calls.
21+
//
22+
// CellClass::RecalcAttributes derives LandType/Passability from
23+
// a cell's iso tile and overlay. Several game code paths clear overlays
24+
// (crates, flags, veinholes, bridges) without calling RecalcAttributes,
25+
// leaving LandType stale. This diverges between players when an aircraft
26+
// passes over the cell(s) which later leads to pathfinding differences.
27+
28+
#include <Utilities/Macro.h>
29+
#include <CellClass.h>
30+
#include <FootClass.h>
31+
#include <MapClass.h>
32+
#include <TechnoClass.h>
33+
34+
// CellClass::RecalcAttributes takes a cell level; -1 means "recalc but keep the
35+
// current level unchanged" (see CellClass.h).
36+
static constexpr int KeepCellLevel = -1;
37+
38+
// CellClass::OverlayTypeIndex sentinel meaning "no overlay present on this cell".
39+
static constexpr int NoOverlay = -1;
40+
41+
// ============================================================
42+
// Aircraft shadow desync fix
43+
// ============================================================
44+
// AircraftClass::Draw_It temporarily calls Set_Height(0) before drawing the
45+
// shadow, which triggers MapClass::Place_Down and Pick_Up, which call
46+
// RecalcAttributes. This only runs for players who can see the aircraft,
47+
// causing LandType to diverge based on fog-of-war state.
48+
// Fix: no-op all three Set_Height calls during shadow drawing.
49+
// 0x5F4300 = ObjectClass::Record_The_Kill_House (empty body, safe NOP target).
50+
DEFINE_JUMP(CALL6, 0x4147D5, 0x5F4300); // Set_Height bridge path
51+
DEFINE_JUMP(CALL6, 0x4147F3, 0x5F4300); // Set_Height non-bridge path
52+
DEFINE_JUMP(CALL6, 0x4148AB, 0x5F4300); // Set_Height restore
53+
54+
// ============================================================
55+
// Crate removal RecalcAttributes
56+
// ============================================================
57+
58+
// Single-player crate removal path.
59+
DEFINE_HOOK(0x4A1BEF, CrateClass_Get_Crate_RecalcAttributes, 0x6)
60+
{
61+
GET(CellClass*, pCell, EBX);
62+
pCell->RecalcAttributes(KeepCellLevel);
63+
return 0;
64+
}
65+
66+
// Multiplayer crate removal path.
67+
DEFINE_HOOK(0x56C1DA, MapClass_Remove_Crate_RecalcAttributes, 0x6)
68+
{
69+
GET(CellClass*, pCell, EBX);
70+
pCell->RecalcAttributes(KeepCellLevel);
71+
return 0;
72+
}
73+
74+
// ============================================================
75+
// Flag removal RecalcAttributes
76+
// ============================================================
77+
78+
// HouseClass::Flag_Remove: clears the flag home cell overlay without RecalcAttributes.
79+
// Unused, but kept for correctness should such a mode be used.
80+
DEFINE_HOOK(0x4FBF3C, HouseClass_Flag_Remove_RecalcAttributes, 0x5)
81+
{
82+
GET(CellClass*, pCell, EAX);
83+
pCell->RecalcAttributes(KeepCellLevel);
84+
return 0;
85+
}
86+
87+
// ============================================================
88+
// Veinhole constructor RecalcAttributes
89+
// ============================================================
90+
91+
// VeinholeMonsterClass constructor directly writes SlopeIndex, IsoTileTypeIndex,
92+
// Height, and Level for a 3x3 cell grid without calling RecalcAttributes.
93+
// Hook after the last per-cell write (Level) each iteration to fix all 9 cells.
94+
DEFINE_HOOK(0x74C775, VeinholeMonster_Constructor_RecalcAttributes, 0x6)
95+
{
96+
GET(CellClass*, pCell, EAX);
97+
pCell->Level = (char)R->DL(); // write Level-1 before RecalcAttributes
98+
pCell->RecalcAttributes(KeepCellLevel);
99+
MapClass::Instance.ResetZones(pCell->MapCoords);
100+
MapClass::Instance.RecalculateSubZones(pCell->MapCoords);
101+
return 0x74C77B;
102+
}
103+
104+
// ============================================================
105+
// Bridge RecalcAttributes helpers
106+
// ============================================================
107+
108+
// Called when a bridge section overlay is fully removed (OverlayTypeIndex = -1).
109+
static void BridgeCellDestroyed(CellClass* pCell)
110+
{
111+
pCell->RecalcAttributes(KeepCellLevel);
112+
MapClass::Instance.ResetZones(pCell->MapCoords);
113+
MapClass::Instance.RecalculateSubZones(pCell->MapCoords);
114+
}
115+
116+
// Called when a bridge cell's damage state changes (OverlayData only, overlay
117+
// type index still valid).
118+
static void BridgeCellDamaged(CellClass* pCell)
119+
{
120+
pCell->RecalcAttributes(KeepCellLevel);
121+
MapClass::Instance.RecalculateZones(pCell->MapCoords);
122+
MapClass::Instance.RecalculateSubZones(pCell->MapCoords);
123+
}
124+
125+
// ============================================================
126+
// Bridge hooks - Group A: ESI = CellClass*, OverlayTypeIndex = -1 (7 bytes)
127+
// ============================================================
128+
129+
DEFINE_HOOK(0x56EFF2, MapBridge_56EF50_RecalcCell, 0x7)
130+
{
131+
GET(CellClass*, pCell, ESI);
132+
pCell->OverlayTypeIndex = NoOverlay;
133+
BridgeCellDestroyed(pCell);
134+
return 0x56EFF9;
135+
}
136+
137+
DEFINE_HOOK(0x56F392, MapBridge_56F2F0_RecalcCell, 0x7)
138+
{
139+
GET(CellClass*, pCell, ESI);
140+
pCell->OverlayTypeIndex = NoOverlay;
141+
BridgeCellDestroyed(pCell);
142+
return 0x56F399;
143+
}
144+
145+
DEFINE_HOOK(0x56F956, MapBridge_Destroy_56F8B0_RecalcCell, 0x7)
146+
{
147+
GET(CellClass*, pCell, ESI);
148+
pCell->OverlayTypeIndex = NoOverlay;
149+
BridgeCellDestroyed(pCell);
150+
return 0x56F95D;
151+
}
152+
153+
DEFINE_HOOK(0x56FD26, MapBridge_56FCC0_RecalcCell, 0x7)
154+
{
155+
GET(CellClass*, pCell, ESI);
156+
pCell->OverlayTypeIndex = NoOverlay;
157+
BridgeCellDestroyed(pCell);
158+
return 0x56FD2D;
159+
}
160+
161+
DEFINE_HOOK(0x5721C2, MapBridge_572100_RecalcCell_A, 0x7)
162+
{
163+
GET(CellClass*, pCell, ESI);
164+
pCell->OverlayTypeIndex = NoOverlay;
165+
BridgeCellDestroyed(pCell);
166+
return 0x5721C9;
167+
}
168+
169+
DEFINE_HOOK(0x5724E2, MapBridge_572480_RecalcCell, 0x7)
170+
{
171+
GET(CellClass*, pCell, ESI);
172+
pCell->OverlayTypeIndex = NoOverlay;
173+
BridgeCellDestroyed(pCell);
174+
return 0x5724E9;
175+
}
176+
177+
DEFINE_HOOK(0x572882, MapBridge_572820_RecalcCell, 0x7)
178+
{
179+
GET(CellClass*, pCell, ESI);
180+
pCell->OverlayTypeIndex = NoOverlay;
181+
BridgeCellDestroyed(pCell);
182+
return 0x572889;
183+
}
184+
185+
DEFINE_HOOK(0x572E46, MapBridge_572DE0_RecalcCell, 0x7)
186+
{
187+
GET(CellClass*, pCell, ESI);
188+
pCell->OverlayTypeIndex = NoOverlay;
189+
BridgeCellDestroyed(pCell);
190+
return 0x572E4D;
191+
}
192+
193+
DEFINE_HOOK(0x573216, MapBridge_5731B0_RecalcCell, 0x7)
194+
{
195+
GET(CellClass*, pCell, ESI);
196+
pCell->OverlayTypeIndex = NoOverlay;
197+
BridgeCellDestroyed(pCell);
198+
return 0x57321D;
199+
}
200+
201+
DEFINE_HOOK(0x57779F, MapBridge_577740_RecalcCell, 0x7)
202+
{
203+
GET(CellClass*, pCell, ESI);
204+
pCell->OverlayTypeIndex = NoOverlay;
205+
BridgeCellDestroyed(pCell);
206+
return 0x5777A6;
207+
}
208+
209+
DEFINE_HOOK(0x5778BB, MapBridge_577860_RecalcCell, 0x7)
210+
{
211+
GET(CellClass*, pCell, ESI);
212+
pCell->OverlayTypeIndex = NoOverlay;
213+
BridgeCellDestroyed(pCell);
214+
return 0x5778C2;
215+
}
216+
217+
// ============================================================
218+
// Bridge hooks - Group B: EAX = CellClass*, OverlayData change (7 bytes)
219+
// ============================================================
220+
221+
DEFINE_HOOK(0x56F712, MapBridge_56F690_Damaged_EAX_F1, 0x7)
222+
{
223+
GET(CellClass*, pCell, EAX);
224+
pCell->OverlayData = 0xF;
225+
BridgeCellDamaged(pCell);
226+
return 0x56F719;
227+
}
228+
229+
DEFINE_HOOK(0x56F71B, MapBridge_56F690_Damaged_EAX_E1, 0x7)
230+
{
231+
GET(CellClass*, pCell, EAX);
232+
pCell->OverlayData = 0xE;
233+
BridgeCellDamaged(pCell);
234+
return 0x56F722;
235+
}
236+
237+
DEFINE_HOOK(0x56F822, MapBridge_56F7A0_Damaged_EAX_F2, 0x7)
238+
{
239+
GET(CellClass*, pCell, EAX);
240+
pCell->OverlayData = 0xF;
241+
BridgeCellDamaged(pCell);
242+
return 0x56F829;
243+
}
244+
245+
DEFINE_HOOK(0x56F82B, MapBridge_56F7A0_Damaged_EAX_D2, 0x7)
246+
{
247+
GET(CellClass*, pCell, EAX);
248+
pCell->OverlayData = 0xD;
249+
BridgeCellDamaged(pCell);
250+
return 0x56F832;
251+
}
252+
253+
DEFINE_HOOK(0x572C02, MapBridge_572BC0_Damaged_EAX_F3, 0x7)
254+
{
255+
GET(CellClass*, pCell, EAX);
256+
pCell->OverlayData = 0xF;
257+
BridgeCellDamaged(pCell);
258+
return 0x572C09;
259+
}
260+
261+
DEFINE_HOOK(0x572C0B, MapBridge_572BC0_Damaged_EAX_E3, 0x7)
262+
{
263+
GET(CellClass*, pCell, EAX);
264+
pCell->OverlayData = 0xE;
265+
BridgeCellDamaged(pCell);
266+
return 0x572C12;
267+
}
268+
269+
DEFINE_HOOK(0x572D12, MapBridge_572CD0_Damaged_EAX_F4, 0x7)
270+
{
271+
GET(CellClass*, pCell, EAX);
272+
pCell->OverlayData = 0xF;
273+
BridgeCellDamaged(pCell);
274+
return 0x572D19;
275+
}
276+
277+
DEFINE_HOOK(0x572D1B, MapBridge_572CD0_Damaged_EAX_D4, 0x7)
278+
{
279+
GET(CellClass*, pCell, EAX);
280+
pCell->OverlayData = 0xD;
281+
BridgeCellDamaged(pCell);
282+
return 0x572D22;
283+
}
284+
285+
// ============================================================
286+
// Bridge hooks - Group C: ESI = CellClass*, OverlayData change (7 bytes)
287+
// ============================================================
288+
289+
DEFINE_HOOK(0x572101, MapBridge_572100_Damaged_ESI, 0x7)
290+
{
291+
GET(CellClass*, pCell, ESI);
292+
pCell->OverlayData = 0xF;
293+
BridgeCellDamaged(pCell);
294+
return 0x572108;
295+
}
296+
297+
DEFINE_HOOK(0x5777FC, MapBridge_577740_Damaged_ESI, 0x7)
298+
{
299+
GET(CellClass*, pCell, ESI);
300+
pCell->OverlayData = 0xF;
301+
BridgeCellDamaged(pCell);
302+
return 0x577803;
303+
}
304+
305+
// ============================================================
306+
// Bridge hooks - Group D: EBP = CellClass*, OverlayTypeIndex = -1 (7 bytes)
307+
// ============================================================
308+
309+
// VeinholeMonsterClass area bridge cell clear (0x74CBEE).
310+
DEFINE_HOOK(0x74CBEE, VeinholeArea_Bridge_RecalcCell, 0x7)
311+
{
312+
GET(CellClass*, pCell, EBP);
313+
pCell->OverlayTypeIndex = NoOverlay;
314+
BridgeCellDestroyed(pCell);
315+
return 0x74CBF5;
316+
}
317+
318+
// ============================================================
319+
// Bridge hooks - Group E: ESI = CellClass*, OverlayData + OverlayTypeIndex (10 bytes)
320+
// ============================================================
321+
322+
DEFINE_HOOK(0x576721, MapBridge_576200_RecalcCell, 0xA)
323+
{
324+
GET(CellClass*, pCell, ESI);
325+
pCell->OverlayData = 0;
326+
pCell->OverlayTypeIndex = NoOverlay;
327+
BridgeCellDestroyed(pCell);
328+
return 0x57672B;
329+
}
330+
331+
// Fix a desync with invisible objects
332+
// see 1) on https://modenc2.markjfox.net/Reconnection_Error
333+
DEFINE_HOOK(0x70F1E3, TechnoClass_DrawBehind_InvisibleDesyncFix, 0x8)
334+
{
335+
enum { CreateBehindAnim = 0x70F1EB, SkipBehindAnim = 0x70F659 };
336+
337+
GET(VisualType, visual, EAX);
338+
GET(TechnoClass*, pThis, ESI);
339+
340+
if (visual != VisualType::Normal) // original gate: only proceed when visually normal
341+
return SkipBehindAnim;
342+
343+
if (pThis->GetTechnoType()->Invisible) // desync fix: owner-dependent VISUAL_NORMAL
344+
return SkipBehindAnim;
345+
346+
return CreateBehindAnim;
347+
}
348+
349+
// Fixes a desync caused by a check for shrouding at a specific cell
350+
// Sets Session.MPGameMode->SkipCheatCheck() to true
351+
// TODO: Remove when Phobos Release is greater than 0.4.0.2
352+
// as that has a better fix for this.
353+
DEFINE_PATCH(0x5C0E30, 0xB0, 0x01)

0 commit comments

Comments
 (0)