-module(ttt_server). -export([ start/0, start_local/0, stop/0, stop/2, loop/1, start_game/1, dump/1]). -import(ttt, [move/4, who_wins/1, format/1, empty_board/0]). -import(orddict, [new/0]). -import(game, [game_loop/0]). -record(ttt_game, { id :: integer(), pid :: pid()}). -record(ttt_state, { seq = 1000 :: integer(), games = []}). -define(GLOBAL_NAME, ttt). loop(State) -> receive {Player, create} -> io:format("Creating game for player ~p~n", [Player]), Id = State#ttt_state.seq, Pid = spawn(game, game_loop, [Id, Player]), Game = #ttt_game{id = Id, pid = Pid}, Player ! {ok, Id}, io:format("Game created: ~p/~p~n", [Id, Pid]), loop(#ttt_state{ seq = Id + 1, games = orddict:append(Id, Game, State#ttt_state.games)}); {Player, show, Id} -> case orddict:find(Id, State#ttt_state.games) of error -> Player ! no_such_game; {ok, [Game]} -> io:format("show: ~p~n", [Game]), Game#ttt_game.pid ! {Player, show} end, loop(State); {Player, join, Id} -> case orddict:find(Id, State#ttt_state.games) of error -> Player ! no_such_game; {ok, [Game]} -> Game#ttt_game.pid ! {Player, join} end, loop(State); dump -> io:format("Server state:~n~p~n", [State]), loop(State); stop -> io:format("Exiting~n", []); {stop, Pid} -> Pid ! ok; code_changed -> io:format("Code changed~n"), io:format("Code changed: ~p~n", [State#ttt_state.games]), orddict:map( fun(_Id, [G]) -> io:format("Id=~p, v=~p~n", [_Id, G]), G#ttt_game.pid ! code_changed end, State#ttt_state.games), ?MODULE:loop(State); X -> io:format("unexpected message: ~p~n", [X]), loop(State) end. start_game(Server) -> Server ! {self(), start}, receive {ok, Game} -> io:format("Game started: ~w~n", [Game]), {ok, Game}; X -> io:format("Game start failed: ~w~n", [X]), X after 100 -> io:format("timeout~n", []), timeout end. dump(Server) -> Server ! dump. start_local() -> InitialState = #ttt_state{games = orddict:new()}, spawn(?MODULE, loop, [InitialState]). start() -> Pid = start_local(), case global:register_name(?GLOBAL_NAME, Pid) of yes -> io:format("Server started, pid=~p~n", [Pid]); X -> io:format("Register name failed: ~p~n", [X]), Pid ! stop end. stop(Server, Pid) -> Server ! {stop, Pid}. stop() -> case global:whereis_name(?GLOBAL_NAME) of undefined -> io:format("Server not running~n"), not_running; Pid -> Pid ! {stop, self()} end.