mirror of
https://github.com/correl/riichi.git
synced 2024-12-18 03:00:17 +00:00
Add websocket server
This commit is contained in:
parent
b88b6f1ae9
commit
9980d93fb2
15 changed files with 392 additions and 63 deletions
|
@ -8,10 +8,12 @@
|
||||||
],
|
],
|
||||||
"exposed-modules": [],
|
"exposed-modules": [],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"elm-community/json-extra": "2.3.0 <= v < 3.0.0",
|
||||||
"elm-community/list-extra": "6.1.0 <= v < 7.0.0",
|
"elm-community/list-extra": "6.1.0 <= v < 7.0.0",
|
||||||
"elm-community/maybe-extra": "4.0.0 <= v < 5.0.0",
|
"elm-community/maybe-extra": "4.0.0 <= v < 5.0.0",
|
||||||
"elm-lang/core": "5.0.0 <= v < 6.0.0",
|
"elm-lang/core": "5.0.0 <= v < 6.0.0",
|
||||||
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
||||||
|
"elm-lang/websocket": "1.0.2 <= v < 2.0.0",
|
||||||
"rtfeldman/elm-css": "9.1.0 <= v < 10.0.0",
|
"rtfeldman/elm-css": "9.1.0 <= v < 10.0.0",
|
||||||
"rtfeldman/elm-css-helpers": "2.1.0 <= v < 3.0.0"
|
"rtfeldman/elm-css-helpers": "2.1.0 <= v < 3.0.0"
|
||||||
},
|
},
|
||||||
|
|
93
priv/src/Client.elm
Normal file
93
priv/src/Client.elm
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
module Client exposing (..)
|
||||||
|
|
||||||
|
import Debug
|
||||||
|
import Client.Decode
|
||||||
|
import Client.Game exposing (Game)
|
||||||
|
import Html exposing (..)
|
||||||
|
import Json.Decode exposing (decodeString)
|
||||||
|
import String
|
||||||
|
import WebSocket
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ game : Maybe Game
|
||||||
|
, log : List String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= Receive String
|
||||||
|
| Send String
|
||||||
|
| Log String
|
||||||
|
| NewState Game
|
||||||
|
|
||||||
|
|
||||||
|
init : Model
|
||||||
|
init =
|
||||||
|
{ game = Nothing
|
||||||
|
, log = []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
Receive m ->
|
||||||
|
socketMsg m
|
||||||
|
|> Maybe.map (\msg -> update msg model)
|
||||||
|
|> Maybe.withDefault ( model, Cmd.none )
|
||||||
|
|
||||||
|
Log m ->
|
||||||
|
( { model | log = m :: model.log }
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
NewState game ->
|
||||||
|
( { model | game = Just (Debug.log "game" game) }
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
socketMsg : String -> Maybe Msg
|
||||||
|
socketMsg message =
|
||||||
|
let
|
||||||
|
splitMsg =
|
||||||
|
case String.split ":" message of
|
||||||
|
msgType :: rest ->
|
||||||
|
Just ( msgType, String.join ":" rest )
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
toMsg ( msgType, rest ) =
|
||||||
|
case msgType of
|
||||||
|
"log" ->
|
||||||
|
Just (Log rest)
|
||||||
|
|
||||||
|
"new_state" ->
|
||||||
|
decodeString Client.Decode.game rest
|
||||||
|
|> Result.toMaybe
|
||||||
|
|> Maybe.map NewState
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Nothing
|
||||||
|
in
|
||||||
|
splitMsg
|
||||||
|
|> Maybe.andThen toMsg
|
||||||
|
|
||||||
|
|
||||||
|
subscriptions : Model -> Sub Msg
|
||||||
|
subscriptions model =
|
||||||
|
WebSocket.listen "ws://localhost:8080/websocket" Receive
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Html msg
|
||||||
|
view model =
|
||||||
|
div []
|
||||||
|
[ Maybe.map Client.Game.view model.game
|
||||||
|
|> Maybe.withDefault (div [] [])
|
||||||
|
, pre [] [ text <| String.join "\n" model.log ]
|
||||||
|
]
|
49
priv/src/Client/Decode.elm
Normal file
49
priv/src/Client/Decode.elm
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
module Client.Decode exposing (..)
|
||||||
|
|
||||||
|
import Client.Hand exposing (Hand)
|
||||||
|
import Client.Game exposing (Game)
|
||||||
|
import Client.Player exposing (Player)
|
||||||
|
import Json.Decode exposing (..)
|
||||||
|
import Json.Decode.Extra exposing (fromResult)
|
||||||
|
import Tile exposing (Tile)
|
||||||
|
|
||||||
|
|
||||||
|
tile : Decoder Tile
|
||||||
|
tile =
|
||||||
|
let
|
||||||
|
combine a b =
|
||||||
|
a ++ " " ++ b
|
||||||
|
|
||||||
|
stringOrInt =
|
||||||
|
oneOf
|
||||||
|
[ string
|
||||||
|
, int |> map toString
|
||||||
|
]
|
||||||
|
in
|
||||||
|
map2 combine
|
||||||
|
(field "value" stringOrInt)
|
||||||
|
(field "suit" string)
|
||||||
|
|> andThen
|
||||||
|
(Tile.fromString
|
||||||
|
>> (Result.fromMaybe "invalid tile")
|
||||||
|
>> fromResult
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
hand : Decoder Hand
|
||||||
|
hand =
|
||||||
|
map Hand
|
||||||
|
(field "tiles" (list tile))
|
||||||
|
|
||||||
|
|
||||||
|
player : Decoder Player
|
||||||
|
player =
|
||||||
|
map2 Player
|
||||||
|
(field "name" string)
|
||||||
|
(field "hand" hand)
|
||||||
|
|
||||||
|
|
||||||
|
game : Decoder Game
|
||||||
|
game =
|
||||||
|
map Game
|
||||||
|
(field "players" (list player))
|
16
priv/src/Client/Game.elm
Normal file
16
priv/src/Client/Game.elm
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
module Client.Game exposing (..)
|
||||||
|
|
||||||
|
import Client.Player exposing (Player)
|
||||||
|
import Html exposing (..)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Game =
|
||||||
|
{ players : List Player }
|
||||||
|
|
||||||
|
|
||||||
|
view : Game -> Html msg
|
||||||
|
view game =
|
||||||
|
div [] <|
|
||||||
|
List.map
|
||||||
|
Client.Player.view
|
||||||
|
game.players
|
18
priv/src/Client/Hand.elm
Normal file
18
priv/src/Client/Hand.elm
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
module Client.Hand exposing (..)
|
||||||
|
|
||||||
|
import Html exposing (Html, div, text)
|
||||||
|
import Html.Attributes exposing (class)
|
||||||
|
import Tile exposing (Tile)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Hand =
|
||||||
|
{ tiles : List Tile
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
view : Hand -> Html a
|
||||||
|
view model =
|
||||||
|
div [ class "hand" ]
|
||||||
|
[ div [ class "tiles open" ] <|
|
||||||
|
List.map Tile.view model.tiles
|
||||||
|
]
|
18
priv/src/Client/Player.elm
Normal file
18
priv/src/Client/Player.elm
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
module Client.Player exposing (..)
|
||||||
|
|
||||||
|
import Client.Hand exposing (Hand)
|
||||||
|
import Html exposing (..)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Player =
|
||||||
|
{ name : String
|
||||||
|
, hand : Hand
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
view : Player -> Html msg
|
||||||
|
view player =
|
||||||
|
fieldset []
|
||||||
|
[ legend [] [ text ("Player: " ++ player.name) ]
|
||||||
|
, Client.Hand.view player.hand
|
||||||
|
]
|
|
@ -1,33 +0,0 @@
|
||||||
module Hand exposing (..)
|
|
||||||
|
|
||||||
import Html exposing (Html, div, text)
|
|
||||||
import Html.Attributes exposing (class)
|
|
||||||
import Maybe.Extra
|
|
||||||
import Tile exposing (Tile)
|
|
||||||
|
|
||||||
|
|
||||||
type alias JSON =
|
|
||||||
{ tiles : List String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
|
||||||
{ tiles : List Tile
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fromJSON : JSON -> Model
|
|
||||||
fromJSON j =
|
|
||||||
{ tiles =
|
|
||||||
j.tiles
|
|
||||||
|> List.map Tile.fromString
|
|
||||||
|> Maybe.Extra.values
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Html a
|
|
||||||
view model =
|
|
||||||
div [ class "hand" ]
|
|
||||||
[ div [ class "tiles open" ] <|
|
|
||||||
List.map Tile.view model.tiles
|
|
||||||
]
|
|
|
@ -1,6 +1,6 @@
|
||||||
module Riichi exposing (..)
|
module Riichi exposing (..)
|
||||||
|
|
||||||
import Hand
|
import Client
|
||||||
import Html exposing (..)
|
import Html exposing (..)
|
||||||
import Html.Attributes exposing (..)
|
import Html.Attributes exposing (..)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
|
@ -14,32 +14,19 @@ import Stylesheets as S
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ tileset : S.Tileset
|
{ tileset : S.Tileset
|
||||||
, hand : Hand.Model
|
, client : Client.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= SetTileset S.Tileset
|
= SetTileset S.Tileset
|
||||||
|
| ClientMsg Client.Msg
|
||||||
|
|
||||||
|
|
||||||
init : ( Model, Cmd Msg )
|
init : ( Model, Cmd Msg )
|
||||||
init =
|
init =
|
||||||
( { tileset = S.White
|
( { tileset = S.White
|
||||||
, hand =
|
, client = Client.init
|
||||||
Hand.fromJSON
|
|
||||||
{ tiles =
|
|
||||||
[ "4 pin"
|
|
||||||
, "5 pin"
|
|
||||||
, "6 pin"
|
|
||||||
, "4 sou"
|
|
||||||
, "5 sou"
|
|
||||||
, "6 sou"
|
|
||||||
, "4 man"
|
|
||||||
, "5 man"
|
|
||||||
, "6 man"
|
|
||||||
, "red dragon"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
@ -53,6 +40,15 @@ update msg model =
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ClientMsg m ->
|
||||||
|
let
|
||||||
|
( client, effects ) =
|
||||||
|
Client.update m model.client
|
||||||
|
in
|
||||||
|
( { model | client = client }
|
||||||
|
, Cmd.map ClientMsg effects
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
main : Program Never Model Msg
|
main : Program Never Model Msg
|
||||||
main =
|
main =
|
||||||
|
@ -60,10 +56,15 @@ main =
|
||||||
{ init = init
|
{ init = init
|
||||||
, update = update
|
, update = update
|
||||||
, view = view
|
, view = view
|
||||||
, subscriptions = \_ -> Sub.none
|
, subscriptions = subscriptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
subscriptions : Model -> Sub Msg
|
||||||
|
subscriptions model =
|
||||||
|
Sub.batch [ Sub.map ClientMsg <| Client.subscriptions model.client ]
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Html Msg
|
view : Model -> Html Msg
|
||||||
view model =
|
view model =
|
||||||
div [ class [ S.Tileset model.tileset ] ]
|
div [ class [ S.Tileset model.tileset ] ]
|
||||||
|
@ -73,7 +74,7 @@ view model =
|
||||||
, radio "tileset" "White" (SetTileset S.White) (model.tileset == S.White)
|
, radio "tileset" "White" (SetTileset S.White) (model.tileset == S.White)
|
||||||
, radio "tileset" "Black" (SetTileset S.Black) (model.tileset == S.Black)
|
, radio "tileset" "Black" (SetTileset S.Black) (model.tileset == S.Black)
|
||||||
]
|
]
|
||||||
, Hand.view model.hand
|
, Client.view model.client
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
%% -*- mode: erlang -*-
|
%% -*- mode: erlang -*-
|
||||||
|
|
||||||
|
{plugins, [rebar3_auto]}.
|
||||||
|
|
||||||
|
{deps, [cowboy,
|
||||||
|
lager,
|
||||||
|
jsx]}.
|
||||||
|
|
||||||
|
{erl_opts, [{parse_transform, lager_transform}]}.
|
||||||
|
|
||||||
{pre_hooks,
|
{pre_hooks,
|
||||||
[{"(linux|darwin|solaris)", compile,"make elm"},
|
[{"(linux|darwin|solaris)", compile,"make elm"},
|
||||||
{"(freebsd|netbsd|openbsd)",compile,"gmake elm"},
|
{"(freebsd|netbsd|openbsd)",compile,"gmake elm"},
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
-export([new/0,
|
-export([new/0,
|
||||||
new/1,
|
new/1,
|
||||||
new/2,
|
new/2,
|
||||||
|
send/2,
|
||||||
discards/1,
|
discards/1,
|
||||||
draw/2]).
|
draw/2]).
|
||||||
|
|
||||||
|
@ -12,11 +13,14 @@ new() ->
|
||||||
new("Computer").
|
new("Computer").
|
||||||
|
|
||||||
new(Name) ->
|
new(Name) ->
|
||||||
new(Name, player_dummy).
|
{ok, Pid} = player_dummy:start_link(Name),
|
||||||
|
new(Name, Pid).
|
||||||
|
|
||||||
new(Name, Type) ->
|
new(Name, Pid) ->
|
||||||
{ok, PID} = Type:start_link(Name),
|
#player{name = Name, pid = Pid}.
|
||||||
#player{name = Name, pid = PID}.
|
|
||||||
|
send(#player{pid = Pid}, Message) ->
|
||||||
|
gen_server:cast(Pid, Message).
|
||||||
|
|
||||||
discards(#player{discards = Discards} = Player) ->
|
discards(#player{discards = Discards} = Player) ->
|
||||||
[{discard, Tile, Player#player{hand = Hand, discards = [Tile|Discards]}}
|
[{discard, Tile, Player#player{hand = Hand, discards = [Tile|Discards]}}
|
||||||
|
|
56
src/player_websocket.erl
Normal file
56
src/player_websocket.erl
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
-module(player_websocket).
|
||||||
|
-behaviour(gen_server).
|
||||||
|
|
||||||
|
-export([start_link/1]).
|
||||||
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
|
||||||
|
code_change/3]).
|
||||||
|
|
||||||
|
-record(state, {socket, game, seat}).
|
||||||
|
|
||||||
|
start_link(Socket) ->
|
||||||
|
gen_server:start_link(?MODULE, [Socket], []).
|
||||||
|
|
||||||
|
init([Socket]) ->
|
||||||
|
Socket ! <<"oh hello!">>,
|
||||||
|
{ok, #state{socket=Socket}}.
|
||||||
|
|
||||||
|
handle_call({choose, Actions}, _From, State) ->
|
||||||
|
%% [Action|_] = sort_actions(Actions),
|
||||||
|
%% {reply, Action, State};
|
||||||
|
State#state.socket ! {choose, Actions},
|
||||||
|
{noreply, State};
|
||||||
|
|
||||||
|
handle_call(get_name, _From, State) ->
|
||||||
|
{reply, io_lib:format("~p", [State#state.socket]), State};
|
||||||
|
|
||||||
|
handle_call(_Msg, _From, State) ->
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
handle_cast(Msg, State) ->
|
||||||
|
State#state.socket! Msg,
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
handle_info(Msg, State) ->
|
||||||
|
State#state.socket ! Msg,
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
|
terminate(_Reason, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
{ok, State}.
|
||||||
|
|
||||||
|
sort_actions(Actions) ->
|
||||||
|
Order = fun({game_action, A, _, _}, {game_action, B, _, _}) ->
|
||||||
|
Weighted = [ron, tsumo, kan, pon, chi],
|
||||||
|
Weights = lists:zip(Weighted, lists:reverse(lists:seq(1, length(Weighted)))),
|
||||||
|
VA = proplists:get_value(A, Weights, 0),
|
||||||
|
VB = proplists:get_value(B, Weights, 0),
|
||||||
|
case VA == VB of
|
||||||
|
true ->
|
||||||
|
A >= B;
|
||||||
|
_ ->
|
||||||
|
VA >= VB
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
lists:sort(Order, Actions).
|
|
@ -6,7 +6,9 @@
|
||||||
{applications, [
|
{applications, [
|
||||||
kernel,
|
kernel,
|
||||||
stdlib,
|
stdlib,
|
||||||
cowboy
|
lager,
|
||||||
|
cowboy,
|
||||||
|
jsx
|
||||||
]},
|
]},
|
||||||
{modules, []},
|
{modules, []},
|
||||||
{mod, { riichi_app, []}},
|
{mod, { riichi_app, []}},
|
||||||
|
|
|
@ -14,7 +14,8 @@ start(_StartType, _StartArgs) ->
|
||||||
[{'_', [{"/", cowboy_static, {priv_file, riichi, "index.html"}},
|
[{'_', [{"/", cowboy_static, {priv_file, riichi, "index.html"}},
|
||||||
{"/js/[...]", cowboy_static, {priv_dir, riichi, "js"}},
|
{"/js/[...]", cowboy_static, {priv_dir, riichi, "js"}},
|
||||||
{"/css/[...]", cowboy_static, {priv_dir, riichi, "css"}},
|
{"/css/[...]", cowboy_static, {priv_dir, riichi, "css"}},
|
||||||
{"/images/[...]", cowboy_static, {priv_dir, riichi, "images"}}
|
{"/images/[...]", cowboy_static, {priv_dir, riichi, "images"}},
|
||||||
|
{"/websocket", server_websocket, []}
|
||||||
]}
|
]}
|
||||||
]),
|
]),
|
||||||
{ok, _} = cowboy:start_http(
|
{ok, _} = cowboy:start_http(
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
-behaviour(gen_fsm).
|
-behaviour(gen_fsm).
|
||||||
|
|
||||||
-export([start_link/0]).
|
-export([start_link/0]).
|
||||||
-export([waiting/3,
|
-export([waiting/2,
|
||||||
playing/2,
|
playing/2,
|
||||||
turn/2]).
|
turn/2]).
|
||||||
-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
|
-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
|
||||||
|
@ -17,16 +17,19 @@ start_link() ->
|
||||||
init([]) ->
|
init([]) ->
|
||||||
{ok, waiting, #state{}}.
|
{ok, waiting, #state{}}.
|
||||||
|
|
||||||
waiting({add_player, Player}, _From, State) ->
|
waiting({add_player, Player}, State) ->
|
||||||
error_logger:info_report({adding_player, [{player, Player}]}),
|
error_logger:info_report({adding_player, [{player, Player}]}),
|
||||||
Players = [Player|State#state.players],
|
Players = [Player|State#state.players],
|
||||||
|
[player:send(P, {joined, Player}) || P <- Players],
|
||||||
case length(Players) of
|
case length(Players) of
|
||||||
4 ->
|
4 ->
|
||||||
Game = game:new(Players),
|
Game = game:new(Players),
|
||||||
error_logger:info_report({starting_game, []}),
|
error_logger:info_report({starting_game, []}),
|
||||||
|
[player:send(P, {log ,<<"starting game">>}) || P <- Players],
|
||||||
gen_fsm:send_event(self(), game_tree:build(Game)),
|
gen_fsm:send_event(self(), game_tree:build(Game)),
|
||||||
{reply, ok, playing, Game};
|
{next_state, playing, Game};
|
||||||
_ -> {reply, ok, waiting, State#state{players=Players}}
|
_ ->
|
||||||
|
{next_state, waiting, State#state{players=Players}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
playing({game_tree, Game, Branches} = Tree, State) ->
|
playing({game_tree, Game, Branches} = Tree, State) ->
|
||||||
|
@ -40,7 +43,7 @@ playing({game_tree, Game, Branches} = Tree, State) ->
|
||||||
[] ->
|
[] ->
|
||||||
[];
|
[];
|
||||||
_ ->
|
_ ->
|
||||||
[gen_server:call(Player#player.pid, {choose, PlayerActions})]
|
[gen_server:call(Player#player.pid, {choose, PlayerActions}, infinity)]
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
[east, south, west, north])),
|
[east, south, west, north])),
|
||||||
|
|
91
src/server_websocket.erl
Normal file
91
src/server_websocket.erl
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
-module(server_websocket).
|
||||||
|
-behaviour(cowboy_http_handler).
|
||||||
|
-behaviour(cowboy_websocket_handler).
|
||||||
|
|
||||||
|
-include("../include/riichi.hrl").
|
||||||
|
|
||||||
|
-export([init/3, handle/2, terminate/3]).
|
||||||
|
-export([
|
||||||
|
websocket_init/3, websocket_handle/3,
|
||||||
|
websocket_info/3, websocket_terminate/3
|
||||||
|
]).
|
||||||
|
|
||||||
|
-record(state, {game, player}).
|
||||||
|
|
||||||
|
init({tcp, http}, _Req, _Opts) ->
|
||||||
|
{upgrade, protocol, cowboy_websocket}.
|
||||||
|
|
||||||
|
handle(_Req, State) ->
|
||||||
|
{ok, Response} = cowboy_http_req:reply(404, [{'Content-Type', <<"text/html">>}]),
|
||||||
|
{ok, Response, State}.
|
||||||
|
|
||||||
|
websocket_init(_TransportName, Req, _Opts) ->
|
||||||
|
lager:info("init websocket"),
|
||||||
|
{ok, Game} = server_game:start_link(),
|
||||||
|
{ok, Pid} = player_websocket:start_link(self()),
|
||||||
|
Player = player:new("Websocket", Pid),
|
||||||
|
gen_fsm:send_event(Game, {add_player, Player}),
|
||||||
|
[gen_fsm:send_event(Game,{add_player, player:new()})
|
||||||
|
|| _N <- lists:seq(1, 3)],
|
||||||
|
{ok, Req, #state{game=Game,player=Player}}.
|
||||||
|
|
||||||
|
websocket_handle({text, Msg}, Req, State) ->
|
||||||
|
lager:info("Got Data: ~p", [Msg]),
|
||||||
|
{reply, {text, << "responding to ", Msg/binary >>}, Req, State, hibernate };
|
||||||
|
|
||||||
|
|
||||||
|
websocket_handle(_Any, Req, State) ->
|
||||||
|
{reply, {text, << "whut?">>}, Req, State, hibernate }.
|
||||||
|
|
||||||
|
websocket_info({timeout, _Ref, Msg}, Req, State) ->
|
||||||
|
{reply, {text, Msg}, Req, State};
|
||||||
|
|
||||||
|
websocket_info(Info, Req, State) ->
|
||||||
|
lager:info("websocket info: ~p", [Info]),
|
||||||
|
case encode(Info) of
|
||||||
|
{ok, Msg} ->
|
||||||
|
{reply, {text, Msg}, Req, State};
|
||||||
|
{error, _Reason} ->
|
||||||
|
lager:error("Unhandled message: ~p", [Info]),
|
||||||
|
{ok, Req, State, hibernate}
|
||||||
|
end.
|
||||||
|
|
||||||
|
websocket_terminate(_Reason, _Req, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
terminate(_Reason, _Req, _State) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
encode({log, Msg}) ->
|
||||||
|
{ok, io_lib:format("log: ~s", [Msg])};
|
||||||
|
encode({new_state, Game}) ->
|
||||||
|
{ok, ["new_state: ", jsx:encode(encode_game(Game))]};
|
||||||
|
encode(_) ->
|
||||||
|
{error, invalid_message}.
|
||||||
|
|
||||||
|
encode_game(Game) ->
|
||||||
|
#{round => Game#game.round,
|
||||||
|
turn => Game#game.turn,
|
||||||
|
phase => Game#game.phase,
|
||||||
|
wall => length(Game#game.wall),
|
||||||
|
dora => lists:map(fun encode_tile/1, Game#game.dora),
|
||||||
|
players => lists:map(fun encode_player/1, Game#game.players)}.
|
||||||
|
|
||||||
|
encode_tile(Tile) ->
|
||||||
|
#{suit => Tile#tile.suit,
|
||||||
|
value => Tile#tile.value,
|
||||||
|
from => Tile#tile.from}.
|
||||||
|
|
||||||
|
encode_player(Player) ->
|
||||||
|
#{name => list_to_binary(Player#player.name),
|
||||||
|
seat => Player#player.seat,
|
||||||
|
hand => encode_hand(Player#player.hand),
|
||||||
|
discards => lists:map(fun encode_tile/1, Player#player.discards)}.
|
||||||
|
|
||||||
|
encode_hand(Hand) ->
|
||||||
|
#{tiles => lists:map(fun encode_tile/1, Hand#hand.tiles),
|
||||||
|
melds => lists:map(fun encode_meld/1, Hand#hand.melds)}.
|
||||||
|
|
||||||
|
encode_meld(Meld) ->
|
||||||
|
#{type => Meld#meld.type,
|
||||||
|
tiles => lists:map(fun encode_tile/1, Meld#meld.tiles)}.
|
Loading…
Reference in a new issue