readers.clj
Branches > main > 0d6765e > 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
[clojure.spec.alpha :as s]
[medley.core :refer [assoc-some]]
[net.bowmap.server.protocol.identifier :as identifier]
[net.bowmap.server.protocol.types :as types])
(:import
[io.netty.buffer ByteBuf]
[java.nio.charset StandardCharsets]
[java.util UUID]))
(s/def ::reader (s/fspec
:args (s/cat :buf #(instance? ByteBuf %))
:ret any?))
(defn- read-key
"Reads a map key via the supplied reader, throwing a useful error message
when reading fails."
[m k reader buf]
(try
(assoc-some m k (reader buf))
(catch Exception e
(throw (ex-info "Error while reading table field."
{:key k
:reader reader}
e)))))
(defn map<
"Takes pairs of map keys and readers, and returns a reader reads each value
via the writers in their defined and maps the value to the corresponding
key in a map if the value is not nil."
[& keys-and-readers]
{:pre [(even? (count keys-and-readers))]}
(let [pairs (partition 2 keys-and-readers)]
(fn [buf]
(reduce (fn [acc [k reader]]
(read-key acc k reader buf))
{} pairs))))
(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 types/max-varint-size)
(throw (Exception. "VarInt too long"))
(let [b (byte< 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 array<
"Returns a readers that 'length' number of values into a vector from
a buffer via 'reader'."
[reader length]
(fn [buf]
(into [] (repeatedly length #(reader buf)))))
(defn list<
"Returns a reader that reads a varint length, then reads that many values
into a vector via 'reader' from a buffer.
When supplied 2 or more key value pairs, reads a vector of maps."
([reader]
(fn [buf]
(let [length (varint< buf)]
((array< reader length) buf))))
([k v & kvs]
(list< (apply map< k v kvs))))
(defn string<
[buf]
(let [len (varint< buf)]
(if (neg? len)
(throw (Exception. "String length cannot be negative"))
(ByteBuf/.readCharSequence buf len StandardCharsets/UTF_8))))
(defn split-identifier<
"Reads an identifier from 2 strings: the namespace and the name."
[buf]
(let [namespace (string< buf)
name (string< buf)]
(identifier/decode-identifier namespace name)))
(defn uuid< [buf]
(UUID. (long< buf) (long< buf)))
(defn enum< [reader m]
(fn [buf]
(m (reader buf))))