b6f2926

Branches > main > b6f2926 > files

Rename connection state to phase and move to transport layer

Grahp, 2026-08-24

Commit: b6f2926e34f07e7601a965a1f3d3ab360bc3b6f3
Parent: 8adfb52

6 changed files:

src/net/bowmap/protocol/packet/clientbound.clj modified (view file)

@@ -6,7 +6,7 @@             light-data-> list-> long-> nbt-> optional-> or-> position->             string-> ubyte-> uuid-> varint->]]    [net.bowmap.protocol.registry :as reg]-   [net.bowmap.protocol.state :as state]+   [net.bowmap.transport.phase :as phase]    [net.bowmap.server.gamemode :as gamemode]))  (def packets@@ -85,10 +85,10 @@                                                              :entries (list-> varint->)])])]})  (def packet-order-  {::state/status+  {::phase/status    [::status-response     ::pong-response]-   ::state/login+   ::phase/login    ; TODO do the message ids actually have to be different just because a different encoded format is used for the same logic message?    [::login-disconnect ; Uses JSON instead of a text component :p     ::encryption-request@@ -96,7 +96,7 @@     ::set-compression     ::login-plugin-request     ::cookie-request]-   ::state/config+   ::phase/config    [::cookie-request     ::plugin-message     ::disconnect@@ -117,7 +117,7 @@     ::clear-dialog     ::show-dialog     ::code-of-conduct]-   ::state/play+   ::phase/play    [::bundle-delimiter     ::spawn-entity     ::entity-animation

src/net/bowmap/protocol/packet/serverbound.clj modified (view file)

@@ -4,7 +4,7 @@     :refer [->bit-mask ->boolean ->byte ->double ->enum ->float ->identifier             ->int ->list ->location ->long ->optional ->short ->string ->ubyte             ->uuid ->varint]]-   [net.bowmap.protocol.state :as state]+   [net.bowmap.transport.phase :as phase]    [net.bowmap.server.position :as position]))  (def unused-fields [])@@ -16,7 +16,7 @@                   :address ->string                   :port ->short                   ; TODO move to protocol state when that ns is sorted-                  :intent (->enum ->varint {1 ::state/status 2 ::state/login 3 ::state/transfer})]+                  :intent (->enum ->varint {1 ::phase/status 2 ::phase/login 3 ::phase/transfer})]     ; Status    ::status-request []@@ -92,19 +92,19 @@    ::swing-arm [:hand (->enum ->varint [:main-hand :off-hand])]})  (def packet-order-  "Map of connection states to vectors of serverbound packet ids in the order the appear in the protocol."-  {::state/handshaking+  "Map of connection phases to vectors of serverbound packet ids in the order the appear in the protocol."+  {::phase/handshaking    [::handshaking]-   ::state/status+   ::phase/status    [::status-request     ::ping-request]-   ::state/login+   ::phase/login    [::login-start     ::encryption-response     ::login-plugin-response     ::login-acknowledged     ::cookie-response]-   ::state/config+   ::phase/config    [::client-information     ::cookie-response     ::plugin-message@@ -115,7 +115,7 @@     ::known-packs     ::custom-click-action     ::accept-code-of-conduct]-   ::state/play+   ::phase/play    [::confirm-teleportation     ::attack     ::query-block-entity-tag

src/net/bowmap/protocol/state.clj deleted (view file)

@@ -1,4 +0,0 @@-(ns net.bowmap.protocol.state-  (:require [clojure.spec.alpha :as s]))--(s/def ::connection-state #{::handshaking ::status ::login ::config ::play})

src/net/bowmap/transport/connection.clj modified (view file)

@@ -5,7 +5,7 @@    [net.bowmap.protocol.packet :as pk]    [net.bowmap.protocol.packet.clientbound :as clientbound]    [net.bowmap.protocol.packet.serverbound :as serverbound]-   [net.bowmap.protocol.state :as state]+   [net.bowmap.transport.phase :as phase]    [net.bowmap.server.player :as player]    [net.bowmap.transport.packets :refer [decode-packet! encode-packet!]]    [net.bowmap.transport.streams :as streams])@@ -13,22 +13,14 @@    [java.io EOFException]    [java.net Socket SocketException])) -; TODO should all this decoding of packets be here instead?-; Connection state definitely should, so maybe packet stuff should as well-; But packets are protocol level stuff, so idk.-; The packets themselves are protocol-level, their parsing is network/transport level-; Or whatever I want to call this package.-; I think the barrier should be bytebuf readers and writers should be here, and the packets can use those functions-; Since they don't just describe serialization, but also schema and structure (defn handle-serverbound-frame!   [state frame]-  ; TODO rename to protocol state, connection state is a misleading name-  (let [{::state/keys! [connection-state]} @state-        {::pk/keys! [id] :as packet} (decode-packet! frame connection-state)]+  (let [{::phase/keys! [phase]} @state+        {::pk/keys! [id] :as packet} (decode-packet! frame phase)]     (case id-      ::serverbound/handshaking        (swap! state assoc ::state/connection-state (req! packet :intent))-      ::serverbound/login-acknowledged (swap! state assoc ::state/connection-state ::state/config)-      ::serverbound/finish-config      (swap! state assoc ::state/connection-state ::state/play)+      ::serverbound/handshaking        (swap! state assoc ::phase/phase (req! packet :intent))+      ::serverbound/login-acknowledged (swap! state assoc ::phase/phase ::phase/config)+      ::serverbound/finish-config      (swap! state assoc ::phase/phase ::phase/play)       ::serverbound/login-start       (let [{:keys! [username uuid]} packet]         (swap! state assoc ::player/username username ::player/uuid uuid))@@ -46,7 +38,7 @@         clientbound-packets (chan 64)         input-stream (Socket/.getInputStream socket)         output-stream (Socket/.getOutputStream socket)-        state (atom {::state/connection-state ::state/handshaking})+        state (atom {::phase/phase ::phase/handshaking})         closed-promise (promise)         conn {::close-fn (fn []                            (Socket/.close socket)@@ -60,7 +52,7 @@       (loop []         (when (= ::not-closed (deref (promise) 0 ::not-closed))           (<!! (timeout 15000))-          (when (#{::state/config ::state/play} (::state/connection-state @state))+          (when (#{::phase/config ::phase/play} (::phase/phase @state))             (put! clientbound-packets                   {::pk/id ::clientbound/keep-alive                    :id (rand-int Integer/MAX_VALUE)}))@@ -77,11 +69,12 @@           (close! serverbound-packets)           (deliver closed-promise (@state ::player/uuid))           (Socket/.close socket))))+     (io-thread       (try         (loop []           (when-some [packet (<!! clientbound-packets)]-            (streams/write-frame!! (encode-packet! packet (req! @state ::state/connection-state)) output-stream)+            (streams/write-frame!! (encode-packet! packet (req! @state ::phase/phase)) output-stream)             (recur)))         (finally           (close! clientbound-packets))))

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

@@ -5,7 +5,7 @@    [net.bowmap.transport.readers :as readers]    [net.bowmap.protocol.packet.serverbound :as serverbound]    [net.bowmap.transport.writers :as writers]-   [net.bowmap.protocol.state :as state])+   [net.bowmap.transport.phase :as phase])   (:import    [io.netty.buffer Unpooled])) @@ -13,9 +13,9 @@ (defn build-clientbound-packet-id-index   [registry]   (into {}-        (for [[state packet-ids] registry+        (for [[phase packet-ids] registry               [i id] (map-indexed vector packet-ids)]-          [{::state/connection-state state+          [{::phase/phase phase             ::pk/id id}            i]))) @@ -30,25 +30,23 @@  (defn encode-packet!   "Encodes a packet into a frame.-   Requires the connection state, as packet ids are not unique across connection states.-   Several packets are logically the same across states."-  [packet state]+   Requires the connection phase, as packet ids are not unique across phases.+   Several packets are logically the same across phases."+  [packet phase]   (let [payload-writer (req! packet-encoders (::pk/id packet))]     (writers/length-prefixed (-> (Unpooled/buffer)-                                 (writers/varint-> (req! clientbound-packet-ids {::pk/id (::pk/id packet) ::state/connection-state state}))+                                 (writers/varint-> (req! clientbound-packet-ids {::pk/id (::pk/id packet) ::phase/phase phase}))                                  (payload-writer packet)))))  ; Serverbound -(defn build-serverbound-packet-id-index-  "Takes a map of connection states to vectors of packet ids and returns a map of [connection-state numeric-id] to packet ids"-  [registry]+(defn build-serverbound-packet-id-index [registry]   (into {}-        (for [[state packet-ids] registry-                [i id] (map-indexed vector packet-ids)]-               [{::state/connection-state state-                 ::pk/numeric-id i}-                id])))+        (for [[phase packet-ids] registry+              [i id] (map-indexed vector packet-ids)]+          [{::phase/phase phase+            ::pk/numeric-id i}+           id])))   (defn build-packet-readers [registry]@@ -63,13 +61,10 @@ (def serverbound-packet-ids (build-serverbound-packet-id-index serverbound/packet-order)) (def packet-decoders (build-packet-readers serverbound/packets)) -; So _this_ belongs in transport-; As do the above maps and functions-; But the schema is fine here :) (defn decode-packet!   "Decodes a packet from a frame."-  [frame connection-state]+  [frame phase]   (let [numeric-id (readers/->varint frame)-        packet-id (req! serverbound-packet-ids {::state/connection-state connection-state ::pk/numeric-id numeric-id})+        packet-id (req! serverbound-packet-ids {::phase/phase phase ::pk/numeric-id numeric-id})         decode-fn (req! packet-decoders packet-id)]     (decode-fn frame)))

src/net/bowmap/transport/phase.clj added (view file)

@@ -0,0 +1,5 @@+(ns net.bowmap.transport.phase+  "The phase of connection, also known as a connection state or protocol state in Minecraft."+  (:require [clojure.spec.alpha :as s]))++(s/def ::phase #{::handshaking ::status ::login ::config ::play})