c2b86ae

Branches > facts > c2b86ae > files

Move client I/O connection to an agent

Grahp, 2026-08-20

Commit: c2b86ae820c25e5ea6944e421995cea1e9d6b2f4
Parent: 754557c

5 changed files:

src/net/bowmap/network/client.clj modified (view file)

@@ -1,35 +1,19 @@ (ns net.bowmap.network.client   (:require    [clojure.spec.alpha :as s]-   [net.bowmap.network.packets.clientbound :refer [encode-packet!]]-   [net.bowmap.network.packets.serverbound :refer [decode-packet!]]+   [net.bowmap.network.connection :as connection]    [net.bowmap.network.state :as state]-   [net.bowmap.network.streams :as streams]    [net.bowmap.server.entity :as entity]-   [net.bowmap.server.server :as server])-  (:import-   [java.net Socket]))+   [net.bowmap.server.server :as server])) -(s/def ::client (s/keys :opt [::state/connection-state ::socket]))+(s/def ::client (s/keys :opt [::state/connection-state ::connection/connection]))  (defn client [socket]   (ref {::state/connection-state ::state/handshaking-        ::socket socket}))--(defn read-packet! [client-ref]-  (let [{:keys! [::socket ::state/connection-state]} @client-ref-        frame (streams/read-frame!! (Socket/.getInputStream socket))]-    (decode-packet! frame connection-state)))--; TODO handle errors here as well to disconnect client?-(defn send-packet! [client-ref packet]-  (let [{:keys! [::socket ::state/connection-state]} @client-ref]-    #_(println "sending packet out: " packet)-    (streams/write-frame!! (encode-packet! packet connection-state) (Socket/.getOutputStream socket))-    client))+        ::connection/connection (connection/connection socket)}))  (defn leave! [client-ref]-  (let [{:keys! [::socket] :keys [::playing? ::entity/uuid]} @client-ref]+  (let [{:keys! [::connection/connection] :keys [::playing? ::entity/uuid]} @client-ref]     (try       (dosync         (when playing?@@ -37,4 +21,4 @@           ; Probably should come up with a better solution           (commute server/players dissoc uuid)))       (finally-        (Socket/.close socket)))))+        (send connection connection/close!)))))

src/net/bowmap/network/connection.clj added (view file)

@@ -0,0 +1,30 @@+(ns net.bowmap.network.connection+  (:require+   [net.bowmap.network.packets.clientbound :refer [encode-packet!]]+   [net.bowmap.network.streams :as streams])+  (:import+   [java.net Socket]))++; Connection can be sent and received frames?+; It is the I/O part of the client+; Clients are then I/O free++(defn connection [socket]+  (agent {::socket socket+          ::input-stream (Socket/.getInputStream socket)+          ::output-stream (Socket/.getOutputStream socket)}))++(defn read-frame! [input-stream]+  (let [{:keys! [::input-stream]} connection]+    (streams/read-frame!! input-stream))+  connection)++(defn send-frame-action! [connection frame]+  (let [{:keys! [::output-stream]} connection]+    (streams/write-frame!! frame output-stream)+    connection))++(defn keep-alive-action! [connection])++(defn close! [connection]+  (Socket/.close (req! connection ::socket)))

src/net/bowmap/network/handle.clj modified (view file)

@@ -1,9 +1,8 @@ (ns net.bowmap.network.handle   (:require-   [clojure.core.async :refer [io-thread]]    [net.bowmap.network.chunk :as chunk]-   [net.bowmap.network.client :refer [send-packet!]]    [net.bowmap.network.client :as client]+   [net.bowmap.network.connection :as connection]    [net.bowmap.network.packet :as pk]    [net.bowmap.network.packets.clientbound :as clientbound]    [net.bowmap.network.packets.registry :as registry]@@ -176,16 +175,9 @@      :keepalive? true}))  (defn handle!! [client-ref packet]-  (let [{:keys! [client packets] :keys [keepalive?]} (handle-packet @client-ref packet)]-    (dosync-      (ref-set client-ref client))-    (doseq [pk packets]-      (send-packet! client-ref pk))-    (when keepalive?-      (io-thread-        (loop []-          (Thread/sleep 20000)-          (client/send-packet! client-ref {::pk/id ::clientbound/keep-alive-                                           :id (long (* (rand) Long/MAX_VALUE))})-          (recur))))-    client))+  (dosync+    (let [{:keys! [client packets] :keys [keepalive?]} (handle-packet @client-ref packet)]+      (ref-set client-ref client)+      (let [{:keys! [::connection/connection ::state/connection-state]} @client-ref]+        (doseq [pk packets]+          (send connection connection/send-frame-action! (clientbound/encode-packet! pk connection-state)))))))

src/net/bowmap/network/packets/clientbound.clj modified (view file)

@@ -283,6 +283,7 @@ (def packet-ids (build-packet-id-index packet-order)) (def packet-encoders (build-packet-encode-functions packets)) +; TODO update docstring (defn encode-packet!   "Encodes a message into a packet.    Like decoding, encoding a packet requires the connection state, as packet

src/net/bowmap/network/server.clj modified (view file)

@@ -2,20 +2,27 @@   (:require    [clojure.core.async :refer [io-thread]]    [net.bowmap.network.client :as client :refer [leave!]]-   [net.bowmap.network.handle :as handle])+   [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 SocketException]))+   [java.net ServerSocket Socket SocketException]))  (defn start-server! [port]   (let [server (ServerSocket. port)]     (loop []       (let [socket (ServerSocket/.accept server)+            input-stream (Socket/.getInputStream socket)             client-ref (client/client socket)]         (io-thread           (try             (loop []-              (handle/handle!! client-ref (client/read-packet! client-ref))+              (let [frame (streams/read-frame!! input-stream)]+                (dosync+                  (->> (decode-packet! frame (::state/connection-state @client-ref))+                       (handle/handle!! client-ref))))               (recur))             (catch EOFException _)             (catch SocketException _)