f4c568a

Branches > main > f4c568a > files / src / net / bowmap

Add identifier, array, and NBT readers and writers

Grahp, 2026-02-28

Commit: f4c568adf16fa03d6b722ffb2f8bdb8bf61f79db
Parent: 61487ea

2 changed files:

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

@@ -77,6 +77,7 @@    :varint read-varint!    :string read-string!    :uuid   read-uuid!+   :identifer read-string!    :table (fn [table]             (let [table (->> (s/conform ::t/table table)                              (mapv #(assoc % :codec (reader (:type %)))))]

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

@@ -29,12 +29,13 @@ (defn write-json! [buf s]   (write-string! buf (json/write-str s))) -(defn write-list! [buf codec xs]-  ;; Reduce :p+(defn write-array! [buf codec xs]   (reduce (fn [acc x]             (codec acc x))-          (write-varint! buf (count xs))-          xs))+          buf xs))++(defn write-list! [buf codec xs]+  (write-array! (write-varint! buf (count xs)) codec xs))  (defn write-uuid! [buf uuid]   (-> buf@@ -75,6 +76,104 @@   (write-boolean! buf x)   (write-context-optional! buf codec x)) +;;; NBT++(defn write-modified-utf8! [buf s]+  (reduce (fn [acc code]+            (cond (zero? code) (-> buf+                                   (ByteBuf/.writeByte 0xC0)+                                   (ByteBuf/.writeByte 0x80))+                  (<= 1 code 0x7F) (ByteBuf/.writeByte acc code)+                  (<= 0x80 code 0x7FF) (-> acc+                                             (ByteBuf/.writeByte (bit-or 0xC0 (bit-shift-right code 6)))+                                             (ByteBuf/.writeByte (bit-or 0x80 (bit-and code 0x3F))))+                  :else (-> acc+                              (ByteBuf/.writeByte (bit-or 0xE0 (bit-shift-right code 12)))+                              (ByteBuf/.writeByte (bit-or 0x80 (bit-and (bit-shift-right code 6) 0x3F)))+                              (ByteBuf/.writeByte (bit-or 0x80 (bit-and code 0x3F))))))+          buf+          (map int (String/.toCharArray s))))++(defn write-nbt-string! [buf s]+  (let [s (name s)]+    (-> buf+        (ByteBuf/.writeShort (count s))+        (write-modified-utf8! s))))++(defn nbt-type [x]+  (cond (string? x) :string+        (ident? x) :string+        (boolean? x) :boolean++        ;; Right now all numbers are encoded as longs since the client allows it, but that's obviously quite wasteful+        (int? x) :long+        (float? x) (if (= (float x) x)+                     :float+                     :double)+        (map? x) :compound+        ;; I believe you can also typed arrays for efficiency as well? I think they might store differently on the client.+        (seqable? x) :list+        :else (raise "Failed to encode NBT value!" {:val x})))++(declare nbt-ids nbt-writers)++(defn write-nbt-list! [buf xs]+  ;; Lists are homogenous, so it just looks at the first element and tries to encode all elements with that type+  ;; Should probably check that all the elements actually have the same nbt type lol+  (let [t (nbt-type (first xs))+        id (nbt-ids t)+        encoder (nbt-writers t)]+    (-> buf+        (ByteBuf/.writeByte id)+        (ByteBuf/.writeInt (count xs))+        (write-array! encoder xs))))++(defn >nbt-compound [buf m]+  (-> (reduce (fn [acc [k v]]+                (let [t (nbt-type v)]+                  (-> acc+                      (ByteBuf/.writeByte (nbt-ids t))+                      (write-nbt-string! (name k))+                      ((nbt-writers t) v))))+              buf m)+      (ByteBuf/.writeByte 0x00)))++(defn nbt-writer+  "Returns the NBT writer function for value 'x'.+   Throws if 'x' cannot be encoded into NBT."+  [x]+  (nbt-writers (nbt-type x)))++(defn write-nbt! [buf x]+  (-> buf+      (ByteBuf/.writeByte 0x0a)+      ((nbt-writer x) x)))++(def nbt-writers {:byte ByteBuf/.writeByte+                  :boolean write-boolean!+                  :short ByteBuf/.writeShort+                  :int ByteBuf/.writeInt+                  :long ByteBuf/.writeLong+                  :float ByteBuf/.writeFloat+                  :double ByteBuf/.writeDouble+                  :string write-nbt-string!+                  :list write-nbt-list!+                  :compound >nbt-compound})++(def nbt-ids {:byte 1+              :boolean 1+              :short 2+              :int 3+              :long 4+              :float 5+              :double 6+              :string 8+              :list 9+              :compound 10})++;; End NBT lol++ (def writers   "Map of type names to functions.    See https://minecraft.wiki/w/Java_Edition_protocol/Packets#Data_types"@@ -88,6 +187,9 @@    :string write-string!    :json   write-json!    :uuid   write-uuid!+   :identifier write-string!++   :nbt write-nbt!     ;; TODO These are kinda named backwards...    :context-optional (fn [t]