server.clj

Branches > main > 181d0a2 > files / src / net / bowmap / network / server.clj

(ns net.bowmap.network.server
  (:require
   [clojure.core.async :refer [io-thread]]
   [net.bowmap.network.client :as client]
   [net.bowmap.network.connection :as connection]
   [net.bowmap.network.handle :as handle]
   [net.bowmap.network.packets.serverbound :refer [decode-packet!]]
   [net.bowmap.network.state :as state]
   [net.bowmap.network.streams :as streams])
  (:import
   [java.io EOFException]
   [java.net ServerSocket Socket SocketException]))

(defonce server (atom nil))

(defn start-server! [port]
  (when-let [server @server]
    (ServerSocket/.close server))
  (let [server-socket (ServerSocket. port)]
    (reset! server server-socket)
    (loop []
      (let [socket (ServerSocket/.accept server-socket)
            input-stream (Socket/.getInputStream socket)
            ; HERE IS THE NEW AGE!
            ; A client is a piece of data, it has state info
            ; Get this ref out of here, we don't like it.
            ; Connections do I/O, and they need no state.
            ; They're just for frames
            ; They also don't need synchronized
            ; There does need to be one point in the program where serverbound packets are handled 1 at a time
            ; And a new client state will be returned from that handling, as well as other things to happen
            client-ref (client/client socket)]
        (io-thread
          (try
            ; TODO also read packets from the clientbound channel!
            (loop []
              (let [frame (streams/read-frame!! input-stream)]
                (dosync
                  (->> (decode-packet! frame (::state/connection-state @client-ref))
                       ; So I want sending a clientbound packet to be kind of a private detail
                       ; But it could happen asynchronously at any time...
                       ; So the client may need to keep its connection object, which I don't love.
                       ; Perhaps a map of player uuids to connections/to a queue you could send packets out onto would be best?
                       (handle/handle!! client-ref))))
              (recur))
            (catch EOFException _)
            (catch SocketException _)
            (finally
              ; TODO Send player disconnected packet
              (send (@client-ref ::connection/connection) connection/close!)))))
      (recur))))