Ver Fonte

Remove the squad attachment ownership model and the hop-on bindings

slot:set_squad_attachment, its ownership checks and its type-26 squad
encoder are removed. slot:set_mission_effect is no longer exposed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
stan há 4 dias atrás
pai
commit
73400ca9b5
22 ficheiros alterados com 21 adições e 598 exclusões
  1. 0 2
      Sunrise/Sunrise.vcxproj
  2. 0 44
      Sunrise/src/middleware/bap/activity_message/activity_scriptable_auth_body_codec.cpp
  3. 0 38
      Sunrise/src/middleware/bap/activity_message/squad_attachment_auth.h
  4. 0 74
      Sunrise/src/server/activity/activity_sdk_device_runtime.cpp
  5. 0 9
      Sunrise/src/server/activity/activity_sdk_device_runtime.h
  6. 0 1
      Sunrise/src/server/activity/activity_sdk_squad_runtime.cpp
  7. 9 28
      Sunrise/src/server/activity/host_runtime.h
  8. 0 1
      Sunrise/src/server/activity/host_runtime_internal.h
  9. 3 22
      Sunrise/src/server/activity/host_runtime_scriptable.cpp
  10. 1 3
      Sunrise/src/server/activity/host_runtime_scriptable_requests.cpp
  11. 1 25
      Sunrise/src/server/activity/host_runtime_scriptable_staging.cpp
  12. 3 53
      Sunrise/src/server/activity/mission/mission_api_slot.h
  13. 0 2
      Sunrise/src/server/activity/mission/mission_script_lua_event_fields.cpp
  14. 0 84
      Sunrise/src/server/activity/mission/mission_script_lua_slot_api.cpp
  15. 0 3
      Sunrise/src/server/activity/mission/mission_script_runtime_delivery.cpp
  16. 0 30
      Sunrise/src/server/activity/mission/mission_script_runtime_dispatch.cpp
  17. 0 168
      Sunrise/src/server/activity/squad_attachment_ownership_validation.h
  18. 0 1
      Sunrise/src/server/bap/bap_activity_link_view.cpp
  19. 2 4
      Sunrise/src/server/bap/bap_scriptable_override_route.cpp
  20. 2 2
      Sunrise/src/server/bap/runtime.h
  21. 0 1
      Sunrise/src/state/activity/mission/activity_mission_state.cpp
  22. 0 3
      Sunrise/src/state/activity/mission/definition.h

+ 0 - 2
Sunrise/Sunrise.vcxproj

@@ -2216,11 +2216,9 @@
     <ClInclude Include="src\middleware\bap\activity_message\mission_auth_patch.h" />
     <ClInclude Include="src\middleware\bap\activity_message\mission_effect_auth.h" />
     <ClInclude Include="src\client\content\activity\activity_sdk_spawn_reference.h" />
-    <ClInclude Include="src\middleware\bap\activity_message\squad_attachment_auth.h" />
     <ClInclude Include="src\middleware\bap\activity_message\volume_toggle_auth.h" />
     <ClInclude Include="src\server\activity\mission\mission_observation_epoch.h" />
     <ClInclude Include="src\server\activity\mission\mission_trigger_observation.h" />
-    <ClInclude Include="src\server\activity\squad_attachment_ownership_validation.h" />
     <ClInclude Include="src\middleware\bap\activity_message\music_section_auth.h" />
     <ClInclude Include="src\middleware\bap\activity_message\roster_presence.h" />
     <ClInclude Include="src\middleware\bap\activity_message\scene_events_auth.h" />

+ 0 - 44
Sunrise/src/middleware/bap/activity_message/activity_scriptable_auth_body_codec.cpp

@@ -5,7 +5,6 @@
 #include "combatant_auth.h"
 #include "mission_auth_patch.h"
 #include "scriptable_auth_internal.h"
-#include "squad_attachment_auth.h"
 
 // The two scriptable-auth bodies whose encoded width depends on the caller's values: the type-2
 // actor channel set and keyed lanes, and the type-34 filter predicate list. Both select their
@@ -844,47 +843,4 @@ bool validate_type2_body(std::span<const std::byte> input, std::size_t bitCount)
            && reader.remaining_bits() == input.size() * 8U - bitCount && finish_padding(reader);
 }
 
-bool encode_type26_squad_selection(const Type26SquadSelection& selection,
-                                   std::span<std::byte> output,
-                                   std::size_t& written,
-                                   std::size_t& writtenBits) noexcept {
-    written = 0;
-    writtenBits = 0;
-    if ((selection.active && selection.slotIndex < 0)
-        || (!selection.active
-            && (selection.registryKey != kClientRefAbsentKey || selection.slotIndex != -1))) {
-        return false;
-    }
-    const std::size_t bitCount = selection.active ? kType26SquadBitCount : kType26EmptyBitCount;
-    const std::size_t byteCount = (bitCount + 7U) / 8U;
-    if (output.size() < byteCount) {
-        return false;
-    }
-    bits::Writer writer(output.first(byteCount));
-    if (!writer.write(0, kBoolWidth) || !writer.write(0, kBoolWidth)) {
-        return false;
-    }
-    // Preserve all four authored zero scalars. Two are native control counters, not revisions
-    // that an attachment writer may freely advance. Auth dirty reconciliation owns the update.
-    for (std::size_t index = 0; index < 4; ++index) {
-        if (!writer.write(kSigned32Bias, kSigned32Width)) {
-            return false;
-        }
-    }
-    if (!write_absent_client_ref(writer) || !writer.write(selection.active ? 1U : 0U, kBoolWidth)) {
-        return false;
-    }
-    if (selection.active
-        && (!writer.write(kType26SquadSelectionSchema, kSigned32Width)
-            || !write_lane_client_ref(writer, {selection.registryKey, 1, selection.slotIndex}))) {
-        return false;
-    }
-    if (writer.bit_count() != bitCount || !writer.finish(written) || written != byteCount) {
-        written = 0;
-        return false;
-    }
-    writtenBits = bitCount;
-    return true;
-}
-
 } // namespace sunrise::middleware::bap::activity_message::scriptable_auth

+ 0 - 38
Sunrise/src/middleware/bap/activity_message/squad_attachment_auth.h

