123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785 |
- %%% -------------------------------------------------------------------
- %%% Author : Sergii Polkovnikov <serge.polkovnikov@gmail.com>
- %%% Description : The "Quick play" logic
- %%%
- %%% Created : Oct 16, 2012
- %%% -------------------------------------------------------------------
- %%% Terms explanation:
- %%% GameId - uniq identifier of the tournament. Type: integer().
- %%% PlayerId - registration number of a player in the tournament. Type: integer()
- %%% UserId - cross system identifier of a physical user. Type: binary() (or string()?).
- %%% TableId - uniq identifier of a table in the tournament. Used by the
- %%% tournament logic. Type: integer().
- %%% TableGlobalId - uniq identifier of a table in the system. Can be used
- %%% to refer to a table directly - without pointing to a tournament.
- %%% Type: integer()
- -module(lucky).
- -behaviour(gen_fsm).
- %% --------------------------------------------------------------------
- %% Include files
- %% --------------------------------------------------------------------
- -include_lib("server/include/basic_types.hrl").
- -include_lib("db/include/table.hrl").
- -include_lib("db/include/scoring.hrl").
- -include_lib("db/include/transaction.hrl").
- %% --------------------------------------------------------------------
- %% External exports
- -export([start/1, start/2, start_link/2, reg/2]).
- %% gen_fsm callbacks
- -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
- -export([table_message/3, client_message/2, client_request/2, client_request/3]).
- -record(state,
- {%% Static values
- game_id :: pos_integer(),
- game :: atom(),
- game_mode :: atom(),
- table_params :: proplists:proplist(),
- table_module :: atom(),
- bot_module :: atom(),
- seats_per_table :: integer(),
- game_name :: string(),
- mode :: normal | exclusive,
- %% Dynamic values
- players, %% The register of tournament players
- tables, %% The register of tournament tables
- seats, %% Stores relation between players and tables seats
- player_id_counter :: pos_integer(),
- table_id_counter :: pos_integer(),
- cr_tab_requests,
- reg_requests,
- tab_requests
- }).
- -record(player,
- {
- id :: pos_integer(),
- user_id,
- is_bot :: boolean()
- }).
- -record(table,
- {
- id :: pos_integer(),
- global_id :: pos_integer(),
- pid,
- relay :: {atom(), pid()}, %%{RelayMod, RelayPid}
- mon_ref,
- state :: initializing | ready | in_progress | finished,
- scoring_state,
- timer :: reference()
- }).
- -record(seat,
- {
- table :: pos_integer(),
- seat_num :: integer(),
- player_id :: undefined | pos_integer(),
- is_bot :: undefined | boolean(),
- registered_by_table :: undefined | boolean(),
- connected :: undefined | boolean()
- }).
- -define(STATE_INIT, state_init).
- -define(STATE_PROCESSING, state_processing).
- -define(TOURNAMENT_TYPE, lucky).
- -define(TABLE_STATE_INITIALIZING, initializing).
- -define(TABLE_STATE_READY, ready).
- -define(TABLE_STATE_IN_PROGRESS, in_progress).
- -define(TABLE_STATE_FINISHED, finished).
- -define(REST_TIMEOUT, 5000). %% Time between game finsh and start of new round
- %% ====================================================================
- %% External functions
- %% ====================================================================
- start([GameId, Params]) -> %% XXX WTF?
- gas:info(?MODULE," +++ START LUCKY"),
- start(GameId,Params).
- start(GameId, Params) ->
- gen_fsm:start(?MODULE, [GameId, Params, self()], []).
- start_link(GameId, Params) ->
- gen_fsm:start_link(?MODULE, [GameId, Params, self()], []).
- reg(Pid, User) ->
- client_request(Pid, {reg, User}, 10000).
- table_message(Pid, TableId, Message) ->
- gen_fsm:send_all_state_event(Pid, {table_message, TableId, Message}).
- client_message(Pid, Message) ->
- gen_fsm:send_all_state_event(Pid, {client_message, Message}).
- client_request(Pid, Message) ->
- client_request(Pid, Message, 5000).
- client_request(Pid, Message, Timeout) ->
- gen_fsm:sync_send_all_state_event(Pid, {client_request, Message}, Timeout).
- %% ====================================================================
- %% Server functions
- %% ====================================================================
- init([GameId, Params, _Manager]) ->
- SeatsPerTable = get_param(seats, Params),
- Game = get_param(game, Params),
- GameMode = get_param(game_mode, Params),
- GameName = get_param(game_name, Params),
- %%% XXX QuotaPerRound = get_param(quota_per_round, Params),
- TableParams = get_param(table_params, Params),
- TableModule = get_param(table_module, Params),
- BotModule = get_param(bot_module, Params),
- gas:info(?MODULE,"TRN_LUCKY <~p> All parameteres are read. Send the directive to start the game.", [GameId]),
- gen_fsm:send_all_state_event(self(), go),
- {ok, ?STATE_INIT,
- #state{game_id = GameId,
- game = Game,
- game_mode = GameMode,
- game_name = GameName,
- seats_per_table = SeatsPerTable,
- table_params = [{parent, {?MODULE, self()}} | TableParams],
- table_module = TableModule,
- bot_module = BotModule
- }}.
- %%===================================================================
- handle_event(go, ?STATE_INIT, #state{game_id = GameId, game = Game, game_mode = GameMode,
- game_name = GameName} = StateData) ->
- gas:info(?MODULE,"TRN_LUCKY <~p> Received the directive to start the game.", [GameId]),
- DeclRec = #game_table{id = GameId,
- game_type = Game,
- game_mode = GameMode,
- game_process = self(),
- game_module = ?MODULE,
- name = GameName,
- age_limit = 100,
- game_speed = undefined,
- feel_lucky = true,
- owner = undefined,
- creator = undefined,
- rounds = undefined,
- pointing_rules = [],
- pointing_rules_ex = [],
- users = []
- },
- gproc:reg({p,l,self()}, DeclRec),
- {next_state, ?STATE_PROCESSING,
- StateData#state{players = players_init(),
- tables = tables_init(),
- seats = seats_init(),
- player_id_counter = 1,
- table_id_counter = 1,
- cr_tab_requests = dict:new(),
- reg_requests = dict:new(),
- tab_requests = dict:new()
- }};
- handle_event({client_message, Message}, StateName, StateData) ->
- handle_client_message(Message, StateName, StateData);
- handle_event({table_message, TableId, Message}, StateName, StateData) ->
- handle_table_message(TableId, Message, StateName, StateData);
- handle_event(_Event, StateName, StateData) ->
- {next_state, StateName, StateData}.
- handle_sync_event({client_request, Request}, From, StateName, StateData) ->
- handle_client_request(Request, From, StateName, StateData);
- handle_sync_event(_Event, _From, StateName, StateData) ->
- Reply = ok,
- {reply, Reply, StateName, StateData}.
- %%===================================================================
- handle_info({'DOWN', MonRef, process, _Pid, _}, StateName,
- #state{game_id = GameId, tables = Tables,
- seats = Seats, players = Players} = StateData) ->
- case get_table_by_mon_ref(MonRef, Tables) of
- #table{id = TabId, timer = TRef} ->
- gas:info(?MODULE,"TRN_LUCKY <~p> Table <~p> is down. Cleaning up registeres.", [GameId, TabId]),
- case TRef == undefined of false -> erlang:cancel_timer(TRef); true -> skip end,
- PlayersIds =
- [PlayerId || #seat{player_id = PlayerId} <- find_seats_with_players_for_table_id(TabId, Seats)],
- NewTables = del_table(TabId, Tables),
- NewSeats = del_seats_by_table_id(TabId, Seats),
- NewPlayers = del_players(PlayersIds, Players),
- {next_state, StateName, StateData#state{tables = NewTables,
- seats = NewSeats,
- players = NewPlayers}};
- not_found ->
- {next_state, StateName, StateData}
- end;
- handle_info({rest_timeout, TableId}, StateName,
- #state{game_id = GameId, tables = Tables, table_module = TableModule} = StateData) ->
- gas:info(?MODULE,"TRN_LUCKY <~p> Time to start new round for table <~p>.", [GameId, TableId]),
- case get_table(TableId, Tables) of
- {ok, #table{pid = TablePid}} ->
- gas:info(?MODULE,"TRN_LUCKY <~p> Initiating new round at table <~p>.", [GameId, TableId]),
- NewTables = set_table_state(TableId, ?TABLE_STATE_IN_PROGRESS, Tables),
- send_to_table(TableModule, TablePid, start_round),
- {next_state, StateName, StateData#state{tables = NewTables}};
- error -> %% If no such table ignore the timeout
- gas:info(?MODULE,"TRN_LUCKY <~p> There is no table <~p>. Can't start new round for it.", [GameId, TableId]),
- {next_state, StateName, StateData}
- end;
- handle_info(Message, StateName, #state{game_id = GameId} = StateData) ->
- gas:info(?MODULE,"TRN_STANDALONE <~p> Unhandled message(info) received in state <~p>: ~p.",
- [GameId, StateName, Message]),
- {next_state, StateName, StateData}.
- %%===================================================================
- terminate(_Reason, _StateName, #state{game_id=GameId}=_StatData) ->
- gas:info(?MODULE,"TRN_LUCKY <~p> Shutting down at state: <~p>. Reason: ~p",
- [GameId, _StateName, _Reason]),
- ok.
- %%===================================================================
- code_change(_OldVsn, StateName, StateData, _Extra) ->
- {ok, StateName, StateData}.
- %% --------------------------------------------------------------------
- %%% Internal functions
- %% --------------------------------------------------------------------
- handle_client_message(_Msg, StateName, StateData) ->
- {next_state, StateName, StateData}.
- %%===================================================================
- %% handle_table_message(TableId, Message, StateName, StateData)
- handle_table_message(TableId, {player_connected, PlayerId},
- ?STATE_PROCESSING,
- #state{game_id = GameId, seats = Seats, seats_per_table = SeatsNum,
- tables = Tables, table_module = TableModule} = StateData)
- when is_integer(TableId), is_integer(PlayerId) ->
- gas:info(?MODULE,"TRN_LUCKY <~p> The player_connected notification received from "
- "table <~p>. PlayerId: <~p>", [GameId, TableId, PlayerId]),
- case find_seats_by_player_id(PlayerId, Seats) of
- [#seat{seat_num = SeatNum}] ->
- NewSeats = update_seat_connect_status(TableId, SeatNum, true, Seats),
- case fetch_table(TableId, Tables) of
- #table{state = ?TABLE_STATE_READY, pid = TabPid} ->
- case is_all_players_connected(TableId, SeatsNum, NewSeats) of
- true ->
- gas:info(?MODULE,"TRN_LUCKY <~p> All clients connected. Starting a game.",
- [GameId]),
- NewTables = set_table_state(TableId, ?TABLE_STATE_IN_PROGRESS, Tables),
- send_to_table(TableModule, TabPid, start_round),
- {next_state, ?STATE_PROCESSING, StateData#state{seats = NewSeats,
- tables = NewTables}};
- false ->
- {next_state, ?STATE_PROCESSING, StateData#state{seats = NewSeats}}
- end;
- _ ->
- {next_state, ?STATE_PROCESSING, StateData#state{seats = NewSeats}}
- end;
- [] -> %% Ignoring the message
- {next_state, ?STATE_PROCESSING, StateData}
- end;
- handle_table_message(TableId, {player_disconnected, PlayerId},
- ?STATE_PROCESSING, #state{game_id = GameId, seats = Seats} = StateData)
- when is_integer(TableId), is_integer(PlayerId) ->
- gas:info(?MODULE,"TRN_LUCKY <~p> The player_disconnected notification received from "
- "table <~p>. PlayerId: <~p>", [GameId, TableId, PlayerId]),
- case find_seats_by_player_id(PlayerId, Seats) of
- [#seat{seat_num = SeatNum, is_bot = true}] ->
- gas:info(?MODULE,"TRN_LUCKY <~p> Bot Replaces the Bot?", [GameId, TableId]),
- {next_state, ?STATE_PROCESSING, StateData};
- [#seat{seat_num = SeatNum, is_bot = IsBot}] ->
- case real_players_at_table(TableId, Seats) of
- 1 when not IsBot -> %% Last real player gone
- gas:info(?MODULE,"TRN_LUCKY <~p> Last real player gone from "
- "table <~p>. Closing the table.", [GameId, TableId]),
- unreg_player_and_eliminate_table(PlayerId, TableId, StateData);
- _ ->
- gas:info(?MODULE,"TRN_LUCKY <~p> Al least one real player is at table <~p>. "
- "Starting a bot to replace free seat.", [GameId, TableId]),
- replace_player_by_bot(PlayerId, TableId, SeatNum, StateData)
- end;
- [] -> %% Ignoring the message
- {next_state, ?STATE_PROCESSING, StateData}
- end;
- handle_table_message(TableId, {table_created, Relay}, ?STATE_PROCESSING,
- #state{game_id = GameId, tables = Tables, seats = Seats,
- cr_tab_requests = TCrRequests, table_module = TableModule,
- reg_requests = RegRequests} = StateData)
- when is_integer(TableId) ->
- gas:info(?MODULE,"TRN_LUCKY <~p> The <table_created> notification received from table: ~p.",
- [GameId, TableId]),
- TabInitPlayers = dict:fetch(TableId, TCrRequests),
- %% Update status of players
- TabSeats = find_seats_by_table_id(TableId, Seats),
- F = fun(#seat{player_id = PlayerId} = S, Acc) ->
- case lists:member(PlayerId, TabInitPlayers) of
- true -> store_seat(S#seat{registered_by_table = true}, Acc);
- false -> Acc
- end
- end,
- NewSeats = lists:foldl(F, Seats, TabSeats),
- %% Process delayed registration requests
- TablePid = get_table_pid(TableId, Tables),
- F2 = fun(PlayerId, Acc) ->
- case dict:find(PlayerId, Acc) of
- {ok, From} ->
- gen_fsm:reply(From, {ok, {PlayerId, Relay, {TableModule, TablePid}}}),
- dict:erase(PlayerId, Acc);
- error -> Acc
- end
- end,
- NewRegRequests = lists:foldl(F2, RegRequests, TabInitPlayers),
- NewTCrRequests = dict:erase(TableId, TCrRequests),
- NewTables = update_created_table(TableId, Relay, Tables),
- {next_state, ?STATE_PROCESSING, StateData#state{tables = NewTables,
- seats = NewSeats,
- cr_tab_requests = NewTCrRequests,
- reg_requests = NewRegRequests}};
- handle_table_message(TableId, {round_finished, NewScoringState, RoundScore, _TotalScore},
- ?STATE_PROCESSING,
- #state{game_id = GameId, tables = Tables, table_module = TableModule,
- game_mode = GameMode, game = GameType, players = Players} = StateData)
- when is_integer(TableId) ->
- gas:info(?MODULE,"TRN_LUCKY <~p> The <round_finished> received from table: ~p~nScore: ~p.",
- [GameId, TableId,RoundScore]),
- %% Add score per round
- UsersPoints = lists:flatten(
- [ begin
- case midict:find(PlayerId, Players) of
- {ok, #player{id = UserId, is_bot = false}} -> {UserId, Points};
- _Error -> gas:info(?MODULE, "get_player_info_by_user_id ~p", [_Error]), [] end
- end|| {PlayerId, Points} <- RoundScore]),
- add_points_to_accounts(UsersPoints, GameId, GameType, GameMode),
- #table{pid = TablePid} = Table = fetch_table(TableId, Tables),
- TRef = erlang:send_after(?REST_TIMEOUT, self(), {rest_timeout, TableId}),
- NewTable = Table#table{scoring_state = NewScoringState, state = ?TABLE_STATE_FINISHED, timer = TRef},
- NewTables = store_table(NewTable, Tables),
- send_to_table(TableModule, TablePid, show_round_result),
- {next_state, ?STATE_PROCESSING, StateData#state{tables = NewTables}};
- handle_table_message(TableId, {response, RequestId, Response},
- ?STATE_PROCESSING,
- #state{game_id = GameId, tab_requests = TabRequests} = StateData)
- when is_integer(TableId) ->
- NewTabRequests = dict:erase(RequestId, TabRequests),
- case dict:find(RequestId, TabRequests) of
- {ok, ReqContext} ->
- gas:info(?MODULE,"TRN_LUCKY <~p> A response received from table <~p>. "
- "RequestId: ~p. Request context: ~p. Response: ~p",
- [GameId, TableId, RequestId, ReqContext, Response]),
- handle_table_response(ReqContext, Response, ?STATE_PROCESSING,
- StateData#state{tab_requests = NewTabRequests});
- error ->
- gas:error(?MODULE,"TRN_LUCKY <~p> Table <~p> sent a response for unknown request. "
- "RequestId: ~p. Response", []),
- {next_state, ?STATE_PROCESSING, StateData#state{tab_requests = NewTabRequests}}
- end;
- handle_table_message(_TableId, _Event, StateName, StateData) ->
- {next_state, StateName, StateData}.
- %%===================================================================
- %% handle_table_response(RequestContext, Response, StateName, StateData)
- handle_table_response({replace_player, PlayerId, TableId, SeatNum}, ok = _Response,
- ?STATE_PROCESSING,
- #state{reg_requests = RegRequests, seats = Seats,
- tables = Tables, table_module = TableModule} = StateData) ->
- Seat = fetch_seat(TableId, SeatNum, Seats),
- NewSeats = store_seat(Seat#seat{registered_by_table = true}, Seats),
- %% Send response to a client for a delayed request
- NewRegRequests =
- case dict:find(PlayerId, RegRequests) of
- {ok, From} ->
- #table{relay = Relay, pid = TablePid} = fetch_table(TableId, Tables),
- gen_fsm:reply(From, {ok, {PlayerId, Relay, {TableModule, TablePid}}}),
- dict:erase(PlayerId, RegRequests);
- error -> RegRequests
- end,
- {next_state, ?STATE_PROCESSING, StateData#state{seats = NewSeats,
- reg_requests = NewRegRequests}}.
- %%===================================================================
- handle_client_request({reg, User}, From, ?STATE_PROCESSING,
- #state{game_id = GameId, reg_requests = RegRequests,
- seats = Seats, players=Players, tables = Tables,
- table_module = TableModule} = StateData) ->
- #'PlayerInfo'{id = UserId, robot = IsBot} = User,
- gas:info(?MODULE,"TRN_LUCKY <~p> The Register request received from user: ~p.", [GameId, UserId]),
- case IsBot of
- true -> %% Bots can't initiate a registration
- case get_player_id_by_user_id(UserId, Players) of
- {ok, PlayerId} -> %% Already registered. Send table requsites.
- [#seat{table = TableId, registered_by_table = RegByTable}] = find_seats_by_player_id(PlayerId, Seats),
- case RegByTable of
- false -> %% Store delayed request
- NewRegRequests = dict:store(PlayerId, From, RegRequests),
- {next_state, ?STATE_PROCESSING, StateData#state{reg_requests = NewRegRequests}};
- _ ->
- #table{relay = Relay, pid = TPid} = fetch_table(TableId, Tables),
- {reply, {ok, {PlayerId, Relay, {TableModule, TPid}}}, ?STATE_PROCESSING, StateData}
- end;
- error -> %% Not registered
- gas:info(?MODULE,"TRN_LUCKY <~p> User ~p is a bot. The user not registered. "
- "Rejecting registration.", [GameId, UserId]),
- {reply, {error, indy_bots_not_allowed}, ?STATE_PROCESSING, StateData}
- end;
- false -> %% Normal user
- IgnoredPlayers = [Id || #player{id = Id} <- midict:geti(UserId, user_id, Players)],
- gas:info(?MODULE,"TRN_LUCKY <~p> There are no table with free seats.", [GameId]),
- case find_bot_seat_without_players(Seats, IgnoredPlayers) of
- #seat{table = TabId, seat_num = SeatNum, player_id = OldPlayerId} ->
- gas:info(?MODULE,"TRN_LUCKY <~p> Found a seat with a bot. Replacing by the user. "
- "UserId:~p TabId: ~p SeatNum: ~p.", [GameId, UserId, TabId, SeatNum]),
- reg_player_with_replace(User, TabId, SeatNum, OldPlayerId, From, StateData);
- not_found ->
- gas:info(?MODULE,"TRN_LUCKY <~p> There are no seats with bots. "
- "Creating new table for user: ~p.", [GameId, UserId]),
- reg_player_at_new_table(User, From, StateData)
- end
- end;
- handle_client_request(Request, From, StateName, #state{game_id = GameId} = StateData) ->
- gas:info(?MODULE,"TRN_LUCKY <~p> Unhandled client request received from ~p in "
- "state <~p>: ~p.", [GameId, From, StateName, Request]),
- {reply, {error, unexpected_request}, StateName, StateData}.
- %%===================================================================
- reg_player_with_replace(UserInfo, TableId, SeatNum, OldPlayerId, From,
- #state{game_id = GameId, players = Players, tables = Tables,
- seats = Seats, player_id_counter = PlayerId,
- tab_requests = TabRequests, reg_requests = RegRequests,
- table_module = TableModule} = StateData) ->
- #'PlayerInfo'{id = UserId, robot = IsBot} = UserInfo,
- NewPlayers = del_player(OldPlayerId, Players),
- NewPlayers2 = reg_player(#player{id = PlayerId, user_id = UserId, is_bot = IsBot}, NewPlayers),
- gas:info(?MODULE,"TRN_LUCKY <~p> User ~p registered as player <~p>.", [GameId, UserId, PlayerId]),
- NewSeats = assign_seat(TableId, SeatNum, PlayerId, IsBot, false, false, Seats),
- gas:info(?MODULE,"TRN_LUCKY <~p> User ~p assigned to seat <~p> of table <~p>.", [GameId, UserId, SeatNum, TableId]),
- NewRegRequests = dict:store(PlayerId, From, RegRequests),
- TablePid = get_table_pid(TableId, Tables),
- NewTabRequests = table_req_replace_player(TableModule, TablePid, PlayerId, UserInfo, TableId, SeatNum, TabRequests),
- {next_state, ?STATE_PROCESSING, StateData#state{players = NewPlayers2,
- seats = NewSeats,
- player_id_counter = PlayerId + 1,
- tab_requests = NewTabRequests,
- reg_requests = NewRegRequests}}.
- reg_player_at_new_table(User, From,
- #state{game_id = GameId, players = Players,
- tables = Tables, seats = Seats, seats_per_table = SeatsNum,
- player_id_counter = PlayerIdCounter,
- table_id_counter = TableId, table_module = TableModule,
- bot_module = BotModule, table_params = TableParams,
- reg_requests = RegRequests, cr_tab_requests = TCrRequests
- } = StateData) ->
- #'PlayerInfo'{id = UserId, robot = IsBot} = User,
- RobotsInfo = spawn_bots(GameId, BotModule, SeatsNum - 1),
- gas:info(?MODULE,"TRN_LUCKY <~p> Bots for table <~p> are spawned.", [GameId, TableId]),
- F = fun(BotInfo, {PlId,SNum}) -> {{PlId, BotInfo, SNum, _Points = 0}, {PlId + 1, SNum + 1}} end,
- {RobotsRegData, {PlayerId, SeatNum}} = lists:mapfoldl(F, {PlayerIdCounter, 1}, RobotsInfo),
- TPlayers = [{PlayerId, User, SeatNum, 0} | RobotsRegData],
- TableParams2 = [{players, TPlayers}, {table_name, "I'm filling lucky"} | TableParams],
- {ok, TabPid} = spawn_table(TableModule, GameId, TableId, TableParams2),
- MonRef = erlang:monitor(process, TabPid),
- %% FIXME: Table global id should use a persistent counter
- NewTables = reg_table(TableId, TabPid, MonRef, 0, undefined, Tables),
- gas:info(?MODULE,"TRN_LUCKY <~p> New table created: ~p.", [GameId, TableId]),
- NewPlayers = reg_player(#player{id = PlayerId, user_id = UserId, is_bot = IsBot}, Players),
- F2 = fun({PlId, #'PlayerInfo'{id = UId, robot=Bot}, _SNum, _Points}, Acc) ->
- reg_player(#player{id = PlId, user_id = UId, is_bot = Bot}, Acc)
- end,
- NewPlayers2 = lists:foldl(F2, NewPlayers, RobotsRegData),
- gas:info(?MODULE,"TRN_LUCKY <~p> User ~p registered as player <~p>.", [GameId, UserId, PlayerId]),
- NewSeats = assign_seat(TableId, SeatNum, PlayerId, IsBot, false, false, Seats),
- F3 = fun({PlId, _UserInfo, SNum, _Points}, Acc) ->
- assign_seat(TableId, SNum, PlId, true, false, false, Acc)
- end,
- NewSeats2 = lists:foldl(F3, NewSeats, RobotsRegData),
- gas:info(?MODULE,"TRN_LUCKY <~p> User ~p assigned to seat <~p> of table <~p>.", [GameId, UserId, SeatNum, TableId]),
- NewRegRequests = dict:store(PlayerId, From, RegRequests),
- PlayersIds = [PlayerId | [PlId || {PlId, _, _, _} <- RobotsRegData]],
- NewTCrRequests = dict:store(TableId, PlayersIds, TCrRequests),
- {next_state, ?STATE_PROCESSING, StateData#state{players = NewPlayers2,
- seats = NewSeats2,
- tables = NewTables,
- player_id_counter = PlayerId + 1,
- table_id_counter = TableId + 1,
- reg_requests = NewRegRequests,
- cr_tab_requests = NewTCrRequests}}.
- unreg_player_and_eliminate_table(PlayerId, TableId,
- #state{players = Players, tables = Tables,
- table_module = TableModule, seats = Seats} = StateData) ->
- NewPlayers = del_player(PlayerId, Players),
- TablePid = get_table_pid(TableId, Tables),
- NewSeats = del_seats_by_table_id(TableId, Seats),
- NewTables = del_table(TableId, Tables),
- send_to_table(TableModule, TablePid, stop),
- {next_state, ?STATE_PROCESSING, StateData#state{players = NewPlayers,
- seats = NewSeats,
- tables = NewTables}}.
- replace_player_by_bot(PlayerId, TableId, SeatNum,
- #state{players = Players, seats = Seats, game_id = GameId,
- bot_module = BotModule, table_module = TableModule,
- player_id_counter = NewPlayerId, tables = Tables,
- tab_requests = Requests} = StateData) ->
- NewPlayers = del_player(PlayerId, Players),
- [#'PlayerInfo'{id = UserId} = UserInfo] = spawn_bots(GameId, BotModule, 1),
- NewPlayers2 = reg_player(#player{id = NewPlayerId, user_id = UserId, is_bot = true}, NewPlayers),
- NewSeats = assign_seat(TableId, SeatNum, NewPlayerId, true, false, false, Seats),
- TablePid = get_table_pid(TableId, Tables),
- NewRequests = table_req_replace_player(TableModule, TablePid, NewPlayerId, UserInfo, TableId, SeatNum, Requests),
- {next_state, ?STATE_PROCESSING, StateData#state{players = NewPlayers2,
- seats = NewSeats,
- player_id_counter = NewPlayerId + 1,
- tab_requests = NewRequests}}.
- %% table_req_replace_player(TableModule, TablePid, PlayerId, UserInfo, TableId, SeatNum, TabRequests) -> NewRequests
- table_req_replace_player(TableModule, TablePid, PlayerId, UserInfo, TableId, SeatNum, TabRequests) ->
- RequestId = make_ref(),
- NewRequests = dict:store(RequestId, {replace_player, PlayerId, TableId, SeatNum}, TabRequests),
- send_to_table(TableModule, TablePid, {replace_player, RequestId, UserInfo, PlayerId, SeatNum}),
- NewRequests.
- %% players_init() -> players()
- players_init() ->
- midict:new().
- %% reg_player(#player{}, Players) -> NewPlayers
- reg_player(#player{id =Id, user_id = UserId} = Player, Players) ->
- midict:store(Id, Player, [{user_id, UserId}], Players).
- get_player_id_by_user_id(UserId, Players) ->
- case midict:geti(UserId, user_id, Players) of
- [#player{id = PlayerId}] -> {ok, PlayerId};
- [] -> error
- end.
- %% del_player(PlayerId, Players) -> NewPlayers
- del_player(PlayerId, Players) ->
- midict:erase(PlayerId, Players).
- %% del_player(PlayersIds, Players) -> NewPlayers
- del_players([], Players) -> Players;
- del_players([PlayerId | Rest], Players) ->
- del_players(Rest, del_player(PlayerId, Players)).
- tables_init() ->
- midict:new().
- reg_table(TableId, Pid, MonRef, GlobalId, Scoring, Tables) ->
- Table = #table{id = TableId, pid = Pid, mon_ref = MonRef, global_id = GlobalId,
- state = initializing, scoring_state = Scoring},
- store_table(Table, Tables).
- update_created_table(TableId, Relay, Tables) ->
- Table = midict:fetch(TableId, Tables),
- NewTable = Table#table{relay = Relay, state = ?TABLE_STATE_READY},
- store_table(NewTable, Tables).
- store_table(#table{id = TableId, pid = Pid, mon_ref = MonRef, global_id = GlobalId} = Table, Tables) ->
- midict:store(TableId, Table, [{pid, Pid}, {global_id, GlobalId}, {mon_ref, MonRef}], Tables).
- fetch_table(TableId, Tables) ->
- midict:fetch(TableId, Tables).
- get_table(TableId, Tables) ->
- midict:find(TableId, Tables).
- get_table_pid(TabId, Tables) ->
- {ok, #table{pid = TabPid}} = midict:find(TabId, Tables),
- TabPid.
- del_table(TabId, Tables) ->
- midict:erase(TabId, Tables).
- get_table_by_mon_ref(MonRef, Tables) ->
- case midict:geti(MonRef, mon_ref, Tables) of
- [Table] -> Table;
- [] -> not_found
- end.
- set_table_state(TableId, State, Tables) ->
- Table = midict:fetch(TableId, Tables),
- store_table(Table#table{state = State}, Tables).
- seats_init() ->
- midict:new().
- find_bot_seat_without_players(Seats, PlayersList) ->
- case midict:geti(true, is_bot, Seats) of
- [] -> not_found;
- List ->
- TabList = lists:usort([TabId || #seat{table = TabId} <- List]),
- lookup_bot_seat_without_players(TabList, PlayersList, Seats)
- end.
- lookup_bot_seat_without_players([], _, _) -> not_found;
- lookup_bot_seat_without_players([TabId | Rest], PlayersList, Seats) ->
- TabPlayers = [Id || #seat{player_id=Id} <-
- midict:geti(TabId, non_free_at_tab, Seats), lists:member(Id, PlayersList)],
- if TabPlayers == [] ->
- gas:info(?MODULE,"TRN_LUCKY Seats:~p", [midict:geti(TabId, table_id, Seats)]),
- hd(midict:geti(TabId, bot_at_tab, Seats));
- true -> lookup_bot_seat_without_players(Rest, PlayersList, Seats)
- end.
- find_seats_with_players_for_table_id(TabId, Seats) ->
- midict:geti(TabId, non_free_at_tab, Seats).
- find_seats_by_player_id(PlayerId, Seats) ->
- midict:geti(PlayerId, player_id, Seats).
- find_seats_by_table_id(TabId, Seats) ->
- midict:geti(TabId, table_id, Seats).
- %% real_players_at_table(TabId, Seats) -> Num
- real_players_at_table(TabId, Seats) ->
- length(find_real_players_seats_at_tab(TabId, Seats)).
- is_all_players_connected(TableId, TableSeatsNum, Seats) ->
- TableSeatsNum == length(midict:geti(true, {connected, TableId}, Seats)).
- find_real_players_seats_at_tab(TabId, Seats) ->
- midict:geti(TabId, real_player_at_tab, Seats).
- fetch_seat(TableId, SeatNum, Seats) ->
- midict:fetch({TableId, SeatNum}, Seats).
- %% assign_seat(TabId, SeatNum, PlayerId, IsBot, RegByTable, Connected, Seats) -> NewSeats
- %% PlayerId = integer()
- %% IsBot = RegByTable = Connected = undefined | boolean()
- assign_seat(TabId, SeatNum, PlayerId, IsBot, RegByTable, Connected, Seats) ->
- Seat = #seat{table = TabId, seat_num = SeatNum, player_id = PlayerId,
- is_bot = IsBot, registered_by_table = RegByTable, connected = Connected},
- store_seat(Seat, Seats).
- update_seat_connect_status(TableId, SeatNum, ConnStatus, Seats) ->
- Seat = midict:fetch({TableId, SeatNum}, Seats),
- NewSeat = Seat#seat{connected = ConnStatus},
- store_seat(NewSeat, Seats).
- store_seat(#seat{table = TabId, seat_num = SeatNum, player_id = PlayerId,
- is_bot = IsBot, registered_by_table = _RegByTable,
- connected = Connected} = Seat, Seats) ->
- Indices = if PlayerId == undefined ->
- [{table_id, TabId}, {free, true}, {free_at_tab, TabId}];
- true ->
- I = [{table_id, TabId}, {free, false}, {non_free_at_tab, TabId},
- {player_id, PlayerId}, {is_bot, IsBot},
- {{connected, TabId}, Connected}],
- if IsBot -> [{bot_at_tab, TabId} | I];
- true -> [{real_player_at_tab, TabId} | I]
- end
- end,
- midict:store({TabId, SeatNum}, Seat, Indices, Seats).
- create_seats(_TabId, 0, Seats) -> Seats;
- create_seats(TabId, SeatNum, Seats) ->
- NewSeats = assign_seat(TabId, SeatNum, undefined, undefined, undefined, undefined, Seats),
- create_seats(TabId, SeatNum - 1, NewSeats).
- del_seats_by_table_id(TabId, Seats) ->
- F = fun(#seat{seat_num = SeatNum}, Acc) ->
- midict:erase({TabId, SeatNum}, Acc)
- end,
- lists:foldl(F, Seats, find_seats_by_table_id(TabId, Seats)).
- spawn_bots(GameId, BotModule, 1) ->
- [ spawn_bot(BotModule, GameId, auth_server:robot_credentials()) ];
- spawn_bots(GameId, BotModule, BotsNum) ->
- [ spawn_bot(BotModule, GameId, PlayerInfo)
- || PlayerInfo <- lists:sublist(auth_server:spare(),BotsNum) ].
- spawn_bot(BotModule, GameId, PlayerInfo) ->
- {NPid, UserInfo} = create_robot(BotModule, GameId, PlayerInfo),
- BotModule:join_game(NPid),
- PlayerInfo.
- create_robot(BotModule, GameId, UserInfo) ->
- {ok, NPid} = BotModule:start(self(), UserInfo, GameId),
- BotModule:get_session(NPid), %% Hack for the tavla_bot. Creates a game session process.
- {NPid, UserInfo}.
- spawn_table(TabMod, GameId, TableId, Params) ->
- Pid = TabMod:start(GameId, TableId, Params),
- Pid.
- send_to_table(TabMod, TabPid, Message) ->
- TabMod:parent_message(TabPid, Message).
- get_param(ParamId, Params) ->
- {_, Value} = lists:keyfind(ParamId, 1, Params),
- Value.
- %% add_points_to_accounts(Points, GameId, GameType, GameMode) -> ok
- %% Types: Points = [{UserId, GamePoints}]
- add_points_to_accounts(Points, GameId, GameType, GameMode) ->
- TI = #ti_game_event{game_name = GameType, game_mode = GameMode,
- id = GameId, double_points = 1,
- type = game_end, tournament_type = lucky},
- [begin
- if GamePoints =/= 0 ->
-
- %{UserId,game_points,GameType,GameMode,TournamentType}
- kvs:add(#transaction{
- id=kvs:next_id(transaction,1),
- feed_id={game_points,UserId},
- amount=GamePoints,
- comment=TI});
- true -> do_nothing
- end
- end || {UserId, GamePoints} <- Points],
- ok.
|