This commit is contained in:
2026-07-15 22:52:16 -06:00
parent da4451560e
commit d75a2fe251
5 changed files with 45 additions and 50 deletions

View File

@@ -43,7 +43,7 @@
(>! tx-chan (js/JSON.stringify (clj->js msg))))))
(assoc state :outbox []))
(defn ticker [app state layers entity-id rx-chan tx-chan t]
(defn ticker [app state layers rx-chan tx-chan t]
(let [dt (.-deltaTime t)
{world-layer :world-layer
entity-layer :entity-layer} layers
@@ -53,7 +53,7 @@
elapsed (.-elapsedMS t)
{:keys [player entities]} (-> (update @state :elapsed + elapsed)
(update/update-entities dt layers)
(update/update-player dt send-interval layers entity-id entity-relative-mouse-pos)
(update/update-player dt send-interval layers entity-relative-mouse-pos)
(update/update-from-server rx-chan)
(drain-outbox! tx-chan)
(->> (swap! state merge)))
@@ -78,7 +78,6 @@
(defn init []
(let [app (doto (pixi/Application.)
(.init {:resizeTo js/window :autoDensity true :resolution (.-devicePixelRatio js/window)}))
entity-id (atom 1)
state (atom {:player {:velocity {:x 0 :y 0}
:keymap {:w 0 :a 0 :s 0 :d 0 :lmb 0}
:last-attack -1
@@ -98,8 +97,8 @@
world-layer (pixi/Container.)
entity-layer (pixi/Container.)
ui-layer (pixi/Container.)
tx-chan (async/chan (async/sliding-buffer 10))
rx-chan (async/chan (async/sliding-buffer 2))
tx-chan (async/chan (async/dropping-buffer 10))
rx-chan (async/chan)
layers {:entity-layer entity-layer
:world-layer world-layer
:ui-layer ui-layer}
@@ -134,4 +133,4 @@
(partial sb-callback false))
player (assoc e :renderable p :shootbox sb)]
(swap! state update-in [:player] merge player)
(.. app -ticker (add (partial ticker app state layers entity-id rx-chan tx-chan)))))))))
(.. app -ticker (add (partial ticker app state layers rx-chan tx-chan)))))))))

View File

