client.clj
Branches > main > d81a5c0 > files / src / net / bowmap / packets / client.clj
(ns net.bowmap.packets.client
"Clientbound packets"
(:require
[clojure.alpha.spec :as s]
[dev.grahp.error :refer [assert!]]
[dev.grahp.pairs :refer [pairs]]
[net.bowmap.client.state :as state]
[net.bowmap.codecs.write :as write :refer [write-varint!]]
[net.bowmap.packet :as pk :refer [packet?]])
(:import
[io.netty.buffer ByteBuf Unpooled]))
;; A lot of work to do in this file lol
;; TODO this also looks identical to the serverbound packet ids...
(defmacro build-packet-ids [registry]
(into {} (mapcat (fn [[state packet-ids]]
(into {} (map-indexed (fn [i pk] [{::state/state state ::pk/id pk} i]))
packet-ids)))
registry))
;; May return nil
;; Should assert or something idk
;; TODO this function looks just like the one in serverbound packets
(defn type->codec [t]
(if (vector? t)
(let [[codec & args] t
writer (write/composite-writers codec)]
(when writer
(apply writer args)))
(write/writers t)))
(defn build-packet [pk-name schema]
(let [fn-name (symbol (str "encode-" (name pk-name)))
;; TODO types can be []
[fields types] (pairs schema)
;; ?????
codecs (map type->codec types)
_ (assert codecs)
name-codec-pairs (for [codec codecs]
[(gensym) codec])
codec-names (map first name-codec-pairs)
bindings (apply concat name-codec-pairs)
pairs? (map vector fields codec-names)
buf (gensym "buf__")
pk (gensym "pk__")]
`(let [~@bindings]
(fn ~fn-name [~buf ~pk]
~@(for [[field codec] pairs?]
`(~codec ~buf (~field ~pk)))
~buf))))
(defmacro build-packets [registry]
(reduce (fn [acc [packet-id schema]]
(assoc acc packet-id (build-packet packet-id schema)))
{} (update-vals registry #(remove string? %))))
;; TODO document why packet ids don't map to numeric ids directly
(def packet-ids
^{:spec (s/map-of (s/schema [::state/state ::pk/id]) ::pk/numeric-id)}
(build-packet-ids
{::state/status
[::status-response
::pong-response]}))
;; Can return nil
(defn numeric-id [state id]
(packet-ids {::state/state state ::pk/id id}))
(def packets
^{:spec (s/map-of ::pk/id (s/fspec :args (s/cat #(instance? ByteBuf %) ::pk/packet)
:ret #(instance? ByteBuf %)))}
(build-packets
{::status-response [:response "Response to ::server/status-request" :json]
::pong-response [:timestamp "The same timestamp as ::server/ping-request" :long]}))
;; TODO can be nil
(defn packet-encoder [pk]
(packets (::pk/id pk)))
(comment
(packets ::status-response))
(defn encode-packet! [buf pk state]
{:pre [(packet? pk)]}
(let [id (assert! (numeric-id state (::pk/id pk)))
encoder (assert! (packet-encoder pk))
data-buf (-> (Unpooled/buffer)
(write-varint! id)
(encoder pk))]
(write-varint! buf (ByteBuf/.writerIndex data-buf))
(ByteBuf/.writeBytes buf data-buf)))
(comment
(.array (encode-packet! (Unpooled/buffer)
{::pk/id ::pong-response
:timestamp 42}
::state/status)))