state_settings.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. #include <algorithm>
  2. #include <array>
  3. #include "../../state/activity/defaults/activity_defaults_validation.h"
  4. #include "parser.h"
  5. namespace sunrise::core::settings::parser {
  6. /**
  7. * Fills one flag bank from [start, length] runs. A run outside the bank is refused.
  8. * @param bank Expanded destination bank.
  9. * @return True when every run fits.
  10. */
  11. bool Parser::flag_runs(std::span<std::uint8_t> bank) noexcept {
  12. if (!consume('[')) {
  13. return false;
  14. }
  15. if (consume(']')) {
  16. return true;
  17. }
  18. for (;;) {
  19. std::uint64_t start = 0;
  20. std::uint64_t length = 0;
  21. if (!consume('[') || !unsigned_integer(start) || !consume(',') || !unsigned_integer(length)
  22. || !consume(']') || length == 0 || start > bank.size()
  23. || length > bank.size() - start) {
  24. return false;
  25. }
  26. for (std::uint64_t offset = 0; offset < length; ++offset) {
  27. bank[static_cast<std::size_t>(start + offset)] = state::unlocks::kFlagSet;
  28. }
  29. if (consume(']')) {
  30. return true;
  31. }
  32. if (!consume(',')) {
  33. return false;
  34. }
  35. }
  36. }
  37. /**
  38. * Fills one flag bank from plain indices.
  39. * @param bank Expanded destination bank.
  40. * @return True when every index fits.
  41. */
  42. bool Parser::flag_indices(std::span<std::uint8_t> bank) noexcept {
  43. if (!consume('[')) {
  44. return false;
  45. }
  46. if (consume(']')) {
  47. return true;
  48. }
  49. for (;;) {
  50. std::uint64_t index = 0;
  51. if (!unsigned_integer(index) || index >= bank.size()) {
  52. return false;
  53. }
  54. bank[static_cast<std::size_t>(index)] = state::unlocks::kFlagSet;
  55. if (consume(']')) {
  56. return true;
  57. }
  58. if (!consume(',')) {
  59. return false;
  60. }
  61. }
  62. }
  63. /**
  64. * Fills one objective bank from [index, value] pairs.
  65. * @param bank Expanded destination bank.
  66. * @return True when every index fits.
  67. */
  68. bool Parser::objective_values(std::span<std::int32_t> bank) noexcept {
  69. if (!consume('[')) {
  70. return false;
  71. }
  72. if (consume(']')) {
  73. return true;
  74. }
  75. for (;;) {
  76. std::uint64_t index = 0;
  77. std::int32_t value = 0;
  78. if (!consume('[') || !unsigned_integer(index) || !consume(',') || !signed_32(value)
  79. || !consume(']') || index >= bank.size()) {
  80. return false;
  81. }
  82. bank[static_cast<std::size_t>(index)] = value;
  83. if (consume(']')) {
  84. return true;
  85. }
  86. if (!consume(',')) {
  87. return false;
  88. }
  89. }
  90. }
  91. /**
  92. * Fills one progression bank from [definition_index, lane 0, lane 1, lane 2] rows.
  93. * Repeated rows for one definition merge by lane maximum, not by last write. The Client's
  94. * own definition-indexed bank merges the same way.
  95. * @param bank Authored lanes addressed by definition index.
  96. * @return True when every row names a possible definition and carries all 3 lanes.
  97. */
  98. bool Parser::progression_values(state::unlocks::ProgressionBank& bank) noexcept {
  99. std::array<bool, state::build_data::progressions::kDefinitionCapacity> authored{};
  100. if (!consume('[')) {
  101. return false;
  102. }
  103. if (consume(']')) {
  104. return true;
  105. }
  106. for (;;) {
  107. std::uint64_t index = 0;
  108. state::unlocks::ProgressionLanes lanes{};
  109. if (!consume('[') || !unsigned_integer(index) || index >= bank.size()) {
  110. return false;
  111. }
  112. for (std::int32_t& lane : lanes) {
  113. if (!consume(',') || !signed_32(lane)) {
  114. return false;
  115. }
  116. }
  117. if (!consume(']')) {
  118. return false;
  119. }
  120. const auto slot = static_cast<std::size_t>(index);
  121. state::unlocks::ProgressionLanes& row = bank[slot];
  122. for (std::size_t lane = 0; lane < row.size(); ++lane) {
  123. // The first row of a definition is taken whole, so a negative lane is not clamped.
  124. row[lane] = authored[slot] ? (std::max)(row[lane], lanes[lane]) : lanes[lane];
  125. }
  126. authored[slot] = true;
  127. if (consume(']')) {
  128. return true;
  129. }
  130. if (!consume(',')) {
  131. return false;
  132. }
  133. }
  134. }
  135. /**
  136. * Parses the four authored unlock banks.
  137. * @param output Receives every expanded bank.
  138. * @return True when the object is valid JSON and every entry fits its bank.
  139. */
  140. bool Parser::unlocks(state::unlocks::Table& output) noexcept {
  141. output = {};
  142. if (!consume('{')) {
  143. return false;
  144. }
  145. if (consume('}')) {
  146. return true;
  147. }
  148. for (;;) {
  149. std::string_view key;
  150. if (!string(key) || !consume(':')) {
  151. return false;
  152. }
  153. bool parsed = false;
  154. if (key == "account_flag_runs") {
  155. parsed = flag_runs(output.accountFlags);
  156. } else if (key == "profile_flag_runs") {
  157. parsed = flag_runs(output.profileFlags);
  158. } else if (key == "character_flags") {
  159. parsed = flag_indices(output.characterFlags);
  160. } else if (key == "objective_values") {
  161. parsed = objective_values(output.objectiveValues);
  162. } else if (key == "character_flag_runs") {
  163. parsed = flag_runs(output.characterObjectFlags);
  164. } else if (key == "character_objective_values") {
  165. parsed = objective_values(output.characterObjectValues);
  166. } else if (key == "account_progressions") {
  167. parsed = progression_values(output.accountProgressions);
  168. } else if (key == "character_progressions") {
  169. parsed = progression_values(output.characterProgressions);
  170. } else {
  171. parsed = skip_value(0);
  172. }
  173. if (!parsed) {
  174. return false;
  175. }
  176. if (consume('}')) {
  177. return true;
  178. }
  179. if (!consume(',')) {
  180. return false;
  181. }
  182. }
  183. }
  184. /**
  185. * Parses authored State ids. Native mapping data is refused.
  186. * @param output Receives the initial account, unlock policy and activity defaults.
  187. * @return True when activity appears once and the cross-field rules hold.
  188. */
  189. bool Parser::state_settings(Settings& output) noexcept {
  190. output.initialAccount = {};
  191. output.initialUnlocks = {};
  192. output.initialFamily5 = {};
  193. output.initialActivityDefaults = state::activity::defaults::authored();
  194. if (!consume('{')) {
  195. return false;
  196. }
  197. if (consume('}')) {
  198. return true;
  199. }
  200. bool hasActivity = false;
  201. bool hasInvestment = false;
  202. for (;;) {
  203. std::string_view key;
  204. if (!string(key) || !consume(':')) {
  205. return false;
  206. }
  207. if (key == "account") {
  208. if (!account(output.initialAccount)) {
  209. return false;
  210. }
  211. } else if (key == "characters") {
  212. if (!characters(output.initialAccount)) {
  213. return false;
  214. }
  215. } else if (key == "unlocks") {
  216. if (!unlocks(output.initialUnlocks)) {
  217. return false;
  218. }
  219. } else if (key == "investment") {
  220. if (hasInvestment || !investment(output.initialFamily5)) {
  221. return false;
  222. }
  223. hasInvestment = true;
  224. } else if (key == "activity") {
  225. if (hasActivity || !activity_settings(output.initialActivityDefaults)) {
  226. return false;
  227. }
  228. hasActivity = true;
  229. } else if (!skip_value(0)) {
  230. return false;
  231. }
  232. if (consume('}')) {
  233. return state::account::valid_authored(output.initialAccount)
  234. && state::activity::defaults::valid(output.initialActivityDefaults);
  235. }
  236. if (!consume(',')) {
  237. return false;
  238. }
  239. }
  240. }
  241. /**
  242. * Parses an optional account id object.
  243. * @param output Receives the authored primary account SOID.
  244. * @return True for null or an object with one nonzero SOID and complete settings.
  245. */
  246. bool Parser::account(state::AccountState& output) noexcept {
  247. if (literal("null")) {
  248. output = {};
  249. return true;
  250. }
  251. if (!consume('{')) {
  252. return false;
  253. }
  254. bool hasPrimarySoid = false;
  255. bool hasSettings = false;
  256. bool hasDismantleRewards = false;
  257. if (consume('}')) {
  258. return false;
  259. }
  260. for (;;) {
  261. std::string_view key;
  262. if (!string(key) || !consume(':')) {
  263. return false;
  264. }
  265. if (key == "primary_soid") {
  266. if (hasPrimarySoid || !unsigned_value(output.primarySoid) || output.primarySoid == 0) {
  267. return false;
  268. }
  269. hasPrimarySoid = true;
  270. } else if (key == "settings") {
  271. if (hasSettings || !account_settings(output.settings)) {
  272. return false;
  273. }
  274. hasSettings = true;
  275. } else if (key == "profile_items") {
  276. if (!profile_items(output)) {
  277. return false;
  278. }
  279. } else if (key == "dismantle_rewards") {
  280. if (hasDismantleRewards || !dismantle_rewards(output)) {
  281. return false;
  282. }
  283. hasDismantleRewards = true;
  284. } else if (!skip_value(0)) {
  285. return false;
  286. }
  287. if (consume('}')) {
  288. return hasPrimarySoid && hasSettings;
  289. }
  290. if (!consume(',')) {
  291. return false;
  292. }
  293. }
  294. }
  295. } // namespace sunrise::core::settings::parser