Compare commits

...

11 commits

Author SHA1 Message Date
Ralph Sennhauser
740bef981e
Unbrick linux ci 2026-08-03 20:34:03 +02:00
Ralph Sennhauser
a95f274faf
Use dynamic events for Cinema Section
Replace static events with dynamic events and name delete path function
more aptly.

Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
2026-08-03 20:14:28 +02:00
phosit
7aa756f6a4
Remove line numbers from cppcheck supression list
Some checks are pending
checkrefs / lfscheck (push) Waiting to run
checkrefs / checkrefs (push) Waiting to run
lint / cppcheck (push) Waiting to run
lint / copyright (push) Waiting to run
lint / jenkinsfiles (push) Waiting to run
pre-commit / build (push) Waiting to run
They have to be changed to ofthen when code gets moved.
2026-08-02 14:03:28 +02:00
phosit
e712601602
PreInitGame and InitGame in the Loader
This makes the frame before the simulation starts less stuttery. Doing
it in the Loader also allows to make the progressbar more acurate.
2026-08-02 14:03:28 +02:00
Dunedan
865ca4cd55
Update the pre-commit hooks
Some checks failed
checkrefs / lfscheck (push) Has been cancelled
checkrefs / checkrefs (push) Has been cancelled
lint / cppcheck (push) Has been cancelled
lint / copyright (push) Has been cancelled
lint / jenkinsfiles (push) Has been cancelled
pre-commit / build (push) Has been cancelled
2026-08-01 16:19:18 +02:00
Dunedan
dcb7cef308
Update Github actions used for pre-commit action 2026-08-01 16:19:03 +02:00
vyordan
71be79791f Use turn_id_t (int32_t) for turn-count across simulation, network and replay
Some checks failed
checkrefs / lfscheck (push) Has been cancelled
checkrefs / checkrefs (push) Has been cancelled
lint / cppcheck (push) Has been cancelled
lint / copyright (push) Has been cancelled
lint / jenkinsfiles (push) Has been cancelled
pre-commit / build (push) Has been cancelled
CSimulation2 used , CSimulation2Impl mixed /, and
CTurnManager used  for the same turn-count concept, forcing a
static_cast<int64_t> at the one place these types already met
(rejoin-test comparison).

Add  (alias for std::int32_t) in SimulationCommand.h and use
it consistently for every turn counter in CSimulation2/Impl,
CTurnManager and its subclasses (CLocalTurnManager,
CReplayTurnManager), CNetServerTurnManager/CNetClientTurnManager, and
the network wire format (m_Turn in CEndCommandBatchMessage,
CSimulationMessage, CSyncCheckMessage, CSyncErrorMessage, and
m_CurrentTurn in CLoadedGameMessage).

Turn *duration* fields (m_TurnLength, m_CommandDelay,
DEFAULT_TURN_LENGTH, COMMAND_DELAY_SP/MP, SetTurnLength,
GetSavedTurnLength return type) are left as u32 — they're milliseconds,
not a counter, and out of scope here.

Remaining turn_id_t vs size_t comparisons use std::cmp_equal or
explicit casts, matching the existing pattern in Simulation2.cpp.

Fixes #8718
2026-07-31 10:34:20 +02:00
Atrik
9ab89b01c2 Add Langbart to art credits
Some checks are pending
checkrefs / lfscheck (push) Waiting to run
checkrefs / checkrefs (push) Waiting to run
lint / cppcheck (push) Waiting to run
lint / copyright (push) Waiting to run
lint / jenkinsfiles (push) Waiting to run
pre-commit / build (push) Waiting to run
2026-07-30 20:54:19 +02:00
Atrik
dcce6666ae Add missing garrison flag to some houses
Art work by @Langbart

Fixes #6911
2026-07-30 20:54:19 +02:00
Atrik
202bdb133a Add Atrik to art credits 2026-07-30 20:54:19 +02:00
Atrik
46981d2b90 Add missing garrison flag to German houses 2026-07-30 20:54:19 +02:00
63 changed files with 305 additions and 214 deletions

View file

@ -7,17 +7,17 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v7
- uses: actions/setup-python@v5 - uses: actions/setup-python@v7
with: with:
python-version: "3.11" python-version: "3.11"
- id: restore-pip-cache - id: restore-pip-cache
uses: actions/cache/restore@v4 uses: actions/cache/restore@v6
with: with:
key: pip-cache-v1-${{ github.workflow }} key: pip-cache-v1-${{ github.workflow }}
path: ~/.cache/pip path: ~/.cache/pip
- uses: pre-commit/action@v3.0.1 - uses: pre-commit/action@v3.0.1
- uses: actions/cache/save@v4 - uses: actions/cache/save@v6
if: steps.restore-pip-cache.outcome == 'success' if: steps.restore-pip-cache.outcome == 'success'
with: with:
key: pip-cache-v1-${{ github.workflow }} key: pip-cache-v1-${{ github.workflow }}

View file

@ -34,7 +34,7 @@ repos:
\.patch$ \.patch$
) )
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.9 rev: v0.16.0
hooks: hooks:
- id: ruff-check - id: ruff-check
args: args:
@ -56,7 +56,7 @@ repos:
files: ^binaries/ files: ^binaries/
exclude: (^binaries/data/mods/(mod|public)/art/.*\.xml|\.dae$) exclude: (^binaries/data/mods/(mod|public)/art/.*\.xml|\.dae$)
- repo: https://github.com/scop/pre-commit-shfmt - repo: https://github.com/scop/pre-commit-shfmt
rev: v3.12.0-2 rev: v3.13.1-1
hooks: hooks:
- id: shfmt - id: shfmt
args: args:
@ -67,7 +67,7 @@ repos:
hooks: hooks:
- id: shellcheck - id: shellcheck
- repo: https://github.com/igorshubovych/markdownlint-cli - repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.45.0 rev: v0.47.0
hooks: hooks:
- id: markdownlint - id: markdownlint
language_version: 22.14.0 language_version: 22.14.0
@ -77,13 +77,13 @@ repos:
^source/third_party/ ^source/third_party/
) )
- repo: https://github.com/adrienverge/yamllint - repo: https://github.com/adrienverge/yamllint
rev: v1.37.1 rev: v1.38.0
hooks: hooks:
- id: yamllint - id: yamllint
args: args:
- --strict - --strict
- repo: https://github.com/eslint/eslint - repo: https://github.com/eslint/eslint
rev: v9.39.2 rev: v10.8.0
hooks: hooks:
- id: eslint - id: eslint
language_version: 22.14.0 language_version: 22.14.0

View file

@ -51,6 +51,18 @@
</textures> </textures>
</variant> </variant>
</group> </group>
<group>
<variant name="ungarrisoned" frequency="1" />
<variant name="garrisoned">
<props>
<prop
actor="props/special/common/garrison_flag_achaemenids.xml"
attachpoint="garrisoned"
selectable="false"
/>
</props>
</variant>
</group>
<group> <group>
<variant name="alive" frequency="1"/> <variant name="alive" frequency="1"/>
<variant file="structures/destruction_small.xml"/> <variant file="structures/destruction_small.xml"/>

View file

@ -33,6 +33,18 @@
</textures> </textures>
</variant> </variant>
</group> </group>
<group>
<variant frequency="1" name="ungarrisoned" />
<variant name="garrisoned">
<props>
<prop
actor="props/special/common/garrison_flag_celt.xml"
attachpoint="garrisoned"
selectable="false"
/>
</props>
</variant>
</group>
<group> <group>
<variant name="alive" frequency="1"/> <variant name="alive" frequency="1"/>
<variant file="structures/destruction_small.xml"/> <variant file="structures/destruction_small.xml"/>

View file

@ -53,6 +53,18 @@
</textures> </textures>
</variant> </variant>
</group> </group>
<group>
<variant name="ungarrisoned" frequency="1" />
<variant name="garrisoned">
<props>
<prop
actor="props/special/common/garrison_flag_celt.xml"
attachpoint="garrisoned"
selectable="false"
/>
</props>
</variant>
</group>
<group> <group>
<variant name="alive" frequency="1"/> <variant name="alive" frequency="1"/>
<variant file="structures/destruction_small.xml"/> <variant file="structures/destruction_small.xml"/>

View file

@ -134,6 +134,20 @@
</props> </props>
</variant> </variant>
</group> </group>
<group>
<variant frequency="1" name="ungarrisoned" />
<variant name="garrisoned">
<props>
<prop
actor="props/special/common/garrison_flag_germ.xml"
attachpoint="garrisoned"
selectable="false"
/>
</props>
</variant>
</group>
<group> <group>
<variant name="alive" frequency="1"/> <variant name="alive" frequency="1"/>
<variant file="structures/destruction_small.xml"/> <variant file="structures/destruction_small.xml"/>

View file

@ -106,6 +106,18 @@
</props> </props>
</variant> </variant>
</group> </group>
<group>
<variant name="ungarrisoned" frequency="1" />
<variant name="garrisoned">
<props>
<prop
actor="props/special/common/garrison_flag_iber.xml"
attachpoint="garrisoned"
selectable="false"
/>
</props>
</variant>
</group>
<group> <group>
<variant name="alive" frequency="1"/> <variant name="alive" frequency="1"/>
<variant file="structures/destruction_small.xml"/> <variant file="structures/destruction_small.xml"/>

View file

@ -47,6 +47,18 @@
</textures> </textures>
</variant> </variant>
</group> </group>
<group>
<variant name="ungarrisoned" frequency="1" />
<variant name="garrisoned">
<props>
<prop
actor="props/special/common/garrison_flag_rome.xml"
attachpoint="garrisoned"
selectable="false"
/>
</props>
</variant>
</group>
<group> <group>
<variant name="alive" frequency="1"/> <variant name="alive" frequency="1"/>
<variant file="structures/destruction_small.xml"/> <variant file="structures/destruction_small.xml"/>

