unlocks_expression.h 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. #pragma once
  2. #include <cstddef>
  3. #include <cstdint>
  4. #include <span>
  5. namespace sunrise::state::unlocks {
  6. /** Stack slots one expression may use. The deepest installed reward condition needs nine. */
  7. inline constexpr std::size_t kExpressionStackCapacity = 16;
  8. /** Opcodes the client's expression evaluator implements that the server evaluates. */
  9. enum class Opcode : std::uint8_t {
  10. /** Pushes 1 when the bound flag is set, otherwise 0. */
  11. flag = 1,
  12. /** Pops one value and pushes 1 when it was zero. */
  13. logicalNot = 2,
  14. /** Binary operators pop right then left and push the result. */
  15. logicalOr = 3,
  16. logicalAnd = 4,
  17. equal = 8,
  18. /** Pushes one bound value. */
  19. loadValue = 10,
  20. /** Pushes the operand as a signed constant. */
  21. constant = 11,
  22. greaterThan = 13,
  23. greaterOrEqual = 14,
  24. lessThan = 15,
  25. lessOrEqual = 16,
  26. add = 17,
  27. /** Pops one value and pushes its two's-complement negation. */
  28. negate = 22,
  29. };
  30. /** Saved bank a flag or value instruction reads once its authored unlock slot is bound. */
  31. enum class Bank : std::uint8_t {
  32. /** Operators and constants read no bank. */
  33. none,
  34. account,
  35. profile,
  36. character,
  37. /** A flag that is set when the operand names the selected character's class. */
  38. characterClass,
  39. /** Resolved outside the saved banks. The operand is its identity hash. */
  40. external,
  41. };
  42. /** One bound expression instruction. */
  43. struct Instruction {
  44. Opcode opcode{};
  45. Bank bank{Bank::none};
  46. /** Bank index, class index, external identity hash, or signed constant. */
  47. std::uint32_t operand{};
  48. };
  49. /** Reads one bound flag. Returns false when the flag cannot be read. */
  50. using FlagReader = bool (*)(const void* context,
  51. const Instruction& instruction,
  52. bool& set) noexcept;
  53. /** Reads one bound value. Returns false when the value cannot be read. */
  54. using ValueReader = bool (*)(const void* context,
  55. const Instruction& instruction,
  56. std::int32_t& value) noexcept;
  57. /** The two readers one evaluation needs, and the caller state they share. */
  58. struct Inputs {
  59. FlagReader flag{};
  60. ValueReader value{};
  61. const void* context{};
  62. };
  63. /**
  64. * Converts one native opcode word to an evaluated opcode.
  65. * @return False for an opcode the server does not evaluate.
  66. */
  67. [[nodiscard]] bool decode_opcode(std::uint32_t native, Opcode& opcode) noexcept;
  68. /** Checks one instruction's bank and its operand against that bank's capacity. */
  69. [[nodiscard]] bool valid(const Instruction& instruction) noexcept;
  70. /**
  71. * Evaluates one expression program.
  72. * Every failure is refused, never defaulted: bad opcode, bad stack, unreadable flag or value.
  73. * @param program Instructions in evaluation order.
  74. * @param inputs Flag and value readers.
  75. * @param result Receives whether the final value is nonzero, and is cleared on any refusal.
  76. * @return True only when the whole program evaluated to one value.
  77. */
  78. [[nodiscard]] bool
  79. evaluate(std::span<const Instruction> program, const Inputs& inputs, bool& result) noexcept;
  80. } // namespace sunrise::state::unlocks