fe193f3

Bowmap · bowmap.git · Tickets

Branches > main > fe193f3 > files / src / net / bowmap / server / game / handle.clj

Use ByteBufs for packet writing

Grahp, 2026-10-09

Commit: fe193f30327c8c24f80c9454f998733916029f9c
Parent: 3732624

4 changed files:

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

@@ -1,31 +0,0 @@-(ns net.bowmap.server.protocol.buffer-  (:require-   [clojure.spec.alpha :as s]-   [net.bowmap.server.protocol.frame :as frame])-  (:import-   [java.io ByteArrayOutputStream]))--(defprotocol WriteBuffer-  "A single-threaded transiently mutable growable buffer of bytes."-  (write-byte! [buf b] "Returns this buffer after adding one byte to the end.-                        Byte may be signed or unsigned.")-  (->frame [buf] "Returns a frame from this buf.-                  After this call, the buffer is not to be mutated."))--(s/def ::write-buffer #(satisfies? WriteBuffer %))--(defn write-buffer []-  (let [baos (ByteArrayOutputStream.)]-    ^{:type ::write-buffer}-    (reify WriteBuffer-      (write-byte! [buf b]-        (^[int] ByteArrayOutputStream/.write baos (bit-and 0xFF b))-        buf)-      (->frame [_]-        (frame/frame (ByteArrayOutputStream/.toByteArray baos))))))--(defmethod print-method ::write-buffer [buf writer]-  (frame/write! (->frame buf)-                (fn [array length]-                  (.write writer "#WriteBuffer ")-                  (.write writer (pr-str (subvec (vec array) 0 length))))))

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

@@ -2,7 +2,7 @@   (:require    [clojure.spec.alpha :as s])   (:import-   [io.netty.buffer ByteBuf Unpooled]))+   [io.netty.buffer ByteBuf ByteBufUtil Unpooled]))  (defprotocol Frame   "A immutable sequence of bytes representing one framed, serialized Minecraft protocol packet.@@ -31,3 +31,7 @@   (write! frame           (fn [array length]             (ByteBuf/.asReadOnly (Unpooled/wrappedBuffer array 0 length)))))++(defn ByteBuf->frame+  [buf]+  (frame (ByteBufUtil/getBytes buf)))

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

@@ -1,13 +1,14 @@ (ns net.bowmap.server.protocol.packet-serialization   (:require-   [net.bowmap.server.protocol.buffer :as buffer]    [net.bowmap.server.protocol.clientbound-packets :as clientbound]    [net.bowmap.server.protocol.frame :as frame]    [net.bowmap.server.protocol.packet :as packet]    [net.bowmap.server.protocol.phase :as phase]    [net.bowmap.server.protocol.readers :as readers]    [net.bowmap.server.protocol.serverbound-packets :as serverbound]-   [net.bowmap.server.protocol.writers :as writers]))+   [net.bowmap.server.protocol.writers :as writers])+  (:import+   [io.netty.buffer Unpooled]))  (defn encode-packet   "Encodes a packet into a frame.@@ -15,8 +16,8 @@   [{::phase/keys! [phase]} {::packet/keys! [id] :as packet}]   (let [numeric-id (req! (clientbound/phase->id->numeric-id phase) id)         writer (req! (clientbound/phase->id->writer phase) id)]-    (buffer/->frame-      (-> (buffer/write-buffer)+    (frame/ByteBuf->frame+      (-> (Unpooled/buffer)           (writers/varint> numeric-id)           (writer packet))))) 

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

@@ -7,19 +7,19 @@    [clojure.set :as set]    [clojure.spec.alpha :as s]    [net.bowmap.server.protocol.block :as block]-   [net.bowmap.server.protocol.buffer :as buffer]    [net.bowmap.server.protocol.identifier :as identifier]    [net.bowmap.server.protocol.nbt :as nbt]    [net.bowmap.server.protocol.schema :as p])   (:import+   [io.netty.buffer ByteBuf ByteBufOutputStream]    [java.nio.charset StandardCharsets]    [java.util UUID]))  (s/def ::writer (s/fspec-                  :args (s/cat :buf ::buffer/write-buffer+                  :args (s/cat :buf #(instance? ByteBuf %)                                :value any?)-                  :fn #(= (-> % :args :buf)-                          (:ret %))))+                  :fn #(identical? (-> % :args :buf)+                                   (:ret %))))  (defn- write-key   "Writes a map key via the supplied writer, throwing a useful error message@@ -46,15 +46,6 @@                 (write-key writer acc m k))               buf pairs)))) -(def byte> buffer/write-byte!)--(defn- integer> [byte-count]-  (let [range (range (* 8 (dec byte-count)) -1 -8)]-    (fn [buf n]-      (reduce (fn [acc bits]-                (byte> acc (bit-shift-right n bits)))-              buf range))))- (defn- apply>   "Returns a writer that writes (f x) via 'writer'."   [f writer]@@ -64,6 +55,15 @@       (catch Exception e         (throw (ex-info "Failed to call writer with applied function" {:f f :writer writer} e)))))) +(def byte> ByteBuf/.writeByte)+(def ubyte> ByteBuf/.writeByte)+(def short> ByteBuf/.writeShort)+(def ushort> ByteBuf/.writeShort)+(def int> ByteBuf/.writeInt)+(def long> ByteBuf/.writeLong)+(def float> (apply> (comp Float/floatToRawIntBits float) int>))+(def double> (apply> Double/doubleToRawLongBits long>))+ (defn then>   "Returns a writer that writes (f x) to the buffer, then (g x)."   [f g]@@ -72,14 +72,6 @@         (f x)         (g x)))) -(def ubyte> byte>)-(def short> (integer> 2))-(def ushort> short>)-(def int> (integer> 4))-(def long> (integer> 8))-(def float> (apply> (comp Float/floatToRawIntBits float) int>))-(def double> (apply> Double/doubleToRawLongBits long>))- (defn boolean>   "Writes the byte 1 if 'x' is truthy, else the byte 0."   [buf x]@@ -115,7 +107,16 @@   ([k v & kvs]    (list> (apply map> k v kvs)))) -(def bytes> (list> byte>))+(defn- write-string [buf s max-length]+  (let [bytes (String/.getBytes s StandardCharsets/UTF_8)]+    (if (> (count bytes) max-length)+      (throw (ex-info "String exceeds maximum length for writing."+                      {:max-length max-length+                       :actual-count (count bytes)+                       :string s}))+      (-> buf+          (varint> (count bytes))+          (ByteBuf/.writeBytes bytes)))))  (defn string>   "Writes a string as a varint byte length followed by its UTF-8 bytes.@@ -124,11 +125,9 @@    exceeds the supplied maximum number of UTF-8 bytes."   ([max-length]    (fn [buf s]-     (if (> (count s) max-length)-       (throw (ex-info "Cannot encode string, too long." {:max-length max-length :string s}))-       (string> buf s))))+     (write-string buf s max-length)))   ([buf s]-   (bytes> buf (String/.getBytes s StandardCharsets/UTF_8))))+   (write-string buf s ##Inf)))  (defn identifier>   "Writes an identifier as an encoded string."@@ -223,28 +222,6 @@            (bit-shift-left (bit-and z 0x3FFFFFF) 12)            (bit-and y 0xFFF)))) -(defn- utf-16-code-units [s]-  (map int (String/.toCharArray s)))--(defn- write-modified-utf8-byte! [buf code-unit]-  (cond (zero? code-unit) (-> buf-                              (byte> 0xC0)-                              (byte> 0x80))-        (<= 1 code-unit 0x7F) (byte> buf code-unit)-        (<= 0x80 code-unit 0x7FF) (-> buf-                                      (byte> (bit-or 0xC0 (bit-shift-right code-unit 6)))-                                      (byte> (bit-or 0x80 (bit-and code-unit 0x3F))))-        :else (-> buf-                  (byte> (bit-or 0xE0 (bit-shift-right code-unit 12)))-                  (byte> (bit-or 0x80 (bit-and (bit-shift-right code-unit 6) 0x3F)))-                  (byte> (bit-or 0x80 (bit-and code-unit 0x3F))))))--(defn- write-modified-utf8!-  "Writes Java's modified UTF-8 to a buffer.-   See https://docs.oracle.com/javase/8/docs/api/java/io/DataInput.html#modified-utf-8"-  [buf s]-  (reduce write-modified-utf8-byte! buf (utf-16-code-units s)))- (defn nbt-string>   "Writes an NBT string to buf. May take a string, keyword, or protocol identifier.    Strings are prefixed by an unsigned short length, followed by up to 65,535@@ -253,8 +230,9 @@   (let [s (if (identifier/identifier? s)             (identifier/encode-identifier s)             (name s))]-    (-> (ushort> buf (count s))-        (write-modified-utf8! s))))+    (doto buf+      (-> (ByteBufOutputStream.)+          (ByteBufOutputStream/.writeUTF s)))))  (declare nbt-writer)