interactable_object_test.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. #include "../Sunrise/src/server/activity/host_runtime.h"
  2. #include <array>
  3. #include <cassert>
  4. #include <string_view>
  5. #include "../Sunrise/src/middleware/bap/activity_message/interactable_object_auth.h"
  6. #include "../Sunrise/src/server/activity/mission/mission_script_object_sense.h"
  7. #include "../Sunrise/src/middleware/bap/activity_message/sense_observation_packet.h"
  8. namespace sense = sunrise::middleware::bap::activity_message::sense_update;
  9. namespace {
  10. sense::TargetStatus group(const void*, std::uint32_t key, sense::GroupTarget& out) noexcept {
  11. if (key != 0xF6FFB59E && key != 0x382608B7) return sense::TargetStatus::targetUnavailable;
  12. out.objectTag=0x80B3CB5E; return sense::TargetStatus::resolved;
  13. }
  14. sense::TargetStatus slot(const void*,const sense::GroupTarget&,std::uint8_t type,
  15. std::uint16_t index,sense::SlotTarget& out) noexcept {
  16. if (type != 4 || (index != 19 && index != 31)) return sense::TargetStatus::targetUnavailable;
  17. out.senseSchema=0x8080992E; return sense::TargetStatus::resolved;
  18. }
  19. }
  20. int main() {
  21. namespace object=sunrise::middleware::bap::activity_message::interactable_object;
  22. using namespace sunrise::server::activity::mission;
  23. std::array<std::byte,object::kBytes> auth{}; std::size_t written{};
  24. assert(object::encode(1,auth,written) && written==47 && object::validate(auth,375));
  25. // Native F32CD0 permits use with +704=false and no player filter. F33930
  26. // maps wire 0/native -1 to false; wire 2/native +1 would hide the prompt.
  27. sunrise::middleware::encoding::bits::Reader enabled(auth);
  28. std::uint64_t mode{};
  29. assert(enabled.skip(285) && enabled.read(2,mode) && mode==0);
  30. const auto nativeMode = static_cast<int>(mode)-1;
  31. assert(nativeMode!=0 && !(nativeMode==1));
  32. auto disabled = auth;
  33. disabled[35] |= std::byte{4}; // wire mode 2 at bit offset 285
  34. assert(!object::validate(disabled,375));
  35. assert(!object::encode(0,auth,written));
  36. assert(!object::encode(1,std::span(auth).first(46),written));
  37. auth.back() |= std::byte{1}; assert(!object::validate(auth,375));
  38. std::array<std::byte,object::kOwnerBytes> ownerAuth{};
  39. assert(object::encode(3,ownerAuth,written,true) && written==51 && object::validate(ownerAuth,408));
  40. assert(!object::validate(ownerAuth,375));
  41. // Accepted delivery retires the carried entity under a newer generation.
  42. std::array<std::byte,object::kOwnerBytes> consumed{};
  43. assert(object::encode(4,consumed,written,true,false));
  44. assert(object::validate(consumed,object::kOwnerBits));
  45. sunrise::middleware::encoding::bits::Reader retired(consumed);
  46. std::uint64_t active{}, generation{};
  47. assert(retired.read(32,generation) && generation==0x80000004ULL);
  48. assert(retired.skip(32) && retired.read(1,active) && active==0);
  49. ownerAuth.back() ^= std::byte{1}; assert(!object::validate(ownerAuth,408));
  50. constexpr std::array<std::string_view,2> fixtures{
  51. "000000000000000000000000000000003edff6b3c00000241f6ffb59e0b002780000001e000000000000000400000001c04027dba00000000000000080",
  52. "000000000000000000000000000000003edff6b3c00000241f6ffb59e0b002780000001e000000000000000400000001c04027dbe00000000000000080",
  53. };
  54. ObjectInteractionLevel level{};
  55. const sense::Resolver resolver{nullptr,&group,&slot};
  56. for (std::size_t i=0;i<fixtures.size();++i) {
  57. std::array<std::byte,61> packet{};
  58. const auto size=fixtures[i].size()/2;
  59. auto nibble=[](char c) { return c<='9' ? c-'0' : c-'a'+10; };
  60. for (std::size_t j=0;j<size;++j)
  61. packet[j]=std::byte((nibble(fixtures[i][j*2])<<4)|nibble(fixtures[i][j*2+1]));
  62. sense::SenseUpdate decoded{}; std::size_t consumed{};
  63. assert(sense::decode_sense_update(std::span(packet).first(size),resolver,decoded,consumed));
  64. assert(decoded.decoded.status==sense::DecodeStatus::complete);
  65. assert(decoded.decoded.objectCount==1 && decoded.decoded.objects[0].deltaBits==199);
  66. const auto& o=decoded.decoded.objects[0];
  67. const auto values=std::span(decoded.decoded.values).subspan(o.firstValue,o.valueCount);
  68. assert(update_object_interaction(level,values,o.schemaRow)==(i==1));
  69. assert(!update_object_interaction(level,values,o.schemaRow));
  70. assert(level.generation==1 && level.interacted==(i==1));
  71. // An unrelated unsupported group must not discard this fully decoded use receipt.
  72. auto mixed=decoded.decoded;
  73. mixed.status=sense::DecodeStatus::partial;
  74. mixed.groupsSkipped=1;
  75. auto& unknown=mixed.objects[mixed.objectCount++];
  76. unknown.status=sense::ObjectStatus::unsupportedField;
  77. unknown.firstValue=static_cast<std::uint32_t>(mixed.valueCount);
  78. assert(sense::observation_packet(mixed));
  79. sunrise::server::activity::host::Event event{};
  80. event.kind = sunrise::server::activity::host::EventKind::senseUpdate;
  81. event.senseDecodeStatus = mixed.status;
  82. event.senseSnapshotRetained = sense::observation_packet(mixed);
  83. assert(event.has_sense_observations());
  84. mixed.objectsTruncated=true; assert(!sense::observation_packet(mixed));
  85. event.senseSnapshotRetained = sense::observation_packet(mixed);
  86. // Framed partial packets can still exceed observation storage. Their input
  87. // sequence must be consumed without dispatching a nonexistent snapshot.
  88. assert(event.senseDecodeStatus == sense::DecodeStatus::partial && !event.has_sense_observations());
  89. event.senseDecodeStatus = sense::DecodeStatus::complete;
  90. assert(!event.has_sense_observations());
  91. mixed.objectsTruncated=false; mixed.valuesTruncated=true;
  92. assert(!sense::observation_packet(mixed));
  93. mixed.valuesTruncated=false; mixed.objects[0].hasGeneration=false;
  94. assert(!sense::observation_packet(mixed));
  95. mixed.objects[0].hasGeneration=true; mixed.status=sense::DecodeStatus::malformed;
  96. assert(!sense::observation_packet(mixed));
  97. assert(!sense::decode_sense_update(std::span(packet).first(size-1),resolver,decoded,consumed));
  98. }
  99. // SDK 80809ACC flags 0x21: required custom uint64, with no presence bit.
  100. // Root generation/live state and the native ownership reply, unheld then held.
  101. constexpr std::array<std::string_view,2> ownerFixtures{
  102. "000000000000000000000000000000002704c116e00000281382608b70b003f80000001e000000000000000400000001c0404d6600000000000000000000000040",
  103. "000000000000000000000000000000002704c116e00000281382608b70b003f80000001e000000000000000400000001c0404d6667aa8c0040040040c000000040",
  104. };
  105. ObjectInteractionLevel ownerLevel{};
  106. for (std::size_t i=0; i<ownerFixtures.size(); ++i) {
  107. std::array<std::byte,65> packet{};
  108. auto nibble=[](char c) { return c<='9' ? c-'0' : c-'a'+10; };
  109. for (std::size_t j=0; j<packet.size(); ++j)
  110. packet[j]=std::byte((nibble(ownerFixtures[i][j*2])<<4)|nibble(ownerFixtures[i][j*2+1]));
  111. sense::SenseUpdate decoded{}; std::size_t consumed{};
  112. assert(sense::decode_sense_update(packet,resolver,decoded,consumed));
  113. assert(decoded.decoded.status==sense::DecodeStatus::complete);
  114. assert(sense::observation_packet(decoded.decoded));
  115. const auto& o=decoded.decoded.objects[0];
  116. assert(o.registryKey==0x382608B7 && o.slotIndex==31 && o.hasGeneration);
  117. static_cast<void>(update_object_interaction(ownerLevel,
  118. std::span(decoded.decoded.values).subspan(o.firstValue,o.valueCount),o.schemaRow));
  119. assert(ownerLevel.generation==1 && ownerLevel.present && ownerLevel.alive && ownerLevel.ownerKnown);
  120. assert(ownerLevel.hasOwner==(i==1));
  121. assert(ownerLevel.ownerKey==(i==1 ? 0x9EAA300100100103ULL : 0));
  122. }
  123. std::array<sense::DecodedValue,1> values{};
  124. auto& value=values[0]; value.present=true; value.schemaRow=0x8080992E;
  125. value.signedValue=2;
  126. assert(!update_object_interaction(level,values,0x8080992E));
  127. assert(!level.interactionKnown); // generation-only update cannot reuse the old used latch.
  128. value.schemaRow=object::kReply; value.unsignedValue=1;
  129. assert(update_object_interaction(level,values,0x8080992E));
  130. std::array<sense::DecodedValue,5> ownership{};
  131. for(auto& v:ownership)v.present=true;
  132. ownership[0].schemaRow=0x8080992E;ownership[0].signedValue=3;
  133. ownership[1].schemaRow=0x8080992E;ownership[1].fieldOrdinal=2;ownership[1].unsignedValue=1;
  134. ownership[2].schemaRow=0x80809ACC;ownership[2].unsignedValue=1;
  135. ownership[3].schemaRow=0x80809ACC;ownership[3].fieldOrdinal=1;ownership[3].unsignedValue=1234;
  136. ownership[4].schemaRow=0x8080992E;ownership[4].fieldOrdinal=1;ownership[4].unsignedValue=1;
  137. static_cast<void>(update_object_interaction(level,ownership,0x8080992E));
  138. assert(level.generation==3 && level.present && level.alive && level.ownerKnown && level.hasOwner && level.ownerKey==1234);
  139. ownership[0].signedValue=2;ownership[2].unsignedValue=0;
  140. static_cast<void>(update_object_interaction(level,ownership,0x8080992E));
  141. assert(level.hasOwner && level.generation==3);
  142. ownership[0].signedValue=4;
  143. static_cast<void>(update_object_interaction(level,std::span(ownership).first(2),0x8080992E));
  144. assert(!level.ownerKnown && !level.hasOwner && level.ownerKey==0);
  145. value.schemaRow=0x8080992E; value.signedValue=1;
  146. assert(!update_object_interaction(level,values,0x8080992E) && level.generation==4);
  147. }