--- a/rts/Game/WebbarCloakEvents.h +++ b/rts/Game/WebbarCloakEvents.h @@ -20,6 +20,18 @@ inline uint32_t nextID = 0, dropped = 0; inline std::unordered_map ownershipLifetimes; inline uint32_t nextLifetime = 0; +inline std::unordered_map publishedBuildStates; + +// Observe only after the same owner/live/applicability/cap filters as WBC4. +// A new lifetime or completion needs current native command descriptions in +// this publication, before the browser applies its ordinary default policy. +inline bool CommandMetadataChanged(int unitID, bool beingBuilt) +{ + const auto [it, inserted] = publishedBuildStates.emplace(unitID, beingBuilt); + const bool changed = inserted || it->second != beingBuilt; + it->second = beingBuilt; + return changed; +} // Called only after the publisher's local-owner and record-limit filters. // Hidden unit creation/transfers cannot advance this presentation counter. @@ -46,6 +58,7 @@ inline void OwnershipChanged(int unitID) { ownershipLifetimes.erase(unitID); + publishedBuildStates.erase(unitID); pending.erase(std::remove_if(pending.begin(), pending.end(), [unitID](const Pending& entry) { return entry.event.unit == uint32_t(unitID); }), pending.end()); --- a/rts/Game/WebbarCloak.h +++ b/rts/Game/WebbarCloak.h @@ -6,7 +6,7 @@ // Owner-only, read-only presentation. BAR owns cloak requests, resource use, // proximity checks and recloak delays. Never infer a reason by scanning enemies. -static void PublishWebbarCloak() +static bool PublishWebbarCloak() { struct Record { uint32_t unit, flags; @@ -17,11 +17,13 @@ static_assert(sizeof(Record) == 32); std::vector records; uint32_t dropped = 0; + bool commandsChanged = false; for (const CUnit* unit: unitHandler.GetActiveUnits()) { if (unit->isDead || unit->team != gu->myTeam) continue; const auto* def = unit->unitDef; if (!def->canCloak && !unit->isCloaked && !unit->stealth && !unit->sonarStealth && !def->canResurrect && unit->stockpileWeapon == nullptr) continue; if (records.size() >= 4096) { ++dropped; continue; } + commandsChanged |= webbarCloakEvents::CommandMetadataChanged(unit->id, unit->beingBuilt); const auto rule = [&](const char* name) { const auto it = unit->modParams.find(name); if (it == unit->modParams.end() || !(it->second.los & LuaRulesParams::RULESPARAMLOS_PRIVATE_MASK)) return -1.0f; @@ -54,4 +56,5 @@ MAIN_THREAD_EM_ASM({ if (Module['webbarCloak']) Module['webbarCloak'](HEAPU8.slice($0, $0 + $1).buffer); }, packet.data(), packet.size()); + return commandsChanged; } --- a/rts/Game/WebbarBrowser.h +++ b/rts/Game/WebbarBrowser.h @@ -98,7 +98,8 @@ } } } - if (gs->frameNum % 30 == 0) { + const bool cloakCommandsChanged = PublishWebbarCloak(); + if (cloakCommandsChanged || gs->frameNum % 30 == 0) { // Batch descriptions into one host call, including native state labels. // Strings are decoded synchronously before the temporary pointer table dies. std::vector words(1, 0); @@ -126,8 +127,8 @@ } units.push([unit, commands]); } - Module['webbarCommandDescriptions'](units); - }, words.data(), words.size()); + Module['webbarCommandDescriptions'](units, $2); + }, words.data(), words.size(), gs->frameNum); } const CTeam* team = teamHandler.Team(gu->myTeam); const float economy[] = {team->res.metal, team->resStorage.metal, team->resPrevIncome.metal, team->resPrevExpense.metal, team->resPrevPull.metal, @@ -156,7 +157,6 @@ if (Module['webbarUnitState']) Module['webbarUnitState'](HEAPU8.slice($0, $0 + $1).buffer); }, packet.data(), packet.size()); PublishWebbarShields(); - PublishWebbarCloak(); webbarWorkAudio::Publish(); PublishWebbarWorld(); PublishWebbarResurrection();