server.clj

Branches > main > f23702d > files / src / net / bowmap / protocol / server.clj

(ns net.bowmap.protocol.server
  (:require
   [clojure.core.async :refer [<!! >!! alt!! chan io-thread put! timeout]]
   [net.bowmap.protocol.client :as client]
   [net.bowmap.protocol.handle :as handle]
   [net.bowmap.protocol.packet :as pk]
   [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.stormbow.effect-execution :refer [execute-effects!]]
   [net.bowmap.transport.connection :as connection]
   [net.bowmap.transport.server :as mc]))

(defn handle-connection [connection]
  (io-thread
    (let [client (client/client connection)
          clientbound-packets (req! client ::client/clientbound-packets)
          keep-alive (chan)
          {::connection/keys! [serverbound-frames clientbound-frames close-fn]} connection]
      (try
        (loop [client client]
          (alt!!
            serverbound-frames ([frame]
                                (when (some? frame)
                                  (let [packet (decode-packet! frame (::state/connection-state client))
                                        {:keys [client packets facts keep-alive?] :or {:client client}} (handle/handle-packet client packet)]
                                    ; TODO probably want to clean up this side effecting stuff
                                    (doseq [pk packets]
                                      (put! clientbound-packets pk))
                                    (doseq [fact facts]
                                      (execute-effects! (mapv #(assoc % ::client/client client) (logic/handle-fact fact))))
                                    (when keep-alive?
                                      (put! keep-alive true))
                                    (recur client))))
            clientbound-packets ([packet]
                                 (if (some? packet)
                                   (do (>!! clientbound-frames (encode-packet! packet (::state/connection-state client)))
                                       (recur client))
                                   (throw (Exception. "Clientbound packets was closed idk why"))))
            keep-alive ([x]
                        (when x
                          (put! clientbound-packets {::pk/id ::clientbound/keep-alive
                                                     :id (rand-int Integer/MAX_VALUE)})
                          (io-thread
                            (<!! (timeout 20000))
                            (put! keep-alive true)))
                        (recur client))))
        (finally
          ; TODO Send player disconnected fact, and update the player list from that
          (close-fn))))))

(defn start-server! [port]
  (let [connections (mc/start-server! port)]
    (loop []
      (when-let [connection (<!! connections)]
        (handle-connection connection))
      (recur))))