--- a/rts/Game/WebbarBrowser.h +++ b/rts/Game/WebbarBrowser.h @@ -43,6 +43,7 @@ #include "WebbarWeapons.h" #include "WebbarShields.h" +#include "WebbarCloak.h" static bool WebbarSupports(const UnitDef* def) { @@ -78,7 +79,7 @@ if (!catalogSent) { catalogSent = true; if (std::getenv("WEBBAR_NETWORK") != nullptr) { static WebbarNetworkResult result; } - MAIN_THREAD_EM_ASM({ if (Module['webbarProtocol']) Module['webbarProtocol'](8, 3 /* area restore | shield state */); }); + MAIN_THREAD_EM_ASM({ if (Module['webbarProtocol']) Module['webbarProtocol'](8, 7 /* area restore | shield state | owner cloak */); }); MAIN_THREAD_EM_ASM({ if (Module['webbarIdentity']) Module['webbarIdentity']($0, $1, $2, $3); }, gu->myPlayerNum, gu->myTeam, gu->myAllyTeam, int(gu->spectating)); @@ -152,6 +153,7 @@ if (Module['webbarUnitState']) Module['webbarUnitState'](HEAPU8.slice($0, $0 + $1).buffer); }, packet.data(), packet.size()); PublishWebbarShields(); + PublishWebbarCloak(); PublishWebbarWorld(); } --- /dev/null +++ b/rts/Game/WebbarCloak.h @@ -0,0 +1,43 @@ +/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ +#pragma once + +#include "Lua/LuaRulesParams.h" + +// 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() +{ + struct Record { + uint32_t unit, flags; + float idleCost, movingCost, speed, decloakDistance; + }; + static_assert(sizeof(Record) == 24); + std::vector records; + uint32_t dropped = 0; + 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; } + 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; + const float* value = std::get_if(&it->second.value); + return value != nullptr && std::isfinite(*value) ? *value : -1.0f; + }; + const float requested = rule("wantcloak"); + const uint32_t flags = uint32_t(def->canCloak) | (uint32_t(requested >= 0) << 1) | (uint32_t(requested == 1) << 2) | + (uint32_t(unit->isCloaked) << 3) | (uint32_t(unit->wantCloak) << 4) | (uint32_t(rule("cannotcloak") == 1) << 5) | + (uint32_t(rule("areacloaked") == 1) << 6) | (uint32_t(unit->IsStunned()) << 7) | (uint32_t(unit->beingBuilt) << 8) | + (uint32_t(unit->stealth) << 9) | (uint32_t(unit->sonarStealth) << 10); + records.push_back({uint32_t(unit->id), flags, std::max(0.0f, def->cloakCost), std::max(0.0f, def->cloakCostMoving), + unit->speed.w, std::max(0.0f, unit->decloakDistance)}); + } + const uint32_t header[] = {0x31434257, uint32_t(gs->frameNum), uint32_t(records.size()), dropped}; + std::vector packet(sizeof(header) + records.size() * sizeof(Record)); + std::memcpy(packet.data(), header, sizeof(header)); + if (!records.empty()) std::memcpy(packet.data() + sizeof(header), records.data(), records.size() * sizeof(Record)); + MAIN_THREAD_EM_ASM({ + if (Module['webbarCloak']) Module['webbarCloak'](HEAPU8.slice($0, $0 + $1).buffer); + }, packet.data(), packet.size()); +}