readers.clj
Branches > main > 23ec393 > files / src / net / bowmap / server / protocol / readers.clj
(ns net.bowmap.server.protocol.readers
"Functions for reading values from io.netty.ByteBufs.
Buffers are assumed to be properly framed and contain exactly 1 packet.
Functions look like type<, where 'type' is a Minecraft protocol type.
See https://minecraft.wiki/w/Java_Edition_protocol/Packets#Data_types"
(:require
[medley.core :refer [assoc-some]]
[net.bowmap.server.protocol.types :refer [max-varint-size]])
(:import
[io.netty.buffer ByteBuf]
[java.nio.charset Charset]))
(def byte< ByteBuf/.readByte)
(def ubyte< ByteBuf/.readUnsignedByte)
(def short< ByteBuf/.readShort)
(def ushort< ByteBuf/.readUnsignedShort)
(def int< ByteBuf/.readInt)
(def long< ByteBuf/.readLong)
(def float< ByteBuf/.readFloat)
(def double< ByteBuf/.readDouble)
(defn varint< [buf]
(loop [num-read 0
result 0]
(if (>= num-read max-varint-size)
(throw (Exception. "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)]
(if (neg? len)
(throw (Exception. "String length cannot be negative"))
(ByteBuf/.readCharSequence buf len (Charset/forName "UTF-8")))))
(defn enum< [reader m]
(fn [buf]
(m (reader buf))))
(defn table< [schema]
(fn [buf]
(reduce (fn [acc [field reader]]
(assoc-some acc field (reader buf)))
{} (partition 2 schema))))
(defn to-reader
"Attempts to coerces 'reader' into a reader. Throws if it cannot.
Vectors with pairs of [:key reader] are turned into tables readers.
Nil is coerced to the empty table reader."
[reader]
(cond
(or (vector? reader) (nil? reader)) (table< reader)
(fn? reader) reader
:else (throw (IllegalArgumentException. (str "Cannot coerce into reader: " (pr-str reader))))))