@@ -1,38 +0,0 @@
-#pragma once
-
-#include <cstddef>
-#include <cstdint>
-#include <span>
-
-#include "mission_effect_auth.h"
-
-namespace sunrise::middleware::bap::activity_message::scriptable_auth {
-
-/**
- * Type-26 attachment Auth: the authored prefix left neutral, then an optional squad selection. The
- * slot type and schema are the mission-effect ones; only the selection tail differs.
- */
-inline constexpr std::uint8_t kType26SlotType = mission_effect::kSlotType;
-inline constexpr std::uint32_t kType26Schema = mission_effect::kSchema;
-inline constexpr std::uint32_t kType26SquadSelectionSchema = 0x80809157U;
-inline constexpr std::size_t kType26EmptyBitCount = mission_effect::kBits;
-inline constexpr std::size_t kType26SquadBitCount = 273;
-inline constexpr std::size_t kType26MaximumByteCount = (kType26SquadBitCount + 7U) / 8U;
-
-/** Selects a squad by its ClientRef; the client resolves its members, never a raw actor handle. */
-struct Type26SquadSelection final {
-    std::uint32_t registryKey{0x811C9DC5U};
-    std::int16_t slotIndex{-1};
-    bool active{};
-};
-
-/**
- * Encodes the squad selection, or the empty body that clears it. The caller owns the attachment
- * source; the body only moves the attachment and does not make the squad immune to damage.
- */
-[[nodiscard]] bool encode_type26_squad_selection(const Type26SquadSelection& selection,
-                                                 std::span<std::byte> output,
-                                                 std::size_t& written,
-                                                 std::size_t& writtenBits) noexcept;
-
-} // namespace sunrise::middleware::bap::activity_message::scriptable_auth

+ 0 - 74
Sunrise/src/server/activity/activity_sdk_device_runtime.cpp

@@ -13,7 +13,6 @@
 #include "../../middleware/bap/activity_message/music_section_auth.h"
 #include "../../middleware/bap/activity_message/scene_events_auth.h"
 #include "../../middleware/bap/activity_message/sensor_auth_update.h"
-#include "../../middleware/bap/activity_message/squad_attachment_auth.h"
 #include "../../middleware/bap/activity_message/squad_objective_auth.h"
 #include "../../middleware/content/packages/tables/region_reader.h"
 #include "../../state/activity/runtime.h"
@@ -23,7 +22,6 @@
 #include "activity_sdk_actor_sequences.h"
 #include "activity_sdk_device_internal.h"
 #include "host_runtime.h"
