connections.clj

Bowmap · bowmap.git · Tickets

Branches > main > 4808874 > files / src / net / bowmap / server / runtime / connections.clj

(ns net.bowmap.server.runtime.connections
  (:require
   [clojure.core.async :refer [io-thread]]
   [clojure.tools.logging :as log]
   [net.bowmap.server.game.handle :as handle]
   [net.bowmap.server.game.server :as state]
   [net.bowmap.server.network.connection :as connection]
   [net.bowmap.server.network.server :as server]
   [net.bowmap.server.packet.serialize :as serialize])
  (:import
   [java.io IOException]))

(defn handle-frame [server session frame]
  (handle/handle-packet server session
                        (serialize/decode-frame session frame)))

(defn cas-when!
  "Sets the state of atom to be (get m k) when k is in m.
   Returns the old value when the key is not present, the new value when the
   swap succeeded, or nil when the swap failed."
  [a old k m]
  (if (not (contains? m k))
    old
    (let [v (get m k)]
      (when (compare-and-set! a old v)
        v))))

(defn commit!
  "Calls 'f' with the server state, retrying until the :server key it returns,
   if any, is committed. Returns the result of 'f'."
  [f]
  (loop []
    (let [server @state/state
          result (f server)]
      (if (cas-when! state/state server :server result)
        result
        (recur)))))

(defn connection-loop [connection]
  (let [session-atom (atom handle/initial-session)]
    (loop []
      (let [frame (connection/receive-frame!! connection)
            session @session-atom
            result (commit! #(handle-frame % session frame))]
        (if-let [session' (cas-when! session-atom session :session result)]
          (doseq [packet (:packets result)]
            (connection/send-frame! connection (serialize/encode-packet session' packet)))
          (throw (ex-info "Session state changed while receiving packet."
                          {:old-session session
                           :new-session (:session result)}))))
      (recur))))

(defn handle-connection! [connection]
  (try
    (connection-loop connection)
    (catch IOException _)
    (catch Exception e
      (log/warn e))
    (finally
      (connection/close! connection))))

(defn handle-connections!
  [server]
  (loop []
    (let [connection (server/accept!! server)]
      (io-thread
        (handle-connection! connection))
      (recur))))