do not send whole state on action
This commit is contained in:
parent
86489c5394
commit
caffcbb527
6 changed files with 135 additions and 66 deletions
|
|
@ -51,11 +51,12 @@ let papy_up = C2d.image_src_of_el (get_el "papy_up")
|
||||||
|
|
||||||
let water = C2d.image_src_of_el (get_el "water")
|
let water = C2d.image_src_of_el (get_el "water")
|
||||||
|
|
||||||
let draw_map =
|
let draw =
|
||||||
let papy_x = float_of_int (width - tile_size) /. 2. in
|
let papy_x = float_of_int (width - tile_size) /. 2. in
|
||||||
let papy_y = (float_of_int height /. 2.) -. (float_of_int tile_size *. 1.5) in
|
let papy_y = (float_of_int height /. 2.) -. (float_of_int tile_size *. 1.5) in
|
||||||
fun map ->
|
fun state ->
|
||||||
let player_x, player_y = map.Map.player_pos in
|
let open State in
|
||||||
|
let player_x, player_y, player_dir = state.player_pos in
|
||||||
for x = 0 to tiles_per_w - 1 do
|
for x = 0 to tiles_per_w - 1 do
|
||||||
let map_x = x + player_x - (tiles_per_w / 2) in
|
let map_x = x + player_x - (tiles_per_w / 2) in
|
||||||
let tile_x = float_of_int ((x * tile_size) + orig_x) in
|
let tile_x = float_of_int ((x * tile_size) + orig_x) in
|
||||||
|
|
@ -63,7 +64,7 @@ let draw_map =
|
||||||
let map_y = y + player_y - (tiles_per_h / 2) in
|
let map_y = y + player_y - (tiles_per_h / 2) in
|
||||||
let tile_y = float_of_int ((y * tile_size) + orig_y) in
|
let tile_y = float_of_int ((y * tile_size) + orig_y) in
|
||||||
let tile_img =
|
let tile_img =
|
||||||
match Map.get_tile_kind ~x:map_x ~y:map_y map with
|
match Map.get_tile_kind ~x:map_x ~y:map_y state.map with
|
||||||
| Grass -> grass
|
| Grass -> grass
|
||||||
| Water -> water
|
| Water -> water
|
||||||
| Black -> water
|
| Black -> water
|
||||||
|
|
@ -72,7 +73,7 @@ let draw_map =
|
||||||
done
|
done
|
||||||
done;
|
done;
|
||||||
let papy =
|
let papy =
|
||||||
match map.Map.player_dir with
|
match player_dir with
|
||||||
| Left -> papy_left
|
| Left -> papy_left
|
||||||
| Right -> papy_right
|
| Right -> papy_right
|
||||||
| Down -> papy_down
|
| Down -> papy_down
|
||||||
|
|
@ -80,19 +81,43 @@ let draw_map =
|
||||||
in
|
in
|
||||||
C2d.draw_image context papy ~x:papy_x ~y:papy_y
|
C2d.draw_image context papy ~x:papy_x ~y:papy_y
|
||||||
|
|
||||||
let kb_handler state ev =
|
(* queue for action to be done *)
|
||||||
let move = Map.move !state.State.map in
|
let input_queue = Queue.create ()
|
||||||
match ev |> Ev.as_type |> Ev.Keyboard.code |> Jstr.to_string with
|
|
||||||
| "KeyW" | "ArrowUp" -> move Up
|
(* queue for action' to apply to client state *)
|
||||||
| "KeyA" | "ArrowLeft" -> move Left
|
let to_apply_queue : State.action' Queue.t = Queue.create ()
|
||||||
| "KeyS" | "ArrowDown" -> move Down
|
|
||||||
| "KeyD" | "ArrowRight" -> move Right
|
let send_action state action =
|
||||||
| "KeyM" -> Ws_client.send State.Meditate
|
match State.check_action state action with
|
||||||
| _s -> ()
|
| Error e -> Format.printf "Invalid action: %s@\n" e
|
||||||
|
| Ok _ -> Ws_client.send (Network.Action_msg action)
|
||||||
|
|
||||||
|
let kb_handler ev =
|
||||||
|
let open State in
|
||||||
|
let act =
|
||||||
|
match ev |> Ev.as_type |> Ev.Keyboard.code |> Jstr.to_string with
|
||||||
|
| "KeyW" | "ArrowUp" -> Move Up
|
||||||
|
| "KeyA" | "ArrowLeft" -> Move Left
|
||||||
|
| "KeyS" | "ArrowDown" -> Move Down
|
||||||
|
| "KeyD" | "ArrowRight" -> Move Right
|
||||||
|
| "KeyM" -> Meditate
|
||||||
|
| _s -> Do_nothing
|
||||||
|
in
|
||||||
|
Queue.add act input_queue
|
||||||
|
|
||||||
let rec game_loop state _timestamp =
|
let rec game_loop state _timestamp =
|
||||||
draw_map !state.State.map;
|
draw state;
|
||||||
let new_state = state in
|
let new_state =
|
||||||
|
(* TODO repesct order of action *)
|
||||||
|
(* apply to_apply_queue *)
|
||||||
|
let state = Queue.fold State.perform_action state to_apply_queue in
|
||||||
|
(* TODO can this bug because of concurrency? *)
|
||||||
|
Queue.clear to_apply_queue;
|
||||||
|
(* send input action to server *)
|
||||||
|
Queue.iter (send_action state) input_queue;
|
||||||
|
Queue.clear input_queue;
|
||||||
|
state
|
||||||
|
in
|
||||||
G.request_animation_frame (game_loop new_state)
|
G.request_animation_frame (game_loop new_state)
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
|
|
@ -106,16 +131,27 @@ let () =
|
||||||
(* get state from server*)
|
(* get state from server*)
|
||||||
let initial_state_fut = Ev.next Message.Ev.message Ws_client.ws_target in
|
let initial_state_fut = Ev.next Message.Ev.message Ws_client.ws_target in
|
||||||
|
|
||||||
|
(* attach message listener to update state *)
|
||||||
|
Ws_client.on_update_state_message (fun server_msg ->
|
||||||
|
match server_msg with
|
||||||
|
| Full_state _state ->
|
||||||
|
(* TODO reset state to received state *)
|
||||||
|
Format.printf "received Full_state message@\n"
|
||||||
|
| Update_result res -> (
|
||||||
|
match res with
|
||||||
|
| Error e -> Format.printf "received update result error: %s" e
|
||||||
|
| Ok action' -> Queue.add action' to_apply_queue ) );
|
||||||
|
(* bind keys *)
|
||||||
|
let _e : Ev.listener =
|
||||||
|
Ev.listen Ev.keydown kb_handler (Window.as_target G.window)
|
||||||
|
in
|
||||||
|
|
||||||
Fut.await initial_state_fut (fun msg ->
|
Fut.await initial_state_fut (fun msg ->
|
||||||
let initial_state = Ws_client.to_server_msg msg in
|
match Ws_client.to_server_msg msg with
|
||||||
let state_ref = ref initial_state in
|
| Update_result _res_msg ->
|
||||||
(* bind keys *)
|
failwith
|
||||||
let _e : Ev.listener =
|
"invalid first server message received; received Update expected \
|
||||||
Ev.listen Ev.keydown (kb_handler state_ref) (Window.as_target G.window)
|
Full_state"
|
||||||
in
|
| Full_state state ->
|
||||||
(* attach message listener to update state *)
|
(* start game *)
|
||||||
Ws_client.on_update_state_message (fun received ->
|
G.request_animation_frame (game_loop state) )
|
||||||
state_ref := received;
|
|
||||||
Format.printf "YOUR MANA IS: %d@." !state_ref.mana );
|
|
||||||
(* start game *)
|
|
||||||
G.request_animation_frame (game_loop state_ref) )
|
|
||||||
|
|
|
||||||
25
src/map.ml
25
src/map.ml
|
|
@ -9,10 +9,10 @@ type background =
|
||||||
| Water
|
| Water
|
||||||
| Black
|
| Black
|
||||||
|
|
||||||
|
type position = int * int * dir
|
||||||
|
|
||||||
type t =
|
type t =
|
||||||
{ tiles : background array array
|
{ tiles : background array array
|
||||||
; mutable player_pos : int * int
|
|
||||||
; mutable player_dir : dir
|
|
||||||
; width : int
|
; width : int
|
||||||
; height : int
|
; height : int
|
||||||
}
|
}
|
||||||
|
|
@ -25,23 +25,22 @@ let init () =
|
||||||
Array.init height (fun _y ->
|
Array.init height (fun _y ->
|
||||||
if Random.int 1000 <= 42 then Water else Grass ) )
|
if Random.int 1000 <= 42 then Water else Grass ) )
|
||||||
in
|
in
|
||||||
{ tiles; player_pos = (20, 3); player_dir = Down; width; height }
|
{ tiles; width; height }
|
||||||
|
|
||||||
let get_tile_kind ~x ~y map =
|
let get_tile_kind ~x ~y map =
|
||||||
try map.tiles.(x).(y) with Invalid_argument _ -> Black
|
try map.tiles.(x).(y) with Invalid_argument _ -> Black
|
||||||
|
|
||||||
let move map dir =
|
let check_move map entity_pos dir =
|
||||||
if map.player_dir = dir then begin
|
let x, y, current_dir = entity_pos in
|
||||||
let x, y = map.player_pos in
|
let x, y =
|
||||||
let x, y =
|
if current_dir <> dir then (x, y)
|
||||||
|
else
|
||||||
match dir with
|
match dir with
|
||||||
| Left -> (x - 1, y)
|
| Left -> (x - 1, y)
|
||||||
| Right -> (x + 1, y)
|
| Right -> (x + 1, y)
|
||||||
| Down -> (x, y + 1)
|
| Down -> (x, y + 1)
|
||||||
| Up -> (x, y - 1)
|
| Up -> (x, y - 1)
|
||||||
in
|
in
|
||||||
match get_tile_kind ~x ~y map with
|
match get_tile_kind ~x ~y map with
|
||||||
| Black | Water -> ()
|
| Black | Water -> Error "invalid terrain"
|
||||||
| Grass -> map.player_pos <- (x, y)
|
| Grass -> Ok (x, y, dir)
|
||||||
end
|
|
||||||
else map.player_dir <- dir
|
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,9 @@ let marshal o = Marshal.to_string o [] |> Format.sprintf "%S"
|
||||||
let unmarshal o =
|
let unmarshal o =
|
||||||
let s = Scanf.sscanf o "%S" (fun s -> s) in
|
let s = Scanf.sscanf o "%S" (fun s -> s) in
|
||||||
Marshal.from_string s 0
|
Marshal.from_string s 0
|
||||||
|
|
||||||
|
type server_message =
|
||||||
|
| Full_state of State.t
|
||||||
|
| Update_result of (State.action', string) result
|
||||||
|
|
||||||
|
type client_message = Action_msg of State.action
|
||||||
|
|
|
||||||
36
src/state.ml
36
src/state.ml
|
|
@ -1,17 +1,33 @@
|
||||||
type t =
|
type t =
|
||||||
{ map : Map.t
|
{ map : Map.t
|
||||||
; mutable mana : int
|
; mana : int
|
||||||
|
; player_pos : Map.position
|
||||||
}
|
}
|
||||||
|
|
||||||
let init () = { map = Map.init (); mana = 0 }
|
let init () = { map = Map.init (); mana = 0; player_pos = (20, 3, Down) }
|
||||||
|
|
||||||
type action = Meditate
|
type action =
|
||||||
|
| Meditate
|
||||||
|
(* TODO some action do not needs to be checked by server *)
|
||||||
|
| Move of Map.dir
|
||||||
|
| Do_nothing
|
||||||
|
|
||||||
(* TODO do not send whole state *)
|
(* type for result of action send to the client by the server *)
|
||||||
let handle_action state action =
|
type action' =
|
||||||
match action with
|
| Add_mana of int
|
||||||
|
| Set_player_position of Map.position
|
||||||
|
| Look_at_the_sky
|
||||||
|
|
||||||
|
let check_action state = function
|
||||||
| Meditate ->
|
| Meditate ->
|
||||||
if state.mana < 99 then (
|
if state.mana < 99 then Ok (Add_mana 1) else Error "maximum mana"
|
||||||
state.mana <- succ state.mana;
|
| Move dir -> (
|
||||||
Ok state )
|
match Map.check_move state.map state.player_pos dir with
|
||||||
else Error "maximum mana"
|
| Error _e as error -> error
|
||||||
|
| Ok pos -> Ok (Set_player_position pos) )
|
||||||
|
| Do_nothing -> Ok Look_at_the_sky
|
||||||
|
|
||||||
|
let perform_action state = function
|
||||||
|
| Add_mana n -> { state with mana = state.mana + n }
|
||||||
|
| Set_player_position player_pos -> { state with player_pos }
|
||||||
|
| Look_at_the_sky -> state
|
||||||
|
|
|
||||||
34
src/ws.ml
34
src/ws.ml
|
|
@ -6,23 +6,37 @@ let handle_client request client =
|
||||||
| None -> Dream.log "User does not exists" |> Lwt.return
|
| None -> Dream.log "User does not exists" |> Lwt.return
|
||||||
| Some user_id ->
|
| Some user_id ->
|
||||||
(* TODO catch marshal failure *)
|
(* TODO catch marshal failure *)
|
||||||
Dream.log " SEND USER ISLAND";
|
|
||||||
(* send user island state *)
|
|
||||||
let state =
|
let state =
|
||||||
match User.get_state user_id with
|
match User.get_state user_id with
|
||||||
| Error _e -> assert false
|
| Error _e -> assert false
|
||||||
| Ok state -> state
|
| Ok state -> state
|
||||||
in
|
in
|
||||||
let* () = Dream.send ~text_or_binary:`Text client (Network.marshal state) in
|
let state_msg = Network.Full_state state in
|
||||||
Dream.log " SENDED USER ISLAND";
|
|
||||||
|
|
||||||
let rec loop () =
|
(* send user island state *)
|
||||||
|
let* () =
|
||||||
|
Dream.send ~text_or_binary:`Text client (Network.marshal state_msg)
|
||||||
|
in
|
||||||
|
|
||||||
|
let rec loop state =
|
||||||
match%lwt Dream.receive client with
|
match%lwt Dream.receive client with
|
||||||
| None -> Dream.close_websocket client
|
| None -> Dream.close_websocket client
|
||||||
| Some s ->
|
| Some s ->
|
||||||
let action : State.action = Network.unmarshal s in
|
let (Network.Action_msg action : Network.client_message) =
|
||||||
let state_res = State.handle_action state action in
|
Network.unmarshal s
|
||||||
let* () = Dream.send client (Network.marshal state_res) in
|
in
|
||||||
loop ()
|
let res, state =
|
||||||
|
match State.check_action state action with
|
||||||
|
| Error _e as error -> (error, state)
|
||||||
|
| Ok action' ->
|
||||||
|
(* update server state *)
|
||||||
|
let state = State.perform_action state action' in
|
||||||
|
User.set_state user_id state;
|
||||||
|
(Ok action', state)
|
||||||
|
in
|
||||||
|
let* () =
|
||||||
|
Dream.send client (Network.marshal (Network.Update_result res))
|
||||||
|
in
|
||||||
|
loop state
|
||||||
in
|
in
|
||||||
loop ()
|
loop state
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,15 @@ let on_event ws_event log_msg f =
|
||||||
let to_server_msg ev =
|
let to_server_msg ev =
|
||||||
Format.printf "to_server_msg@.";
|
Format.printf "to_server_msg@.";
|
||||||
let data = Message.Ev.data (Ev.as_type ev) |> Jstr.to_string in
|
let data = Message.Ev.data (Ev.as_type ev) |> Jstr.to_string in
|
||||||
let state_res : (State.t, string) result = Network.unmarshal data in
|
let server_msg : Network.server_message = Network.unmarshal data in
|
||||||
Format.printf "un-marshaled message from server yay ~ @\n";
|
Format.printf "un-marshaled message from server yay ~ @\n";
|
||||||
match state_res with
|
server_msg
|
||||||
| Error e -> failwith (Format.sprintf "action resulted in error: %s" e)
|
|
||||||
| Ok state -> state
|
|
||||||
|
|
||||||
let on_update_state_message f =
|
let on_update_state_message f =
|
||||||
on_event Message.Ev.message "Websocket reveived message!" (fun ev ->
|
on_event Message.Ev.message "Websocket reveived message!" (fun ev ->
|
||||||
f (to_server_msg ev) )
|
f (to_server_msg ev) )
|
||||||
|
|
||||||
let send msg =
|
let send (msg : Network.client_message) =
|
||||||
Format.printf "send msg on websocket ~~ @\n";
|
Format.printf "send msg on websocket ~~ @\n";
|
||||||
let s = Jstr.of_string (Network.marshal msg) in
|
let s = Jstr.of_string (Network.marshal msg) in
|
||||||
Websocket.send_string ws s;
|
Websocket.send_string ws s;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue