669730f

Bowmap · bowmap.git · Tickets

Branches > main > 669730f > files / src / net / bowmap / server

Add map readers and writers

Grahp, 2026-10-04

Commit: 669730f7bd160dcbf254c24bfaace597b7e147cb
Parent: 431c728

5 changed files:

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

@@ -10,12 +10,12 @@    The order of packet ids determines their numeric ID."   {::phase/status    [::status-response status-response>-    ::pong-response [::p/timestamp long>]]+    ::pong-response (map> ::p/timestamp long>)]    ::phase/login    [::disconnect nil     ::encryption-request nil-    ::login-success [::p/game-profile game-profile>-                     ::p/session-uuid uuid>]+    ::login-success (map> ::p/game-profile game-profile>+                          ::p/session-uuid uuid>)     ::set-compression nil     ::login-plugin-request nil     ::cookie-request nil]@@ -34,8 +34,8 @@     ::transfer nil     ::feature-flags nil     ::update-tags nil-    ::known-packs [::p/known-packs (list> [::p/identifier split-identifier>-                                           ::p/pack-version string>])]+    ::known-packs (map> ::p/known-packs (list> ::p/identifier split-identifier>+                                               ::p/pack-version string>))     ::custom-report-details nil     ::server-links nil     ::clear-dialog nil@@ -185,8 +185,6 @@     ::show-dialog-play nil]})  (def phase->id->numeric-id (update-vals clientbound-packets #(zipmap (take-nth 2 %) (range))))-(def phase->id->writer (update-vals clientbound-packets #(-> (apply hash-map %)-                                                             some-vals-                                                             (update-vals ->writer))))+(def phase->id->writer (update-vals clientbound-packets #(some-vals (apply hash-map %))))  (def ids (set (mapcat #(take-nth 2 %) (vals clientbound-packets))))

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

@@ -27,6 +27,7 @@   (let [buf (frame/->ByteBuf frame)         numeric-id (readers/varint< buf)         id (req! (serverbound/phase->numeric-id->id phase) numeric-id)-        reader (req! (serverbound/phase->id->reader phase) id)]-    (assoc (reader buf)+        reader (id (serverbound/phase->id->reader phase))]+    (assoc (when reader+             (reader buf))            ::packet/id id)))

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

