ghost_link_test.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. #include <array>
  2. #include <cassert>
  3. #include <limits>
  4. #include <string_view>
  5. #include "../Sunrise/src/middleware/bap/activity_message/ghost_link_auth.h"
  6. #include "../Sunrise/src/server/activity/mission/mission_script_ghost_sense.h"
  7. namespace sense = sunrise::middleware::bap::activity_message::sense_update;
  8. namespace {
  9. sense::TargetStatus group(const void*, std::uint32_t key, sense::GroupTarget& out) noexcept {
  10. if (key != 0xF6FFB59E) return sense::TargetStatus::targetUnavailable;
  11. out.objectTag = 0x80B3CB5E;
  12. 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 != 65 || index != 60) return sense::TargetStatus::targetUnavailable;
  17. out.senseSchema = 0x80804D3E;
  18. return sense::TargetStatus::resolved;
  19. }
  20. void packet_regression() {
  21. using sunrise::server::activity::mission::GhostLevel;
  22. using sunrise::server::activity::mission::update_ghost_level;
  23. // Independent complete msg-6 fixtures: group length 155, type 65 / slot 60,
  24. // required active + raw float + biased generation, then publication generation.
  25. constexpr std::array<std::string_view, 3> fixtures{
  26. "000000000000000000000000000000003edff6b3c00000137f6ffb59e8500790000000040000000800000018",
  27. "000000000000000000000000000000003edff6b3c00000137f6ffb59e8500799f80000040000001000000018",
  28. "000000000000000000000000000000003edff6b3c00000137f6ffb59e8500791fc0000040000001000000018",
  29. };
  30. GhostLevel level{};
  31. const sense::Resolver resolver{nullptr, &group, &slot};
  32. for (std::size_t i = 0; i < fixtures.size(); ++i) {
  33. std::array<std::byte, 44> packet{};
  34. auto nibble = [](char c) { return c <= '9' ? c - '0' : c - 'a' + 10; };
  35. for (std::size_t j = 0; j < packet.size(); ++j)
  36. packet[j] = std::byte((nibble(fixtures[i][j * 2]) << 4) | nibble(fixtures[i][j * 2 + 1]));
  37. sense::SenseUpdate decoded{};
  38. std::size_t consumed{};
  39. assert(sense::decode_sense_update(packet, resolver, decoded, consumed));
  40. assert(decoded.decoded.status == sense::DecodeStatus::complete);
  41. assert(consumed == 352 && decoded.decoded.objectCount == 1);
  42. const auto& object = decoded.decoded.objects[0];
  43. assert(object.status == sense::ObjectStatus::decoded && object.valueCount == 3);
  44. assert(object.generationPlusOne == 3 && object.deltaBits == 66);
  45. assert(update_ghost_level(level, std::span(decoded.decoded.values).first(3), object.schemaRow));
  46. assert(level.generation == (i == 0 ? 1 : 2));
  47. assert(level.active == (i == 1));
  48. assert(level.progress == (i == 0 ? 0.0f : i == 1 ? 0.5f : 1.0f));
  49. // Losing the packet tail cannot turn a partial scan into completion.
  50. assert(!sense::decode_sense_update(std::span(packet).first(43), resolver, decoded, consumed));
  51. }
  52. }
  53. }
  54. int main() {
  55. packet_regression();
  56. namespace ghost = sunrise::middleware::bap::activity_message::ghost_link;
  57. using sunrise::middleware::bap::activity_message::sense_update::DecodedValue;
  58. using sunrise::server::activity::mission::GhostLevel;
  59. using sunrise::server::activity::mission::update_ghost_level;
  60. std::array<std::byte, 9> bytes{};
  61. std::size_t written = 0;
  62. assert(ghost::encode(2, true, bytes, written) && written == 9);
  63. // Independent MSB-first wire fixture: biased generation, enabled, authored hash.
  64. constexpr std::array<unsigned, 9> expected{0x80,0,0,2,0xC0,0x8E,0x4E,0xE2,0x80};
  65. for (std::size_t i = 0; i < bytes.size(); ++i) assert(std::to_integer<unsigned>(bytes[i]) == expected[i]);
  66. assert(ghost::validate(bytes, 65));
  67. assert(!ghost::validate(bytes, 64));
  68. bytes.back() |= std::byte{1};
  69. assert(!ghost::validate(bytes, 65));
  70. assert(!ghost::encode(0, true, bytes, written));
  71. assert(!ghost::encode(-1, true, bytes, written));
  72. assert(!ghost::encode(1, true, std::span(bytes).first(8), written));
  73. assert(ghost::encode(1, false, bytes, written) && (std::to_integer<unsigned>(bytes[4]) & 0x80) == 0);
  74. GhostLevel level{};
  75. std::array<DecodedValue, 3> fields{};
  76. for (unsigned i = 0; i < 3; ++i) { fields[i].schemaRow = 7; fields[i].fieldOrdinal = i; fields[i].present = true; }
  77. fields[0].unsignedValue = 1;
  78. fields[1].realValue = 0.5f;
  79. fields[2].signedValue = 2;
  80. assert(!update_ghost_level(level, std::span(fields).first(1), 7));
  81. assert(!update_ghost_level(level, std::span(fields).subspan(1, 1), 7));
  82. assert(update_ghost_level(level, std::span(fields).subspan(2), 7));
  83. assert(level.active && level.progress == .5f && level.generation == 2);
  84. assert(!update_ghost_level(level, fields, 7));
  85. fields[1].realValue = 1;
  86. fields[0].present = fields[2].present = false;
  87. assert(update_ghost_level(level, fields, 7) && level.active && level.generation == 2);
  88. fields[0].present = true; fields[0].unsignedValue = 0; fields[1].present = false;
  89. assert(update_ghost_level(level, fields, 7) && !level.active && level.progress == 1);
  90. fields[1].present = true; fields[1].realValue = std::numeric_limits<float>::quiet_NaN();
  91. assert(!update_ghost_level(level, fields, 7) && level.progress == 1);
  92. assert(!update_ghost_level(level, fields, 8));
  93. }