View file

@ -43,6 +43,18 @@
</textures> </textures>
</variant> </variant>
</group> </group>
<group>
<variant frequency="1" name="ungarrisoned" />
<variant name="garrisoned">
<props>
<prop
actor="props/special/common/garrison_flag_seleucids.xml"
attachpoint="garrisoned"
selectable="false"
/>
</props>
</variant>
</group>
<group> <group>
<variant name="alive" frequency="1"/> <variant name="alive" frequency="1"/>
<variant file="structures/sele/light_damage.xml"/> <variant file="structures/sele/light_damage.xml"/>

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:995da95e21261ae0a2718baee69d76ae5f6823d21eaefaa708a89651f2099bb0 oid sha256:ed243a229225ec51ee090588a63307cbd2efedecca06f4f1a0922a4d51ee3967
size 586243 size 586427

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d8e022a3ac132a32061583743d1322fc8792cdd6e7be57857547a8ed3cefbed6 oid sha256:3ce649aa943771357d7c19f48294af719c50ca14178c32be60992f17845d685d
size 525300 size 525483

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:21971f20f5567f820e0f3fffdd21015e2a5ffaee309be7b6efc38d3fc56aecd3 oid sha256:d1d867c2e9f3db233847da547c466325ba9f0a9e7478175281f3538cbb694709
size 503936 size 504112

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:3c37b1b7f12a429cd07873d0fdb7cc8692f6578b3e5320a6be0e68420fb4688e oid sha256:cc9c95992889a4c2930a39bd20f7c86b73f7c7ddb3b36492650afae2ae424641
size 780868 size 781051

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:1230a92373c4e4684f791594efb35bb7d6c091377a11bd8f0daafd13816d04ec oid sha256:6779d6ca2067d6dd5594b3d2e5ef82d08de97ad058ce7084eb0bf8ae3290f4c2
size 56755 size 56953

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:30a395c3b5a50214a689abe968ad15edb60212c3ea14041a2edc07295a4462c6 oid sha256:9e6aca91d66df31b5820f39025c57937ce6a1593199b8a068722fdec5b0675f6
size 45317 size 45523

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ce554ad452cf75bd4dbb9da5de9830309a1dccf72f72f6a74475078af7f0e1c4 oid sha256:07013561c7e46b020acbd03d4f371300c38d3e364c99a1e7bbcf7467aa8a34e6
size 42851 size 43041

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:60f0614c1092395065c4ba8fe6bed64da873d38c579c9cbc400e9a9b8f30f3a8 oid sha256:1416efa429cd64471d50e969c43ee723382c031c676414433f8fcd58176cfa77
size 272099 size 272274

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c6e7d6264d7b3e03466bf1de58ab366a023120c6f78142aebc6570218a4803d9 oid sha256:9f9e6debd5f2a9659e19e74b5826d3488167edb9a897ae1ed6e4db10742d94e8
size 274339 size 274543

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:0624fc07116aa70c14527457fced3afcf7b36df1535bbf10250455e6254aeed6 oid sha256:091d68b7800d9141e87ba8aaf0fa93c5bc7d260dd459774075de610ca34855e2
size 372821 size 372997

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:6ef199b28b9d9ef19e0817b189d9e6f1e2f9861ca211890b8d714f9fe05f5c61 oid sha256:c1413de5371443f1249d87fa40a821c880c749513a0347d9128a99472bec7b33
size 420601 size 420808

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:adf219c08e65bd7a149726d325b6f2b4fd67effc3dd1bc211c3163774ac8308e oid sha256:8aba3f3dc5a909ddadd9af5e7abfe9094f55ed6c97b39e7f97b09dc1cb951f4d
size 201143 size 201315

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:f8c688df60c4880dc92b6898728d4cef443c86831a2a2e87e61dd44661b58240 oid sha256:0c3298efebf687a07d4693d9014c51e1e870e95fb9d9024c9126b40adfec75a5
size 205000 size 205172

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9d86be00b7e911108e6666422a56eb061a996e6cf0bfe489d6c99f84be752444 oid sha256:b97f6dff13f4639e746e1ce79c120169bccb2e09d4507dc900075cf85e788590
size 216525 size 216697

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:5e258d0b3a7b5c3fe2275c2816c636b7478ee1051df31a2cbe2f516465c692f9 oid sha256:8d764a2d5a712f53f08560504f6841ed00b5268fea2a283d2d5254d57ce1df76
size 239166 size 239339

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:82ff53473c25d6e17aeb97f795b543df3ec1c423e8ecb8398d0d014beac57f4e oid sha256:dccc1521e6e626e75ac2f05d2c8b2c2963ee751da08a9e819e4c28954b19f01a
size 101895 size 102099

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2377bddedc6251acbe8f03bf1820d368b21cbf88fc2783695ff31036af8ed4e3 oid sha256:ae1042461639bca3d3d73fb3e723ca404c8a897ee0e2ad417af1eebb5849d23f
size 82812 size 83027

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:b5f1530adecb42af05b6b81719a5c4d00fa06a5eff33f50eda8312ce2e0fcd5f oid sha256:b71b8848a0a2e503adbff9c1e09c06c1e76e16537f38af5b35395a04f11b365a
size 77866 size 78066

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c393e007e24473c1332c7d0c3a8093e0b542974da00b51b853ee413e45818ade oid sha256:256b3864edc754955814d5eec113e73fded49525d2e855a1171a4e4b495ffcc4
size 91109 size 91304

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:e8fb0f356f48284704d81b601c7947f32e91995757e8c2fc390ecaf772fbd950 oid sha256:4ef0415cce15f4d1ff4e342c23a995ee5081d21b7f84eb6c19fb89c1d63f84a7
size 84604 size 84789

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:7c2b0d0a461535e0802d2a07395afb4546515ef4d3ccc3eab18d55a0993bc165 oid sha256:2ab540ad518de662ba9a4dbe4d0d0590b6ee0b771bc482073296c949ee33d571
size 184407 size 184610

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:828e2e63b9686a601b3641be10f26e3b099ced5b8dc8d453ab94d41270f22b39 oid sha256:39a6a575b8bbb0f82ed8cf4056e0df932a59fd6e3343005625b3b76644c6879c
size 213385 size 213561

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:4825c81a245ddae5637f3b2dee78f7749e27a48d2efa244bc95af075ddabcd1a oid sha256:93341f17ad185e28ce62a533f1db457aec4b69bb2f67c98e4b89e4bcf9503b6a
size 144598 size 144784

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:427f9512af696afaae0721374a86ceb0e1432602013cb3dde818bfcd2791c163 oid sha256:dc27dba302816891bb8f33bc65d9d68b2b330f946256aa966e5d684714af9fe2
size 212264 size 212447

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:19ca2828e5e6b99065d3d901c1a90f1b73df065dc94b3a4b76b4e5804890c137 oid sha256:270fe9095c3894fb448cb91668ad48d886088006c689f0efa148e8a78a3de043
size 349847 size 350053

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:57039fff36fbba71de79b89614fea988a98bdc85b2bdc5ef88ce3a632da95a55 oid sha256:977b2ffb5dc9cf0364d125f0b3dd17e1fb0cd482b5c96fbf7c61b6e6a030db86
size 340693 size 340906

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:221eeca4ee2e46023d346e80c7a3b4b3852699e0c193e31fa58e0619e858f7a2 oid sha256:f177172ef923455050064af60377f2e1b171ff4ec72fa9aa5fdc5c4c28000550
size 272252 size 272435

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:afc01d8e7e7efc988b2f78b58cfb6b48b5453b8786609e9ce5db38bc617a7984 oid sha256:acdaaf09848dc5cb7cf207c7bb535865aa4199a5a50609ca3b632ce5d88d95a2
size 279942 size 280139

View file

