incident_payload_test.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. #include <algorithm>
  2. #include <array>
  3. #include <bit>
  4. #include <cassert>
  5. #include <cstddef>
  6. #include <cstdint>
  7. #include <iostream>
  8. #include <vector>
  9. #include "../Sunrise/src/middleware/bap/activity_message/cinematic_incident.h"
  10. #include "../Sunrise/src/middleware/bap/activity_message/incident.h"
  11. #include "../Sunrise/src/middleware/bap/activity_message/player_trigger_incident.h"
  12. #include "../Sunrise/src/server/activity/mission/mission_script_cinematic.h"
  13. namespace incident = sunrise::middleware::bap::activity_message::incident;
  14. namespace cinematic = sunrise::middleware::bap::activity_message::cinematic_incident;
  15. namespace trigger = sunrise::middleware::bap::activity_message::player_trigger_incident;
  16. namespace bits = sunrise::middleware::encoding::bits;
  17. namespace resolver = sunrise::server::activity::mission::cinematic;
  18. namespace catalog = sunrise::state::build_data::scriptables;
  19. static std::vector<std::byte> encode(const incident::Incident& input) {
  20. std::vector<std::byte> bytes(incident::kMaximumBodyBytes);
  21. bits::Writer writer(bytes);
  22. assert(incident::write(writer, input));
  23. std::size_t size = 0;
  24. assert(writer.finish(size));
  25. bytes.resize(size);
  26. return bytes;
  27. }
  28. static void round_trip_fields() {
  29. // Extra targets move the selector/payload across every possible byte alignment.
  30. for (unsigned extras = 0; extras < 8; ++extras) {
  31. for (bool selector : {false, true}) {
  32. for (bool optional : {false, true}) {
  33. incident::Incident input{};
  34. input.primaryTarget = cinematic::kTerminatedTarget;
  35. input.extraTargetCount = extras;
  36. for (unsigned i = 0; i < extras; ++i)
  37. input.extraTargets[i] = 100 + i;
  38. input.hasCompressedSelector = selector;
  39. input.selectorLength = selector ? incident::kSelectorMaximum : 0;
  40. for (unsigned i = 0; i < input.selectorLength; ++i)
  41. input.selector[i] = static_cast<std::byte>((i * 19 + 7) & 255);
  42. input.hasOptionalBlock = optional;
  43. input.optionalWordA = optional ? 0xFEDCBA98U : 0;
  44. input.optionalWordB = optional ? 0x76543210U : 0;
  45. input.payloadLength = incident::kPayloadMaximum;
  46. for (unsigned i = 0; i < input.payloadLength; ++i)
  47. input.payload[i] = static_cast<std::byte>((i * 37 + 13) & 255);
  48. const auto encoded = encode(input);
  49. incident::Incident parsed{};
  50. assert(incident::validate(encoded, parsed) == incident::Verdict::accepted);
  51. assert(parsed.hasCompressedSelector == selector
  52. && parsed.hasOptionalBlock == optional);
  53. assert(parsed.selector == input.selector && parsed.payload == input.payload);
  54. assert(parsed.optionalWordA == input.optionalWordA
  55. && parsed.optionalWordB == input.optionalWordB);
  56. assert(parsed.hasPayload && parsed.payloadLength == input.payloadLength);
  57. assert(encode(parsed) == encoded);
  58. for (std::size_t size = 0; size < encoded.size(); ++size)
  59. assert(incident::validate(std::span(encoded).first(size), parsed)
  60. == incident::Verdict::truncated);
  61. }
  62. }
  63. }
  64. incident::Incident empty{};
  65. empty.primaryTarget = 100;
  66. empty.hasCompressedSelector = true; // Presence is distinct from a nonzero length.
  67. incident::Incident parsed{};
  68. assert(incident::validate(encode(empty), parsed) == incident::Verdict::accepted);
  69. assert(parsed.hasCompressedSelector && !parsed.hasPayload && parsed.payloadLength == 0);
  70. }
  71. static void cinematic_handoff() {
  72. constexpr std::uint32_t registry = 0x91EAC345U;
  73. constexpr std::uint64_t runtime = 0x123456789ABCDEF0ULL;
  74. std::array<std::byte, cinematic::kPayloadBytes> body{};
  75. bits::Writer writer(body);
  76. for (unsigned i = 0; i < 5; ++i)
  77. assert(writer.write(0xABABABABABABABABULL, 64));
  78. assert(writer.write(0x1234, 15)); // 335 bits of schema prefix.
  79. assert(writer.write(registry, 32));
  80. assert(writer.write(6 + 1, 7));
  81. assert(writer.write(32768, 16)); // Authored slot zero.
  82. assert(writer.write(runtime, 64));
  83. assert(writer.write(std::bit_cast<std::uint32_t>(1.0F), 32));
  84. std::size_t size = 0;
  85. assert(writer.finish(size) && size == body.size());
  86. catalog::Snapshot world{};
  87. catalog::Object object{};
  88. object.registryKey = registry;
  89. object.objectTag = 0x81234567U;
  90. object.stateRow = 3; // Alternate-state identity must survive the event path.
  91. world.objects.push_back(object);
  92. catalog::Slot slot{};
  93. slot.slotType = 6;
  94. world.slots.push_back(slot);
  95. for (const auto target : {cinematic::kStartedTarget, cinematic::kTerminatedTarget, cinematic::kSkipTarget}) {
  96. cinematic::Signal signal{};
  97. assert(cinematic::signal_for_target(target, signal));
  98. if (target == cinematic::kSkipTarget) assert(signal == cinematic::Signal::skipRequested);
  99. incident::Incident input{};
  100. input.primaryTarget = target;
  101. input.payloadLength = body.size();
  102. std::copy(body.begin(), body.end(), input.payload.begin());
  103. incident::Incident parsed{};
  104. assert(incident::validate(encode(input), parsed) == incident::Verdict::accepted);
  105. cinematic::Payload movie{};
  106. assert(cinematic::decode(std::span(parsed.payload).first(parsed.payloadLength), movie));
  107. assert(movie.registryKey == registry && movie.slotType == 6 && movie.slotIndex == 0);
  108. assert(movie.runtimeObjectId == runtime && movie.eventValue == 1.0F);
  109. resolver::Source source{};
  110. assert(resolver::resolve(world, movie, source) == resolver::ResolveStatus::ready);
  111. assert(source.registryKey == registry && source.objectTag == object.objectTag);
  112. movie.registryKey ^= 1;
  113. assert(resolver::resolve(world, movie, source) == resolver::ResolveStatus::absent);
  114. }
  115. }
  116. static void player_trigger_payload() {
  117. incident::Incident input{};
  118. input.primaryTarget = trigger::kPrimaryTarget;
  119. input.payloadLength = trigger::kPayloadBytes;
  120. bits::Writer writer(std::span(input.payload).first(input.payloadLength));
  121. for (unsigned i = 0; i < 5; ++i)
  122. assert(writer.write(0, 64));
  123. assert(writer.write(0, 15));
  124. assert(writer.write(0x87654321, 32));
  125. assert(writer.write(32, 7)); // Type 31 plus bias.
  126. assert(writer.write(32768 + 70, 16));
  127. assert(writer.write(0x10203040, 32));
  128. std::size_t size = 0;
  129. assert(writer.finish(size) && size == input.payloadLength);
  130. incident::Incident parsed{};
  131. assert(incident::validate(encode(input), parsed) == incident::Verdict::accepted);
  132. trigger::Payload decoded{};
  133. assert(trigger::decode(std::span(parsed.payload).first(parsed.payloadLength), decoded));
  134. assert(decoded.registryKey == 0x87654321 && decoded.slotType == 31 && decoded.slotIndex == 70);
  135. assert(decoded.resolvedObjectId == 0x10203040);
  136. }
  137. int main() {
  138. round_trip_fields();
  139. cinematic_handoff();
  140. player_trigger_payload();
  141. std::cout << "Incident retention, truncation, cinematic resolution and player-trigger decoding "
  142. "passed\n";
  143. }