server.clj
Branches > main > 0dda787 > files / src / net / bowmap / protocol / server.clj
(ns net.bowmap.protocol.server
(:require
[clojure.core.async :refer [<!! >!! alt!! io-thread put!]]
[net.bowmap.protocol.client :as client]
[net.bowmap.protocol.handle :as handle]
[net.bowmap.protocol.keepalive :as keepalive]
[net.bowmap.protocol.packet.clientbound :as clientbound :refer [encode-packet!]]
[net.bowmap.protocol.packet.serverbound :refer [decode-packet!]]
[net.bowmap.protocol.state :as state]
[net.bowmap.server.logic :as logic]
[net.bowmap.server.player :as player]
[net.bowmap.stormbow.effect-execution :refer [execute-effects!]]
[net.bowmap.stormbow.event :as event]
[net.bowmap.transport.connection :as connection]
[net.bowmap.transport.server :as mc]))
(defn adorn-effects-with-client [client events]
(mapv #(assoc % ::client/client client) events))
(defn handle-serverbound-frame [client frame]
(let [{:keys! [::state/connection-state ::client/clientbound-packets]} client
packet (decode-packet! frame connection-state)
{:keys [client packets events keep-alive?] :or {:client client}} (handle/interpret-packet client packet)]
; TODO probably want to clean up this side effecting stuff
(doseq [pk packets]
(put! clientbound-packets pk))
(doseq [event events]
(execute-effects! (adorn-effects-with-client client (logic/handle-events event))))
(when keep-alive?
(keepalive/keep-alive! client))
client))
(defn client-lifecycle
"Loops forever, handling server and client-bound packets.
Returns the final client value at the end of the client's lifecycle."
[client]
(let [{:keys! [::client/clientbound-packets ::connection/connection]} client
{::connection/keys! [serverbound-frames clientbound-frames]} connection]
(loop [client client]
(let [res (try
(alt!!
serverbound-frames ([frame]
(if (some? frame)
(handle-serverbound-frame client frame)
::disconnect))
clientbound-packets ([packet]
(if (some? packet)
(do (>!! clientbound-frames (encode-packet! packet (::state/connection-state client)))
client)
(throw (Exception. "Clientbound packets was closed idk why")))))
(catch Throwable t
(Throwable/.printStackTrace t)
::disconnect))]
(if (= ::disconnect res)
client
(recur res))))))
(defn handle-connection [connection]
(io-thread
(let [client (client/client connection)
{::connection/keys! [close-fn]} connection
client (client-lifecycle client)]
(when-let [uuid (::player/uuid client)]
(execute-effects! (adorn-effects-with-client client
(logic/handle-events {::event/id ::event/player-left
::player/uuid uuid}))))
(close-fn))))
(defn start-server! [port]
(let [connections (mc/start-server! port)]
(keepalive/start-keep-alives!)
(loop []
(when-let [connection (<!! connections)]
(handle-connection connection))
(recur))))