@ -72,6 +72,7 @@
"List": [ "List": [
{ "nick": "Alexandermb", "name": "Daniel Morgado" }, { "nick": "Alexandermb", "name": "Daniel Morgado" },
{ "nick": "Athos" }, { "nick": "Athos" },
{ "nick": "Atrik" },
{ "nick": "Basshunter", "name": "Sebastián Gómez" }, { "nick": "Basshunter", "name": "Sebastián Gómez" },
{ "nick": "BigTiger", "name": "Nathanael P. Moore" }, { "nick": "BigTiger", "name": "Nathanael P. Moore" },
{ "nick": "Brightgalrs", "name": "Robert D. Schultz" }, { "nick": "Brightgalrs", "name": "Robert D. Schultz" },
@ -80,6 +81,7 @@
{ "nick": "Enrique", "name": "Enrique Keykens Espolio" }, { "nick": "Enrique", "name": "Enrique Keykens Espolio" },
{ "nick": "Gen.Kenobi", "name": "Daniel Schubert" }, { "nick": "Gen.Kenobi", "name": "Daniel Schubert" },
{ "nick": "Historicity", "name": "Shane" }, { "nick": "Historicity", "name": "Shane" },
{ "nick": "Langbart" },
{ "nick": "Leyto", "name": "Valentin Levalet" }, { "nick": "Leyto", "name": "Valentin Levalet" },
{ "nick": "LordGood", "name": "Johnathan B. Good" }, { "nick": "LordGood", "name": "Johnathan B. Good" },
{ "nick": "m7600" }, { "nick": "m7600" },

View file

@ -9,7 +9,7 @@ cd "$(dirname "$0")"
PV=128.13.0 PV=128.13.0
FOLDER="mozjs-${PV}" FOLDER="mozjs-${PV}"
# If same-version changes are needed, increment this. # If same-version changes are needed, increment this.
LIB_VERSION="${PV}+wfg5" LIB_VERSION="${PV}+wfg6"
LIB_NAME="mozjs128" LIB_NAME="mozjs128"
build_archive() build_archive()

View file

@ -33,6 +33,7 @@ ignore = [
"ANN", "ANN",
"C90", "C90",
"COM812", "COM812",
"CPY001",
"D10", "D10",
"EM", "EM",
"FIX002", "FIX002",

View file

@ -63,7 +63,7 @@ void CNetClientTurnManager::PostCommand(JS::HandleValue data)
// TODO: we should do this when the server stops sending our commands back to us // TODO: we should do this when the server stops sending our commands back to us
} }
void CNetClientTurnManager::NotifyFinishedOwnCommands(u32 turn) void CNetClientTurnManager::NotifyFinishedOwnCommands(turn_id_t turn)
{ {
NETCLIENTTURN_LOG("NotifyFinishedOwnCommands(%d)\n", turn); NETCLIENTTURN_LOG("NotifyFinishedOwnCommands(%d)\n", turn);
@ -79,7 +79,7 @@ void CNetClientTurnManager::NotifyFinishedOwnCommands(u32 turn)
m_NetClient.SendMessage(&msg); m_NetClient.SendMessage(&msg);
} }
void CNetClientTurnManager::NotifyFinishedUpdate(u32 turn, const UpdateCallback&) void CNetClientTurnManager::NotifyFinishedUpdate(turn_id_t turn, const UpdateCallback&)
{ {
bool quick = !TurnNeedsFullHash(turn); bool quick = !TurnNeedsFullHash(turn);
std::string hash; std::string hash;
@ -110,10 +110,10 @@ void CNetClientTurnManager::OnDestroyConnection()
void CNetClientTurnManager::OnSimulationMessage(CSimulationMessage* msg) void CNetClientTurnManager::OnSimulationMessage(CSimulationMessage* msg)
{ {
// Command received from the server - store it for later execution // Command received from the server - store it for later execution
AddCommand(msg->m_Client, msg->m_Player, msg->m_Data, msg->m_Turn); AddCommand(msg->m_Client, msg->m_Player, msg->m_Data, static_cast<turn_id_t>(msg->m_Turn));
} }
void CNetClientTurnManager::OnSyncError(u32 turn, const CStr& expectedHash, const std::vector<CSyncErrorMessage::S_m_PlayerNames>& playerNames) void CNetClientTurnManager::OnSyncError(turn_id_t turn, const CStr& expectedHash, const std::vector<CSyncErrorMessage::S_m_PlayerNames>& playerNames)
{ {
CStr expectedHashHex(Hexify(expectedHash)); CStr expectedHashHex(Hexify(expectedHash));
NETCLIENTTURN_LOG("OnSyncError(%d, %hs)\n", turn, expectedHashHex.c_str()); NETCLIENTTURN_LOG("OnSyncError(%d, %hs)\n", turn, expectedHashHex.c_str());

View file

@ -49,12 +49,12 @@ public:
*/ */
void OnDestroyConnection(); void OnDestroyConnection();
void OnSyncError(u32 turn, const CStr& expectedHash, const std::vector<CSyncErrorMessage::S_m_PlayerNames>& playerNames); void OnSyncError(turn_id_t turn, const CStr& expectedHash, const std::vector<CSyncErrorMessage::S_m_PlayerNames>& playerNames);
private: private:
void NotifyFinishedOwnCommands(u32 turn) override; void NotifyFinishedOwnCommands(turn_id_t turn) override;
void NotifyFinishedUpdate(u32 turn, const UpdateCallback& sendEventToAll) override; void NotifyFinishedUpdate(turn_id_t turn, const UpdateCallback& sendEventToAll) override;
CNetClient& m_NetClient; CNetClient& m_NetClient;
}; };

View file

@ -139,7 +139,7 @@ public:
u32 m_Client; u32 m_Client;
i32 m_Player; i32 m_Player;
u32 m_Turn; i32 m_Turn;
JS::PersistentRooted<JS::Value> m_Data; JS::PersistentRooted<JS::Value> m_Data;
private: private:
const Script::Interface& m_ScriptInterface; const Script::Interface& m_ScriptInterface;

View file

@ -150,7 +150,7 @@ u8* CSimulationMessage::Serialize(u8* pBuffer) const
CBufferBinarySerializer serializer(m_ScriptInterface, pos); CBufferBinarySerializer serializer(m_ScriptInterface, pos);
serializer.NumberU32_Unbounded("client", m_Client); serializer.NumberU32_Unbounded("client", m_Client);
serializer.NumberI32_Unbounded("player", m_Player); serializer.NumberI32_Unbounded("player", m_Player);
serializer.NumberU32_Unbounded("turn", m_Turn); serializer.NumberI32_Unbounded("turn", m_Turn);
serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data)); serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
return serializer.GetBuffer(); return serializer.GetBuffer();
@ -165,7 +165,7 @@ const u8* CSimulationMessage::Deserialize(const u8* pStart, const u8* pEnd)
CStdDeserializer deserializer(m_ScriptInterface, stream); CStdDeserializer deserializer(m_ScriptInterface, stream);
deserializer.NumberU32_Unbounded("client", m_Client); deserializer.NumberU32_Unbounded("client", m_Client);
deserializer.NumberI32_Unbounded("player", m_Player); deserializer.NumberI32_Unbounded("player", m_Player);
deserializer.NumberU32_Unbounded("turn", m_Turn); deserializer.NumberI32_Unbounded("turn", m_Turn);
deserializer.ScriptVal("command", &m_Data); deserializer.ScriptVal("command", &m_Data);
return pEnd; return pEnd;
} }
@ -177,7 +177,7 @@ size_t CSimulationMessage::GetSerializedLength() const
CLengthBinarySerializer serializer(m_ScriptInterface); CLengthBinarySerializer serializer(m_ScriptInterface);
serializer.NumberU32_Unbounded("client", m_Client); serializer.NumberU32_Unbounded("client", m_Client);
serializer.NumberI32_Unbounded("player", m_Player); serializer.NumberI32_Unbounded("player", m_Player);
serializer.NumberU32_Unbounded("turn", m_Turn); serializer.NumberI32_Unbounded("turn", m_Turn);
// TODO: The cast can probably be removed if and when ScriptVal can take a JS::HandleValue instead of // TODO: The cast can probably be removed if and when ScriptVal can take a JS::HandleValue instead of
// a JS::MutableHandleValue (relies on JSAPI change). Also search for other casts like this one in that case. // a JS::MutableHandleValue (relies on JSAPI change). Also search for other casts like this one in that case.

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games. /* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -27,7 +27,7 @@
#define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?' #define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?'
#define PS_PROTOCOL_MAGIC_RESPONSE 0x50630121 // 'P', 'c', 0x01, '!' #define PS_PROTOCOL_MAGIC_RESPONSE 0x50630121 // 'P', 'c', 0x01, '!'
#define PS_PROTOCOL_VERSION 0x01010019 // Arbitrary protocol #define PS_PROTOCOL_VERSION 0x0101001A // Arbitrary protocol
#define PS_DEFAULT_PORT 0x5073 // 'P', 's' #define PS_DEFAULT_PORT 0x5073 // 'P', 's'
// Set when lobby authentication is required. Used in the SrvHandshakeResponseMessage. // Set when lobby authentication is required. Used in the SrvHandshakeResponseMessage.
@ -231,7 +231,7 @@ START_NMT_CLASS_(ClientPaused, NMT_CLIENT_PAUSED)
END_NMT_CLASS() END_NMT_CLASS()
START_NMT_CLASS_(LoadedGame, NMT_LOADED_GAME) START_NMT_CLASS_(LoadedGame, NMT_LOADED_GAME)
NMT_FIELD_INT(m_CurrentTurn, u32, 4) NMT_FIELD_INT(m_CurrentTurn, i32, 4)
END_NMT_CLASS() END_NMT_CLASS()
START_NMT_CLASS_(GameStart, NMT_GAME_START) START_NMT_CLASS_(GameStart, NMT_GAME_START)
@ -243,17 +243,17 @@ START_NMT_CLASS_(GameSavedStart, NMT_SAVED_GAME_START)
END_NMT_CLASS() END_NMT_CLASS()
START_NMT_CLASS_(EndCommandBatch, NMT_END_COMMAND_BATCH) START_NMT_CLASS_(EndCommandBatch, NMT_END_COMMAND_BATCH)
NMT_FIELD_INT(m_Turn, u32, 4) NMT_FIELD_INT(m_Turn, i32, 4)
NMT_FIELD_INT(m_TurnLength, u32, 2) NMT_FIELD_INT(m_TurnLength, u32, 2)
END_NMT_CLASS() END_NMT_CLASS()
START_NMT_CLASS_(SyncCheck, NMT_SYNC_CHECK) START_NMT_CLASS_(SyncCheck, NMT_SYNC_CHECK)
NMT_FIELD_INT(m_Turn, u32, 4) NMT_FIELD_INT(m_Turn, i32, 4)
NMT_FIELD(CStr, m_Hash) NMT_FIELD(CStr, m_Hash)
END_NMT_CLASS() END_NMT_CLASS()
START_NMT_CLASS_(SyncError, NMT_SYNC_ERROR) START_NMT_CLASS_(SyncError, NMT_SYNC_ERROR)
NMT_FIELD_INT(m_Turn, u32, 4) NMT_FIELD_INT(m_Turn, i32, 4)
NMT_FIELD(CStr, m_HashExpected) NMT_FIELD(CStr, m_HashExpected)
NMT_START_ARRAY(m_PlayerNames) NMT_START_ARRAY(m_PlayerNames)
NMT_FIELD(CStrW, m_Name) NMT_FIELD(CStrW, m_Name)

View file

@ -49,6 +49,7 @@
#include "scriptinterface/Context.h" #include "scriptinterface/Context.h"
#include "scriptinterface/Interface.h" #include "scriptinterface/Interface.h"
#include "scriptinterface/Request.h" #include "scriptinterface/Request.h"
#include "simulation2/helpers/SimulationCommand.h"
#include "simulation2/system/TurnManager.h" #include "simulation2/system/TurnManager.h"
#include <algorithm> #include <algorithm>
@ -1172,9 +1173,9 @@ bool CNetServerWorker::OnSimulationCommand(CNetServerSession* session, CFsmEvent
server.Multicast(message, { NSS_INGAME }); server.Multicast(message, { NSS_INGAME });
// Save all the received commands // Save all the received commands
if (server.m_SavedCommands.size() < message->m_Turn + 1) if (server.m_SavedCommands.size() < static_cast<size_t>(message->m_Turn) + 1)
server.m_SavedCommands.resize(message->m_Turn + 1); server.m_SavedCommands.resize(static_cast<size_t>(message->m_Turn) + 1);
server.m_SavedCommands[message->m_Turn].push_back(*message); server.m_SavedCommands[static_cast<size_t>(message->m_Turn)].push_back(*message);
// TODO: we shouldn't send the message back to the client that first sent it // TODO: we shouldn't send the message back to the client that first sent it
return true; return true;
@ -1396,14 +1397,14 @@ bool CNetServerWorker::OnJoinSyncingLoadedGame(CNetServerSession* session, CFsmE
CLoadedGameMessage* message = (CLoadedGameMessage*)event->GetParamRef(); CLoadedGameMessage* message = (CLoadedGameMessage*)event->GetParamRef();
u32 turn = message->m_CurrentTurn; turn_id_t turn = message->m_CurrentTurn;
u32 readyTurn = server.m_ServerTurnManager->GetReadyTurn(); turn_id_t readyTurn = server.m_ServerTurnManager->GetReadyTurn();
// Send them all commands received since their saved state, // Send them all commands received since their saved state,
// and turn-ended messages for any turns that have already been processed // and turn-ended messages for any turns that have already been processed
for (size_t i = turn + 1; i < std::max(readyTurn+1, (u32)server.m_SavedCommands.size()); ++i) for (turn_id_t i = turn + 1; i < std::max(readyTurn + 1, static_cast<turn_id_t>(server.m_SavedCommands.size())); ++i)
{ {
if (i < server.m_SavedCommands.size()) if (static_cast<size_t>(i) < server.m_SavedCommands.size())
for (size_t j = 0; j < server.m_SavedCommands[i].size(); ++j) for (size_t j = 0; j < server.m_SavedCommands[i].size(); ++j)
session->SendMessage(&server.m_SavedCommands[i][j]); session->SendMessage(&server.m_SavedCommands[i][j]);

View file

@ -27,6 +27,7 @@
#include "network/NetServerSession.h" #include "network/NetServerSession.h"
#include "ps/CLogger.h" #include "ps/CLogger.h"
#include "ps/ConfigDB.h" #include "ps/ConfigDB.h"
#include "simulation2/helpers/SimulationCommand.h"
#include "simulation2/system/TurnManager.h" #include "simulation2/system/TurnManager.h"
#include <limits> #include <limits>
@ -50,7 +51,7 @@ CNetServerTurnManager::CNetServerTurnManager(CNetServerWorker& server)
m_SavedTurnLengths.push_back(m_TurnLength); m_SavedTurnLengths.push_back(m_TurnLength);
} }
void CNetServerTurnManager::NotifyFinishedClientCommands(CNetServerSession& session, u32 turn) void CNetServerTurnManager::NotifyFinishedClientCommands(CNetServerSession& session, turn_id_t turn)
{ {
int client = session.GetHostID(); int client = session.GetHostID();
@ -105,11 +106,11 @@ void CNetServerTurnManager::CheckClientsReady()
msg.m_Turn = m_ReadyTurn; msg.m_Turn = m_ReadyTurn;
m_NetServer.Multicast(&msg, { NSS_INGAME }); m_NetServer.Multicast(&msg, { NSS_INGAME });
ENSURE(m_SavedTurnLengths.size() == m_ReadyTurn); ENSURE(std::cmp_equal(m_SavedTurnLengths.size(), m_ReadyTurn));
m_SavedTurnLengths.push_back(m_TurnLength); m_SavedTurnLengths.push_back(m_TurnLength);
} }
void CNetServerTurnManager::NotifyFinishedClientUpdate(CNetServerSession& session, u32 turn, const CStr& hash) void CNetServerTurnManager::NotifyFinishedClientUpdate(CNetServerSession& session, turn_id_t turn, const CStr& hash)
{ {
int client = session.GetHostID(); int client = session.GetHostID();
@ -138,13 +139,13 @@ void CNetServerTurnManager::NotifyFinishedClientUpdate(CNetServerSession& sessio
m_ClientStateHashes[turn][client] = hash; m_ClientStateHashes[turn][client] = hash;
// Find the newest turn which we know all clients have simulated // Find the newest turn which we know all clients have simulated
u32 newest = std::numeric_limits<u32>::max(); turn_id_t newest = std::numeric_limits<turn_id_t>::max();
for (const std::pair<const int, Client>& clientData : m_ClientsData) for (const std::pair<const int, Client>& clientData : m_ClientsData)
if (clientData.second.simulatedTurn < newest) if (clientData.second.simulatedTurn < newest)
newest = clientData.second.simulatedTurn; newest = clientData.second.simulatedTurn;
// For every set of state hashes that all clients have simulated, check for OOS // For every set of state hashes that all clients have simulated, check for OOS
for (const std::pair<const u32, std::map<int, std::string>>& clientStateHash : m_ClientStateHashes) for (const std::pair<const turn_id_t, std::map<int, std::string>>& clientStateHash : m_ClientStateHashes)
{ {
if (clientStateHash.first > newest) if (clientStateHash.first > newest)
break; break;
@ -187,7 +188,7 @@ void CNetServerTurnManager::NotifyFinishedClientUpdate(CNetServerSession& sessio
m_ClientStateHashes.erase(m_ClientStateHashes.begin(), m_ClientStateHashes.lower_bound(newest+1)); m_ClientStateHashes.erase(m_ClientStateHashes.begin(), m_ClientStateHashes.lower_bound(newest+1));
} }
void CNetServerTurnManager::InitialiseClient(int client, u32 turn, bool observer) void CNetServerTurnManager::InitialiseClient(int client, turn_id_t turn, bool observer)
{ {
NETSERVERTURN_LOG("InitialiseClient(client=%d, turn=%d)\n", client, turn); NETSERVERTURN_LOG("InitialiseClient(client=%d, turn=%d)\n", client, turn);
@ -206,7 +207,7 @@ void CNetServerTurnManager::UninitialiseClient(int client)
bool checkOOS = m_ClientsData[client].isOOS; bool checkOOS = m_ClientsData[client].isOOS;
m_ClientsData.erase(client); m_ClientsData.erase(client);
for (std::pair<const u32, std::map<int, std::string>>& clientStateHash : m_ClientStateHashes) for (std::pair<const turn_id_t, std::map<int, std::string>>& clientStateHash : m_ClientStateHashes)
clientStateHash.second.erase(client); clientStateHash.second.erase(client);
// Check whether we're ready for the next turn now that we're not // Check whether we're ready for the next turn now that we're not
@ -228,8 +229,8 @@ void CNetServerTurnManager::SetTurnLength(u32 msecs)
m_TurnLength = msecs; m_TurnLength = msecs;
} }
u32 CNetServerTurnManager::GetSavedTurnLength(u32 turn) u32 CNetServerTurnManager::GetSavedTurnLength(turn_id_t turn)
{ {
ENSURE(turn <= m_ReadyTurn); ENSURE(turn <= m_ReadyTurn);
return m_SavedTurnLengths.at(turn); return m_SavedTurnLengths.at(static_cast<size_t>(turn));
} }

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games. /* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -21,6 +21,7 @@
#include "lib/code_annotation.h" #include "lib/code_annotation.h"
#include "lib/types.h" #include "lib/types.h"
#include "ps/CStr.h" #include "ps/CStr.h"
#include "simulation2/helpers/SimulationCommand.h"
#include <map> #include <map>
#include <string> #include <string>
@ -44,15 +45,15 @@ class CNetServerTurnManager
public: public:
CNetServerTurnManager(CNetServerWorker& server); CNetServerTurnManager(CNetServerWorker& server);
void NotifyFinishedClientCommands(CNetServerSession& session, u32 turn); void NotifyFinishedClientCommands(CNetServerSession& session, turn_id_t turn);
void NotifyFinishedClientUpdate(CNetServerSession& session, u32 turn, const CStr& hash); void NotifyFinishedClientUpdate(CNetServerSession& session, turn_id_t turn, const CStr& hash);
/** /**
* Inform the turn manager of a new client * Inform the turn manager of a new client
* @param observer - whether this client is an observer. * @param observer - whether this client is an observer.
*/ */
void InitialiseClient(int client, u32 turn, bool observer); void InitialiseClient(int client, turn_id_t turn, bool observer);
/** /**
* Inform the turn manager that a previously-initialised client has left the game. * Inform the turn manager that a previously-initialised client has left the game.
@ -65,13 +66,13 @@ public:
* Returns the latest turn for which all clients are ready; * Returns the latest turn for which all clients are ready;
* they will have already been told to execute this turn. * they will have already been told to execute this turn.
*/ */
u32 GetReadyTurn() { return m_ReadyTurn; } turn_id_t GetReadyTurn() { return m_ReadyTurn; }
/** /**
* Returns the turn length that was used for the given turn. * Returns the turn length that was used for the given turn.
* Requires turn <= GetReadyTurn(). * Requires turn <= GetReadyTurn().
*/ */
u32 GetSavedTurnLength(u32 turn); u32 GetSavedTurnLength(turn_id_t turn);
private: private:
void CheckClientsReady(); void CheckClientsReady();
@ -80,9 +81,9 @@ private:
{ {
CStrW playerName; CStrW playerName;
// Latest turn for which all commands have been received. // Latest turn for which all commands have been received.
u32 readyTurn; turn_id_t readyTurn;
// Last known simulated turn. // Last known simulated turn.
u32 simulatedTurn; turn_id_t simulatedTurn;
bool isObserver; bool isObserver;
bool isOOS = false; bool isOOS = false;
}; };
@ -93,10 +94,10 @@ private:
bool m_HasSyncError = false; bool m_HasSyncError = false;
// Map of turn -> {Client ID -> state hash}; old indexes <= min(m_ClientsSimulated) are deleted // Map of turn -> {Client ID -> state hash}; old indexes <= min(m_ClientsSimulated) are deleted
std::map<u32, std::map<int, std::string>> m_ClientStateHashes; std::map<turn_id_t, std::map<int, std::string>> m_ClientStateHashes;
/// The latest turn for which we have received all commands from all clients /// The latest turn for which we have received all commands from all clients
u32 m_ReadyTurn; turn_id_t m_ReadyTurn;
// Current turn length // Current turn length
u32 m_TurnLength; u32 m_TurnLength;