@@ -19,36 +19,16 @@
(assoc e :renderable renderable))
e))
(defn apply-player-shoot [state entity-id mouse-pos]
(defn apply-player-shoot [state mouse-pos]
(if (and (get-in state [:player :keymap :lmb] false)
(< (+ (get-in state [:player :last-attack] -1)
(* 1000 (get-in state [:player :attack-speed] 0.75)))
(js/Date.now)))
(let [player-sprite (get-in state [:player :renderable])
speed 15
image "/sprite/projectile.png"
start-x (.-x player-sprite)
start-y (.-y player-sprite)
position mouse-pos
target-x (.-x position)
target-y (.-y position)
dx (- target-x start-x)
dy (- target-y start-y)
h (js/Math.sqrt (+ (* dx dx) (* dy dy)))
vx (* (/ dx h) speed)
vy (* (/ dy h) speed)
eid (-> (swap! entity-id inc)
(str)
(keyword))
e {:sprite image
:hypotenuse h
:x start-x
:y start-y
:velocity {:x vx :y vy}}]
(-> state
(update :outbox conj {:type "shoot"})
(assoc-in [:entities eid] e)
(assoc-in [:player :last-attack] (js/Date.now))))
(-> state
(update :outbox conj {:type "shoot"
:x (.-x mouse-pos)
:y (.-y mouse-pos)})
(assoc-in [:player :last-attack] (js/Date.now)))
state))
(defn apply-player-position [state send-interval]
@@ -69,14 +49,14 @@
(apply-velocity dt)))]
(assoc state :entities new-entities)))
(defn update-player [state dt send-interval layers entity-id mouse-pos]
(defn update-player [state dt send-interval layers mouse-pos]
(let [{player :player} state
updated-player (-> player
(apply-renderable layers)
(apply-velocity dt))]
(-> (assoc state :player updated-player)
(apply-player-position send-interval)
(apply-player-shoot entity-id mouse-pos))))
(apply-player-shoot mouse-pos))))
(defn- deep-merge [a b]
(if (and (map? a) (map? b))

View File

@@ -11,19 +11,21 @@
(defn ws-handler [state state-chan player-id]
(go-websocket [in out err]
(s/change state-chan update :players assoc player-id {:out out :in in :err err})
(s/change state-chan update :players assoc player-id {:out out :in in :err err :x 0 :y 0})
(go-loop []
(when-let [e (<! err)]
(println (.getMessage e))
(recur)))
(go-loop []
(if-let [raw (<! in)]
(let [msg (j/from-json raw)
type (get msg :type "invalid")]
(cond
(= type "shoot") (handler/handle-shoot state state-chan player-id msg)
(= type "invalid") (>! out "Hello World!"))
(println msg)
(when (contains? (@state :players) player-id) ; Only if player has been initialized do we accept messages
(let [msg (j/from-json raw)
type (get msg :type "invalid")]
(cond
(= type "position") (handler/handle-position @state state-chan player-id msg)
(= type "shoot") (handler/handle-shoot @state state-chan player-id msg)
(= type "invalid") (>! out "Hello World!"))
(println msg))
(recur))
(do
(s/change state-chan update :players dissoc player-id)
@@ -42,14 +44,14 @@
(defn build-application [state state-chan]
(c/routes
(c/GET "/" r (index-handler r))
(c/GET "/ws" [] (ws-handler @state state-chan (random-uuid))) ; TODO: Auth here
(c/GET "/ws" [] (ws-handler state state-chan (random-uuid))) ; TODO: Auth here
(route/resources "/")
(route/not-found "<h1>The requested resource does not exist...</h1>")))
(defn game-loop [state state-chan]
(go-loop []
(let [start-time (System/currentTimeMillis)
max-inputs-per-tick 15000 ; Stops the game from spinning endlessly, dropping ticks
max-inputs-per-tick 15000 ; Stops the game from spinning for too long with events endlessly, dropping ticks
events-to-apply (loop [inputs []
count 0]
(if (>= count max-inputs-per-tick)
@@ -57,15 +59,22 @@
(if-let [args (async/poll! state-chan)]
(recur (conj inputs args) (inc count))
inputs)))]
(doseq [event-args events-to-apply]
(apply swap! state event-args))
(doseq [{out :out} (vals (:players @state))]
(>! out (j/to-json {:type "sync"
:entities (:entities @state)})))
(let [event-state (reduce (fn [state event-args] ((event-args 0) state (rest event-args)))
@state
events-to-apply)
system-state (reduce (fn [state system] (system state))
event-state
(event-state :systems))
entities (:entities @state)
players (:players @state)]
(swap! state merge system-state)
(doseq [{out :out} (vals players)]
(>! out (j/to-json {:type "sync"
:entities entities}))))
(let [elapsed (- (System/currentTimeMillis) start-time)
sleep-time (max 0 (- 50 elapsed))]
(<! (async/timeout sleep-time))))
(recur)))
(<! (async/timeout sleep-time)))
(recur))))
(defn -main []
(let [state (atom {:systems []

View File

@@ -1,10 +1,16 @@
(ns il.handler
(:require [il.state :as s]))
(defn handle-position [_ state-chan player-id {x :x y :y}]
(s/change state-chan update-in [:players player-id] assoc :x x :y y))
(defn handle-shoot [state state-chan player-id msg]
(let [bullet-id (random-uuid)
{x :x y :y} (get-in state [:players player-id])
{tx :x ty :y} msg
_ (println state)
_ (println msg)
_ (println (get-in state [:players player-id]))
dx (- tx x)
dy (- ty y)
h (Math/sqrt (+ (Math/pow dx 2) (Math/pow dy 2)))

View File

@@ -2,4 +2,5 @@
(:require [clojure.core.async :refer [>! go]]))
(defn change [state-chan & args]
(println "Appending to state-chan: " args)
(go (>! state-chan args)))