summaryrefslogtreecommitdiff |
diff options
Diffstat (limited to 'src/battle/query')
-rw-r--r-- | src/battle/query/btl_character_turn.erl | 282 | ||||
-rw-r--r-- | src/battle/query/btl_load_state.erl | 159 |
2 files changed, 441 insertions, 0 deletions
diff --git a/src/battle/query/btl_character_turn.erl b/src/battle/query/btl_character_turn.erl new file mode 100644 index 0000000..c232ab0 --- /dev/null +++ b/src/battle/query/btl_character_turn.erl @@ -0,0 +1,282 @@ +-module(btl_character_turn). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-include("../../../include/yaws_api.hrl"). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export([out/1]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%-spec send_to_database (list(database_diff:type()), character_turn_request:type()) -> 'ok'. + + +%%%% REQUEST DECODING %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec decode_request (binary()) -> btl_character_turn_request:type(). +decode_request (BinaryRequest) -> + JSONMap = jiffy:decode(BinaryRequest, [return_maps]), + + btl_character_turn_request:decode(JSONMap). + +%%%% USER AUTHENTICATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec authenticate_user (btl_character_turn_request:type()) -> 'ok'. +authenticate_user (Request) -> + PlayerID = btl_character_turn_request:get_player_id(Request), + SessionToken = btl_character_turn_request:get_session_token(Request), + + btl_security:assert_identity(PlayerID, SessionToken), + btl_security:lock_queries(PlayerID), + + ok. + +%%%% MAIN LOGIC %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec fetch_data + ( + btl_character_turn_request:type() + ) + -> btl_character_turn_data:type(). +fetch_data (Request) -> + PlayerID = btl_character_turn_request:get_player_id(Request), + BattleID = btl_character_turn_request:get_battle_id(Request), + CharacterIX = btl_character_turn_request:get_character_ix(Request), + Battle = sh_timed_cache:fetch(battle_db, PlayerID, BattleID), + + btl_character_turn_data:new(Battle, CharacterIX). + +%%%% ASSERTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec assert_user_is_current_player + ( + btl_character_turn_data:type(), + btl_character_turn_request:type() + ) -> 'ok'. +assert_user_is_current_player (Data, Request) -> + PlayerID = btl_character_turn_request:get_player_id(Request), + Battle = btl_character_turn_data:get_battle(Data), + CurrentPlayerTurn = btl_battle:get_current_player_turn(Battle), + CurrentPlayerIX = btl_player_turn:get_player_ix(CurrentPlayerTurn), + CurrentPlayer = btl_battle:get_player(CurrentPlayerIX, Battle), + + true = (PlayerID == btl_player:get_id(CurrentPlayer)), + + ok. + +-spec assert_user_owns_played_character + ( + btl_character_turn_data:type(), + btl_character_turn_request:type() + ) -> 'ok'. +assert_user_owns_played_character (Data, Request) -> + PlayerID = btl_character_turn_request:get_player_id(Request), + Battle = btl_character_turn_data:get_battle(Data), + Players = btl_battle:get_players(Battle), + Character = btl_character_turn_data:get_character(Data), + CharacterPlayerIX = btl_character:get_player_index(Character), + CharacterPlayer = array:get(CharacterPlayerIX, Players), + CharacterPlayerID = btl_player:get_id(CharacterPlayer), + + true = (PlayerID == CharacterPlayerID), + + ok. + +-spec assert_character_can_be_played (btl_character_turn_data:type()) -> 'ok'. +assert_character_can_be_played (Data) -> + Character = btl_character_turn_data:get_character(Data), + + true = btl_character:get_is_active(Character), + + ok. + +-spec assert_user_permissions + ( + btl_character_turn_data:type(), + btl_character_turn_request:type() + ) -> 'ok'. +assert_user_permissions (Data, Request) -> + assert_user_is_current_player(Data, Request), + assert_user_owns_played_character(Data, Request), + assert_character_can_be_played(Data), + + ok. + +%%%% QUERY LOGIC HANDLING %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec finalize_character + ( + btl_character_turn_update:type() + ) + -> btl_character_turn_update:type(). +finalize_character (Update) -> + Data = btl_character_turn_update:get_data(Update), + Character = btl_character_turn_data:get_character(Data), + + DisabledCharacter = btl_character:set_is_active(false, Character), + UpdatedData = btl_character_turn_data:set_character(DisabledCharacter, Data), + FinalizedData = btl_character_turn_data:clean_battle(UpdatedData), + + DBQuery = + sh_db_query:update_indexed + ( + btl_battle:get_characters_field(), + btl_character_turn_data:get_character_ix(Data), + [ sh_db_query:set_field(btl_character:get_is_active_field(), false) ] + ), + + S0Update = btl_character_turn_update:set_data(FinalizedData, Update), + S1Update = btl_character_turn_update:add_to_db(DBQuery, S0Update), + + S1Update. + +-spec handle_actions + ( + btl_character_turn_data:type(), + btl_character_turn_request:type() + ) + -> btl_character_turn_update:type(). +handle_actions (Data, Request) -> + Actions = btl_character_turn_request:get_actions(Request), + + EmptyUpdate = btl_character_turn_update:new(Data), + PostActionsUpdate = + lists:foldl(fun btl_turn_actions:handle/2, EmptyUpdate, Actions), + + finalize_character(PostActionsUpdate). + +-spec update_timeline + ( + btl_character_turn_update:type() + ) + -> btl_character_turn_update:type(). +update_timeline (Update) -> + NewTimelineElements = btl_character_turn_update:get_timeline(Update), + Data = btl_character_turn_update:get_data(Update), + Battle = btl_character_turn_data:get_battle(Data), + PlayerTurn = btl_battle:get_current_player_turn(Battle), + PlayerIX = btl_player_turn:get_player_ix(PlayerTurn), + Player = btl_battle:get_player(PlayerIX, Battle), + + UpdatedPlayer = btl_player:add_to_timeline(NewTimelineElements, Player), + UpdatedBattle = btl_battle:set_player(PlayerIX, UpdatedPlayer, Battle), + UpdatedData = btl_character_turn_data:set_battle(UpdatedBattle, Data), + + DBQuery = + sh_db_query:update_indexed + ( + btl_battle:get_players_field(), + PlayerIX, + [ + sh_db_query:add_to_field + ( + btl_player:get_timeline_field(), + NewTimelineElements, + true % We add those to the start of the list + ) + ] + ), + + S0Update = btl_character_turn_update:set_data(UpdatedData, Update), + S1Update = btl_character_turn_update:add_to_db(DBQuery, S0Update), + + S1Update. + + +-spec update_data + ( + btl_character_turn_data:type(), + btl_character_turn_request:type() + ) + -> btl_character_turn_update:type(). +update_data (Data, Request) -> + PostActionsUpdate = handle_actions(Data, Request), + PostCharacterTurnUpdate = update_timeline(PostActionsUpdate), + + btl_next_turn:update_if_needed(PostCharacterTurnUpdate). + +%%%% DATABASE UPDATES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec send_to_database + ( + btl_character_turn_update:type(), + btl_character_turn_request:type() + ) + -> 'ok'. +send_to_database (Update, Request) -> + PlayerID = btl_character_turn_request:get_player_id(Request), + BattleID = btl_character_turn_request:get_battle_id(Request), + Ops = btl_character_turn_update:get_db(Update), + Query = sh_db_query:new(battle_db, BattleID, {user, PlayerID}, Ops), + + sh_database:commit(Query), + + ok. + +-spec send_to_cache + ( + btl_character_turn_update:type(), + btl_character_turn_request:type() + ) + -> 'ok'. +send_to_cache (Update, Request) -> + PlayerID = btl_character_turn_request:get_player_id(Request), + BattleID = btl_character_turn_request:get_battle_id(Request), + Data = btl_character_turn_update:get_data(Update), + Battle = btl_character_turn_data:get_battle(Data), + + sh_timed_cache:update(battle_db, PlayerID, BattleID, Battle), + + ok. + +-spec commit_update + ( + btl_character_turn_update:type(), + btl_character_turn_request:type() + ) + -> 'ok'. +commit_update (Update, Request) -> + send_to_database(Update, Request), + send_to_cache(Update, Request), + + ok. + +%%%% USER DISCONNECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec disconnect_user (btl_character_turn_request:type()) -> 'ok'. +disconnect_user (Request) -> + PlayerID = btl_character_turn_request:get_player_id(Request), + + btl_security:unlock_queries(PlayerID), + + ok. + +%%%% REPLY GENERATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec generate_reply (btl_character_turn_update:type()) -> binary(). +generate_reply (Update) -> + NewTimelineItems = btl_character_turn_update:get_timeline(Update), + + TurnResultReply = btl_turn_results:generate(NewTimelineItems), + + jiffy:encode([TurnResultReply]). + +%%%% MAIN LOGIC %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec handle (binary()) -> binary(). +handle (EncodedRequest) -> + Request = decode_request(EncodedRequest), + authenticate_user(Request), + Data = fetch_data(Request), + assert_user_permissions(Data, Request), + Update = update_data(Data, Request), + commit_update(Update, Request), + disconnect_user(Request), + generate_reply(Update). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +out(A) -> + { + content, + "application/json; charset=UTF-8", + handle(A#arg.clidata) + }. diff --git a/src/battle/query/btl_load_state.erl b/src/battle/query/btl_load_state.erl new file mode 100644 index 0000000..f79e24e --- /dev/null +++ b/src/battle/query/btl_load_state.erl @@ -0,0 +1,159 @@ +-module(btl_load_state). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-include("../../../include/yaws_api.hrl"). + +-record +( + input, + { + player_id :: btl_player:id(), + session_token :: binary(), + battle_id :: binary() + } +). + +-record +( + query_state, + { + battle :: btl_battle:type() + } +). + +-type input() :: #input{}. +-type query_state() :: #query_state{}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export([out/1]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec parse_input (binary()) -> input(). +parse_input (Req) -> + JSONReqMap = jiffy:decode(Req, [return_maps]), + PlayerID = maps:get(<<"pid">>, JSONReqMap), + SessionToken = maps:get(<<"stk">>, JSONReqMap), + BattleID = maps:get(<<"bid">>, JSONReqMap), + + #input + { + player_id = PlayerID, + session_token = SessionToken, + battle_id = BattleID + }. + +-spec fetch_data (input()) -> query_state(). +fetch_data (Input) -> + PlayerID = Input#input.player_id, + BattleID = Input#input.battle_id, + + Battle = sh_timed_cache:fetch(battle_db, PlayerID, BattleID), + + #query_state + { + battle = Battle + }. + +-spec generate_reply(query_state(), input()) -> binary(). +generate_reply (QueryState, Input) -> + PlayerID = Input#input.player_id, + Battle = QueryState#query_state.battle, + Players = btl_battle:get_players(Battle), + + PlayerIX = + sh_array_util:first + ( + fun (Player) -> + (btl_player:get_id(Player) == PlayerID) + end, + Players + ), + + true = (PlayerIX >= 0), + + SetTimeline = + btl_set_timeline:generate + ( + btl_battle:get_encoded_last_turns_effects(Battle) + ), + + SetMap = btl_set_map:generate(btl_battle:get_battlemap(Battle)), + + AddCharList = + array:sparse_to_list + ( + array:map + ( + fun (IX, Character) -> + btl_add_char:generate(IX, Character, PlayerIX) + end, + btl_battle:get_characters(Battle) + ) + ), + + AddWeaponList = + lists:map + ( + fun (WeaponID) -> + btl_add_weapon:generate(sh_weapon:from_id(WeaponID)) + end, + btl_battle:get_used_weapon_ids(Battle) + ), + + AddArmorList = + lists:map + ( + fun (ArmorID) -> + btl_add_armor:generate(sh_armor:from_id(ArmorID)) + end, + btl_battle:get_used_armor_ids(Battle) + ), + + AddTileList = + lists:map + ( + fun (TileID) -> + btl_add_tile:generate(btl_tile:from_id(TileID)) + end, + btl_battle:get_used_tile_ids(Battle) + ), + + OutputList = + ( + AddTileList + ++ [SetTimeline, SetMap | AddWeaponList] + ++ AddArmorList + ++ AddCharList + ), + Output = jiffy:encode(OutputList), + + Output. + +-spec handle (binary()) -> binary(). +handle (Req) -> + Input = parse_input(Req), + btl_security:assert_identity + ( + Input#input.player_id, + Input#input.session_token + ), + btl_security:lock_queries(Input#input.player_id), + QueryState = fetch_data(Input), + btl_security:unlock_queries(Input#input.player_id), + generate_reply(QueryState, Input). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +out(A) -> + { + content, + "application/json; charset=UTF-8", + handle(A#arg.clidata) + }. |