Skip to content

Commit 8353079

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix-lobby-hang
2 parents 0e01df0 + d1c5082 commit 8353079

File tree

3,686 files changed

+31582
-42464
lines changed

Some content is hidden

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

3,686 files changed

+31582
-42464
lines changed

.github/copilot-instructions.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# AI Coding Agent Instructions
2+
3+
## Project Overview
4+
5+
This is the **GeneralsGameCode** project - a community-driven effort to fix and improve the classic RTS games *Command & Conquer: Generals* and *Zero Hour*. The codebase has been modernized from Visual Studio 6/C++98 to Visual Studio 2022/C++20 while maintaining retail compatibility.
6+
7+
## Architecture
8+
9+
### Dual Game Structure
10+
- **Generals/**: Original C&C Generals (v1.08) codebase
11+
- **GeneralsMD/**: Zero Hour expansion (v1.04) codebase - **primary focus**
12+
- **Core/**: Shared game engine and libraries used by both games
13+
14+
### Key Components
15+
- **Core/GameEngine/**: Base game engine with GameClient/GameLogic separation
16+
- **Core/Libraries/**: Internal libraries including WWVegas graphics framework
17+
- **Core/GameEngineDevice/**: Platform-specific rendering (DirectX 8)
18+
- **Core/Tools/**: Development tools (W3DView, texture compression, etc.)
19+
- **Dependencies/**: External dependencies (MaxSDK for VC6, utilities)
20+
21+
## Build System
22+
23+
### CMake Presets (Critical)
24+
- **vc6**: Visual Studio 6 compatible build (retail compatibility required)
25+
- **win32**: Modern Visual Studio 2022 build
26+
- **vc6-debug/vc6-profile**: Debug/profiling variants
27+
- Use `cmake --preset <preset-name>` followed by `cmake --build build/<preset>`
28+
29+
### Build Commands
30+
```bash
31+
# Configure with specific preset
32+
cmake --preset vc6
33+
34+
# Build (from project root)
35+
cmake --build build/vc6
36+
37+
# Build with tools and extras
38+
cmake --build build/vc6 --target <game>_tools <game>_extras
39+
```
40+
41+
### Retail Compatibility
42+
- VC6 builds are required for replay compatibility testing
43+
- Debug builds break retail compatibility
44+
- Use RTS_BUILD_OPTION_DEBUG=OFF for compatibility testing
45+
46+
## Development Workflow
47+
48+
### Code Change Documentation
49+
**Every user-facing change requires TheSuperHackers comment format:**
50+
```cpp
51+
// TheSuperHackers @keyword author DD/MM/YYYY Description
52+
```
53+
54+
Common keywords: `@bugfix`, `@feature`, `@performance`, `@refactor`, `@tweak`, `@build`
55+
56+
### Pull Request Guidelines
57+
- Title format: `type: Description starting with action verb`
58+
- Types: `bugfix:`, `feat:`, `fix:`, `refactor:`, `perf:`, `build:`
59+
- Zero Hour changes take precedence over Generals
60+
- Changes must be identical between both games when applicable
61+
62+
### Code Style
63+
- Maintain consistency with surrounding legacy code
64+
- Prefer C++98 style unless modern features add significant value
65+
- No big refactors mixed with logical changes
66+
- Use present tense in documentation ("Fixes" not "Fixed")
67+
68+
## Testing
69+
70+
### Replay Compatibility Testing
71+
Located in `GeneralsReplays/` - critical for ensuring retail compatibility:
72+
```bash
73+
generalszh.exe -jobs 4 -headless -replay subfolder/*.rep
74+
```
75+
- Requires VC6 optimized build with RTS_BUILD_OPTION_DEBUG=OFF
76+
- Copies replays to `%USERPROFILE%/Documents/Command and Conquer Generals Zero Hour Data/Replays`
77+
- CI automatically tests GeneralsMD builds against known replays
78+
79+
### Build Validation
80+
- CI tests multiple presets: vc6, vc6-profile, vc6-debug, win32 variants
81+
- Path-based change detection triggers relevant builds
82+
- Tools and extras are built with `+t+e` flags
83+
84+
## Common Patterns
85+
86+
### Memory Management
87+
- Manual memory management (delete/delete[]) - this is legacy C++98 code
88+
- STLPort for VC6 compatibility (see `cmake/stlport.cmake`)
89+
90+
### Game Engine Separation
91+
- **GameLogic**: Game state, rules, simulation
92+
- **GameClient**: Rendering, UI, platform-specific code
93+
- Clean separation maintained for potential future networking
94+
95+
### Module Structure
96+
```
97+
Core/
98+
├── GameEngine/Include/Common/ # Shared interfaces
99+
├── GameEngine/Include/GameLogic/ # Game simulation
100+
├── GameEngine/Include/GameClient/ # Rendering/UI
101+
├── Libraries/Include/rts/ # RTS-specific utilities
102+
└── Libraries/Source/WWVegas/ # Graphics framework
103+
```
104+
105+
## External Dependencies
106+
107+
### Required for Building
108+
- **VC6 builds**: Requires MSVC 6.0 toolchain (automated in CI via itsmattkc/MSVC600)
109+
- **Modern builds**: Visual Studio 2022, Ninja generator
110+
- **vcpkg** (optional): zlib, ffmpeg for enhanced builds
111+
112+
### Platform-Specific
113+
- **Windows**: DirectX 8, Miles Sound System, Bink Video
114+
- **Registry detection**: Automatic game install path detection from EA registry keys
115+
116+
## Tools and Utilities
117+
118+
### Development Scripts (`scripts/cpp/`)
119+
- `fixInludesCase.sh`: Fix include case sensitivity
120+
- `refactor_*.py`: Code refactoring utilities
121+
- `remove_trailing_whitespace.py`: Code cleanup
122+
123+
### Build Tools
124+
- W3DView: 3D model viewer
125+
- TextureCompress: Asset optimization
126+
- MapCacheBuilder: Map preprocessing
127+
128+
## Key Files to Understand
129+
- `CMakePresets.json`: All build configurations
130+
- `cmake/config-build.cmake`: Build options and feature flags
131+
- `Core/GameEngine/Include/`: Core engine interfaces
132+
- `**/Code/Main/WinMain.cpp`: Application entry points
133+
- `GeneralsReplays/`: Compatibility test data

.github/workflows/check-replays.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: check-replays
1+
name: Check Replays
22

33
permissions:
44
contents: read
@@ -23,7 +23,7 @@ on:
2323
jobs:
2424
build:
2525
name: ${{ inputs.preset }}
26-
runs-on: windows-latest
26+
runs-on: windows-2022
2727
timeout-minutes: 15
2828
env:
2929
GAME_PATH: C:\GameData
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
name: Weekly Release
2+
3+
permissions:
4+
contents: write
5+
pull-requests: write
6+
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
build_notes:
11+
description: 'Build notes (optional)'
12+
required: false
13+
default: ''
14+
type: string
15+
known_issues:
16+
description: 'Known issues (optional)'
17+
required: false
18+
default: ''
19+
type: string
20+
force_changed:
21+
description: 'Force build'
22+
required: false
23+
default: 'false'
24+
type: choice
25+
options:
26+
- 'false'
27+
- 'true'
28+
pre-release:
29+
description: 'Mark release as pre-release'
30+
required: false
31+
default: 'false'
32+
type: choice
33+
options:
34+
- 'false'
35+
- 'true'
36+
37+
schedule:
38+
- cron: '0 9 * * 5'
39+
40+
concurrency:
41+
group: ${{ github.workflow }}-${{ github.ref }}
42+
cancel-in-progress: true
43+
44+
jobs:
45+
get-date:
46+
runs-on: ubuntu-latest
47+
outputs:
48+
date: ${{ steps.date.outputs.date }}
49+
steps:
50+
- name: Get current date
51+
id: date
52+
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
53+
54+
detect-scm-changes:
55+
needs: [get-date]
56+
runs-on: ubuntu-latest
57+
outputs:
58+
changed: ${{ steps.check.outputs.changed }}
59+
steps:
60+
- uses: actions/checkout@v4
61+
with:
62+
fetch-depth: 0
63+
fetch-tags: true
64+
- id: check
65+
run: |
66+
if [ "${{ github.event.inputs.force_changed }}" = "true" ]; then
67+
echo "changed=true" >> $GITHUB_OUTPUT
68+
exit 0
69+
fi
70+
71+
echo LAST TAG:
72+
git describe --tags --abbrev=0 2>/dev/null || echo ""
73+
74+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
75+
if [ -z "$LAST_TAG" ]; then
76+
echo "changed=true" >> $GITHUB_OUTPUT
77+
exit 0
78+
fi
79+
CHANGED=$(git diff --name-only $LAST_TAG..HEAD | grep -v '.github/workflows/' | wc -l)
80+
if [ "$CHANGED" -eq "0" ]; then
81+
echo "changed=false" >> $GITHUB_OUTPUT
82+
else
83+
echo "changed=true" >> $GITHUB_OUTPUT
84+
fi
85+
86+
build-generals:
87+
needs: [detect-scm-changes, get-date]
88+
if: needs.detect-scm-changes.outputs.changed == 'true'
89+
name: Build Generals${{ matrix.preset && '' }}
90+
strategy:
91+
matrix:
92+
include:
93+
- preset: "vc6"
94+
tools: true
95+
extras: false
96+
release: true
97+
fail-fast: false
98+
uses: ./.github/workflows/build-toolchain.yml
99+
with:
100+
game: "Generals"
101+
preset: ${{ matrix.preset }}
102+
tools: ${{ matrix.tools }}
103+
extras: ${{ matrix.extras }}
104+
secrets: inherit
105+
106+
build-generalsmd:
107+
needs: [detect-scm-changes, get-date]
108+
if: needs.detect-scm-changes.outputs.changed == 'true'
109+
name: Build GeneralsMD${{ matrix.preset && '' }}
110+
strategy:
111+
matrix:
112+
include:
113+
- preset: "vc6"
114+
tools: true
115+
extras: false
116+
release: true
117+
fail-fast: false
118+
uses: ./.github/workflows/build-toolchain.yml
119+
with:
120+
game: "GeneralsMD"
121+
preset: ${{ matrix.preset }}
122+
tools: ${{ matrix.tools }}
123+
extras: ${{ matrix.extras }}
124+
secrets: inherit
125+
126+
create-release:
127+
name: Create Release
128+
needs: [build-generals, build-generalsmd, get-date]
129+
runs-on: ubuntu-latest
130+
steps:
131+
- name: Checkout repository
132+
uses: actions/checkout@v4
133+
with:
134+
fetch-depth: 0
135+
fetch-tags: true
136+
137+
- name: Collect commits since last release
138+
id: changelog
139+
run: |
140+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
141+
if [ -z "$LAST_TAG" ]; then
142+
CHANGELOG_COMMITS=$(git log --pretty="format:- %s" --no-merges HEAD | head -n 10 || true)
143+
else
144+
CHANGELOG_COMMITS=$(git log --pretty="format:- %s" --no-merges "$LAST_TAG"..HEAD || true)
145+
fi
146+
if [ -z "$CHANGELOG_COMMITS" ]; then
147+
CHANGELOG_COMMITS="- No relevant changes detected since the last release."
148+
fi
149+
{
150+
echo 'commits<<CHANGELOG_EOF'
151+
echo "$CHANGELOG_COMMITS"
152+
echo 'CHANGELOG_EOF'
153+
} >> "$GITHUB_OUTPUT"
154+
155+
# Generals vc6
156+
- name: Download Generals VC6 Artifacts
157+
uses: actions/download-artifact@v4
158+
with:
159+
name: Generals-vc6+t
160+
path: generals-vc6-artifacts
161+
162+
- name: Prepare and Zip Generals VC6
163+
run: |
164+
zip -jr generals-weekly-${{ needs.get-date.outputs.date }}.zip generals-vc6-artifacts/*
165+
166+
# GeneralsMD vc6
167+
- name: Download GeneralsMD VC6 Artifacts
168+
uses: actions/download-artifact@v4
169+
with:
170+
name: GeneralsMD-vc6+t
171+
path: generalsmd-vc6-artifacts
172+
173+
- name: Prepare and Zip GeneralsMD VC6
174+
run: |
175+
zip -jr generalszh-weekly-${{ needs.get-date.outputs.date }}.zip generalsmd-vc6-artifacts/*
176+
177+
- name: Generate release notes
178+
id: release_body
179+
run: |
180+
BODY=""
181+
if [ "${{ github.event.inputs.build_notes }}" != "" ]; then
182+
BODY="${BODY}### Build notes\n${{ github.event.inputs.build_notes }}\n"
183+
fi
184+
if [ "${{ github.event.inputs.known_issues }}" != "" ]; then
185+
BODY="${BODY}### Known issues\n${{ github.event.inputs.known_issues }}\n"
186+
fi
187+
BODY="${BODY}### Changelog\n${{ steps.changelog.outputs.commits }}"
188+
echo "body<<EOF" >> $GITHUB_OUTPUT
189+
echo -e "$BODY" >> $GITHUB_OUTPUT
190+
echo "EOF" >> $GITHUB_OUTPUT
191+
192+
- name: Create GitHub Release
193+
uses: softprops/action-gh-release@v2
194+
with:
195+
tag_name: weekly-${{ needs.get-date.outputs.date }}
196+
name: weekly-${{ needs.get-date.outputs.date }}
197+
prerelease: ${{ github.event.inputs.pre-release == 'true' }}
198+
body: ${{ steps.release_body.outputs.body }}
199+
files: |
200+
generals-weekly-${{ needs.get-date.outputs.date }}.zip
201+
generalszh-weekly-${{ needs.get-date.outputs.date }}.zip
202+
env:
203+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
204+
205+
- name: Clean up release folders
206+
if: always()
207+
run: |
208+
rm -rf generals-vc6-artifacts generalsmd-vc6-artifacts
209+
rm -f generals-weekly-${{ needs.get-date.outputs.date }}.zip
210+
rm -f generalszh-weekly-${{ needs.get-date.outputs.date }}.zip

Core/GameEngine/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ set(GAMEENGINE_SRC
3939
# Include/Common/Errors.h
4040
Include/Common/file.h
4141
Include/Common/FileSystem.h
42+
Include/Common/FramePacer.h
4243
Include/Common/FrameRateLimit.h
4344
# Include/Common/FunctionLexicon.h
4445
Include/Common/GameAudio.h
@@ -53,6 +54,7 @@ set(GAMEENGINE_SRC
5354
# Include/Common/GameState.h
5455
# Include/Common/GameStateMap.h
5556
# Include/Common/GameType.h
57+
Include/Common/GameUtility.h
5658
# Include/Common/Geometry.h
5759
# Include/Common/GlobalData.h
5860
# Include/Common/Handicap.h
@@ -113,7 +115,6 @@ set(GAMEENGINE_SRC
113115
# Include/Common/StateMachine.h
114116
# Include/Common/StatsCollector.h
115117
# Include/Common/STLTypedefs.h
116-
Include/Common/STLUtils.h
117118
Include/Common/StreamingArchiveFile.h
118119
# Include/Common/SubsystemInterface.h
119120
# Include/Common/SystemInfo.h
@@ -570,10 +571,12 @@ set(GAMEENGINE_SRC
570571
# Source/Common/DamageFX.cpp
571572
# Source/Common/Dict.cpp
572573
# Source/Common/DiscreteCircle.cpp
574+
Source/Common/FramePacer.cpp
573575
Source/Common/FrameRateLimit.cpp
574576
# Source/Common/GameEngine.cpp
575577
# Source/Common/GameLOD.cpp
576578
# Source/Common/GameMain.cpp
579+
Source/Common/GameUtility.cpp
577580
# Source/Common/GlobalData.cpp
578581
# Source/Common/INI/INI.cpp
579582
# Source/Common/INI/INIAiData.cpp

0 commit comments

Comments
 (0)