read.clj
Branches > client-handling-rewrite > 1e827df > files / src / net / bowmap / network / packet / read.clj
(ns net.bowmap.network.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
[dev.grahp.error :refer [adorn ensure-not! raise]]
[dev.grahp.spec :as S]
[net.bowmap.network.packet.types :as t])
(:import
[io.netty.buffer ByteBuf]
[java.nio.charset Charset]
[java.util UUID]))
(defn can-read-packet?
"Returns whether a varint length can be read from buf, and whether buf has enough bytes to read a full packet.
Can throw if varint is too long, meaning it is completely invalid."
[buf]
(loop [num-read 0
result 0]
(if (>= num-read t/max-varint-size)
(raise "VarInt too long")
(when (< num-read (ByteBuf/.readableBytes buf))
(let [b (ByteBuf/.getByte buf (+ (ByteBuf/.readerIndex buf)
num-read))
result' (bit-or result (bit-shift-left (bit-and b 0x7F) (* 7 num-read)))]
(if (zero? (bit-and b 0x80))
(>= (ByteBuf/.readableBytes buf) (+ result' num-read 1))
(recur (inc num-read) result')))))))
(defn ->varint [buf]
(loop [num-read 0
result 0]
(if (>= num-read t/max-varint-size)
(raise "VarInt too long")
(let [b (ByteBuf/.readByte buf)
result' (bit-or result (bit-shift-left (bit-and b 0x7F) (* 7 num-read)))]
(if (zero? (bit-and b 0x80))
result'
(recur (inc num-read) result'))))))
(defn ->string [buf]
(let [len (->varint buf)]
(ensure-not! neg? len)
(ByteBuf/.readCharSequence buf len (Charset/forName "UTF-8"))))
(defn ->uuid [buf]
(UUID. (ByteBuf/.readLong buf) (ByteBuf/.readLong buf)))
#_(defn read-array [codec len buf])
(defn ^:prviate read-bit-mask! [buf codec mask]
(let [n (codec buf)]
(reduce-kv (fn [acc bit x]
(if (bit-test n bit)
(conj acc x)
acc))
#{} mask)))
(def ->byte ByteBuf/.readByte)
(def ->ubyte (comp unchecked-byte ByteBuf/.readByte))
(def ->short ByteBuf/.readShort)
(def ->ushort (comp unchecked-byte ByteBuf/.readShort))
(def ->int ByteBuf/.readInt)
(def ->long ByteBuf/.readLong)
(def ->float ByteBuf/.readFloat)
(def ->double ByteBuf/.readDouble)
(def ->identifer ->string)
(defn ->enum [reader m]
(fn [buf]
(m (reader buf))))
; Table is a keyword?
(defn ->table [table-fields]
; TODO other one too
(let [table (S/ensure-conform ::t/table table-fields)]
(fn [buf]
(reduce (fn [acc {::t/keys [field codec]}]
(adorn (if-some [x (codec buf)]
(assoc acc field x)
acc)
"Error while reading message table field" {:table table :field field :codec codec}))
{} table))))
(defn ->bit-mask [reader mask]
(let [mask (t/mapize-vector-or-something mask)]
(fn [buf]
(read-bit-mask! buf reader mask))))