diff --git a/games/CWR-CE/Makefile b/games/CWR-CE/Makefile index 614406cd3e5c..40bc1ff79861 100644 --- a/games/CWR-CE/Makefile +++ b/games/CWR-CE/Makefile @@ -1,79 +1,79 @@ PORTNAME= CWR-CE DISTVERSIONPREFIX= v DISTVERSION= 3.01 +PORTREVISION= 1 CATEGORIES= games PATCH_SITES= https://github.com/ocochard/CWR-CE/compare/ -PATCHFILES= 9abbdf2769f3008d76c47ac0af219388ff0323dc..b6c9690f03cf0abb6af577db7294b00c30f4172c.patch:-p1 \ - b6c9690f03cf0abb6af577db7294b00c30f4172c..85dd554fc9e21312ee52d7f9eced6b171a6a892b.patch:-p1 +PATCHFILES= 9abbdf2769f3008d76c47ac0af219388ff0323dc..55e703228bf5649a6a847d724e94b97d94d7057f.patch:-p1 MAINTAINER= olivier@FreeBSD.org COMMENT= Arma: Cold War Assault - Remastered Community Edition WWW= https://github.com/ofpisnotdead-com/CWR-CE LICENSE= GPLv3+ LICENSE_FILE= ${WRKSRC}/LICENSE BUILD_DEPENDS= stb>0:devel/stb \ ${LOCALBASE}/include/CLI/CLI.hpp:devel/cli11 \ ${LOCALBASE}/include/vulkan/vulkan.h:graphics/vulkan-headers \ ${LOCALBASE}/lib/cmake/Catch2/Catch2Config.cmake:devel/catch2 \ ${LOCALBASE}/lib/cmake/glslang/glslang-config.cmake:graphics/glslang LIB_DEPENDS= libcjson.so:devel/libcjson \ libcurl.so:ftp/curl \ libenkiTS.so:devel/enkits \ libfmt.so:devel/libfmt \ libfreetype.so:print/freetype2 \ libimgui.so:x11-toolkits/imgui \ libmimalloc.so:devel/mimalloc \ libogg.so:audio/libogg \ libopenal.so:audio/openal-soft \ libopus.so:audio/opus \ libSDL3.so:devel/sdl3 \ libvulkan.so:graphics/vulkan-loader \ libspdlog.so:devel/spdlog \ libvorbis.so:audio/libvorbis \ libzstd.so:archivers/zstd USES= cmake compiler:c++20-lang dos2unix gl localbase pkgconfig DOS2UNIX_REGEX= .*\.(c|h|cpp|hpp) USE_GITHUB= yes GH_ACCOUNT= ofpisnotdead-com GH_PROJECT= CWR-CE GH_TAGNAME= 9abbdf2769f3008d76c47ac0af219388ff0323dc USE_GL= gl SUB_FILES= pkg-message # Disable vcpkg toolchain wiring — find_package(... CONFIG REQUIRED) picks up # system libraries from ${LOCALBASE}/lib/cmake instead. CMAKE_ARGS= -DVCPKG_MANIFEST_MODE=OFF \ -DCMAKE_DISABLE_FIND_PACKAGE_VCPKG=ON CMAKE_OFF= POSEIDON_DISABLE_PCH POSEIDON_BUILD_FUZZERS PLIST_FILES= bin/PoseidonGame \ bin/PoseidonGameDemo \ bin/PoseidonServer \ bin/PoseidonStudio \ bin/PoseidonTetris \ bin/PoseidonTools \ bin/cwr-ce \ bin/install-cwr-data do-install: ${INSTALL_PROGRAM} \ ${BUILD_WRKSRC}/apps/cwr/Game/PoseidonGame \ ${BUILD_WRKSRC}/apps/cwr/GameDemo/PoseidonGameDemo \ ${BUILD_WRKSRC}/apps/cwr/Server/PoseidonServer \ ${BUILD_WRKSRC}/apps/tools/Studio/PoseidonStudio \ ${BUILD_WRKSRC}/apps/tetris/Tetris/PoseidonTetris \ ${BUILD_WRKSRC}/apps/tools/Tools/PoseidonTools \ ${STAGEDIR}${PREFIX}/bin ${INSTALL_SCRIPT} ${FILESDIR}/install-cwr-data.sh \ ${STAGEDIR}${PREFIX}/bin/install-cwr-data ${INSTALL_SCRIPT} ${FILESDIR}/cwr-ce.sh \ ${STAGEDIR}${PREFIX}/bin/cwr-ce .include diff --git a/games/CWR-CE/distinfo b/games/CWR-CE/distinfo index 670262e54b4a..290b2c2de3e3 100644 --- a/games/CWR-CE/distinfo +++ b/games/CWR-CE/distinfo @@ -1,7 +1,5 @@ -TIMESTAMP = 1782950316 +TIMESTAMP = 1783002492 SHA256 (ofpisnotdead-com-CWR-CE-v3.01-9abbdf2769f3008d76c47ac0af219388ff0323dc_GH0.tar.gz) = 87df6297ed74993d56d3ceec9d90fb9287cebc58f47030a285a2eafb61c3ce22 SIZE (ofpisnotdead-com-CWR-CE-v3.01-9abbdf2769f3008d76c47ac0af219388ff0323dc_GH0.tar.gz) = 4944610 -SHA256 (9abbdf2769f3008d76c47ac0af219388ff0323dc..b6c9690f03cf0abb6af577db7294b00c30f4172c.patch) = 8add6877aae7534967c2f609026cd4c4aa861a41f27d6cd68ec89c052e56d31d -SIZE (9abbdf2769f3008d76c47ac0af219388ff0323dc..b6c9690f03cf0abb6af577db7294b00c30f4172c.patch) = 23346 -SHA256 (b6c9690f03cf0abb6af577db7294b00c30f4172c..85dd554fc9e21312ee52d7f9eced6b171a6a892b.patch) = d87ab832d44d46ae761c8a920463a70b5e8369477a0eda327b4be6995b139e7f -SIZE (b6c9690f03cf0abb6af577db7294b00c30f4172c..85dd554fc9e21312ee52d7f9eced6b171a6a892b.patch) = 18184 +SHA256 (9abbdf2769f3008d76c47ac0af219388ff0323dc..55e703228bf5649a6a847d724e94b97d94d7057f.patch) = c744b9b9bf7b1917ac159b8af41cfe354d2f8e646eb7b6722953f56583053df8 +SIZE (9abbdf2769f3008d76c47ac0af219388ff0323dc..55e703228bf5649a6a847d724e94b97d94d7057f.patch) = 41794 diff --git a/games/CWR-CE/files/patch-engine_Poseidon_AI_AIGroupImpl.cpp b/games/CWR-CE/files/patch-engine_Poseidon_AI_AIGroupImpl.cpp new file mode 100644 index 000000000000..9c4ce16cee2e --- /dev/null +++ b/games/CWR-CE/files/patch-engine_Poseidon_AI_AIGroupImpl.cpp @@ -0,0 +1,118 @@ +diff --git engine/Poseidon/AI/AIGroupImpl.cpp engine/Poseidon/AI/AIGroupImpl.cpp +index eeaffd9..22d895b 100644 +--- engine/Poseidon/AI/AIGroupImpl.cpp ++++ engine/Poseidon/AI/AIGroupImpl.cpp +@@ -41,6 +41,22 @@ namespace Poseidon + { + using namespace Foundation; + ++// Diagnostic for msgCmd->GetFrom() == this invariant violations in the radio ++// queue scans below. Returns the result of the check so call sites can be ++// written as `if (!CheckMsgCmdFrom(...)) continue;` in future patches. ++static bool CheckMsgCmdFrom(const AIGroup* self, RadioMessageCommand* msgCmd, const char* callSite) ++{ ++ if (msgCmd->GetFrom() == self) ++ { ++ return true; ++ } ++ LOG_ERROR(AI, "AIGroup radio queue holds foreign command at {}: this={} ({}), from={} ({}), msgType={}", callSite, ++ self ? (const char*)self->GetDebugName() : "null", (const void*)self, ++ msgCmd->GetFrom() ? (const char*)msgCmd->GetFrom()->GetDebugName() : "null", ++ (const void*)msgCmd->GetFrom(), msgCmd->GetType()); ++ return false; ++} ++ + // Parameters + + bool AIGroup::CommandSent(bool channelCenter) +@@ -72,7 +88,10 @@ bool AIGroup::CommandSent(bool channelCenter) + AI_ERROR(dynamic_cast(msg)); + RadioMessageCommand* msgCmd = static_cast(msg); + AI_ERROR(msgCmd); +- AI_ERROR(msgCmd->GetFrom() == this); ++ if (!channelCenter) ++ { ++ CheckMsgCmdFrom(this, msgCmd, "CommandSent(bool)/FindPrevMessage"); ++ } + if (msgCmd->IsToMainSubgroup()) + { + return true; +@@ -86,7 +105,10 @@ bool AIGroup::CommandSent(bool channelCenter) + AI_ERROR(dynamic_cast(msg)); + RadioMessageCommand* msgCmd = static_cast(msg); + AI_ERROR(msgCmd); +- AI_ERROR(msgCmd->GetFrom() == this); ++ if (!channelCenter) ++ { ++ CheckMsgCmdFrom(this, msgCmd, "CommandSent(bool)/GetActualMessage"); ++ } + if (msgCmd->IsToMainSubgroup()) + { + return true; +@@ -139,7 +161,10 @@ bool AIGroup::CommandSent(Command::Message message, bool channelCenter) + AI_ERROR(dynamic_cast(msg)); + RadioMessageCommand* msgCmd = static_cast(msg); + AI_ERROR(msgCmd); +- AI_ERROR(msgCmd->GetFrom() == this); ++ if (!channelCenter) ++ { ++ CheckMsgCmdFrom(this, msgCmd, "CommandSent(msg,bool)/FindPrevMessage"); ++ } + if (msgCmd->GetCmdMessage() == message) + { + return true; +@@ -152,7 +177,10 @@ bool AIGroup::CommandSent(Command::Message message, bool channelCenter) + AI_ERROR(dynamic_cast(msg)); + RadioMessageCommand* msgCmd = static_cast(msg); + AI_ERROR(msgCmd); +- AI_ERROR(msgCmd->GetFrom() == this); ++ if (!channelCenter) ++ { ++ CheckMsgCmdFrom(this, msgCmd, "CommandSent(msg,bool)/GetActualMessage"); ++ } + if (msgCmd->GetCmdMessage() == message) + { + return true; +@@ -200,7 +228,10 @@ bool AIGroup::CommandSent(AIUnit* to, Command::Message message, bool channelCent + AI_ERROR(dynamic_cast(msg)); + RadioMessageCommand* msgCmd = static_cast(msg); + AI_ERROR(msgCmd); +- AI_ERROR(msgCmd->GetFrom() == this); ++ if (!channelCenter) ++ { ++ CheckMsgCmdFrom(this, msgCmd, "CommandSent(to,msg,bool)/FindPrevMessage"); ++ } + if (msgCmd->IsTo(to) && msgCmd->GetCmdMessage() == message) + { + return true; +@@ -213,7 +244,10 @@ bool AIGroup::CommandSent(AIUnit* to, Command::Message message, bool channelCent + AI_ERROR(dynamic_cast(msg)); + RadioMessageCommand* msgCmd = static_cast(msg); + AI_ERROR(msgCmd); +- AI_ERROR(msgCmd->GetFrom() == this); ++ if (!channelCenter) ++ { ++ CheckMsgCmdFrom(this, msgCmd, "CommandSent(to,msg,bool)/GetActualMessage"); ++ } + if (msgCmd->IsTo(to) && msgCmd->GetCmdMessage() == message) + { + return true; +@@ -237,7 +271,7 @@ void AIGroup::ClearGetInCommands(AIUnit* to) + AI_ERROR(dynamic_cast(msg)); + RadioMessageCommand* msgCmd = static_cast(msg); + AI_ERROR(msgCmd); +- AI_ERROR(msgCmd->GetFrom() == this); ++ CheckMsgCmdFrom(this, msgCmd, "ClearGetInCommands/FindPrevMessage"); + if (msgCmd->IsTo(to) && msgCmd->GetCmdMessage() == Command::GetIn && + (msgCmd->GetContext() == Command::CtxAuto || msgCmd->GetContext() == Command::CtxAutoSilent)) + { +@@ -252,8 +286,7 @@ void AIGroup::ClearGetInCommands(AIUnit* to) + AI_ERROR(dynamic_cast(msg)); + RadioMessageCommand* msgCmd = static_cast(msg); + AI_ERROR(msgCmd); +- AI_ERROR(msgCmd->GetFrom() == this); +- AI_ERROR(msgCmd->GetFrom() == this); ++ CheckMsgCmdFrom(this, msgCmd, "ClearGetInCommands/GetActualMessage"); + if (msgCmd->IsTo(to) && msgCmd->GetCmdMessage() == Command::GetIn && + (msgCmd->GetContext() == Command::CtxAuto || msgCmd->GetContext() == Command::CtxAutoSilent)) + { diff --git a/games/CWR-CE/files/patch-engine_Poseidon_World_Entities_Infantry_SoldierOldMove.cpp b/games/CWR-CE/files/patch-engine_Poseidon_World_Entities_Infantry_SoldierOldMove.cpp new file mode 100644 index 000000000000..0997c5ae7df3 --- /dev/null +++ b/games/CWR-CE/files/patch-engine_Poseidon_World_Entities_Infantry_SoldierOldMove.cpp @@ -0,0 +1,15 @@ +diff --git engine/Poseidon/World/Entities/Infantry/SoldierOldMove.cpp engine/Poseidon/World/Entities/Infantry/SoldierOldMove.cpp +index 87c660d..7ac63e8 100644 +--- engine/Poseidon/World/Entities/Infantry/SoldierOldMove.cpp ++++ engine/Poseidon/World/Entities/Infantry/SoldierOldMove.cpp +@@ -1006,7 +1006,9 @@ namespace Poseidon + + void Man::KilledBy(EntityAI* owner) + { +- PoseidonAssert(_brain); ++ // _brain is null on respawn transfer (NetworkClient.cpp moves the brain ++ // to the new Soldier and nulls the old body's brain). A brainless ++ // corpse taking damage lands here; nothing to do. + if (!_brain) + { + return; diff --git a/games/CWR-CE/files/patch-engine_Poseidon_World_Entities_Vehicles_Transport.cpp b/games/CWR-CE/files/patch-engine_Poseidon_World_Entities_Vehicles_Transport.cpp new file mode 100644 index 000000000000..7910fd3863ec --- /dev/null +++ b/games/CWR-CE/files/patch-engine_Poseidon_World_Entities_Vehicles_Transport.cpp @@ -0,0 +1,16 @@ +diff --git engine/Poseidon/World/Entities/Vehicles/Transport.cpp engine/Poseidon/World/Entities/Vehicles/Transport.cpp +index eb2d624..44da61b 100644 +--- engine/Poseidon/World/Entities/Vehicles/Transport.cpp ++++ engine/Poseidon/World/Entities/Vehicles/Transport.cpp +@@ -1591,7 +1591,10 @@ void VehicleSupply::SupplyStarted(AIUnit* unit) + { + LOG_DEBUG(Physics, "{} SupplyStarted for {}", (const char*)GetDebugName(), (const char*)unit->GetDebugName()); + +- PoseidonAssert(_supplyUnits.Find(unit) < 0); ++ // A re-entry of the supply FSM state (e.g. Supply->Move->Supply when the ++ // client drifts out of range and comes back) can call SupplyStarted for ++ // a unit that never emitted a matching SupplyFinished. AddUnique below ++ // handles the duplicate; the assert is stale. + _supplyUnits.AddUnique(unit); + } + diff --git a/games/CWR-CE/files/patch-engine_Poseidon_World_Entities_Vehicles_TransportCore.cpp b/games/CWR-CE/files/patch-engine_Poseidon_World_Entities_Vehicles_TransportCore.cpp new file mode 100644 index 000000000000..18575224684a --- /dev/null +++ b/games/CWR-CE/files/patch-engine_Poseidon_World_Entities_Vehicles_TransportCore.cpp @@ -0,0 +1,39 @@ +diff --git engine/Poseidon/World/Entities/Vehicles/TransportCore.cpp engine/Poseidon/World/Entities/Vehicles/TransportCore.cpp +index df48f09..c6fb37b 100644 +--- engine/Poseidon/World/Entities/Vehicles/TransportCore.cpp ++++ engine/Poseidon/World/Entities/Vehicles/TransportCore.cpp +@@ -350,7 +350,8 @@ void Transport::DammageCrew(EntityAI* killer, float howMuch, RString ammo) + } + if (CrewDammage(this, man, killer, howMuch, ammo)) + { +- if (man) ++ // Skip brainless corpses (post-respawn bodies still riding as cargo). ++ if (man && man->Brain()) + { + man->KilledBy(killer); + } +@@ -358,21 +359,21 @@ void Transport::DammageCrew(EntityAI* killer, float howMuch, RString ammo) + } + if (CrewDammage(this, _driver, killer, howMuch, ammo)) + { +- if (_driver) ++ if (_driver && _driver->Brain()) + { + _driver->KilledBy(killer); + } + } + if (CrewDammage(this, _gunner, killer, howMuch, ammo)) + { +- if (_gunner) ++ if (_gunner && _gunner->Brain()) + { + _gunner->KilledBy(killer); + } + } + if (CrewDammage(this, _commander, killer, howMuch, ammo)) + { +- if (_commander) ++ if (_commander && _commander->Brain()) + { + _commander->KilledBy(killer); + }