writers.clj

Bowmap · bowmap.git · Tickets

Branches > main > b368129 > files / src / net / bowmap / server / protocol / writers.clj

(ns net.bowmap.server.protocol.writers
  "Functions for writing values into io.netty.ByteBufs.
   Functions look like type->, where 'type' is a Minecraft protocol type.
   See https://minecraft.wiki/w/Java_Edition_protocol/Packets#Data_types"
  (:require
   [clojure.data.json :as json]
   [net.bowmap.server.protocol.schema :as p])
  (:import
   [io.netty.buffer ByteBuf]
   [java.nio.charset Charset]))

(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-> ByteBuf/.writeFloat)
(def double-> ByteBuf/.writeDouble)

(defn varint-> [buf n]
  (loop [buf buf
         n n]
    (let [b (bit-and n 0x7F)
          nxt (unsigned-bit-shift-right n 7)]
      (if (zero? nxt)
        (ByteBuf/.writeByte buf b)
        (recur (ByteBuf/.writeByte buf (bit-or b 0x80))
               nxt)))))

(defn string-> [buf s]
  {:pre [(string? s)]}
  (doto buf
    (varint-> (count s))
    (ByteBuf/.writeCharSequence s (Charset/forName "UTF-8"))))

(defn json-> [buf s]
  (string-> buf (json/write-str s)))

(defn status-response-> [buf {::p/keys! [version-name protocol]}]
  (json-> buf {:version {:name version-name
                         :protocol protocol}}))

(defn table-> [schema]
  (fn [buf m]
    (reduce (fn [acc [field writer]]
              (writer acc (get m field)))
            buf (partition 2 schema))))

(defn to-writer
  "Attempts to coerces 'writer' into a writer. Throws if it cannot.
   Vectors with pairs of [:key writer] are turned into tables writers.
   Nil is coerced to the empty table writer."
  [writer]
  (cond
    (or (vector? writer) (nil? writer)) (table-> writer)
    (fn? writer) writer
    :else (throw (IllegalArgumentException. (str "Cannot coerce into writer: " (pr-str writer))))))