@@ -9,47 +9,36 @@    [net.bowmap.server.protocol.types :as types])   (:import    [io.netty.buffer ByteBuf]-   [java.nio.charset Charset]+   [java.nio.charset StandardCharsets]    [java.util UUID]))  (s/def ::reader (s/fspec                   :args (s/cat :buf #(instance? ByteBuf %))                   :ret any?)) -(defn- read-field-  "Reads a field into map 'm' from 'buf' via 'reader'.-   Throws a useful error message when that reading fails."-  [m field reader buf]+(defn- read-key+  "Reads a map key via the supplied reader, throwing a useful error message+   when reading fails."+  [m k reader buf]   (try-    (assoc-some m field (reader buf))-    (catch Exception t-      (throw (ex-info "Error while reading table field" {:field field-                                                         :reader reader}-                      t)))))+    (assoc-some m k (reader buf))+    (catch Exception e+      (throw (ex-info "Error while reading table field."+                      {:key k+                       :reader reader}+                      e))))) -(defn table<-  "Takes a sequence of alternating keys and readers, e.g.-   [:key reader< :key2 reader<].--   Returns a reader that builds a map where, for each key/reader pair,-   the read value from the buffer and is mapped with the corresponding key.-   Keys are omitted when the value is nil."-  [table]-  (let [t (partition 2 table)]+(defn map<+  "Takes pairs of map keys and readers, and returns a reader reads each value+   via the writers in their defined and maps the value to the corresponding+   key in a map if the value is not nil."+  [& keys-and-readers]+  {:pre [(even? (count keys-and-readers))]}+  (let [pairs (partition 2 keys-and-readers)]     (fn [buf]-      (reduce (fn [acc [field reader]]-                (read-field acc field reader buf))-              {} t))))--(defn ->reader-  "Attempts to coerces 'x' into a reader. Throws if it cannot.-   Vectors with pairs of [:key reader] are turned into tables readers.-   Nil returns an empty table reader."-  [x]-  (cond-    (or (vector? x) (nil? x)) (table< x)-    (fn? x) x-    :else (throw (ex-info "Cannot coerce into reader." {:x x}))))+      (reduce (fn [acc [k reader]]+                (read-key acc k reader buf))+              {} pairs))))  (def byte< ByteBuf/.readByte) (def ubyte< ByteBuf/.readUnsignedByte)@@ -65,7 +54,7 @@          result 0]     (if (>= num-read types/max-varint-size)       (throw (Exception. "VarInt too long"))-      (let [b (ByteBuf/.readByte buf)+      (let [b (byte< buf)             result' (bit-or result (bit-shift-left (bit-and b 0x7F) (* 7 num-read)))]         (if (zero? (bit-and b 0x80))           result'@@ -75,25 +64,26 @@   "Returns a readers that 'length' number of values into a vector from    a buffer via 'reader'."   [reader length]-  (let [reader (->reader reader)]-    (fn [buf]-      (into [] (repeatedly length #(reader buf))))))+  (fn [buf]+    (into [] (repeatedly length #(reader buf)))))  (defn list<   "Returns a reader that reads a varint length, then reads that many values-   into a vector via 'reader' from a buffer."-  [reader]-  (let [reader (->reader reader)]-    (fn [buf]-      (let [length (varint< buf)]-        ((array< reader length) buf)))))+   into a vector via 'reader' from a buffer.+   When supplied 2 or more key value pairs, reads a vector of maps."+  ([reader]+   (fn [buf]+     (let [length (varint< buf)]+       ((array< reader length) buf))))+  ([k v & kvs]+   (list< (apply map< k v kvs))))  (defn string<   [buf]   (let [len (varint< buf)]     (if (neg? len)       (throw (Exception. "String length cannot be negative"))-      (ByteBuf/.readCharSequence buf len (Charset/forName "UTF-8")))))+      (ByteBuf/.readCharSequence buf len StandardCharsets/UTF_8))))  (defn split-identifier<   "Reads an identifier from 2 strings: the namespace and the name."

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

@@ -9,19 +9,19 @@    table that will turn into a reader, or nil for an empty reader.    The order of packet ids determines their numeric ID."   {::phase/handshaking-   [::handshaking [:protocol varint<-                   :address string<-                   :port short<-                   ::phase/phase (enum< varint< {1 ::phase/status 2 ::phase/login 3 ::phase/transfer})]]+   [::handshaking (map< :protocol varint<+                        :address string<+                        :port short<+                        ::phase/phase (enum< varint< {1 ::phase/status 2 ::phase/login 3 ::phase/transfer}))]    ::phase/status-   [::status-request []-    ::ping-request [::p/timestamp long<]]+   [::status-request (map<)+    ::ping-request (map< ::p/timestamp long<)]    ::phase/login-   [::login-start [::p/username string<-                   ::p/uuid uuid<]+   [::login-start (map< ::p/username string<+                        ::p/uuid uuid<)     ::encryption-response nil     ::login-plugin-response nil-    ::login-acknowledged []+    ::login-acknowledged (map<)     ::cookie-response nil]    ::phase/config    [::client-information nil@@ -31,8 +31,8 @@     ::keep-alive nil     ::pong nil     ::resource-pack-response nil-    ::known-packs [::p/known-packs (list< [::p/identifier split-identifier<-                                           ::p/pack-version string<])]+    ::known-packs (map< ::p/known-packs (list< ::p/identifier split-identifier<+                                               ::p/pack-version string<))     ::custom-click-action nil     ::accept-code-of-conduct nil]    ::phase/play@@ -107,7 +107,6 @@     ::custom-click-action nil]})  (def phase->numeric-id->id (update-vals serverbound-packets #(zipmap (range) (take-nth 2 %))))-(def phase->id->reader (update-vals serverbound-packets #(-> (apply hash-map %)-                                                             (update-vals ->reader))))+(def phase->id->reader (update-vals serverbound-packets #(some-vals (apply hash-map %))))  (def ids (set (mapcat #(take-nth 2 %) (vals serverbound-packets))))

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

@@ -18,41 +18,30 @@                   :fn #(= (-> % :args :buf)                           (:ret %)))) -(defn- write-field-  "Writes a field from map 'm' into 'buf' via 'writer'.-   Throws a useful error message when that writing fails."-  [writer buf m field]+(defn- write-key+  "Writes a map key via the supplied writer, throwing a useful error message+   when writing fails."+  [writer buf m k]   (try-    (writer buf (get m field))-    (catch Exception t-      (throw (ex-info "Error while writing table field" {:field field-                                                         field (get m field)-                                                         :writer writer-                                                         :table m}-                      t)))))+    (writer buf (get m k))+    (catch Exception e+      (throw (ex-info "Error while writing map key."+                      {:key k+                       :val (get m k)+                       :writer writer+                       :in-map m}+                      e))))) -(defn table>-  "Takes a sequence of alternating keys and writers, e.g.-   [:key writer> :key2 writer>].--   Returns a writer that takes a map and, for each key/writer pair,-   writes the value associated (or nil) with the key using the corresponding writer."-  [table]-  (let [t (partition 2 table)]-    (fn table-writer [buf m]-      (reduce (fn [acc [field writer]]-                (write-field writer acc m field))-              buf t))))--(defn ->writer-  "Attempts to coerces 'x' into a writer. Throws if it cannot.-   Vectors with pairs of [:key writer] are turned into tables writers.-   Nil is not a valid writer."-  [x]-  (cond-    (vector? x) (table> x)-    (fn? x) x-    :else (throw (ex-info "Cannot coerce into writer." {:x x}))))+(defn map>+  "Takes pairs of map keys and writers, and returns a writer that takes a map+   and writes each key's mapped values (or nil) via its corresponding writer."+  [& keys-and-writers]+  {:pre [(even? (count keys-and-writers))]}+  (let [pairs (partition 2 keys-and-writers)]+    (fn map-writer [buf m]+      (reduce (fn [acc [k writer]]+                (write-key writer acc m k))+              buf pairs))))  (def byte> buffer/write-byte!) @@ -108,18 +97,20 @@ (defn array>   "Returns a writer that writes every value in a coll via 'writer'."   [writer]-  (let [writer (->writer writer)]-    (fn [buf xs]-      (reduce (fn [acc x]-                (writer acc x))-              buf xs))))+  (fn [buf xs]+    (reduce (fn [acc x]+              (writer acc x))+            buf xs)))  (defn list>   "Returns a writer that writes every value in a coll via 'writer', preceded-   the varint length of the list."-  [writer]-  (then> (apply> count varint>)-         (array> writer)))+   by the varint length of the coll.+   When supplied 2 or more key value pairs, writes a collection of maps via map>."+  ([writer]+   (then> (apply> count varint>)+          (array> writer)))+  ([k v & kvs]+   (list> (apply map> k v kvs))))  (def bytes> (list> byte>)) @@ -127,11 +118,11 @@   "Writes a string as a varint byte length followed by its UTF-8 bytes.     When passed 1 arg, returns a string writer that throws when the string-   exceeds 'max-length' characters."+   exceeds the supplied maximum number of UTF-8 bytes."

Show all 124 lines