connections.clj
Branches > main > latest > 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))))