server.clj
Branches > facts > 5534b1b > 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 fact, and update the player list from that
(send (@client-ref ::connection/connection) connection/close!)))))
(recur))))