169f063

Branches > main > 169f063 > files / src / net / bowmap

Docstrings comments and move to bytebuf efficiency

Grahp, 2026-03-13

Commit: 169f06356d14f5b66773410c090800c5d17d2ad6
Parent: e8d0abe

7 changed files:

src/dev/grahp/bytebuf.clj modified (view file)

@@ -1,16 +1,19 @@ (ns dev.grahp.bytebuf   (:import-   [io.netty.buffer ByteBuf Unpooled]+   [io.netty.buffer ByteBuf]    [java.io Writer])) +(defn byte-buf-bytes [buf]+  (let [arr (byte-array (ByteBuf/.readableBytes buf))]+    (ByteBuf/.getBytes buf (ByteBuf/.readerIndex buf) arr)+    (vec arr)))+ (defn print-bb [buf]   (into []         (comp           (map #(bit-and % 0xFF))           (interpose ", "))-        (subvec (vec (.array buf))-                (.readerIndex buf)-                (.writerIndex buf))))+        (byte-buf-bytes buf)))  (defmethod print-method ByteBuf [bb w]   (Writer/.write w (str "#" (Class/.getSimpleName (type bb)) " "))

src/dev/grahp/error.clj modified (view file)

@@ -20,6 +20,14 @@      `(throw (~msg ~m ~cause))      `(throw (ex-info ~msg ~m ~cause))))) +(defmacro adorn+  "Catches any throwables 'body' throws and throws an exception caused by the+   throwable with an additional message and data map."+  [body msg m]+  `(try ~body+        (catch Throwable t#+          (raise ~msg ~m t#))))+ (defmacro assert!   "Throws an exception with an optional map and message when 'x' is falsey and *assert* is truthy,     otherwise, returns 'x'"

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

@@ -3,12 +3,12 @@   (:require    [clojure.alpha.spec :as s]    [dev.grahp.build :refer [build]]-   [dev.grahp.error :refer [assert! raise]]+   [dev.grahp.error :refer [adorn assert!]]    [net.bowmap.client.state :as state]    [net.bowmap.packet :as pk]    [net.bowmap.packet.write :as write])   (:import-   [io.netty.buffer ByteBuf Unpooled]))+   [io.netty.buffer ByteBuf CompositeByteBuf Unpooled]))  (defn build-packet-ids [registry]   (build [[state packet-ids] registry@@ -248,29 +248,33 @@      ::game-event [:event [:enum :byte {3 :set-gamemode 13 :wait-for-chunks}]                    :value [:or :float 0]]})) -(defn numeric-id [state id]-  (packet-ids {::state/state state ::pk/id id}))+(defn- numeric-id [pk state]+  (assert! (packet-ids {::state/state state ::pk/id (::pk/id pk)})+           {:pk pk :state state}+           "No such packet with that id.")) -(defn packet-encoder [pk]-  (packets (::pk/id pk)))+(defn- packet-encoder [pk]+  (assert! (packets (::pk/id pk))+           {:pk pk}+           "No such encoder for that packet.")) -;; Maybe this should just be a function of the packet :p-(defn- encode-packet*+(defn- length-prefixed+  "Returns a composite bytebuf prefixed by the varint length of 'buf'."+  [buf]+  (-> (Unpooled/compositeBuffer 2)+      (CompositeByteBuf/.addComponent true (write/write-varint! (Unpooled/buffer 5) (ByteBuf/.writerIndex buf)))+      (CompositeByteBuf/.addComponent true buf)))++(defn- encoded-packet+  "Returns an encoded packet."   [pk state]-  {:pre [(pk/packet? pk)]}-  (let [id (assert! (numeric-id state (::pk/id pk)) {:pk pk :state state} "No such packet with that id.")-        encoder (assert! (packet-encoder pk) {:pk pk :state state :id id} "No such encoder for that packet!")-        data-buf (-> (Unpooled/buffer)-                     (write/write-varint! id)-                     (encoder pk))]-    ;; This buffer only contains the data + a varint, which can never be larger than 5 bytes-    ;; TODO apparently there's a 'CompositeByteBuf' for this very purpose :p-    (-> (Unpooled/buffer (+ (ByteBuf/.writerIndex data-buf) 5))-        (write/write-varint! (ByteBuf/.writerIndex data-buf))-        (ByteBuf/.writeBytes data-buf))))+  (let [encode-body (packet-encoder pk)]+    (length-prefixed (-> (Unpooled/buffer)+                         (write/write-varint! (numeric-id pk state))+                         (encode-body pk)))))  (defn encode-packet-  "Encodes packet 'pk' into a (new) ByteBuf of the packet bytes.+  "Encodes packet 'pk' into a ByteBuf of the packet bytes.    Like decoding, encoding a packet also requires the connection state, as     packet ids are not unique across connection state, so there isn't a     direct mapping of packet ids to numeric ids.@@ -278,6 +282,5 @@     same across connection states, and so can be sent without needing to switch     on the connection state."   [pk state]-  (try (encode-packet* pk state)-       (catch Throwable e-         (raise "Error while encoding packet:" {:pk pk :state state} e))))+  (adorn (encoded-packet pk state)+         "Error while encoding packet" {:pk pk :state state}))

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

@@ -1,4 +1,7 @@ (ns net.bowmap.packet.read+  "Functions for reading data from serialized packet ByteBufs.+   When given bad data, read functions will throw.+   See https://minecraft.wiki/w/Java_Edition_protocol/Packets#Data_types"   (:require    [clojure.alpha.spec :as s]    [dev.grahp.error :refer [ensure-not! raise]]@@ -55,7 +58,9 @@               acc))           {} codec-fields)) -(defn- mapize-vector-or-something [x]+(defn- mapize-vector-or-something+  "Returns a map of keys to vals, where vectors will map their indecies to their values."+  [x]   {:pre [(or (map? x) (vector? x))]}   (if (vector? x)     (zipmap (range) x)@@ -82,6 +87,8 @@         (raise (str "No such type! " (name t)) {:type t}))))  (def readers+  "Map of type names to read functions.+   See https://minecraft.wiki/w/Java_Edition_protocol/Packets#Data_types"   {:byte   ByteBuf/.readByte    :ubyte  (comp unchecked-byte ByteBuf/.readByte)    :short  ByteBuf/.readShort

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

@@ -142,16 +142,14 @@      ::player-loaded []      ::player-input [:flags [:bit-mask :ubyte [:forward :backward :left :right :jump :sneak :sprint]]]})) -#_"Decodes and returns one packet from ByteBuf 'buf', which should contain at least one readable packet.-   The initial length field of the packet should not be stripped from 'buf'.-   After calling, the buf will contain one less packet.-   Can throw if:-    1. No such packet with the packet id and state exists-    2. 'buf' is not properly framed, and does not contain a full packet-    3. The contents of 'buf' are not a valid packet"- (defn decode-packet-  ;; Pre-framed+  "Decodes/deserializes one packet (::net.bowmap.packet) from framed ByteBuf 'buf'.+   The initial length field of the packet should be stripped from the buffer.+   Can throw if:+    1. No such packet with the numeric id and state exists+    2. No schema exists for that packet id+    3. 'buf' is not properly framed+    4. The contents of 'buf' are not a valid packet"   [buf state]   (let [numeric-id (read/read-varint! buf)         packet-id (packet-ids {::state/state state ::pk/numeric-id numeric-id})

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

@@ -2,6 +2,8 @@   (:require    [clojure.alpha.spec :as s])) +; Tables used by packet schemas+; See read-table! and write-table! (s/def ::table   (s/* (s/cat :field keyword?               :doc (s/? string?)

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

@@ -1,11 +1,12 @@ (ns net.bowmap.packet.write   "Functions to write Minecraft protocol types.+   When given bad data, write functions will throw.    See https://minecraft.wiki/w/Java_Edition_protocol/Packets#Data_types"   (:require    [clojure.alpha.spec :as s]    [clojure.data.json :as json]    [clojure.set :as set]-   [dev.grahp.error :refer [raise]]+   [dev.grahp.error :refer [adorn raise]]    [net.bowmap.packet.types :as t])   (:import    [io.netty.buffer ByteBuf]@@ -43,13 +44,11 @@       (ByteBuf/.writeLong (UUID/.getMostSignificantBits uuid))       (ByteBuf/.writeLong (UUID/.getLeastSignificantBits uuid)))) -;; TODO this is where optionality and nil checking should be, and the packet stuff should use this.+; This is where you could check for optionality I think. If the fields aren't [:optional ...] (defn write-table! [buf codec-fields m]   (reduce (fn [acc {:keys [field codec]}]-            (try-              (codec acc (get m field))-              (catch Throwable e-                (raise "Error while encoding packet field" {:field field :value (get m field)} e))))+            (adorn (codec acc (get m field))+                   "Error while encoding packet field" {:field field :value (get m field)}))           buf codec-fields))  (defn write-boolean! [buf x]@@ -184,7 +183,7 @@         (raise (str "No such type! " (name t)) {:type t}))))  (def writers-  "Map of type names to functions.+  "Map of type names to write functions.    See https://minecraft.wiki/w/Java_Edition_protocol/Packets#Data_types"   {:byte   ByteBuf/.writeByte    :ubyte  ByteBuf/.writeByte