f4c568a
Branches > main > f4c568a > files / src / net / bowmap
Add identifier, array, and NBT readers and writers
Commit: f4c568adf16fa03d6b722ffb2f8bdb8bf61f79db
Parent: 61487ea
2 changed files:
src/net/bowmap/packet/read.clj 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 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]