game_manager.erl 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. -module(game_manager).
  2. -behaviour(gen_server).
  3. -author('Maxim Sokhatsky <maxim@synrc.com>').
  4. -compile(export_all).
  5. -export([init/1, start/0, start_link/0, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
  6. -include_lib("server/include/requests.hrl").
  7. -include_lib("server/include/conf.hrl").
  8. -include_lib("db/include/table.hrl").
  9. -include_lib("db/include/tournaments.hrl").
  10. -include_lib("server/include/log.hrl").
  11. -include_lib("stdlib/include/qlc.hrl").
  12. -include_lib("db/include/accounts.hrl").
  13. -record(state, { game_tavla = 0, game_okey = 0 }).
  14. destroy_game(Pid,Sup) -> game_sup:stop_game(Sup,Pid).
  15. gen_game_id() ->
  16. PoolNum = nsx_opt:get_env(nsx_idgen,game_pool,5000000) div 1000000,
  17. PoolNumStr = integer_to_list(PoolNum),
  18. nsm_db:next_id("game_id_pool_"++PoolNumStr, PoolNum*1000000 + 200, 1). %% 200 is reserved for lucky games and for already created games
  19. create_game(GameId, GameFSM, Params) ->
  20. {ok, Pid} = create_game_monitor(GameId, GameFSM, Params),
  21. {ok, GameId, Pid}.
  22. %% rank_table(GameId) -> {ok, {LastTourNum, TourResult}} | {error, Reason}
  23. %% TourResult = {UserId, Pos, Points, Status}
  24. rank_table(GameId) ->
  25. case get_relay_mod_pid(GameId) of
  26. {Module, Pid} -> Module:system_request(Pid, last_tour_result);
  27. undefined -> {error, not_found}
  28. end.
  29. get_lucky_pid(Sup) ->
  30. [X]=game_manager:get_lucky_table(Sup),
  31. X#game_table.game_process.
  32. get_relay_pid(GameId) -> case get_tables(GameId) of [] -> undefined;
  33. [#game_table{game_process = P} | _] -> ?INFO("GameRelay: ~p",[P]), P end.
  34. get_relay_mod_pid(GameId) -> case get_tables(GameId) of [] -> undefined;
  35. [#game_table{game_process = P, game_module = M} | _] -> ?INFO("GameRelay: ~p",[{M,P}]), {M,P} end.
  36. get_relay(GameId) -> gen_server:call(?MODULE, {get_relay, GameId}).
  37. game_requirements(GameAtom) -> GameAtom:get_requirements().
  38. game_requirements(game_tavla,paired) -> paired_tavla:get_requirements();
  39. game_requirements(GameAtom,_) -> GameAtom:get_requirements().
  40. counter(Game) -> PL = supervisor:count_children(case Game of game_okey -> okey_sup;
  41. game_tavla -> tavla_sup; _ -> game_sup end),
  42. Res = proplists:get_value(active, PL, 0),
  43. case Game of
  44. game_okey -> Res;
  45. game_tavla -> Res;
  46. _ -> 0 end.
  47. start() -> gen_server:start({local, ?MODULE}, ?MODULE, [], []).
  48. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
  49. stop(Ref) -> gen_server:cast(Ref, stop).
  50. init([]) -> {ok,#state{}}.
  51. handle_call({get_relay, Topic}, _From, State) -> Res = get_relay_pid(Topic), {reply, Res, State};
  52. handle_call({game_counter, FSM}, _From, State) ->
  53. {reply, case FSM of game_tavla -> State#state.game_tavla; game_okey -> State#state.game_okey; _ -> 0 end, State};
  54. handle_call(Event, From, State) -> {stop, {unknown_call, Event, From}, State}.
  55. handle_cast(stop, State) -> {stop, normal, State};
  56. handle_cast(Event, State) -> {stop, {unknown_cast, Event}, State}.
  57. handle_info({'DOWN', _, process, Pid, Reason}, State) -> {noreply, State};
  58. handle_info(Info, State) -> {stop, {unknown_info, Info}, State}.
  59. terminate(_Reason, _State) -> ok.
  60. code_change(_OldVsn, State, _Extra) -> {ok, State}.
  61. game_sup_domain(Module, Params) ->
  62. case Module of
  63. game_tavla_ng_trn_paired -> tavla_sup;
  64. nsg_trn_standalone ->
  65. case proplists:get_value(game, Params) of
  66. game_okey -> okey_sup;
  67. game_tavla -> tavla_sup;
  68. _ -> game_sup
  69. end;
  70. nsg_trn_elimination ->
  71. case proplists:get_value(game_type, Params) of
  72. game_okey -> okey_sup;
  73. game_tavla -> tavla_sup;
  74. _ -> game_sup
  75. end;
  76. _ -> game_sup
  77. end.
  78. create_game_monitor(Topic, GameFSM, Params) ->
  79. Sup = game_sup_domain(GameFSM, Params),
  80. ?INFO("Create Root Game Process (Game Monitor2): ~p Params: ~p Sup: ~p",[GameFSM, Params,Sup]),
  81. RelayInit = Sup:start_game(GameFSM,[Topic,Params],Topic),
  82. ?INFO("RelayInit ~p",[RelayInit]),
  83. RelayInit.
  84. get_lucky_table(Game) ->
  85. Lucky = true,
  86. Check = fun(undefined, _Value) -> true;
  87. (Param, Value) -> Param == Value
  88. end,
  89. Cursor = fun() ->
  90. qlc:cursor(qlc:q([V || {{_,_,_K},_,V=#game_table{game_type=G,
  91. feel_lucky = L}}
  92. <- gproc:table(props),
  93. Check(Game, G),
  94. Check(Lucky, L)]))
  95. end,
  96. Tables = qlc:next_answers(Cursor(), 1),
  97. Tables.
  98. get_tournament(TrnId) ->
  99. Check = fun(undefined, _Value) -> true;
  100. (Param, Value) -> Param == Value
  101. end,
  102. Cursor = fun() ->
  103. qlc:cursor(qlc:q([V || {{_,_,_K},_, V = #game_table{trn_id=TId}} <- gproc:table(props),
  104. Check(TrnId, TId)]))
  105. end,
  106. Table = case qlc:next_answers(Cursor(), 1) of
  107. [T] -> X = T#game_table.id, X;
  108. _ -> []
  109. end,
  110. % ?INFO("~w:get_tournament Table = ~p", [?MODULE, Table]),
  111. Table.
  112. %% stress_test(NumberOfRooms) ->
  113. %% OkeyPlayers = [begin
  114. %% {ok,GameId,A} = game_manager:create_table(game_okey,[{table_name,"okey maxim and alice + 2 robots"},
  115. %% {speed,normal},
  116. %% {rounds,80},
  117. %% {sets,1},
  118. %% {game_mode,standard},
  119. %% {owner,"kate"}],[<<"maxim">>,<<"alice">>,robot,robot]),
  120. %%
  121. %% Clients = [ proc_lib:spawn_link(fun() ->
  122. %% test_okey:init_with_join_game(self(), '127.0.0.1', ?LISTEN_PORT, GameId, Id, 1, normal)
  123. %% end) || Id <- [<<"maxim">>,<<"alice">>] ],
  124. %%
  125. %% {ok,GameId,A}
  126. %%
  127. %%
  128. %% end ||X<-lists:seq(1,NumberOfRooms)],
  129. %% [{ok,OP1,_}|_] = OkeyPlayers,
  130. %% [{ok,OP2,_}|_] = lists:reverse(OkeyPlayers),
  131. %% ?INFO("Okey bot rooms runned (STRESS): ~p~n",[{OP1,OP2}]).
  132. create_standalone_game(Game, Params, Users) ->
  133. ?INFO("create_standalone_game/3 Params:~p", [Params]),
  134. case Game of
  135. game_okey ->
  136. #pointing_rule{quota = Quota,
  137. kakush_winner = KakushForWinners,
  138. kakush_other = KakushForLoser,
  139. game_points = WinGamePoints
  140. } = proplists:get_value(pointing_rules, Params),
  141. GameId = proplists:get_value(game_id, Params),
  142. TableName = proplists:get_value(table_name, Params),
  143. MulFactor = proplists:get_value(double_points, Params, 1),
  144. SlangAllowed = proplists:get_value(slang, Params, false),
  145. ObserversAllowed = proplists:get_value(observers, Params, false),
  146. Speed = proplists:get_value(speed, Params, normal),
  147. GameMode = proplists:get_value(game_mode, Params),
  148. Rounds = case GameMode of
  149. countdown -> undefined;
  150. _ -> proplists:get_value(rounds, Params, undefined)
  151. end,
  152. GostergeFinishAllowed = proplists:get_value(gosterge_finish, Params, false),
  153. BotsReplacementMode = case proplists:get_value(robots_replacement_allowed, Params, true) of
  154. true -> enabled;
  155. false -> disabled
  156. end,
  157. TableParams = [
  158. {table_name, TableName},
  159. {mult_factor, MulFactor},
  160. {slang_allowed, SlangAllowed},
  161. {observers_allowed, ObserversAllowed},
  162. {tournament_type, standalone},
  163. {round_timeout, infinity},
  164. %% {round_timeout, 30 * 1000},
  165. {set_timeout, infinity},
  166. %% {set_timeout, 10 * 60 *1000},
  167. {speed, Speed},
  168. {game_type, GameMode},
  169. {rounds, Rounds},
  170. {reveal_confirmation, true},
  171. {next_series_confirmation, no_exit},
  172. {pause_mode, normal},
  173. {social_actions_enabled, true},
  174. {gosterge_finish_allowed, GostergeFinishAllowed}
  175. ],
  176. create_game(GameId, nsg_trn_standalone,
  177. [{game, Game},
  178. {game_mode, GameMode},
  179. {game_name, TableName},
  180. {seats, 4},
  181. {registrants, Users},
  182. {initial_points, 0},
  183. {quota_per_round, Quota},
  184. {kakush_for_winners, KakushForWinners},
  185. {kakush_for_loser, KakushForLoser},
  186. {win_game_points, WinGamePoints},
  187. {mul_factor, MulFactor},
  188. {table_module, game_okey_ng_table_trn},
  189. {bot_module, game_okey_bot},
  190. {bots_replacement_mode, BotsReplacementMode},
  191. {table_params, TableParams},
  192. {common_params, Params}
  193. ]);
  194. game_tavla ->
  195. #pointing_rule{quota = Quota,
  196. kakush_winner = KakushForWinners,
  197. kakush_other = KakushForLoser,
  198. game_points = WinGamePoints
  199. } = proplists:get_value(pointing_rules, Params),
  200. GameId = proplists:get_value(game_id, Params),
  201. TableName = proplists:get_value(table_name, Params),
  202. MulFactor = proplists:get_value(double_points, Params, 1),
  203. SlangAllowed = proplists:get_value(slang, Params, false),
  204. ObserversAllowed = proplists:get_value(observers, Params, false),
  205. Speed = proplists:get_value(speed, Params, normal),
  206. GameMode = proplists:get_value(game_mode, Params),
  207. Rounds = case GameMode of
  208. _ -> proplists:get_value(rounds, Params, undefined)
  209. end,
  210. BotsReplacementMode = case proplists:get_value(robots_replacement_allowed, Params, true) of
  211. true -> enabled;
  212. false -> disabled
  213. end,
  214. TableParams = [
  215. {table_name, TableName},
  216. {mult_factor, MulFactor},
  217. {slang_allowed, SlangAllowed},
  218. {observers_allowed, ObserversAllowed},
  219. {tournament_type, standalone},
  220. {round_timeout, infinity},
  221. %% {round_timeout, 30 * 1000},
  222. {set_timeout, infinity},
  223. %% {set_timeout, 10 * 60 *1000},
  224. {speed, Speed},
  225. {game_mode, GameMode},
  226. {rounds, Rounds},
  227. {next_series_confirmation, no_exit},
  228. {pause_mode, normal},
  229. {social_actions_enabled, true},
  230. {tables_num, 1}
  231. ],
  232. create_game(GameId, nsg_trn_standalone,
  233. [{game, Game},
  234. {game_mode, GameMode},
  235. {game_name, TableName},
  236. {seats, 2},
  237. {registrants, Users},
  238. {initial_points, 0},
  239. {quota_per_round, Quota},
  240. {kakush_for_winners, KakushForWinners},
  241. {kakush_for_loser, KakushForLoser},
  242. {win_game_points, WinGamePoints},
  243. {mul_factor, MulFactor},
  244. {table_module, game_tavla_ng_table},
  245. {bot_module, game_tavla_bot},
  246. {bots_replacement_mode, BotsReplacementMode},
  247. {table_params, TableParams},
  248. {common_params, Params}
  249. ])
  250. end.
  251. create_paired_game(Game, Params, Users) ->
  252. ?INFO("create_paired_game/3 Params:~p", [Params]),
  253. case Game of
  254. game_tavla ->
  255. #pointing_rule{quota = Quota,
  256. kakush_winner = KakushForWinners,
  257. kakush_other = KakushForLoser,
  258. game_points = WinGamePoints
  259. } = proplists:get_value(pointing_rules, Params),
  260. GameId = proplists:get_value(game_id, Params),
  261. TableName = proplists:get_value(table_name, Params),
  262. MulFactor = proplists:get_value(double_points, Params, 1),
  263. SlangAllowed = proplists:get_value(slang, Params, false),
  264. ObserversAllowed = proplists:get_value(observers, Params, false),
  265. Speed = proplists:get_value(speed, Params, normal),
  266. GameMode = proplists:get_value(game_mode, Params),
  267. Rounds = case GameMode of
  268. _ -> proplists:get_value(rounds, Params, undefined)
  269. end,
  270. BotsReplacementMode = case proplists:get_value(robots_replacement_allowed, Params, true) of
  271. true -> enabled;
  272. false -> disabled
  273. end,
  274. TablesNum = length(Users) div 2 + length(Users) rem 2,
  275. TableParams = [
  276. {table_name, TableName},
  277. {mult_factor, MulFactor},
  278. {slang_allowed, SlangAllowed},
  279. {observers_allowed, ObserversAllowed},
  280. {tournament_type, paired},
  281. {round_timeout, infinity},
  282. {set_timeout, infinity},
  283. {speed, Speed},
  284. {game_mode, GameMode},
  285. {rounds, Rounds},
  286. {next_series_confirmation, no_exit},
  287. {pause_mode, disabled},
  288. {social_actions_enabled, true},
  289. {tables_num, TablesNum}
  290. ],
  291. create_game(GameId, game_tavla_ng_trn_paired,
  292. [{game, Game},
  293. {game_mode, GameMode},
  294. {game_name, TableName},
  295. {tables_num, TablesNum},
  296. {registrants, Users},
  297. {quota_per_round, Quota},
  298. {kakush_for_winners, KakushForWinners},
  299. {kakush_for_loser, KakushForLoser},
  300. {win_game_points, WinGamePoints},
  301. {mul_factor, MulFactor},
  302. {table_module, game_tavla_ng_table},
  303. {bot_module, game_tavla_bot},
  304. {bots_replacement_mode, BotsReplacementMode},
  305. {table_params, TableParams},
  306. {common_params, Params}
  307. ])
  308. end.
  309. create_elimination_trn(GameType, Params, Registrants) ->
  310. ?INFO("create_elimination_trn/3 Params:~p", [Params]),
  311. TrnId = proplists:get_value(trn_id, Params),
  312. QuotaPerRound = proplists:get_value(quota_per_round, Params),
  313. PlayersNumber = proplists:get_value(players_number, Params),
  314. Tours = proplists:get_value(tours, Params),
  315. GameMode = proplists:get_value(game_mode, Params),
  316. Speed = proplists:get_value(speed, Params),
  317. Awards = proplists:get_value(awards, Params),
  318. RegistrantsNum = length(Registrants),
  319. if RegistrantsNum =/= PlayersNumber ->
  320. ?ERROR("create_elimination_trn/3 Error: Wrong number of the registrants: ~p (required: ~p). ",
  321. [RegistrantsNum, PlayersNumber]),
  322. exit(wrong_registrants_number);
  323. true -> do_nothing
  324. end,
  325. {ok, Plan} = nsg_matrix_elimination:get_plan(GameType, QuotaPerRound, PlayersNumber, Tours),
  326. case GameType of
  327. game_okey ->
  328. Rounds = 10,
  329. {ok, SetTimeout} = nsm_db:get(config,"games/okey/trn/elim/tour_time_limit/"++integer_to_list(Tours), 35*60*1000),
  330. TableParams = [
  331. {table_name, ""},
  332. {tournament_type, elimination},
  333. {round_timeout, infinity},
  334. {set_timeout, SetTimeout},
  335. {speed, Speed},
  336. {game_type, GameMode},
  337. {rounds, Rounds},
  338. {gosterge_finish_allowed, undefined},
  339. {reveal_confirmation, true},
  340. {next_series_confirmation, no},
  341. {pause_mode, disabled},
  342. {observers_allowed, false},
  343. {slang_allowed, false},
  344. {social_actions_enabled, false},
  345. {mult_factor, 1}
  346. ],
  347. create_game(TrnId, nsg_trn_elimination,
  348. [{game_type, GameType},
  349. {game_mode, GameMode},
  350. {registrants, Registrants},
  351. {plan, Plan},
  352. {quota_per_round, QuotaPerRound},
  353. {rounds_per_tour, Rounds},
  354. {tours, Tours},
  355. {players_per_table, 4},
  356. {speed, Speed},
  357. {awards, Awards},
  358. {trn_id, TrnId},
  359. {table_module, game_okey_ng_table_trn},
  360. {demo_mode, false},
  361. {table_params, TableParams}
  362. ]);
  363. game_tavla ->
  364. Rounds = 3,
  365. {ok, SetTimeout} = nsm_db:get(config,"games/tavla/trn/elim/tour_time_limit/"++integer_to_list(Tours), 35*60*1000),
  366. TableParams = [
  367. {table_name, ""},
  368. {tournament_type, elimination},
  369. {round_timeout, infinity},
  370. {set_timeout, SetTimeout},
  371. {speed, Speed},
  372. {game_mode, GameMode},
  373. {rounds, Rounds},
  374. {next_series_confirmation, no},
  375. {pause_mode, disabled},
  376. {slang_allowed, false},
  377. {observers_allowed, false},
  378. {social_actions_enabled, false},
  379. {mult_factor, 1},
  380. {tables_num, 1}
  381. ],
  382. create_game(TrnId, nsg_trn_elimination,
  383. [{game_type, GameType},
  384. {game_mode, GameMode},
  385. {registrants, Registrants},
  386. {plan, Plan},
  387. {quota_per_round, QuotaPerRound},
  388. {rounds_per_tour, Rounds},
  389. {tours, Tours},
  390. {players_per_table, 2},
  391. {speed, Speed},
  392. {awards, Awards},
  393. {trn_id,TrnId},
  394. {table_module, game_tavla_ng_table},
  395. {demo_mode, false},
  396. {table_params, TableParams}
  397. ])
  398. end.
  399. start_tournament(TrnId,NumberOfTournaments,NumberOfPlayers,_Quota,_Tours,_Speed,GiftIds) ->
  400. ?INFO("START TOURNAMENT: ~p",[{TrnId,NumberOfTournaments,NumberOfPlayers,_Quota,_Tours,_Speed,GiftIds}]),
  401. {ok,Tournament} = nsm_db:get(tournament,TrnId),
  402. RealPlayersUnsorted = nsm_tournaments:joined_users(TrnId),
  403. if NumberOfPlayers - length(RealPlayersUnsorted) > 300 ->
  404. nsm_db:put(Tournament#tournament{status=canceled}),
  405. nsx_msg:notify([tournament, TrnId, cancel], {TrnId}),
  406. error;
  407. true ->
  408. #tournament{quota = QuotaPerRound,
  409. tours = Tours,
  410. game_type = GameType,
  411. game_mode = GameMode,
  412. speed = Speed} = Tournament,
  413. %% ImagioUsers = nsm_auth:imagionary_users2(),
  414. RealPlayersPR = lists:keysort(#play_record.other, RealPlayersUnsorted),
  415. ?INFO("Head: ~p",[hd(RealPlayersPR)]),
  416. RealPlayers = [list_to_binary(Who)||#play_record{who=Who}<-RealPlayersPR, Who /= undefined],
  417. %% Registrants = case NumberOfPlayers > length(RealPlayers) of
  418. %% true -> nsm_db:put(Tournament#tournament{status=canceled}), RealPlayers;
  419. %% false -> [lists:nth(N,RealPlayers)||N<-lists:seq(1,NumberOfPlayers)] end,
  420. RealPlayersNumber = length(RealPlayers),
  421. Registrants = if NumberOfPlayers == RealPlayersNumber -> RealPlayers;
  422. NumberOfPlayers > RealPlayersNumber ->
  423. RealPlayers ++ [list_to_binary(nsm_auth:ima_gio2(N)) ||
  424. N <- lists:seq(1, NumberOfPlayers-RealPlayersNumber)];
  425. true -> lists:sublist(RealPlayers, NumberOfPlayers)
  426. end,
  427. ?INFO("Registrants: ~p",[Registrants]),
  428. OkeyTournaments =
  429. [begin
  430. Params = [{trn_id, TrnId},
  431. {quota_per_round, QuotaPerRound},
  432. {players_number, NumberOfPlayers},
  433. {tours, Tours},
  434. {game_mode, GameMode},
  435. {speed, Speed},
  436. {awards, GiftIds}],
  437. {ok,GameId,A} = create_elimination_trn(GameType, Params, Registrants),
  438. nsm_db:put(Tournament#tournament{status=activated,start_time=time()}),
  439. nsx_msg:notify([tournament, TrnId, activate], {TrnId}),
  440. {ok,GameId,A}
  441. end || _ <-lists:seq(1,NumberOfTournaments)],
  442. [{ok,OP1,_}|_] = OkeyTournaments,
  443. [{ok,OP2,_}|_] = lists:reverse(OkeyTournaments),
  444. ?INFO("Okey tournaments runned: ~p~n",[{OP1,OP2}]),
  445. OP1
  446. end.
  447. get_tables(Id) ->
  448. qlc:e(qlc:q([Val || {{_,_,_Key},_,Val=#game_table{id = _Id}} <- gproc:table(props), Id == _Id ])).
  449. qlc_id(Id) ->
  450. qlc:e(qlc:q([Val || {{_,_,_Key},_,Val=#game_table{gameid = _GameId, id = _Id,
  451. owner = _Owner, creator = _Creator}} <-
  452. gproc:table(props), Id == _Id])).
  453. qlc_id_creator(Id,Creator,Owner) ->
  454. qlc:e(qlc:q([Val || {{_,_,_Key},_,Val=#game_table{gameid = _GameId, id = _Id,
  455. owner = _Owner, creator = _Creator}} <-
  456. gproc:table(props), Id == _Id, Creator == _Creator, Owner ==_Owner])).