// Copyright (c) 2026 Juhapekka Piiroinen // All Rights Reserved. // AmoosedWeb project: https://amoosed.com #pragma once #include #include #include #include #include #include #include namespace amoosed { struct GameItem { std::string id, kind, path; std::vector dependencies; }; struct GameCatalogue { std::string game_id, min_game_version, max_game_version; std::size_t revision = 0; std::vector items; }; struct ExperimentVariant { std::string id; std::size_t weight = 0; std::map parameters; }; struct Experiment { std::string id, assignment_salt, start_utc, end_utc; std::size_t revision = 0; std::vector variants; }; struct ParameterRule { double minimum = 0, maximum = 0; }; bool parse_game_catalogue(std::string_view json, GameCatalogue& output, std::string& error); bool catalogue_accepts_game(const GameCatalogue& catalogue, std::string_view game_id, std::string_view game_version); bool parse_experiment(std::string_view json, std::string_view expected_id, Experiment& output, std::string& error); // Anonymous IDs are generated and stored by the game. Selection is repeatable // for one assignment salt; this is not an access-control or analytics primitive. using Sha256 = bool (*)(void* context, const std::uint8_t* bytes, std::size_t length, std::array& digest); bool select_variant(const Experiment& experiment, std::string_view anonymous_id, Sha256 sha256, void* context, ExperimentVariant& output, std::string& error); // A shipped game supplies the permitted parameter names and bounds. No // downloaded value is applied unless the whole experiment passes these checks. // On false, callers retain their compiled-in defaults for this session. bool evaluate_experiment(const GameCatalogue& catalogue, const Experiment& experiment, std::string_view game_version, std::string_view now_utc, std::string_view anonymous_id, const std::map& allowed, Sha256 sha256, void* context, ExperimentVariant& output, std::string& error); }