logic.clj
Branches > main > 495549a > files / src / net / bowmap / server / logic.clj
(ns net.bowmap.server.logic
(:require
[net.bowmap.server.player :as player]
[net.bowmap.server.position :as position]
[net.bowmap.server.server :as server]
[net.bowmap.server.text :as text]
[net.bowmap.stormbow.effect :as effect]
[net.bowmap.stormbow.event :as event]))
; TODO so switch this all to pure functions, and call them from a tick function!
(defmulti handle-event
"Returns a sequence of effects and events yielded from an event."
(fn [event]
(::event/id event)))
(defmethod handle-event ::event/player-joining [{:keys! [::player/uuid ::player/username] :select p}]
(dosync
(if (@server/players uuid)
(throw (Exception. "Player tried to join with an existing UUID"))
(commute server/players update uuid merge p))
[]))
(defmethod handle-event ::event/player-joined [{:keys! [::player/uuid ::player/player]}]
(dosync
(commute server/players update uuid merge player)
[{::event/id ::event/broadcast
::text/component {:text (str (or (::player/username (server/player uuid)) "Unknown Player") " Joined!")}}]))
; TODO if these were atoms, could handle-events even work?
(defmethod handle-event ::event/player-left [{:keys! [::player/uuid]}]
(dosync
(let [username (::player/username (server/player uuid))]
(commute server/players dissoc uuid)
[{::event/id ::event/broadcast
::text/component {:text (str (or username "Unknown Player") " Left.")}}])))
(defmethod handle-event ::event/broadcast [{:keys! [::text/component]}]
(dosync
(for [[recipient] @server/players]
{::effect/id ::effect/send-chat-message
::player/uuid recipient
::text/component component})))
(defmethod handle-event ::event/updated-player-movement [{:keys! [::player/uuid ::position/position] :as e}]
(dosync
(when (not= position (::position/position (@server/players uuid)))
[(assoc e ::event/id ::event/player-moved)])))
; Called when the player actually moves, not just when a position is sent
(defmethod handle-event ::event/player-moved [{:keys! [::player/uuid ::position/position]}]
(dosync
(commute server/players update-in [uuid ::position/position] merge position)
[]))
(defmethod handle-event ::event/player-sent-chat-message [{:keys! [message ::player/uuid]}]
(for [[recipient] @server/players]
{::effect/id ::effect/send-chat-message
::player/uuid recipient
::text/component {:text (str (::player/username (server/player uuid)) ": " message)}}))
; TODO because this mutates stuff, it should probably be coupled with the effect handling?
; I also have concerns about stale state, i.e. if you update state, send an effect and another event, and that event updates state to a bad value for the effect
(defn handle-events
"Handles the supplied event, and all events returned recursively from this one.
Returns a seq of all effects that are yielded from handling all events recursively."
[event]
(reduce (fn [effects result]
(if (::event/id result)
(into effects (handle-events result))
(conj effects result)))
[] (handle-event event)))