fe193f3
Branches > main > fe193f3 > files
Use ByteBufs for packet writing
Commit: fe193f30327c8c24f80c9454f998733916029f9c
Parent: 3732624
4 changed files:
src/net/bowmap/server/protocol/buffer.clj 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 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 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 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)))