View file

@ -90,7 +90,6 @@ CGame::CGame(bool replayLog, const SimulationDebugOptions debugOptions):
m_SimRate(1.0f), m_SimRate(1.0f),
m_PlayerID(-1), m_PlayerID(-1),
m_ViewedPlayerID(-1), m_ViewedPlayerID(-1),
m_IsSavedGame(false),
m_IsVisualReplay(false), m_IsVisualReplay(false),
m_ReplayStream(NULL) m_ReplayStream(NULL)
{ {
@ -224,7 +223,7 @@ void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& saved
const Script::Interface& scriptInterface = m_Simulation2->GetScriptInterface(); const Script::Interface& scriptInterface = m_Simulation2->GetScriptInterface();
Script::Request rq(scriptInterface); Script::Request rq(scriptInterface);
m_IsSavedGame = !savedState.empty(); const bool isSavedGame{!savedState.empty()};
m_Simulation2->SetInitAttributes(attribs); m_Simulation2->SetInitAttributes(attribs);
@ -285,7 +284,7 @@ void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& saved
co_return g_Renderer.GetSceneRenderer().GetWaterManager().LoadWaterTextures(); co_return g_Renderer.GetSceneRenderer().GetWaterManager().LoadWaterTextures();
}, L"LoadWaterTextures", 80); }, L"LoadWaterTextures", 80);
if (m_IsSavedGame) if (isSavedGame)
PS::Loader::Register(std::bind_front( PS::Loader::Register(std::bind_front(
[](CGame* game, const std::string& state) -> PS::Loader::Task [](CGame* game, const std::string& state) -> PS::Loader::Task
{ {
@ -298,13 +297,32 @@ void CGame::RegisterInit(const JS::HandleValue attribs, const std::string& saved
co_return game->LoadVisualReplayData(); co_return game->LoadVisualReplayData();
}, this), L"Loading visual replay data", 1000); }, this), L"Loading visual replay data", 1000);
// Call the script function InitGame only for new games, not saved games
if (!isSavedGame)
{
// Perform some simulation initializations (replace skirmish entities, explore territories, etc.)
// that needs to be done before setting up the AI and shouldn't be done in Atlas
if (!g_AtlasGameLoop->running)
{
PS::Loader::Register(std::bind_front([](CGame* game) -> PS::Loader::Task
{
game->m_Simulation2->PreInitGame();
co_return 0;
}, this), L"PreInitGame", 5000);
}
PS::Loader::Register(std::bind_front([](CGame* game) -> PS::Loader::Task
{
game->m_Simulation2->InitGame();
co_return 0;
}, this), L"InitGame", 4000);
}
PS::Loader::EndRegistering(); PS::Loader::EndRegistering();
} }
int CGame::LoadInitialState(const std::string& savedState) int CGame::LoadInitialState(const std::string& savedState)
{ {
ENSURE(m_IsSavedGame);
std::stringstream stream(savedState); std::stringstream stream(savedState);
bool ok = m_Simulation2->DeserializeState(stream); bool ok = m_Simulation2->DeserializeState(stream);
@ -324,17 +342,6 @@ int CGame::LoadInitialState(const std::string& savedState)
**/ **/
PSRETURN CGame::ReallyStartGame() PSRETURN CGame::ReallyStartGame()
{ {
// Call the script function InitGame only for new games, not saved games
if (!m_IsSavedGame)
{
// Perform some simulation initializations (replace skirmish entities, explore territories, etc.)
// that needs to be done before setting up the AI and shouldn't be done in Atlas
if (!g_AtlasGameLoop->running)
m_Simulation2->PreInitGame();
m_Simulation2->InitGame();
}
// We need to do an initial Interpolate call to set up all the models etc, // We need to do an initial Interpolate call to set up all the models etc,
// because Update might never interpolate (e.g. if the game starts paused) // because Update might never interpolate (e.g. if the game starts paused)
// and we could end up rendering before having set up any models (so they'd // and we could end up rendering before having set up any models (so they'd

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games. /* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -226,7 +226,6 @@ private:
std::vector<CColor> m_PlayerColors; std::vector<CColor> m_PlayerColors;
int LoadInitialState(const std::string& savedState); int LoadInitialState(const std::string& savedState);
bool m_IsSavedGame; // true if loading a saved game; false for a new game
bool m_CheatsEnabled; bool m_CheatsEnabled;

View file

@ -92,13 +92,13 @@ public:
return serializationTestOption ? serializationTestOption->turn : return serializationTestOption ? serializationTestOption->turn :
std::max(CConfigDB::GetIfInitialised("serializationtest", -1), -1); std::max(CConfigDB::GetIfInitialised("serializationtest", -1), -1);
}()}, }()},
m_RejoinTestTurn{[&]() -> std::optional<int> m_RejoinTestTurn{[&]() -> std::optional<turn_id_t>
{ {
const auto* rejoinTestOption{ const auto* rejoinTestOption{
std::get_if<SimulationDebugOptions::RejoinTest>(&debugOptions.test)}; std::get_if<SimulationDebugOptions::RejoinTest>(&debugOptions.test)};
if (rejoinTestOption) if (rejoinTestOption)
return rejoinTestOption->turn; return rejoinTestOption->turn;
const int configVal{CConfigDB::GetIfInitialised("rejointest", -1)}; const turn_id_t configVal{CConfigDB::GetIfInitialised("rejointest", -1)};
if (configVal >= 0) if (configVal >= 0)
return configVal; return configVal;
return std::nullopt; return std::nullopt;
@ -163,17 +163,17 @@ public:
std::set<VfsPath> m_LoadedScripts; std::set<VfsPath> m_LoadedScripts;
uint32_t m_TurnNumber; turn_id_t m_TurnNumber;
bool m_EnableOOSLog{false}; bool m_EnableOOSLog{false};
OsPath m_OOSLogPath; OsPath m_OOSLogPath;
// Functions and data for the serialization test mode: (see Update() for relevant comments) // Functions and data for the serialization test mode: (see Update() for relevant comments)
std::optional<int> m_SerializationTestTurn; std::optional<turn_id_t> m_SerializationTestTurn;
bool m_TestingSerialization{false}; bool m_TestingSerialization{false};
bool m_EnableSerializationTest{false}; bool m_EnableSerializationTest{false};
std::optional<int> m_RejoinTestTurn; std::optional<turn_id_t> m_RejoinTestTurn;
bool m_TestingRejoin{false}; bool m_TestingRejoin{false};
// Secondary simulation (NB: order matters for destruction). // Secondary simulation (NB: order matters for destruction).
@ -383,7 +383,7 @@ void CSimulation2Impl::InitRNGSeedAI()
void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationCommand>& commands) void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationCommand>& commands)
{ {
PROFILE3("sim update"); PROFILE3("sim update");
PROFILE2_ATTR("turn %d", (int)m_TurnNumber); PROFILE2_ATTR("turn %d", m_TurnNumber);
fixed turnLengthFixed = fixed::FromInt(turnLength) / 1000; fixed turnLengthFixed = fixed::FromInt(turnLength) / 1000;
@ -408,11 +408,11 @@ void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationComman
const Script::Interface& scriptInterface = m_ComponentManager.GetScriptInterface(); const Script::Interface& scriptInterface = m_ComponentManager.GetScriptInterface();
const bool startSerializationTest = m_SerializationTestTurn.has_value() && const bool startSerializationTest = m_SerializationTestTurn.has_value() &&
std::cmp_equal(m_SerializationTestTurn.value(), m_TurnNumber); m_SerializationTestTurn.value() == m_TurnNumber;
if (startSerializationTest) if (startSerializationTest)
m_TestingSerialization = true; m_TestingSerialization = true;
const bool startRejoinTest = m_RejoinTestTurn.has_value() && const bool startRejoinTest = m_RejoinTestTurn.has_value() &&
static_cast<int64_t>(m_RejoinTestTurn.value()) == m_TurnNumber; m_RejoinTestTurn.value() == m_TurnNumber;
if (startRejoinTest) if (startRejoinTest)
m_TestingRejoin = true; m_TestingRejoin = true;
@ -891,11 +891,11 @@ bool CSimulation2::DeserializeState(std::istream& stream)
return m->m_ComponentManager.DeserializeState(stream); return m->m_ComponentManager.DeserializeState(stream);
} }
void CSimulation2::ActivateRejoinTest(int turn) void CSimulation2::ActivateRejoinTest(turn_id_t turn)
{ {
if (m->m_RejoinTestTurn.has_value()) if (m->m_RejoinTestTurn.has_value())
return; return;
LOGMESSAGERENDER("Rejoin test will activate in %i turns", turn - m->m_TurnNumber); LOGMESSAGERENDER("Rejoin test will activate in %d turns", turn - m->m_TurnNumber);
m->m_RejoinTestTurn = turn; m->m_RejoinTestTurn = turn;
} }

View file

@ -22,6 +22,7 @@
#include "lib/file/vfs/vfs_path.h" #include "lib/file/vfs/vfs_path.h"
#include "lib/status.h" #include "lib/status.h"
#include "ps/Loader.h" #include "ps/Loader.h"
#include "simulation2/helpers/SimulationCommand.h"
#include "simulation2/system/DebugOptions.h" #include "simulation2/system/DebugOptions.h"
#include "simulation2/system/Entity.h" #include "simulation2/system/Entity.h"
@ -240,7 +241,7 @@ public:
/** /**
* Activate the rejoin-test feature for turn @param turn. * Activate the rejoin-test feature for turn @param turn.
*/ */
void ActivateRejoinTest(int turn); void ActivateRejoinTest(turn_id_t turn);
std::string GenerateSchema(); std::string GenerateSchema();

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games. /* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -24,6 +24,12 @@
#include <js/TypeDecls.h> #include <js/TypeDecls.h>
#include <js/Value.h> #include <js/Value.h>
/**
* At 1000 turns/second, a 32-bit counter would overflow in ~49 days.
* No overflow checks are needed.
*/
using turn_id_t = std::int32_t;
struct JSContext; struct JSContext;
/** /**

View file

@ -42,12 +42,12 @@ void CLocalTurnManager::PostCommand(JS::HandleValue data)
AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + m_CommandDelay); AddCommand(m_ClientId, m_PlayerId, data, m_CurrentTurn + m_CommandDelay);
} }
void CLocalTurnManager::NotifyFinishedOwnCommands(u32 turn) void CLocalTurnManager::NotifyFinishedOwnCommands(turn_id_t turn)
{ {
FinishedAllCommands(turn, m_TurnLength); FinishedAllCommands(turn, m_TurnLength);
} }
void CLocalTurnManager::NotifyFinishedUpdate(u32 /*turn*/, const UpdateCallback&) void CLocalTurnManager::NotifyFinishedUpdate(turn_id_t /*turn*/, const UpdateCallback&)
{ {
#if 0 // this hurts performance and is only useful for verifying log replays #if 0 // this hurts performance and is only useful for verifying log replays
std::string hash; std::string hash;

View file

@ -41,9 +41,9 @@ public:
void PostCommand(player_id_t playerid, JS::HandleValue data); void PostCommand(player_id_t playerid, JS::HandleValue data);
protected: protected:
void NotifyFinishedOwnCommands(u32 turn) override; void NotifyFinishedOwnCommands(turn_id_t turn) override;
void NotifyFinishedUpdate(u32 turn, const UpdateCallback& sendEventToAll) override; void NotifyFinishedUpdate(turn_id_t turn, const UpdateCallback& sendEventToAll) override;
}; };
#endif // INCLUDED_LOCALTURNMANAGER #endif // INCLUDED_LOCALTURNMANAGER

View file

@ -47,18 +47,18 @@ CReplayTurnManager::CReplayTurnManager(CSimulation2& simulation, IReplayLogger&
{ {
} }
void CReplayTurnManager::StoreReplayCommand(u32 turn, int player, const std::string& command) void CReplayTurnManager::StoreReplayCommand(turn_id_t turn, int player, const std::string& command)
{ {
// Using the pair we make sure that commands per turn will be processed in the correct order // Using the pair we make sure that commands per turn will be processed in the correct order
m_ReplayCommands[turn].emplace_back(player, command); m_ReplayCommands[turn].emplace_back(player, command);
} }
void CReplayTurnManager::StoreReplayHash(u32 turn, const std::string& hash, bool quick) void CReplayTurnManager::StoreReplayHash(turn_id_t turn, const std::string& hash, bool quick)
{ {
m_ReplayHash[turn] = std::make_pair(hash, quick); m_ReplayHash[turn] = std::make_pair(hash, quick);
} }
void CReplayTurnManager::StoreReplayTurnLength(u32 turn, u32 turnLength) void CReplayTurnManager::StoreReplayTurnLength(turn_id_t turn, u32 turnLength)
{ {
m_ReplayTurnLengths[turn] = turnLength; m_ReplayTurnLengths[turn] = turnLength;
@ -67,12 +67,12 @@ void CReplayTurnManager::StoreReplayTurnLength(u32 turn, u32 turnLength)
m_TurnLength = m_ReplayTurnLengths[0]; m_TurnLength = m_ReplayTurnLengths[0];
} }
void CReplayTurnManager::StoreFinalReplayTurn(u32 turn) void CReplayTurnManager::StoreFinalReplayTurn(turn_id_t turn)
{ {
m_FinalTurn = turn; m_FinalTurn = turn;
} }
void CReplayTurnManager::NotifyFinishedUpdate(u32 turn, const UpdateCallback& sendEventToAll) void CReplayTurnManager::NotifyFinishedUpdate(turn_id_t turn, const UpdateCallback& sendEventToAll)
{ {
if (turn == 1 && m_FinalTurn == 0) if (turn == 1 && m_FinalTurn == 0)
sendEventToAll(EventNameReplayFinished, std::nullopt); sendEventToAll(EventNameReplayFinished, std::nullopt);
@ -83,7 +83,7 @@ void CReplayTurnManager::NotifyFinishedUpdate(u32 turn, const UpdateCallback& se
DoTurn(turn, sendEventToAll); DoTurn(turn, sendEventToAll);
// Compare hash if it exists in the replay and if we didn't have an OOS already // Compare hash if it exists in the replay and if we didn't have an OOS already
std::map<u32, std::pair<std::string, bool>>::iterator turnHashIt = m_ReplayHash.find(turn); std::map<turn_id_t, std::pair<std::string, bool>>::iterator turnHashIt = m_ReplayHash.find(turn);
if (m_HasSyncError || turnHashIt == m_ReplayHash.end()) if (m_HasSyncError || turnHashIt == m_ReplayHash.end())
return; return;
@ -119,9 +119,9 @@ void CReplayTurnManager::NotifyFinishedUpdate(u32 turn, const UpdateCallback& se
sendEventToAll(EventNameReplayOutOfSync, paramData); sendEventToAll(EventNameReplayOutOfSync, paramData);
} }
void CReplayTurnManager::DoTurn(u32 turn, const UpdateCallback& sendEventToAll) void CReplayTurnManager::DoTurn(turn_id_t turn, const UpdateCallback& sendEventToAll)
{ {
debug_printf("Executing turn %u of %u\n", turn, m_FinalTurn); debug_printf("Executing turn %d of %d\n", turn, m_FinalTurn);
m_TurnLength = m_ReplayTurnLengths[turn]; m_TurnLength = m_ReplayTurnLengths[turn];

View file

@ -40,18 +40,18 @@ class CReplayTurnManager : public CLocalTurnManager
public: public:
CReplayTurnManager(CSimulation2& simulation, IReplayLogger& replay); CReplayTurnManager(CSimulation2& simulation, IReplayLogger& replay);
void StoreReplayCommand(u32 turn, int player, const std::string& command); void StoreReplayCommand(turn_id_t turn, int player, const std::string& command);
void StoreReplayTurnLength(u32 turn, u32 turnLength); void StoreReplayTurnLength(turn_id_t turn, u32 turnLength);
void StoreReplayHash(u32 turn, const std::string& hash, bool quick); void StoreReplayHash(turn_id_t turn, const std::string& hash, bool quick);
void StoreFinalReplayTurn(u32 turn); void StoreFinalReplayTurn(turn_id_t turn);
private: private:
void NotifyFinishedUpdate(u32 turn, const UpdateCallback& sendEventToAll) override; void NotifyFinishedUpdate(turn_id_t turn, const UpdateCallback& sendEventToAll) override;
void DoTurn(u32 turn, const UpdateCallback& sendEventToAll); void DoTurn(turn_id_t turn, const UpdateCallback& sendEventToAll);
static const CStr EventNameReplayFinished; static const CStr EventNameReplayFinished;
static const CStr EventNameReplayOutOfSync; static const CStr EventNameReplayOutOfSync;
@ -59,13 +59,13 @@ private:
bool m_HasSyncError = false; bool m_HasSyncError = false;
// Contains the commands of every player on each turn // Contains the commands of every player on each turn
std::map<u32, std::vector<std::pair<player_id_t, std::string>>> m_ReplayCommands; std::map<turn_id_t, std::vector<std::pair<player_id_t, std::string>>> m_ReplayCommands;
// Contains the length of every turn // Contains the length of every turn
std::map<u32, u32> m_ReplayTurnLengths; std::map<turn_id_t, u32> m_ReplayTurnLengths;
// Contains all replay hash values and weather or not the quick hash method was used // Contains all replay hash values and weather or not the quick hash method was used
std::map<u32, std::pair<std::string, bool>> m_ReplayHash; std::map<turn_id_t, std::pair<std::string, bool>> m_ReplayHash;
}; };
#endif // INCLUDED_REPLAYTURNMANAGER #endif // INCLUDED_REPLAYTURNMANAGER

View file

@ -48,14 +48,14 @@ const CStr CTurnManager::EventNameSavegameLoaded = "SavegameLoaded";
CTurnManager::CTurnManager(CSimulation2& simulation, u32 defaultTurnLength, u32 commandDelay, int clientId, IReplayLogger& replay) CTurnManager::CTurnManager(CSimulation2& simulation, u32 defaultTurnLength, u32 commandDelay, int clientId, IReplayLogger& replay)
: m_Simulation2(simulation), m_CurrentTurn(0), m_CommandDelay(commandDelay), m_ReadyTurn(commandDelay - 1), m_TurnLength(defaultTurnLength), : m_Simulation2(simulation), m_CurrentTurn(0), m_CommandDelay(commandDelay), m_ReadyTurn(commandDelay - 1), m_TurnLength(defaultTurnLength),
m_PlayerId(-1), m_ClientId(clientId), m_DeltaSimTime(0), m_Replay(replay), m_PlayerId(-1), m_ClientId(clientId), m_DeltaSimTime(0), m_Replay(replay),
m_FinalTurn(std::numeric_limits<u32>::max()), m_TimeWarpNumTurns(0) m_FinalTurn(std::numeric_limits<turn_id_t>::max()), m_TimeWarpNumTurns(0)
{ {
Script::Request rq(m_Simulation2.GetScriptInterface()); Script::Request rq(m_Simulation2.GetScriptInterface());
m_QuickSaveMetadata.init(rq.cx); m_QuickSaveMetadata.init(rq.cx);
m_QueuedCommands.resize(1); m_QueuedCommands.resize(1);
} }
void CTurnManager::ResetState(u32 newCurrentTurn, u32 newReadyTurn) void CTurnManager::ResetState(turn_id_t newCurrentTurn, turn_id_t newReadyTurn)
{ {
m_CurrentTurn = newCurrentTurn; m_CurrentTurn = newCurrentTurn;
m_ReadyTurn = newReadyTurn; m_ReadyTurn = newReadyTurn;
@ -141,7 +141,7 @@ bool CTurnManager::Update(float simFrameLength, size_t maxTurns, const UpdateCal
// Put all the client commands into a single list, in a globally consistent order // Put all the client commands into a single list, in a globally consistent order
std::vector<SimulationCommand> commands; std::vector<SimulationCommand> commands;
for (std::pair<const u32, std::vector<SimulationCommand>>& p : m_QueuedCommands[0]) for (std::pair<const turn_id_t, std::vector<SimulationCommand>>& p : m_QueuedCommands[0])
commands.insert(commands.end(), std::make_move_iterator(p.second.begin()), std::make_move_iterator(p.second.end())); commands.insert(commands.end(), std::make_move_iterator(p.second.begin()), std::make_move_iterator(p.second.end()));
m_QueuedCommands.pop_front(); m_QueuedCommands.pop_front();
@ -184,7 +184,7 @@ bool CTurnManager::UpdateFastForward()
// Put all the client commands into a single list, in a globally consistent order // Put all the client commands into a single list, in a globally consistent order
std::vector<SimulationCommand> commands; std::vector<SimulationCommand> commands;
for (std::pair<const u32, std::vector<SimulationCommand>>& p : m_QueuedCommands[0]) for (std::pair<const turn_id_t, std::vector<SimulationCommand>>& p : m_QueuedCommands[0])
commands.insert(commands.end(), std::make_move_iterator(p.second.begin()), std::make_move_iterator(p.second.end())); commands.insert(commands.end(), std::make_move_iterator(p.second.begin()), std::make_move_iterator(p.second.end()));
m_QueuedCommands.pop_front(); m_QueuedCommands.pop_front();
@ -214,7 +214,7 @@ void CTurnManager::Interpolate(float simFrameLength, float realFrameLength)
m_Simulation2.Interpolate(simFrameLength, offset, realFrameLength); m_Simulation2.Interpolate(simFrameLength, offset, realFrameLength);
} }
void CTurnManager::AddCommand(int client, int player, JS::HandleValue data, u32 turn) void CTurnManager::AddCommand(int client, int player, JS::HandleValue data, turn_id_t turn)
{ {
NETTURN_LOG("AddCommand(client=%d player=%d turn=%d current=%d, ready=%d)\n", client, player, turn, m_CurrentTurn, m_ReadyTurn); NETTURN_LOG("AddCommand(client=%d player=%d turn=%d current=%d, ready=%d)\n", client, player, turn, m_CurrentTurn, m_ReadyTurn);
@ -239,7 +239,7 @@ void CTurnManager::AddCommand(int client, int player, JS::HandleValue data, u32
m_QueuedCommands[turn - (m_CurrentTurn+1)][client].emplace_back(player, rq.cx, data); m_QueuedCommands[turn - (m_CurrentTurn+1)][client].emplace_back(player, rq.cx, data);
} }
void CTurnManager::FinishedAllCommands(u32 turn, u32 turnLength) void CTurnManager::FinishedAllCommands(turn_id_t turn, u32 turnLength)
{ {
NETTURN_LOG("FinishedAllCommands(%d, %d)\n", turn, turnLength); NETTURN_LOG("FinishedAllCommands(%d, %d)\n", turn, turnLength);
@ -248,7 +248,7 @@ void CTurnManager::FinishedAllCommands(u32 turn, u32 turnLength)
m_TurnLength = turnLength; m_TurnLength = turnLength;
} }
bool CTurnManager::TurnNeedsFullHash(u32 turn) const bool CTurnManager::TurnNeedsFullHash(turn_id_t turn) const
{ {
// Check immediately for errors caused by e.g. inconsistent game versions // Check immediately for errors caused by e.g. inconsistent game versions
// (The hash is computed after the first sim update, so we start at turn == 1) // (The hash is computed after the first sim update, so we start at turn == 1)

View file

@ -101,7 +101,7 @@ public:
virtual ~CTurnManager() { } virtual ~CTurnManager() { }
void ResetState(u32 newCurrentTurn, u32 newReadyTurn); void ResetState(turn_id_t newCurrentTurn, turn_id_t newReadyTurn);
/** /**
* Set the current user's player ID, which will be added into command messages. * Set the current user's player ID, which will be added into command messages.
@ -146,7 +146,7 @@ public:
* Called when all commands for a given turn have been received. * Called when all commands for a given turn have been received.
* This allows Update to progress to that turn. * This allows Update to progress to that turn.
*/ */
void FinishedAllCommands(u32 turn, u32 turnLength); void FinishedAllCommands(turn_id_t turn, u32 turnLength);
/** /**
* Enables the recording of state snapshots every @p numTurns, * Enables the recording of state snapshots every @p numTurns,
@ -163,52 +163,52 @@ public:
void QuickSave(JS::HandleValue GUIMetadata); void QuickSave(JS::HandleValue GUIMetadata);
std::optional<JS::Value> TryQuickLoad(); std::optional<JS::Value> TryQuickLoad();
u32 GetCurrentTurn() const { return m_CurrentTurn; } turn_id_t GetCurrentTurn() const { return m_CurrentTurn; }
/** /**
* @return how many turns are ready to be computed. * @return how many turns are ready to be computed.
* (used to detect players/observers that fall behind the live game. * (used to detect players/observers that fall behind the live game.
*/ */
u32 GetPendingTurns() const { return m_ReadyTurn - m_CurrentTurn; } turn_id_t GetPendingTurns() const { return m_ReadyTurn - m_CurrentTurn; }
protected: protected:
/** /**
* Store a command to be executed at a given turn. * Store a command to be executed at a given turn.
*/ */
void AddCommand(int client, int player, JS::HandleValue data, u32 turn); void AddCommand(int client, int player, JS::HandleValue data, turn_id_t turn);
/** /**
* Called when this client has finished sending all its commands scheduled for the given turn. * Called when this client has finished sending all its commands scheduled for the given turn.
*/ */
virtual void NotifyFinishedOwnCommands(u32 turn) = 0; virtual void NotifyFinishedOwnCommands(turn_id_t turn) = 0;
/** /**
* Called when this client has finished a simulation update. * Called when this client has finished a simulation update.
*/ */
virtual void NotifyFinishedUpdate(u32 turn, const UpdateCallback& sendEventToAll) = 0; virtual void NotifyFinishedUpdate(turn_id_t turn, const UpdateCallback& sendEventToAll) = 0;
/** /**
* Returns whether we should compute a complete state hash for the given turn, * Returns whether we should compute a complete state hash for the given turn,
* instead of a quick less-complete hash. * instead of a quick less-complete hash.
*/ */
bool TurnNeedsFullHash(u32 turn) const; bool TurnNeedsFullHash(turn_id_t turn) const;
CSimulation2& m_Simulation2; CSimulation2& m_Simulation2;
/// The turn that we have most recently executed /// The turn that we have most recently executed
u32 m_CurrentTurn; turn_id_t m_CurrentTurn;
// Current command delay (commands are scheduled for m_CurrentTurn + m_CommandDelay) // Current command delay (commands are scheduled for m_CurrentTurn + m_CommandDelay)
u32 m_CommandDelay; u32 m_CommandDelay;
/// The latest turn for which we have received all commands from all clients /// The latest turn for which we have received all commands from all clients
u32 m_ReadyTurn; turn_id_t m_ReadyTurn;
// Current turn length // Current turn length
u32 m_TurnLength; u32 m_TurnLength;
/// Commands queued at each turn (index 0 is for m_CurrentTurn+1) /// Commands queued at each turn (index 0 is for m_CurrentTurn+1)
std::deque<std::map<u32, std::vector<SimulationCommand>>> m_QueuedCommands; std::deque<std::map<turn_id_t, std::vector<SimulationCommand>>> m_QueuedCommands;
int m_PlayerId; int m_PlayerId;
uint m_ClientId; uint m_ClientId;
@ -220,7 +220,7 @@ protected:
IReplayLogger& m_Replay; IReplayLogger& m_Replay;
// The number of the last turn that is allowed to be executed (used for replays) // The number of the last turn that is allowed to be executed (used for replays)
u32 m_FinalTurn; turn_id_t m_FinalTurn;
private: private:
size_t m_TimeWarpNumTurns; // 0 if disabled size_t m_TimeWarpNumTurns; // 0 if disabled

View file

@ -45,21 +45,15 @@
using AtlasMessage::Shareable; using AtlasMessage::Shareable;
enum {
ID_PathsDrawing,
ID_PathsList,
ID_AddPath,
ID_DeletePath
};
CinemaSidebar::CinemaSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer) CinemaSidebar::CinemaSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarContainer, wxWindow* bottomBarContainer)
: Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer) : Sidebar(scenarioEditor, sidebarContainer, bottomBarContainer)
{ {
{ {
auto* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _T("Common settings")); auto* sizer = new wxStaticBoxSizer(wxVERTICAL, this, _T("Common settings"));
m_DrawPath = new wxCheckBox(sizer->GetStaticBox(), ID_PathsDrawing, _("Draw all paths")); m_DrawPath = new wxCheckBox(sizer->GetStaticBox(), wxID_ANY, _("Draw all paths"));
m_DrawPath->SetToolTip(_("Display every cinematic path added to the map")); m_DrawPath->SetToolTip(_("Display every cinematic path added to the map"));
m_DrawPath->Bind(wxEVT_CHECKBOX, [this](auto&){ SetPathsDrawing(m_DrawPath->IsChecked()); });
sizer->Add(m_DrawPath, wxSizerFlags().Expand().Border(wxALL, Atlas::Style::STATICBOX_PADDING)); sizer->Add(m_DrawPath, wxSizerFlags().Expand().Border(wxALL, Atlas::Style::STATICBOX_PADDING));
@ -70,20 +64,22 @@ CinemaSidebar::CinemaSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebarCo
auto* boxSizer = new wxStaticBoxSizer(wxVERTICAL, this, _T("Paths")); auto* boxSizer = new wxStaticBoxSizer(wxVERTICAL, this, _T("Paths"));
auto* box = boxSizer->GetStaticBox(); auto* box = boxSizer->GetStaticBox();
m_PathList = new wxListBox(box, ID_PathsList, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_SINGLE | wxLB_SORT); m_PathList = new wxListBox(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxLB_SINGLE | wxLB_SORT);
auto* deleteButton = new wxButton(box, ID_DeletePath, _("Delete")); auto* deleteButton = new wxButton(box, wxID_ANY, _("Delete"));
deleteButton->SetToolTip(_T("Delete selected path")); deleteButton->SetToolTip(_T("Delete selected path"));
deleteButton->Bind(wxEVT_BUTTON, [this](auto&){ DeleteSelectedPath(); });
m_NewPathName = new wxTextCtrl(box, wxID_ANY); auto* newPathName = new wxTextCtrl(box, wxID_ANY);
auto* addButton = new wxButton(box, ID_AddPath, _("Add")); auto* addButton = new wxButton(box, wxID_ANY, _("Add"));
addButton->Bind(wxEVT_BUTTON, [this, newPathName](auto&){ AddPath(newPathName->GetValue()); newPathName->Clear(); });
wxFlexGridSizer* pathsSizer = new wxFlexGridSizer(1, 5, 5); wxFlexGridSizer* pathsSizer = new wxFlexGridSizer(1, 5, 5);
pathsSizer->AddGrowableCol(0); pathsSizer->AddGrowableCol(0);
pathsSizer->Add(m_PathList, wxSizerFlags().Proportion(1).Expand()); pathsSizer->Add(m_PathList, wxSizerFlags().Proportion(1).Expand());
pathsSizer->Add(deleteButton, wxSizerFlags().Expand()); pathsSizer->Add(deleteButton, wxSizerFlags().Expand());
pathsSizer->Add(m_NewPathName, wxSizerFlags().Expand()); pathsSizer->Add(newPathName, wxSizerFlags().Expand());
pathsSizer->Add(addButton, wxSizerFlags().Expand()); pathsSizer->Add(addButton, wxSizerFlags().Expand());
boxSizer->Add(pathsSizer, wxSizerFlags().Expand().Border(wxALL, Atlas::Style::STATICBOX_PADDING)); boxSizer->Add(pathsSizer, wxSizerFlags().Expand().Border(wxALL, Atlas::Style::STATICBOX_PADDING));
@ -106,22 +102,21 @@ void CinemaSidebar::OnMapReload()
ReloadPathList(); ReloadPathList();
} }
void CinemaSidebar::OnTogglePathsDrawing(wxCommandEvent& evt) void CinemaSidebar::SetPathsDrawing(const bool enable)
{ {
POST_COMMAND(SetCinemaPathsDrawing, (evt.IsChecked())); POST_COMMAND(SetCinemaPathsDrawing, (enable));
} }
void CinemaSidebar::OnAddPath(wxCommandEvent&) void CinemaSidebar::AddPath(wxString name)
{ {
if (m_NewPathName->GetValue().empty()) if (name.empty())
return; return;
POST_COMMAND(AddCinemaPath, (m_NewPathName->GetValue().ToStdWstring())); POST_COMMAND(AddCinemaPath, (name.ToStdWstring()));
m_NewPathName->Clear();
ReloadPathList(); ReloadPathList();
} }
void CinemaSidebar::OnDeletePath(wxCommandEvent&) void CinemaSidebar::DeleteSelectedPath()
{ {
int index = m_PathList->GetSelection(); int index = m_PathList->GetSelection();
if (index < 0) if (index < 0)
@ -148,9 +143,3 @@ void CinemaSidebar::ReloadPathList()
m_PathList->SetStringSelection(selection); m_PathList->SetStringSelection(selection);
} }
wxBEGIN_EVENT_TABLE(CinemaSidebar, Sidebar)
EVT_CHECKBOX(ID_PathsDrawing, CinemaSidebar::OnTogglePathsDrawing)
EVT_BUTTON(ID_AddPath, CinemaSidebar::OnAddPath)
EVT_BUTTON(ID_DeletePath, CinemaSidebar::OnDeletePath)
wxEND_EVENT_TABLE();

View file

@ -37,15 +37,12 @@ protected:
void OnFirstDisplay() override; void OnFirstDisplay() override;
private: private:
void OnTogglePathsDrawing(wxCommandEvent& evt); void SetPathsDrawing(const bool enable);
void OnAddPath(wxCommandEvent& evt); void AddPath(wxString name);
void OnDeletePath(wxCommandEvent& evt); void DeleteSelectedPath();
void ReloadPathList(); void ReloadPathList();
wxCheckBox* m_DrawPath; wxCheckBox* m_DrawPath;
wxListBox* m_PathList; wxListBox* m_PathList;
wxTextCtrl* m_NewPathName;
wxDECLARE_EVENT_TABLE();
}; };

View file

@ -273,7 +273,7 @@ class CheckRefs:
(version,) = unpack(int_fmt, f.read(int_len)) (version,) = unpack(int_fmt, f.read(int_len))
if version != 7: if version != 7:
raise ValueError(f"Invalid PMP version ({version}) in '{ffp}'") raise ValueError(f"Invalid PMP version ({version}) in '{ffp}'")
(datasize,) = unpack(int_fmt, f.read(int_len)) (datasize,) = unpack(int_fmt, f.read(int_len)) # noqa: RUF059
(mapsize,) = unpack(int_fmt, f.read(int_len)) (mapsize,) = unpack(int_fmt, f.read(int_len))
f.seek(2 * (mapsize * 16 + 1) * (mapsize * 16 + 1), 1) # skip heightmap f.seek(2 * (mapsize * 16 + 1) * (mapsize * 16 + 1), 1) # skip heightmap
(numtexs,) = unpack(int_fmt, f.read(int_len)) (numtexs,) = unpack(int_fmt, f.read(int_len))

View file

@ -65,8 +65,8 @@ unknownMacro:source/lib/sysdep/os/win/wfirmware.cpp
unknownMacro:source/lib/sysdep/os/win/wposix/wutsname.cpp unknownMacro:source/lib/sysdep/os/win/wposix/wutsname.cpp
unknownMacro:source/ps/CStr.cpp unknownMacro:source/ps/CStr.cpp
uninitvar:source/ps/Game.cpp:246 uninitvar:source/ps/Game.cpp
uninitvar:source/ps/scripting/JSInterface_SavedGame.cpp:149 uninitvar:source/ps/scripting/JSInterface_SavedGame.cpp
danglingLifetime:source/renderer/backend/gl/Device.cpp danglingLifetime:source/renderer/backend/gl/Device.cpp