instance.clj

Branches > client-handling-rewrite > 1a9c01b > files / src / net / bowmap / instance.clj

(ns net.bowmap.instance
  (:require
   [clojure.core.async :refer [<!! >!! alt!! chan io-thread put! timeout]]
   [net.bowmap.message-application :as a]
   [net.bowmap.network.client :as client]
   [net.bowmap.network.message :as msg]
   [net.bowmap.network.message.client :as cl]
   [net.bowmap.network.message.server :as srv]
   [net.bowmap.network.state :as state]))

; The logical server, holds a set of clients connected

; Global state D:
(defonce instance (atom {::clients {}
                         ::client-messages-ids->out-channels? {}
                         ::keepalive-client-ids #{}}))

(defn start! [server]
  (swap! instance assoc ::server server)
  server)

(defn client
  "Returns a new client value"
  []
  ; TODO this state should be server-wide, I think.
  {::state/connection-state ::state/handshaking
   ::client/id (random-uuid)})


(defn send-message! [client msg]
  ; TODO is put! okay?
  (put! ((::client-messages-ids->out-channels? @instance) (::client/id client)) msg))

(defn keep-alive! [client]
  (io-thread
    (loop []
      (<!! (timeout 20000))
      (send-message! client {::msg/id ::cl/keep-alive
                             :id (long (* (rand) Long/MAX_VALUE))})
      (recur))))

(defn read-and-handle-packet
  "Reads/deserializes one packet into a message, and handles it.
   Because packet reading is dependent on the state of the client, and handling a packet can update the state of the client,
   Reading and handling must be synchronized!"
  [pk client]
  (when-some [msg (srv/packet->message pk (::state/connection-state client))]
    (let [{:keys [::client/client ::a/keep-alive? ::a/messages]} (a/apply-message msg client)]
      (when keep-alive?
        (keep-alive! client))
      (doseq [msg messages]
        (send-message! client msg))
      client)))

(defn handle-packets-and-messages [client packets-in messages-out packets-out]
  (alt!!
    messages-out ([msg]
                  (when msg
                    (assert (>!! packets-out (cl/message->packet msg (::state/connection-state client))))
                    client))
    packets-in ([pk]
                (when pk
                  (read-and-handle-packet pk client)))))

(defn connect! [client packets-in packets-out]
  (let [messages-out (chan)]
    (io-thread
      (loop [client client]
        (when-some [client (handle-packets-and-messages client packets-in messages-out packets-out)]
          ; Otherwise, stop?
          (swap! instance update ::clients assoc (::client/id client) client)
          (recur client))))
    (swap! instance update ::clients assoc (::client/id client) client)
    (swap! instance update ::client-messages-ids->out-channels? assoc (::client/id client) messages-out)
    client))