server.clj

Branches > main > ba1da08 > 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))))