do not send whole state on action

This commit is contained in:
Swrup 2022-12-26 02:06:13 +01:00
parent 86489c5394
commit 53a4ae536d
6 changed files with 135 additions and 66 deletions

View file

@ -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,47 @@ 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 = ref []
(* queue for action' to apply to client state *)
let to_apply_queue : State.action' list ref = ref []
let send_action state action =
match State.check_action state action with
| 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 match ev |> Ev.as_type |> Ev.Keyboard.code |> Jstr.to_string with
| "KeyW" | "ArrowUp" -> move Up | "KeyW" | "ArrowUp" -> Some (Move Up)
| "KeyA" | "ArrowLeft" -> move Left | "KeyA" | "ArrowLeft" -> Some (Move Left)
| "KeyS" | "ArrowDown" -> move Down | "KeyS" | "ArrowDown" -> Some (Move Down)
| "KeyD" | "ArrowRight" -> move Right | "KeyD" | "ArrowRight" -> Some (Move Right)
| "KeyM" -> Ws_client.send State.Meditate | "KeyM" -> Some Meditate
| _s -> () | _s -> None
in
input_queue := Option.to_list 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 =
List.fold_left
(fun state action' -> State.perform_action state action')
state !to_apply_queue
in
(* TODO can this bug because of concurrency? *)
to_apply_queue := [];
(* send input action to server *)
List.iter (send_action state) !input_queue;
input_queue := [];
state
in
G.request_animation_frame (game_loop new_state) G.request_animation_frame (game_loop new_state)
let () = let () =
@ -106,16 +135,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
Fut.await initial_state_fut (fun msg -> (* attach message listener to update state *)
let initial_state = Ws_client.to_server_msg msg in Ws_client.on_update_state_message (fun server_msg ->
let state_ref = ref initial_state in 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' -> to_apply_queue := action' :: !to_apply_queue ) );
(* bind keys *) (* bind keys *)
let _e : Ev.listener = let _e : Ev.listener =
Ev.listen Ev.keydown (kb_handler state_ref) (Window.as_target G.window) Ev.listen Ev.keydown kb_handler (Window.as_target G.window)
in in
(* attach message listener to update state *)
Ws_client.on_update_state_message (fun received -> Fut.await initial_state_fut (fun msg ->
state_ref := received; match Ws_client.to_server_msg msg with
Format.printf "YOUR MANA IS: %d@." !state_ref.mana ); | Update_result _res_msg ->
failwith
"invalid first server message received; received Update expected \
Full_state"
| Full_state state ->
(* start game *) (* start game *)
G.request_animation_frame (game_loop state_ref) ) G.request_animation_frame (game_loop state) )

View file

@ -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,15 +25,16 @@ 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)
@ -41,7 +42,5 @@ let move map dir =
| 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

View file

@ -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

View file

@ -1,17 +1,29 @@
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
(* 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
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) )
let perform_action state = function
| Add_mana n -> { state with mana = state.mana + n }
| Set_player_position pos -> { state with player_pos = pos }

View file

@ -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
loop ()
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
loop state

View file

@ -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;