-#include "squad_attachment_ownership_validation.h"
 
 namespace sunrise::server::activity::activity_sdk_devices {
 namespace {
@@ -184,78 +182,6 @@ Status apply_auth_reserved(const sdk::BoundView& view,
     return Status::refused;
 }
 
-/**
- * Selects or clears the squad a type-26 attachment follows. Both slots must belong to one authored
- * object in the same held state, and the source must still be the placement the script named.
- */
-Status
-set_squad_attachment_reserved(const sdk::BoundView& view,
-                              std::uint32_t slotRow,
-                              std::uint32_t sourceRow,
-                              std::uint64_t sourceSpawnGeneration,
-                              bool active,
-                              const host::ScriptableOutputReservation& reservation) noexcept {
-    PreparedDevice target{}, source{};
-    Status status = prepare_slot(view, slotRow, target);
-    if (status != Status::ready) {
-        return status;
-    }
-    status = prepare_slot(view, sourceRow, source);
-    if (status != Status::ready) {
-        return status;
-    }
-    if (target.activityClientGeneration != source.activityClientGeneration
-        || target.effectiveRegion != source.effectiveRegion
-        || target.scenarioRow != source.scenarioRow || target.stateRow != source.stateRow) {
-        return Status::staleActivityClient;
-    }
-    host::SquadAttachmentOwnership owned{};
-    const auto digest = view.catalog->sdk_build_sha256();
-    if (digest.size() != owned.sdkBuildSha256.size()) {
-        return Status::wrongSdkBuild;
-    }
-    std::copy(digest.begin(), digest.end(), owned.sdkBuildSha256.begin());
-    const auto payload = view.catalog->payload_sha256();
-    if (payload.size() != owned.sdkPayloadSha256.size()) {
-        return Status::wrongSdkBuild;
-    }
-    std::copy(payload.begin(), payload.end(), owned.sdkPayloadSha256.begin());
-    owned.source = source.target;
-    owned.sourceSpawnGeneration = sourceSpawnGeneration;
-    owned.active = active;
-    if (!host::attachments::same_owner_pair(target.target, owned)) {
-        return Status::refused;
-    }
-    std::vector<host::PendingScriptableOverride> estate;
-    if (!host::scriptable_auth_estate(view.binding, target.activityClientGeneration, estate)) {
-        return Status::staleBinding;
-    }
-    const auth::Type26SquadSelection selection =
-        active ? auth::Type26SquadSelection{source.target.registryKey,
-                                            static_cast<std::int16_t>(source.target.slotIndex),
-                                            true}
-               : auth::Type26SquadSelection{};
-    std::array<std::byte, auth::kType26MaximumByteCount> body{};
-    std::size_t written = 0, bits = 0;
-    if (!auth::encode_type26_squad_selection(selection, body, written, bits)
-        || !host::attachments::admits(
-            target.target, owned, estate, std::span(body).first(written), bits)) {
-        return Status::refused;
-    }
-    const bool queued = server::bap::request_activity_sdk_auth_override(
-        view.binding,
-        target.target,
-        target.target.stateLocalRoster ? &target.generatedRosterGroup : nullptr,
-        std::span(body).first(written),
-        static_cast<std::uint16_t>(bits),
-        target.effectiveRegion,
-        target.activityClientGeneration,
-        &reservation,
-        host::ScriptableOverrideKind::sdkAuth,
-        owned);
-    return queued ? Status::queued : Status::refused;
-}
-
 /** Sets the active set of one device's objects against a reserved Host output revision. */
 Status set_objects_active_reserved(const sdk::BoundView& view,
                                    std::uint32_t slotRow,

+ 0 - 9
Sunrise/src/server/activity/activity_sdk_device_runtime.h

@@ -129,15 +129,6 @@ play_combatant_sequence_reserved(const state::activity_sdk::BoundView& view,
     const host::ScriptableOutputReservation& reservation,
     host::ScriptableOverrideKind kind = host::ScriptableOverrideKind::sdkAuth) noexcept;
 
-/** Selects or clears the squad a type-26 attachment follows, for one placement spawn generation. */
-[[nodiscard]] Status
-set_squad_attachment_reserved(const state::activity_sdk::BoundView& view,
-                              std::uint32_t slotRow,
-                              std::uint32_t sourceRow,
-                              std::uint64_t sourceSpawnGeneration,
-                              bool active,
-                              const host::ScriptableOutputReservation& reservation) noexcept;
-
 /**
  * Instantiates or removes package-authored type-4 entries, one push for the whole run.
  * A row is dropped, not refused, when the client's state does not hold it, or when it names a

+ 0 - 1
Sunrise/src/server/activity/activity_sdk_squad_runtime.cpp

@@ -7,7 +7,6 @@
 
 #include "../../middleware/content/packages/tables/authored_squad_reader.h"
 #include "../../middleware/content/packages/tables/region_reader.h"
-#include "../../state/activity/membership/activity_membership_query.h"
 #include "../../state/activity/runtime.h"
 #include "../../state/activity_sdk/squad_profiles.h"
 #include "../../state/build_data/runtime.h"

+ 9 - 28
Sunrise/src/server/activity/host_runtime.h

@@ -213,7 +213,6 @@ struct ScriptableTarget final {
     std::uint8_t slotType{};
     /** This group is safe only in the exact selected state pinned by the requesting link. */
     bool stateLocalRoster{};
-    bool operator==(const ScriptableTarget&) const = default;
 };
 
 /** Exact unarmed Host output lane held while Mission State publishes its matching revision. */
@@ -878,19 +877,6 @@ struct PendingIncident final {
     std::uint64_t revision{};
 };
 
-/** The squad a type-26 attachment follows, and the SDK and placement lifetime it was chosen in. */
-struct SquadAttachmentOwnership final {
-    std::array<std::byte, 32> sdkBuildSha256{};
-    /** Catalog payload digest; an update must match the one the retained attachment used. */
-    std::array<std::byte, 32> sdkPayloadSha256{};
-    ScriptableTarget source{};
-    /** Placement spawn generation of the source squad. */
-    std::uint64_t sourceSpawnGeneration{};
-    /** True to select the source, false to clear the selection. */
-    bool active{};
-    bool operator==(const SquadAttachmentOwnership&) const = default;
-};
-
 /** Immutable typed body retained byte-for-byte until exact transport staging. */
 struct PendingScriptableOverride final {
     std::uint32_t actorSpawnGeneration{};
@@ -921,10 +907,6 @@ struct PendingScriptableOverride final {
     /** Activity lifetime state for a lifetime request; ignored by every other kind. */
     std::uint8_t lifetimeState{kDefaultLifetimeState};
     bool sdkCompiled{};
-    /** Present only for a type-26 attachment written through set_squad_attachment. */
-    std::optional<SquadAttachmentOwnership> squadAttachment{};
-    /** For a squadObjective row: spawn generation of the placement it replaced. Zero otherwise. */
-    std::uint64_t squadSpawnGeneration{};
 };
 
 /**
@@ -1223,16 +1205,15 @@ request_lifetime_override(const state::activity::SessionBinding& binding,
                           const ScriptableOutputReservation* reservation = nullptr) noexcept;
 
 /** Queues one exact SDK-compiled Auth body for a generation-bound ClientRef. */
-[[nodiscard]] bool request_sdk_auth_override(
-    const state::activity::SessionBinding& binding,
-    const ScriptableTarget& target,
-    const state::build_data::scenarios::RosterGroup* stateLocalRosterGroup,
-    std::span<const std::byte> body,
-    std::uint16_t bitCount,
-    std::uint64_t expectedActivityClientGeneration,
-    const ScriptableOutputReservation* reservation = nullptr,
-    ScriptableOverrideKind kind = ScriptableOverrideKind::sdkAuth,
-    std::optional<SquadAttachmentOwnership> attachment = std::nullopt) noexcept;
+[[nodiscard]] bool
+request_sdk_auth_override(const state::activity::SessionBinding& binding,
+                          const ScriptableTarget& target,
+                          const state::build_data::scenarios::RosterGroup* stateLocalRosterGroup,
+                          std::span<const std::byte> body,
+                          std::uint16_t bitCount,
+                          std::uint64_t expectedActivityClientGeneration,
+                          const ScriptableOutputReservation* reservation = nullptr,
+                          ScriptableOverrideKind kind = ScriptableOverrideKind::sdkAuth) noexcept;
 
 /** @return True while any live instance holds a committed output the transport has not sent. */
 [[nodiscard]] bool any_output_pending() noexcept;

+ 0 - 1
Sunrise/src/server/activity/host_runtime_internal.h

@@ -49,7 +49,6 @@ struct ScriptableRequest final {
     std::array<std::byte,
                middleware::bap::activity_message::sensor_auth_update::kAuthOverrideByteCapacity>
         authBody{};
-    std::optional<SquadAttachmentOwnership> squadAttachment{};
     std::size_t requestedCountLength{};
     std::uint16_t authBitCount{};
     std::uint16_t authByteCount{};

+ 3 - 22
Sunrise/src/server/activity/host_runtime_scriptable.cpp

@@ -13,7 +13,6 @@
 #include "host_runtime_counter_auth.h"
 #include "host_runtime_ghost_link.h"
 #include "host_runtime_internal.h"
-#include "squad_attachment_ownership_validation.h"
 
 namespace sunrise::server::activity::host {
 namespace {
@@ -186,7 +185,7 @@ namespace detail {
            && left.byteCount == right.byteCount && left.channel == right.channel
            && left.channelValue == right.channelValue && left.channelHash == right.channelHash
            && left.lifetimeState == right.lifetimeState && left.body == right.body
-           && left.sdkCompiled == right.sdkCompiled && left.squadAttachment == right.squadAttachment
+           && left.sdkCompiled == right.sdkCompiled
            && (!left.target.stateLocalRoster
                || same_group(left.stateLocalRosterGroup, right.stateLocalRosterGroup));
 }
@@ -207,14 +206,6 @@ namespace detail {
     }
     for (PendingScriptableOverride& retained : instance.scriptableAuthEstate) {
         if (same_client_ref(retained.target, pending.target)) {
-            // An objective assignment rewrites the squad's ClientRef row (kind squadObjective,
-            // generation = objective revision, patch body without the spawn generation). Carry the
-            // placement's spawn generation so attachment ownership can still name its lifetime.
-            if (owned.kind == ScriptableOverrideKind::squadObjective) {
-                owned.squadSpawnGeneration = retained.kind == ScriptableOverrideKind::squad
-                                                 ? retained.generation
-                                                 : retained.squadSpawnGeneration;
-            }
             retained = owned;
             return true;
         }
@@ -249,8 +240,6 @@ retained_sequence_combatant(const Instance& instance, const ScriptableRequest& r
     // Lifetime requests carry no ClientRef.
     const bool untargeted = request.kind == ScriptableOverrideKind::lifetime;
     if ((!untargeted && !supported_target(request.target))
-        || (request.squadAttachment
-            && (request.kind != ScriptableOverrideKind::sdkAuth || request.burstMember))
         || !state::activity::binding_matches(request.binding)) {
         return false;
     }
@@ -364,7 +353,6 @@ void apply_scriptable_control(const ScriptableRequest& request, std::uint64_t no
     }
     PendingScriptableOverride pending{};
     pending.target = request.target;
-    pending.squadAttachment = request.squadAttachment;
     pending.stateLocalRosterGroup = request.stateLocalRosterGroup;
     pending.revision = request.expectedRevision == 0 ? instance->view.scriptableRevision + 1
                                                      : request.expectedRevision;
@@ -373,13 +361,7 @@ void apply_scriptable_control(const ScriptableRequest& request, std::uint64_t no
     pending.expectedActivityClientGeneration = request.expectedActivityClientGeneration;
     std::size_t written = 0;
     std::size_t writtenBits = 0;
-    bool encoded = (untargeted || guard != nullptr)
-                   && request.authByteCount <= request.authBody.size()
-                   && attachments::admits(request.target,
-                                          request.squadAttachment,
-                                          instance->scriptableAuthEstate,
-                                          std::span(request.authBody).first(request.authByteCount),
-                                          request.authBitCount);
+    bool encoded = untargeted || guard != nullptr;
     if (encoded && request.kind == ScriptableOverrideKind::lifetime) {
         pending.lifetimeState = request.lifetimeState;
     } else if (encoded && request.kind == ScriptableOverrideKind::squad) {
@@ -661,8 +643,7 @@ void apply_scriptable_control(const ScriptableRequest& request, std::uint64_t no
             pending.bitCount = static_cast<std::uint16_t>(composedBits);
         }
     }
-    if (!encoded || written > (std::numeric_limits<std::uint16_t>::max)()
-        || !attachments::permits_source_write(pending, instance->scriptableAuthEstate)) {
+    if (!encoded || written > (std::numeric_limits<std::uint16_t>::max)()) {
         ++g_refusedControls;
     } else {
         if (guard != nullptr && !guard->occupied) {

+ 1 - 3
Sunrise/src/server/activity/host_runtime_scriptable_requests.cpp

@@ -639,8 +639,7 @@ bool request_sdk_auth_override(
     std::uint16_t bitCount,
     std::uint64_t expectedActivityClientGeneration,
     const ScriptableOutputReservation* reservation,
-    ScriptableOverrideKind kind,
-    std::optional<SquadAttachmentOwnership> attachment) noexcept {
+    ScriptableOverrideKind kind) noexcept {
     if ((kind != ScriptableOverrideKind::sdkAuth && !counter_auth::compatible(kind, target))
         || body.empty() || body.size() > scene::kAuthOverrideByteCapacity
         || body.size() > (std::numeric_limits<std::uint16_t>::max)()
@@ -662,7 +661,6 @@ bool request_sdk_auth_override(
     request.authByteCount = static_cast<std::uint16_t>(body.size());
     request.expectedActivityClientGeneration = expectedActivityClientGeneration;
     request.kind = kind;
-    request.squadAttachment = attachment;
     return enqueue_request(request, reservation);
 }
 

+ 1 - 25
Sunrise/src/server/activity/host_runtime_scriptable_staging.cpp

@@ -6,7 +6,6 @@
 #include "../gameplay/squad_entity_retirement.h"
 #include "host_runtime_ghost_link.h"
 #include "host_runtime_internal.h"
-#include "squad_attachment_ownership_validation.h"
 
 namespace sunrise::server::activity::host {
 namespace {
@@ -84,20 +83,6 @@ bool pending_scriptable_override_for_activity_client(const state::activity::Sess
         cancel_pending(*instance, binding, revision);
         pending = false;
     }
-    if (pending
-        && (!attachments::permits_source_write(instance->pendingScriptable,
-                                               instance->scriptableAuthEstate)
-            || (instance->pendingScriptable.squadAttachment
-                && (instance->pendingScriptable.byteCount > instance->pendingScriptable.body.size()
-                    || !attachments::admits(instance->pendingScriptable.target,
-                                            instance->pendingScriptable.squadAttachment,
-                                            instance->scriptableAuthEstate,
-                                            std::span(instance->pendingScriptable.body)
-                                                .first(instance->pendingScriptable.byteCount),
-                                            instance->pendingScriptable.bitCount))))) {
-        cancel_pending(*instance, binding, instance->pendingScriptable.revision);
-        pending = false;
-    }
     if (pending) {
         output = instance->pendingScriptable;
         stamp_output_boundary(binding, output);
@@ -325,16 +310,7 @@ void note_scriptable_transport_staged(const state::activity::SessionBinding& bin
     AcquireSRWLockExclusive(&g_lock);
     Instance* const instance = find_instance(binding);
     ScriptableGuard* guard = instance != nullptr ? find_guard(*instance, pending.target) : nullptr;
-    const bool nextCounter =
-        staged_counter_matches(guard, pending) && pending.byteCount <= pending.body.size()
-        && (instance == nullptr
-            || attachments::permits_source_write(pending, instance->scriptableAuthEstate))
-        && (instance == nullptr || !pending.squadAttachment
-            || attachments::admits(pending.target,
-                                   pending.squadAttachment,
-                                   instance->scriptableAuthEstate,
-                                   std::span(pending.body).first(pending.byteCount),
-                                   pending.bitCount));
+    const bool nextCounter = staged_counter_matches(guard, pending);
     if (instance != nullptr && nextCounter && instance->view.outputPending
         && instance->view.outputKind == OutputKind::scriptableOverride
         && same_pending(instance->pendingScriptable, pending)) {

+ 3 - 53
Sunrise/src/server/activity/mission/mission_api_slot.h

@@ -7,10 +7,8 @@
 #include "../../../middleware/bap/activity_message/darkness_zone_auth.h"
 #include "../../../middleware/bap/activity_message/ghost_link_auth.h"
 #include "../../../middleware/bap/activity_message/map_generator_auth.h"
-#include "../../../middleware/bap/activity_message/mission_effect_auth.h"
 #include "../../../middleware/bap/activity_message/music_section_auth.h"
 #include "../../../middleware/bap/activity_message/scriptable_auth_body.h"
-#include "../../../middleware/bap/activity_message/squad_attachment_auth.h"
 #include "../../../middleware/bap/activity_message/squad_objective_auth.h"
 #include "../../../middleware/bap/activity_message/volume_toggle_auth.h"
 #include "../../../state/activity/mission/definition.h"
@@ -80,13 +78,13 @@ inline constexpr ValueSpec kObjectFilterArguments[]{
      .optional = true,
      .bound = between(1, static_cast<std::int64_t>(slot_auth::kType34PredicateCapacity)),
      .element = kFilterObject,
-     .note = "adds one test per object, for example every crystal a beam hop-on tethers"},
+     .note = "adds one test per object"},
     {.name = "squad",
      .kind = ValueKind::handle,
      .ref = "slot",
      .optional = true,
      .slot = type_gate(slot_format::kSquadSlotType),
-     .note = "adds a test: the squad a shield or tether hop-on hosts on"},
+     .note = "adds a test: this squad"},
     {.name = "inside",
      .kind = ValueKind::handle,
      .ref = "slot",
@@ -100,8 +98,6 @@ inline constexpr ValueSpec kObjectFilterArguments[]{
      .element = kVolume,
      .note = "adds a test: objects inside any of these volumes"},
 };
-inline constexpr ValueSpec kRequiredActive{
-    .name = "active", .kind = ValueKind::boolean, .note = "true turns it on, false off"};
 inline constexpr ValueSpec kVolumeActiveArguments[]{
     {.name = "volume",
      .kind = ValueKind::handle,
@@ -109,38 +105,7 @@ inline constexpr ValueSpec kVolumeActiveArguments[]{
      .slot = type_gate(slot_auth::kVolumeSlotType),
      .scope = Scope::sameObject,
      .note = "an authored volume of the toggle's own object"},
-    kRequiredActive,
-};
-inline constexpr ValueSpec kSquadAttachmentArguments[]{
-    {.name = "source",
-     .kind = ValueKind::handle,
-     .ref = "slot",
-     .slot = exact_gate(slot_format::kSquadSlotType),
-     .scope = Scope::sameObject,
-     .note = "the squad of the same object the attachment follows"},
-    {.name = "spawn_generation",
-     .kind = ValueKind::integer,
-     .bound = between(1, kInt32Maximum),
-     .note = "the source placement's spawn_generation; any other placement is refused as stale"},
-    kRequiredActive,
-};
-inline constexpr ValueSpec kMissionEffectArguments[]{
-    {.name = "filter",
-     .kind = ValueKind::handle,
-     .ref = "slot",
-     .optional = true,
-     .slot = type_gate(slot_auth::kType34SlotType),
-     .note = "the type-34 filter selecting the hosts; required while enabled"},
-    {.name = "enabled",
-     .kind = ValueKind::boolean,
-     .optional = true,
-     .fallback = default_boolean(true)},
-    {.name = "revision",
-     .kind = ValueKind::integer,
-     .optional = true,
-     .fallback = default_integer(1),
-     .bound = between(1, kInt32Maximum),
-     .note = "raise it to make the client apply an unchanged effect again"},
+    {.name = "active", .kind = ValueKind::boolean, .note = "true turns it on, false off"},
 };
 inline constexpr ValueSpec kWatchDamageArguments[]{
     {.name = "target", .kind = ValueKind::handle, .ref = "slot", .slot = kExactObject},
@@ -501,21 +466,6 @@ inline constexpr MemberSpec kSlotMembers[]{
      .arguments = kVolumeActiveArguments,
      .returns = kRequestKey,
      .note = "turns one authored volume on or off through its type-32 toggle"},
-    {.name = "set_squad_attachment",
-     .kind = MemberKind::request,
-     .call = CallForm::named,
-     .gate = exact_gate(slot_auth::kType26SlotType),
-     .arguments = kSquadAttachmentArguments,
-     .returns = kRequestKey,
-     .effect = "slot.set_squad_attachment",
-     .note = "selects or clears the squad a type-26 attachment follows"},
-    {.name = "set_mission_effect",
-     .kind = MemberKind::request,
-     .call = CallForm::named,
-     .gate = schema_gate(slot_auth::kType26SlotType),
-     .arguments = kMissionEffectArguments,
-     .returns = kRequestKey,
-     .note = "attaches the authored hop-on effect to the entities a type-34 filter selects"},
     {.name = "watch_damage",
      .kind = MemberKind::request,
      .call = CallForm::named,

+ 0 - 2
Sunrise/src/server/activity/mission/mission_script_lua_event_fields.cpp

@@ -121,8 +121,6 @@ template <bool Row> void client_message_row(lua_State* state, const host::Event&
         return "slot.set_interactable_object";
     case ActionKind::setGhostLink:
         return "slot.set_ghost_link";
-    case ActionKind::setSquadAttachment:
-        return "slot.set_squad_attachment";
     case ActionKind::watchDamage:
         return "slot.watch_damage";
     case ActionKind::assignCombatObjective:

+ 0 - 84
Sunrise/src/server/activity/mission/mission_script_lua_slot_api.cpp

@@ -7,8 +7,6 @@
 
 #include "../../../middleware/bap/activity_message/auth_fields.h"
 #include "../../../middleware/bap/activity_message/auth_schema_catalog.h"
-#include "../../../middleware/bap/activity_message/mission_effect_auth.h"
-#include "../../../middleware/bap/activity_message/squad_attachment_auth.h"
 #include "../../../middleware/bap/activity_message/volume_toggle_auth.h"
 #include "mission_script_lua_dispatch.h"
 #include "mission_script_lua_internal.h"
@@ -108,8 +106,6 @@ namespace auth_catalog = middleware::bap::activity_message::auth_schema_catalog;
 namespace {
 
 constexpr const api::MemberSpec& kVolumeActive = api::slot_member("set_volume_active");
-constexpr const api::MemberSpec& kSquadAttachment = api::slot_member("set_squad_attachment");
-constexpr const api::MemberSpec& kMissionEffect = api::slot_member("set_mission_effect");
 
 /** Reads one required boolean argument; any other type is an error. */
 [[nodiscard]] bool required_boolean(lua_State* state, const char* name) {
@@ -159,84 +155,6 @@ constexpr const api::MemberSpec& kMissionEffect = api::slot_member("set_mission_
         state, source, scriptable_auth::kType32Schema, scriptable_auth::kType32BitCount, body);
 }
 
-/**
- * Selects or clears the squad a type-26 attachment follows. spawn_generation names the source
- * placement; a request for any other placement is refused as stale.
- */
-[[nodiscard]] int slot_set_squad_attachment(lua_State* state) {
-    const auto* const handle =
-        static_cast<const SlotHandle*>(luaL_checkudata(state, 1, kSlotMetatable));
-    refuse_unknown_arguments(state, kSquadAttachment);
-    const SlotHandle sourceHandle = checked_argument<SlotHandle>(
-        state, required_key(kSquadAttachment, "source", api::ValueKind::handle), kSlotMetatable);
-    const lua_Integer generation = checked_integer_argument(
-        state, required_key(kSquadAttachment, "spawn_generation", api::ValueKind::integer));
-    const bool active =
-        required_boolean(state, required_key(kSquadAttachment, "active", api::ValueKind::boolean));
-    SlotDefinition target{}, source{};
-    if (!current_slot(state, *handle, target) || !current_slot(state, sourceHandle, source)) {
-        return luaL_error(state, "attachment or squad source is stale or invalid");
-    }
-    if (target.slotType != scriptable_auth::kType26SlotType
-        || target.authSchema != scriptable_auth::kType26Schema
-        || (target.flags & format::kSlotSchemaJoinExact) == 0
-        || source.slotType != format::kSquadSlotType
-        || source.authSchema != format::kSquadAuthSchema
-        || (source.flags & format::kSlotSchemaJoinExact) == 0
-        || target.objectTag != source.objectTag || target.registryKey != source.registryKey) {
-        return luaL_error(state, "attachment requires an exact same-owner type-1 source");
-    }
-    if (generation <= 0 || generation > (std::numeric_limits<std::int32_t>::max)()) {
-        return luaL_error(state, "spawn_generation must be a positive signed 32-bit integer");
-    }
-    CallFrame& frame = active_frame(state);
-    Intent intent{};
-    intent.kind = IntentKind::setSquadAttachment;
-    intent.firstRow = target.nativeRow;
-    intent.secondRow = source.nativeRow;
-    intent.sourceSpawnGeneration = static_cast<std::uint64_t>(generation);
-    intent.active = active;
-    return queue_intent(state, frame, intent);
-}
-
-/** Attaches the authored hop-on effect to the entities a type-34 filter selects. */
-[[nodiscard]] int slot_set_mission_effect(lua_State* state) {
-    namespace effect = middleware::bap::activity_message::mission_effect;
-    const auto* const handle =
-        static_cast<const SlotHandle*>(luaL_checkudata(state, 1, kSlotMetatable));
-    refuse_unknown_arguments(state, kMissionEffect);
-    SlotDefinition slot{};
-    if (!current_slot(state, *handle, slot) || slot.slotType != effect::kSlotType
-        || slot.authSchema != effect::kSchema) {
-        return luaL_error(state, "mission effect requires an authored type-26 hop-on");
-    }
-    const bool enabled =
-        optional_boolean_argument(state, boolean_option(kMissionEffect, "enabled"));
-    const char* const filterKey = optional_key(kMissionEffect, "filter", api::ValueKind::handle);
-    scriptable_auth::Type2LaneClientRef filter{};
-    if (enabled) {
-        lua_getfield(state, 2, filterKey);
-        const bool present = !lua_isnil(state, -1);
-        lua_pop(state, 1);
-        if (!present
-            || !optional_slot_reference(
-                state, filterKey, scriptable_auth::kType34SlotType, filter)) {
-            return luaL_error(state, "mission effect requires a type-34 filter");
-        }
-    }
-    const lua_Integer revision =
-        optional_integer_argument(state, integer_option(kMissionEffect, "revision"));
-    if (!valid_counter(revision)) {
-        return luaL_error(state, "effect revision must be positive");
-    }
-    std::array<std::byte, effect::kBytes> body{};
-    std::size_t written = 0;
-    if (!effect::encode(filter, enabled, static_cast<std::int32_t>(revision), body, written)) {
-        return luaL_error(state, "mission effect encoder failed");
-    }
-    return queue_slot_auth(state, slot, effect::kSchema, effect::kBits, body);
-}
-
 /** Pushes one property of the slot's Auth catalog entry, or nil when it has none. */
 template <typename Push> void push_auth(lua_State* state, const SlotDefinition& slot, Push push) {
     const auth_catalog::Type* const auth =
@@ -354,8 +272,6 @@ constexpr std::array<SlotBinding, std::size(api::kSlotMembers)> kSlotBindings{{
      [](lua_State* state, const SlotDefinition& slot) { lua_pushinteger(state, slot.flags); }},
     {"set_object_filter", &slot_set_object_filter},
     {"set_volume_active", &slot_set_volume_active},
-    {"set_squad_attachment", &slot_set_squad_attachment},
-    {"set_mission_effect", &slot_set_mission_effect},
     {"watch_damage", &slot_watch_damage},
     {"set_object_active", &slot_set_object_active},
     {"set_channel", &slot_set_channel},

+ 0 - 3
Sunrise/src/server/activity/mission/mission_script_runtime_delivery.cpp

@@ -419,9 +419,6 @@ void complete_delivery(RuntimeInstance& instance) noexcept {
     case lua_vm::IntentKind::signalAuthoredScene:
         result = "scene_event_staged";
         break;
-    case lua_vm::IntentKind::setSquadAttachment:
-        result = "attachment_staged";
-        break;
     case lua_vm::IntentKind::activateAuthoredScene:
         result = "scene_staged";
         break;

+ 0 - 30
Sunrise/src/server/activity/mission/mission_script_runtime_dispatch.cpp

@@ -408,36 +408,6 @@ void dispatch_intent(RuntimeInstance& instance, std::uint64_t now) noexcept {
         }
         return;
     }
-    case lua_vm::IntentKind::setSquadAttachment: {
-        host::ScriptableOutputReservation reservation{};
-        if (!reserve_delivery(instance, reservation)) {
-            if (instance.programStatus == ProgramStatus::loaded) {
-                refuse_delivery(instance,
-                                "attachment_refused",
-                                "host_reservation_unavailable",
-                                host::EffectOutcome::refused);
-            }
-            return;
-        }
-        const devices::Status status =
-            devices::set_squad_attachment_reserved(instance.view,
-                                                   intent.firstRow,
-                                                   intent.secondRow,
-                                                   intent.sourceSpawnGeneration,
-                                                   intent.active,
-                                                   reservation);
-        if (status == devices::Status::queued) {
-            await_host_commit(instance, now, "attachment_enqueued");
-        } else if (!abandon_reserved_delivery(instance, reservation)) {
-            return;
-        } else {
-            refuse_delivery(instance,
-                            "attachment_refused",
-                            devices::status_name(status),
-                            host::EffectOutcome::refused);
-        }
-        return;
-    }
     case lua_vm::IntentKind::bindCombatantToSquad: {
         host::ScriptableOutputReservation reservation{};
         if (!reserve_or_refuse(instance, reservation, "combatant_binding_refused")) {

+ 0 - 168
Sunrise/src/server/activity/squad_attachment_ownership_validation.h

@@ -1,168 +0,0 @@
-#pragma once
-
-#include <algorithm>
-
-#include "../../middleware/bap/activity_message/squad_attachment_auth.h"
-#include "../../state/activity_sdk/format.h"
-#include "host_runtime.h"
-
-namespace sunrise::server::activity::host::attachments {
-
-/**
- * Requires a type-26 attachment and the squad source that shares its object, registry, roster group
- * and state-local placement. Any authored pair with that shape is admitted; no content is named.
- */
-[[nodiscard]] inline bool same_owner_pair(const ScriptableTarget& target,
-                                          const SquadAttachmentOwnership& owned) noexcept {
-    const auto& source = owned.source;
-    namespace auth = middleware::bap::activity_message::scriptable_auth;
-    return owned.sdkBuildSha256 != std::array<std::byte, 32>{}
-           && owned.sdkPayloadSha256 != std::array<std::byte, 32>{}
-           && owned.sourceSpawnGeneration > 0 && owned.sourceSpawnGeneration <= 0x7FFFFFFFU
-           && target.slotType == auth::kType26SlotType && target.authSchema == auth::kType26Schema
-           && source.objectTag == target.objectTag && source.registryKey == target.registryKey
-           && source.slotType == state::activity_sdk::format::kSquadSlotType
-           && source.authSchema == state::activity_sdk::format::kSquadAuthSchema
-           && source.sdkObjectIndex == target.sdkObjectIndex
-           && source.stateLocalRoster == target.stateLocalRoster
-           && source.stateLocalRegion == target.stateLocalRegion
-           && source.rosterGroupIndex == target.rosterGroupIndex;
-}
-
-/** Full wire identity, independent of the occurrence metadata required by owned attachments. */
-[[nodiscard]] inline bool same_ref(const ScriptableTarget& left,
-                                   const ScriptableTarget& right) noexcept {
-    return left.objectTag == right.objectTag && left.registryKey == right.registryKey
-           && left.slotType == right.slotType && left.slotIndex == right.slotIndex;
-}
-
-/**
- * @return The placement spawn generation a retained squad row stands for. An objective assignment
- * replaces the placement row with a squadObjective patch that carries the generation forward.
- */
-[[nodiscard]] inline bool source_spawn_generation(const PendingScriptableOverride& row,
-                                                  std::uint64_t& value) noexcept {
-    if (row.byteCount == 0 || row.byteCount > row.body.size()) {
-        return false;
-    }
-    if (row.kind == ScriptableOverrideKind::squad) {
-        value = row.generation;
-        return true;
-    }
-    if (row.kind == ScriptableOverrideKind::squadObjective && row.squadSpawnGeneration != 0) {
-        value = row.squadSpawnGeneration;
-        return true;
-    }
-    return false;
-}
-
-/** @return True when the body exactly represents the owned selection and authored prefix. */
-[[nodiscard]] inline bool matches_body(const SquadAttachmentOwnership& owned,
-                                       std::span<const std::byte> body,
-                                       std::size_t bitCount) noexcept {
-    namespace auth = middleware::bap::activity_message::scriptable_auth;
-    std::array<std::byte, auth::kType26MaximumByteCount> expected{};
-    std::size_t written = 0, bits = 0;
-    const auth::Type26SquadSelection selection =
-        owned.active ? auth::Type26SquadSelection{owned.source.registryKey,
-                                                  static_cast<std::int16_t>(owned.source.slotIndex),
-                                                  true}
-                     : auth::Type26SquadSelection{};
-    return auth::encode_type26_squad_selection(selection, expected, written, bits)
-           && bits == bitCount && body.size() == written
-           && std::equal(body.begin(), body.end(), expected.begin());
-}
-
-/**
- * Checks fresh claim/update/clear against delivered source and attachment lifetimes.
- * The caller owns binding/held-region locks and must repeat this before transport publication.
- */
-[[nodiscard]] inline bool admits(const ScriptableTarget& target,
-                                 const std::optional<SquadAttachmentOwnership>& requested,
-                                 std::span<const PendingScriptableOverride> estate,
-                                 std::span<const std::byte> body,
-                                 std::size_t bitCount) noexcept {
-    if (!requested) {
-        // Occurrence metadata can change for shared unowned scriptables. Only retained
-        // attachment ownership requires an owned update, including after an owned clear.
-        for (const auto& row : estate) {
-            if (same_ref(row.target, target) && row.squadAttachment) {
-                return false;
-            }
-        }
-        return true;
-    }
-    const PendingScriptableOverride* previous = nullptr;
-    for (const auto& row : estate) {
-        if (same_ref(row.target, target)) {
-            if (previous != nullptr || row.target != target) {
-                return false;
-            }
-            previous = &row;
-        }
-    }
-    const auto& owned = *requested;
-    if (!same_owner_pair(target, owned)) {
-        return false;
-    }
-    const PendingScriptableOverride* source = nullptr;
-    for (const auto& row : estate) {
-        if (same_ref(row.target, owned.source)) {
-            if (source != nullptr || row.target != owned.source) {
-                return false;
-            }
-            source = &row;
-        }
-    }
-    std::uint64_t sourceSpawn = 0;
-    if (source == nullptr || !source_spawn_generation(*source, sourceSpawn)
-        || sourceSpawn != owned.sourceSpawnGeneration) {
-        return false;
-    }
-    // A request is stale when its source lifetime moved on (checked above) or when it would move an
-    // active attachment to another lifetime without an owned clear first. A fresh claim must
-    // select.
-    if (previous == nullptr) {
-        if (!owned.active) {
-            return false;
-        }
-    } else {
-        if (!previous->squadAttachment
-            || previous->squadAttachment->sdkBuildSha256 != owned.sdkBuildSha256
-            || previous->squadAttachment->sdkPayloadSha256 != owned.sdkPayloadSha256
-            || previous->squadAttachment->source != owned.source
-            || ((previous->squadAttachment->active || !owned.active)
-                && previous->squadAttachment->sourceSpawnGeneration
-                       != owned.sourceSpawnGeneration)) {
-            return false;
-        }
-        if (previous->kind != ScriptableOverrideKind::sdkAuth
-            || previous->byteCount > previous->body.size()
-            || !matches_body(*previous->squadAttachment,
-                             std::span(previous->body).first(previous->byteCount),
-                             previous->bitCount)) {
-            return false;
-        }
-    }
-    return matches_body(owned, body, bitCount);
-}
-
-/** Keeps an active attachment on its selected lifetime until an owned clear is delivered. */
-[[nodiscard]] inline bool
-permits_source_write(const PendingScriptableOverride& pending,
-                     std::span<const PendingScriptableOverride> estate) noexcept {
-    for (const auto& row : estate) {
-        // An objective assignment never changes the selected lifetime; a re-placement must keep it.
-        if (row.squadAttachment && row.squadAttachment->active
-            && same_ref(row.squadAttachment->source, pending.target)
-            && pending.kind != ScriptableOverrideKind::squadObjective
-            && (pending.target != row.squadAttachment->source
-                || pending.kind != ScriptableOverrideKind::squad
-                || pending.generation != row.squadAttachment->sourceSpawnGeneration)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-} // namespace sunrise::server::activity::host::attachments

+ 0 - 1
Sunrise/src/server/bap/bap_activity_link_view.cpp

@@ -2,7 +2,6 @@
 
 #include <cstddef>
 #include <cstdint>
-#include <optional>
 #include <shared_mutex>
 
 #include "../../state/activity/runtime.h"

+ 2 - 4
Sunrise/src/server/bap/bap_scriptable_override_route.cpp

@@ -302,8 +302,7 @@ bool request_activity_sdk_auth_override(
     std::int32_t expectedRegion,
     std::uint64_t expectedGeneration,
     const activity::host::ScriptableOutputReservation* reservation,
-    activity::host::ScriptableOverrideKind kind,
-    std::optional<activity::host::SquadAttachmentOwnership> attachment) noexcept {
+    activity::host::ScriptableOverrideKind kind) noexcept {
     const std::lock_guard lock(session_lock());
     std::size_t linkCount = 0;
     const Session* const session = unique_activity_link_locked(binding, linkCount);
@@ -327,8 +326,7 @@ bool request_activity_sdk_auth_override(
                                                                      bitCount,
                                                                      expectedGeneration,
                                                                      reservation,
-                                                                     kind,
-                                                                     attachment);
+                                                                     kind);
     return queued;
 }
 

+ 2 - 2
Sunrise/src/server/bap/runtime.h

@@ -249,8 +249,8 @@ activity_authority_reset_snapshot(const state::activity::SessionBinding& binding
     std::int32_t expectedRegion,
     std::uint64_t expectedGeneration,
     const activity::host::ScriptableOutputReservation* reservation = nullptr,
-    activity::host::ScriptableOverrideKind kind = activity::host::ScriptableOverrideKind::sdkAuth,
-    std::optional<activity::host::SquadAttachmentOwnership> attachment = std::nullopt) noexcept;
+    activity::host::ScriptableOverrideKind kind =
+        activity::host::ScriptableOverrideKind::sdkAuth) noexcept;
 
 /** Queues a type-31 arm or disarm only while exactly one authenticated link owns the binding. */
 [[nodiscard]] bool request_activity_type31_override(

+ 0 - 1
Sunrise/src/state/activity/mission/activity_mission_state.cpp

@@ -128,7 +128,6 @@ namespace {
            && left.authBody == right.authBody && left.sequenceOwner == right.sequenceOwner
            && left.sdkBuildSha256 == right.sdkBuildSha256 && left.kind == right.kind
            && left.firstRow == right.firstRow && left.secondRow == right.secondRow
-           && left.sourceSpawnGeneration == right.sourceSpawnGeneration
            && left.objectTag == right.objectTag && left.registryKey == right.registryKey
            && left.sceneEventKey == right.sceneEventKey && left.authSchema == right.authSchema
            && left.actorCommandSelector == right.actorCommandSelector

+ 0 - 3
Sunrise/src/state/activity/mission/definition.h

@@ -193,7 +193,6 @@ enum class IntentKind : std::uint8_t {
     watchDamage,
     setGhostLink,
     holdSpawn,
-    setSquadAttachment,
 };
 
 /** Actor sequence values belong to one combatant and one SDK/client generation. */
@@ -241,8 +240,6 @@ struct TypedIntent final {
     IntentKind kind{IntentKind::placeSquad};
     std::uint32_t firstRow{};
     std::uint32_t secondRow{};
-    /** Placement spawn generation of a setSquadAttachment source squad. */
-    std::uint64_t sourceSpawnGeneration{};
     std::uint32_t sceneEventKey{};
     std::uint32_t objectTag{};
     std::uint32_t registryKey{};