88faa97
Branches > main > 88faa97 > files
Use Connection protocol and drop core.async
Commit: 88faa97c4182e48c79d5f243410fc7c94ec88cd0
Parent: 068f95e
4 changed files:
deps.edn view file
@@ -6,6 +6,5 @@ org.clojure/tools.logging {:mvn/version "1.3.1"} nrepl/nrepl {:mvn/version "1.7.0"} dev.weavejester/medley {:mvn/version "1.10.0"}- org.clojure/core.async {:mvn/version "1.9.865"} org.clojure/data.json {:mvn/version "2.5.2"} io.netty/netty-buffer {:mvn/version "4.1.111.Final"}}}src/net/bowmap/server/network/connection.clj view file
@@ -1,46 +1,35 @@ (ns net.bowmap.server.network.connection "A connection to the Minecraft server." (:require- [clojure.core.async :refer [<!! >!! chan close! io-thread]] [clojure.spec.alpha :as s] [net.bowmap.server.network.streams :as streams]) (:import- [java.io IOException] [java.net Socket])) -(s/def ::connection (s/keys :opt [::clientbound-frame-chan ::serverbound-frame-chan]))+(defprotocol Connection+ (send-frame! [conn frame]+ "Sends 'frame' to the connection asynchronously. Returns nil immediately.")+ (receive-frame!! [conn]+ "Blocks until a frame can be read from 'conn'.+ Returns nil when a frame cannot be read."))++(s/def ::connection #(satisfies? Connection %)) (defn connection- "Returns a connection from a given java.net.Socket.- Connection has keys [::clientbound-frame-chan ::serverbound-frame-chan],- which are channels of clientbound and serverbound frames respectively.- The lifecycles of the client socket and both frame channels are coupled."+ "Creates a connection from a given java.net.Socket." [socket]- (let [clientbound-frames (chan 128)- serverbound-frames (chan 128)- input-stream (Socket/.getInputStream socket)+ (let [input-stream (Socket/.getInputStream socket) output-stream (Socket/.getOutputStream socket)] (Socket/.setTcpNoDelay socket true)- (io-thread- (try- (loop []- (when (>!! serverbound-frames (streams/read-frame!! input-stream))- (recur)))- (catch IOException _)- (finally- (close! serverbound-frames)- (close! clientbound-frames)- (Socket/.close socket))))- (io-thread- (try- (loop []- (when-some [frame (<!! clientbound-frames)]- (streams/write-frame!! frame output-stream)- (recur)))- (catch IOException _)- (finally- (close! clientbound-frames)- (close! serverbound-frames)- (Socket/.close socket))))- {::clientbound-frame-chan clientbound-frames- ::serverbound-frame-chan serverbound-frames}))+ (reify Connection+ (send-frame! [_ frame]+ (Thread/startVirtualThread+ #(try (streams/write-frame!! frame output-stream)+ (catch Exception _+ (Socket/.close socket))))+ nil)+ (receive-frame!! [_]+ (try (streams/read-frame!! input-stream)+ (catch Exception _+ (Socket/.close socket)+ nil))))))src/net/bowmap/server/protocol/client.clj view file
@@ -3,4 +3,4 @@ [clojure.spec.alpha :as s] [net.bowmap.server.protocol.phase :as phase])) -(s/def ::client (s/keys :opt [::phase/phase ::clientbound-packet-chan]))+(s/def ::client (s/keys :opt [::phase/phase]))src/net/bowmap/server/runtime/connections.clj view file
@@ -1,44 +1,31 @@ (ns net.bowmap.server.runtime.connections (:require- [clojure.core.async :refer [<!! alts!! chan io-thread put!]] [net.bowmap.server.network.connection :as connection] [net.bowmap.server.network.server :as server]- [net.bowmap.server.protocol.client :as client] [net.bowmap.server.protocol.packet-serialization :as serialize] [net.bowmap.server.protocol.phase :as phase] [net.bowmap.server.runtime.packets :as packets])) -(defn client-step- ([_] {::phase/phase ::phase/handshaking})- ([client in x]- (case in- ::connection/serverbound-frame- (let [[client' packets] (packets/apply-packet client (serialize/decode-frame client x))]- [client' {::client/clientbound-packet-chan packets}])-- ::client/clientbound-packet- [client {::connection/clientbound-frame-chan [(serialize/encode-packet client x)]}])))--(defn handle-connection! [{::connection/keys! [serverbound-frame-chan clientbound-frame-chan]}]- (let [clientbound-packet-chan (chan 1024)]- (io-thread- (loop [client-state (client-step nil)]- (let [[x ch] (alts!! [serverbound-frame-chan clientbound-packet-chan])- in (condp = ch- serverbound-frame-chan ::connection/serverbound-frame- clientbound-packet-chan ::client/clientbound-packet)]- (when (some? x)- (let [[client' {frames ::connection/clientbound-frame-chan- packets ::client/clientbound-packet-chan}]- (client-step client-state in x)]- (doseq [frame frames]- (put! clientbound-frame-chan frame))- (doseq [packet packets]- (put! clientbound-packet-chan packet))- (recur client'))))))))+(defn handle-connection! [connection]+ (let [client (atom {::phase/phase ::phase/handshaking})+ player (atom {})+ send-packet! (fn [packet]+ (connection/send-frame! connection (serialize/encode-packet @client packet)))]+ (loop []+ (when-some [frame (connection/receive-frame!! connection)]+ (let [c @client+ packet (serialize/decode-frame c frame)+ [client' packets] (packets/apply-packet c packet)]+ (assert (compare-and-set! client c client'))+ (let [[player' packets] (packets/handle-packet @player packet)]+ (reset! player player')+ (run! send-packet! packets))+ (recur)))))) (defn handle-connections! [server] (loop []- (handle-connection! (server/accept-connection!! server))- (recur)))+ (let [connection (server/accept-connection!! server)]+ (Thread/startVirtualThread+ #(handle-connection! connection))+ (recur))))