wip: state server side; websocket
This commit is contained in:
parent
3c6a373dc9
commit
5f1d29bda3
11 changed files with 169 additions and 44 deletions
|
|
@ -78,10 +78,6 @@ let log =
|
||||||
|
|
||||||
let () = Dream.log "log: %b" log
|
let () = Dream.log "log: %b" log
|
||||||
|
|
||||||
let random_state = Random.State.make_self_init ()
|
|
||||||
|
|
||||||
let () = Random.set_state random_state
|
|
||||||
|
|
||||||
let about =
|
let about =
|
||||||
let default_about = "Pellest is great !" in
|
let default_about = "Pellest is great !" in
|
||||||
match Scfg.Query.get_dir "about" config with
|
match Scfg.Query.get_dir "about" config with
|
||||||
|
|
@ -90,3 +86,7 @@ let about =
|
||||||
match Scfg.Query.get_param 0 about with
|
match Scfg.Query.get_param 0 about with
|
||||||
| Error e -> failwith e
|
| Error e -> failwith e
|
||||||
| Ok about -> about )
|
| Ok about -> about )
|
||||||
|
|
||||||
|
let random_state = Random.State.make_self_init ()
|
||||||
|
|
||||||
|
let () = Random.set_state random_state
|
||||||
|
|
|
||||||
0
src/common.ml
Normal file
0
src/common.ml
Normal file
13
src/dune
13
src/dune
|
|
@ -14,7 +14,8 @@
|
||||||
syntax
|
syntax
|
||||||
template
|
template
|
||||||
tyx_util
|
tyx_util
|
||||||
user)
|
user
|
||||||
|
ws)
|
||||||
(libraries
|
(libraries
|
||||||
bos
|
bos
|
||||||
caqti
|
caqti
|
||||||
|
|
@ -23,6 +24,7 @@
|
||||||
directories
|
directories
|
||||||
dream
|
dream
|
||||||
emile
|
emile
|
||||||
|
shared
|
||||||
fpath
|
fpath
|
||||||
lambdasoup
|
lambdasoup
|
||||||
lwt
|
lwt
|
||||||
|
|
@ -37,10 +39,15 @@
|
||||||
|
|
||||||
(executable
|
(executable
|
||||||
(name island_client)
|
(name island_client)
|
||||||
(modules island_client)
|
(modules island_client ws_client)
|
||||||
(libraries js_of_ocaml brr)
|
(libraries js_of_ocaml brr shared)
|
||||||
(modes js))
|
(modes js))
|
||||||
|
|
||||||
|
(library
|
||||||
|
(name shared)
|
||||||
|
(modules map network state)
|
||||||
|
(libraries))
|
||||||
|
|
||||||
(rule
|
(rule
|
||||||
(target content.ml)
|
(target content.ml)
|
||||||
(deps
|
(deps
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
open Brr
|
open Brr
|
||||||
|
open Brr_io
|
||||||
open Brr_canvas
|
open Brr_canvas
|
||||||
|
open Shared
|
||||||
|
|
||||||
module G = struct
|
module G = struct
|
||||||
include Brr.G
|
include Brr.G
|
||||||
|
|
@ -8,36 +10,6 @@ module G = struct
|
||||||
(ignore : int -> unit) @@ Brr.G.request_animation_frame f
|
(ignore : int -> unit) @@ Brr.G.request_animation_frame f
|
||||||
end
|
end
|
||||||
|
|
||||||
let () = Random.self_init ()
|
|
||||||
|
|
||||||
type dir =
|
|
||||||
| Left
|
|
||||||
| Right
|
|
||||||
| Down
|
|
||||||
| Up
|
|
||||||
|
|
||||||
module Map = struct
|
|
||||||
type background =
|
|
||||||
| Grass
|
|
||||||
| Water
|
|
||||||
| Black
|
|
||||||
|
|
||||||
let width = 1000
|
|
||||||
|
|
||||||
let height = 1000
|
|
||||||
|
|
||||||
let player_pos = ref (20, 3)
|
|
||||||
|
|
||||||
let player_dir = ref Down
|
|
||||||
|
|
||||||
let m =
|
|
||||||
Array.init width (fun _x ->
|
|
||||||
Array.init height (fun _y ->
|
|
||||||
if Random.int 1000 <= 42 then Water else Grass ) )
|
|
||||||
|
|
||||||
let get_tile_kind ~x ~y = try m.(x).(y) with Invalid_argument _ -> Black
|
|
||||||
end
|
|
||||||
|
|
||||||
let get_el id =
|
let get_el id =
|
||||||
match Document.find_el_by_id G.document (Jstr.of_string id) with
|
match Document.find_el_by_id G.document (Jstr.of_string id) with
|
||||||
| None -> failwith (Format.sprintf {|Could not find element by id: "%s"|} id)
|
| None -> failwith (Format.sprintf {|Could not find element by id: "%s"|} id)
|
||||||
|
|
@ -79,6 +51,11 @@ 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 map =
|
||||||
|
(* TODO receive map / state *)
|
||||||
|
(* dummy map; should ask for map to server *)
|
||||||
|
ref (Map.init ())
|
||||||
|
|
||||||
let draw_map =
|
let draw_map =
|
||||||
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
|
||||||
|
|
@ -91,7 +68,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 with
|
match Map.get_tile_kind ~x:map_x ~y:map_y !map with
|
||||||
| Grass -> grass
|
| Grass -> grass
|
||||||
| Water -> water
|
| Water -> water
|
||||||
| Black -> water
|
| Black -> water
|
||||||
|
|
@ -118,7 +95,7 @@ let move dir =
|
||||||
| Down -> (x, y + 1)
|
| Down -> (x, y + 1)
|
||||||
| Up -> (x, y - 1)
|
| Up -> (x, y - 1)
|
||||||
in
|
in
|
||||||
match Map.get_tile_kind ~x ~y with
|
match Map.get_tile_kind ~x ~y !map with
|
||||||
| Black | Water -> ()
|
| Black | Water -> ()
|
||||||
| Grass -> Map.player_pos := (x, y)
|
| Grass -> Map.player_pos := (x, y)
|
||||||
end
|
end
|
||||||
|
|
@ -130,6 +107,7 @@ let kb_handler ev =
|
||||||
| "KeyA" | "ArrowLeft" -> move Left
|
| "KeyA" | "ArrowLeft" -> move Left
|
||||||
| "KeyS" | "ArrowDown" -> move Down
|
| "KeyS" | "ArrowDown" -> move Down
|
||||||
| "KeyD" | "ArrowRight" -> move Right
|
| "KeyD" | "ArrowRight" -> move Right
|
||||||
|
| "KeyM" -> Ws_client.send State.Meditate
|
||||||
| _s -> ()
|
| _s -> ()
|
||||||
|
|
||||||
let rec game_loop state _timestamp =
|
let rec game_loop state _timestamp =
|
||||||
|
|
@ -137,9 +115,6 @@ let rec game_loop state _timestamp =
|
||||||
let new_state = state in
|
let new_state = state in
|
||||||
G.request_animation_frame (game_loop new_state)
|
G.request_animation_frame (game_loop new_state)
|
||||||
|
|
||||||
(* type will change later ! *)
|
|
||||||
let initial_state = ()
|
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
(* init canvas *)
|
(* init canvas *)
|
||||||
Canvas.set_w canvas width;
|
Canvas.set_w canvas width;
|
||||||
|
|
@ -151,5 +126,16 @@ let () =
|
||||||
let _e : Ev.listener =
|
let _e : Ev.listener =
|
||||||
Ev.listen Ev.keydown kb_handler (Window.as_target G.window)
|
Ev.listen Ev.keydown kb_handler (Window.as_target G.window)
|
||||||
in
|
in
|
||||||
(* start game *)
|
|
||||||
G.request_animation_frame (game_loop initial_state)
|
(* get state from server*)
|
||||||
|
let initial_state_fut = Ev.next Message.Ev.message Ws_client.ws_target in
|
||||||
|
|
||||||
|
Fut.await initial_state_fut (fun msg ->
|
||||||
|
let initial_state = Ws_client.to_server_msg msg in
|
||||||
|
let state_ref = ref initial_state in
|
||||||
|
(* attach message listener to update state *)
|
||||||
|
Ws_client.on_update_state_message (fun received ->
|
||||||
|
state_ref := received;
|
||||||
|
Format.printf "YOUR MANA IS: %d@." !state_ref.mana );
|
||||||
|
(* start game *)
|
||||||
|
G.request_animation_frame (game_loop state_ref) )
|
||||||
|
|
|
||||||
27
src/map.ml
Normal file
27
src/map.ml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
type dir =
|
||||||
|
| Left
|
||||||
|
| Right
|
||||||
|
| Down
|
||||||
|
| Up
|
||||||
|
|
||||||
|
type background =
|
||||||
|
| Grass
|
||||||
|
| Water
|
||||||
|
| Black
|
||||||
|
|
||||||
|
type t = background array array
|
||||||
|
|
||||||
|
let width = 1000
|
||||||
|
|
||||||
|
let height = 1000
|
||||||
|
|
||||||
|
let player_pos = ref (20, 3)
|
||||||
|
|
||||||
|
let player_dir = ref Down
|
||||||
|
|
||||||
|
let init () =
|
||||||
|
Array.init width (fun _x ->
|
||||||
|
Array.init height (fun _y ->
|
||||||
|
if Random.int 1000 <= 42 then Water else Grass ) )
|
||||||
|
|
||||||
|
let get_tile_kind ~x ~y map = try map.(x).(y) with Invalid_argument _ -> Black
|
||||||
5
src/network.ml
Normal file
5
src/network.ml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
let marshal o = Marshal.to_string o [] |> Format.sprintf "%S"
|
||||||
|
|
||||||
|
let unmarshal o =
|
||||||
|
let s = Scanf.sscanf o "%S" (fun s -> s) in
|
||||||
|
Marshal.from_string s 0
|
||||||
|
|
@ -6,6 +6,8 @@ let () =
|
||||||
[ get "/assets/**" Asset.get
|
[ get "/assets/**" Asset.get
|
||||||
; get "/" Home.get
|
; get "/" Home.get
|
||||||
; get "/island" Island.get
|
; get "/island" Island.get
|
||||||
|
; get "/island/ws" (fun request ->
|
||||||
|
Dream.websocket @@ Ws.handle_client request )
|
||||||
; get "/login" Login.get
|
; get "/login" Login.get
|
||||||
; post "/login" Login.post
|
; post "/login" Login.post
|
||||||
; get "logout" Logout.get
|
; get "logout" Logout.get
|
||||||
|
|
|
||||||
17
src/state.ml
Normal file
17
src/state.ml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
type t =
|
||||||
|
{ map : Map.t
|
||||||
|
; mutable mana : int
|
||||||
|
}
|
||||||
|
|
||||||
|
let init () = { map = Map.init (); mana = 0 }
|
||||||
|
|
||||||
|
type action = Meditate
|
||||||
|
|
||||||
|
(* TODO do not send whole state *)
|
||||||
|
let handle_action state action =
|
||||||
|
match action with
|
||||||
|
| Meditate ->
|
||||||
|
if state.mana < 99 then (
|
||||||
|
state.mana <- succ state.mana;
|
||||||
|
Ok state )
|
||||||
|
else Error "maximum mana"
|
||||||
11
src/user.ml
11
src/user.ml
|
|
@ -223,3 +223,14 @@ let assert_logged request =
|
||||||
let assert_not_logged request =
|
let assert_not_logged request =
|
||||||
if is_logged_in request then Error (`Forbidden, "you shoudn't be logged in")
|
if is_logged_in request then Error (`Forbidden, "you shoudn't be logged in")
|
||||||
else Ok ()
|
else Ok ()
|
||||||
|
|
||||||
|
(* TODO save states *)
|
||||||
|
|
||||||
|
let state_ht : (string, Shared.State.t) Hashtbl.t = Hashtbl.create 1
|
||||||
|
|
||||||
|
let set_state = Hashtbl.replace state_ht
|
||||||
|
|
||||||
|
let get_state user_id =
|
||||||
|
match Hashtbl.find_opt state_ht user_id with
|
||||||
|
| Some state -> Ok state
|
||||||
|
| None -> Ok (Shared.State.init ())
|
||||||
|
|
|
||||||
27
src/ws.ml
Normal file
27
src/ws.ml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
open Lwt.Syntax
|
||||||
|
open Shared
|
||||||
|
|
||||||
|
let handle_client request client =
|
||||||
|
match Dream.session "user_id" request with
|
||||||
|
| None -> Dream.log "User does not exists" |> Lwt.return
|
||||||
|
| Some user_id ->
|
||||||
|
(* TODO catch marshal failure *)
|
||||||
|
|
||||||
|
(* send user island state *)
|
||||||
|
let state =
|
||||||
|
match User.get_state user_id with
|
||||||
|
| Error _e -> assert false
|
||||||
|
| Ok state -> state
|
||||||
|
in
|
||||||
|
let* () = Dream.send ~text_or_binary:`Text client (Network.marshal state) in
|
||||||
|
|
||||||
|
let rec loop () =
|
||||||
|
match%lwt Dream.receive client with
|
||||||
|
| None -> Dream.close_websocket client
|
||||||
|
| Some s ->
|
||||||
|
let action : State.action = Network.unmarshal s in
|
||||||
|
let state_res = State.handle_action state action in
|
||||||
|
let* () = Dream.send client (Network.marshal state_res) in
|
||||||
|
loop ()
|
||||||
|
in
|
||||||
|
loop ()
|
||||||
43
src/ws_client.ml
Normal file
43
src/ws_client.ml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
open Brr
|
||||||
|
open Brr_io
|
||||||
|
open Shared
|
||||||
|
|
||||||
|
let ws =
|
||||||
|
Format.printf "create websocket@\n";
|
||||||
|
let ws_url =
|
||||||
|
(* TODO fix hostname *)
|
||||||
|
Jstr.of_string "ws://localhost:3696/island/ws"
|
||||||
|
in
|
||||||
|
Websocket.create ws_url
|
||||||
|
|
||||||
|
let ws_target = Websocket.as_target ws
|
||||||
|
|
||||||
|
let on_event ws_event log_msg f =
|
||||||
|
let (_ : Ev.listener) =
|
||||||
|
Ev.listen ws_event
|
||||||
|
(fun ev ->
|
||||||
|
Format.printf "%s@\n" log_msg;
|
||||||
|
f ev )
|
||||||
|
ws_target
|
||||||
|
in
|
||||||
|
()
|
||||||
|
|
||||||
|
let to_server_msg ev =
|
||||||
|
Format.printf "to_server_msg@.";
|
||||||
|
let data = Message.Ev.data (Ev.as_type ev) |> Jstr.to_string in
|
||||||
|
let state_res : (State.t, string) result = Network.unmarshal data in
|
||||||
|
Format.printf "un-marshaled message from server yay ~ @\n";
|
||||||
|
match state_res with
|
||||||
|
| Error e -> failwith (Format.sprintf "action resulted in error: %s" e)
|
||||||
|
| Ok state -> state
|
||||||
|
|
||||||
|
let on_update_state_message f =
|
||||||
|
on_event Message.Ev.message "Websocket reveived message!" (fun ev ->
|
||||||
|
f (to_server_msg ev) )
|
||||||
|
|
||||||
|
let send msg =
|
||||||
|
Format.printf "send msg on websocket ~~ @\n";
|
||||||
|
let s = Jstr.of_string (Network.marshal msg) in
|
||||||
|
Websocket.send_string ws s;
|
||||||
|
Format.printf "send action on websocket ~~ DONE @\n";
|
||||||
|
()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue