client.clj
Branches > main > 8155797 > files / src / net / bowmap / packet / client.clj
(ns net.bowmap.packet.client
"Clientbound packets"
(:require
[clojure.alpha.spec :as s]
[dev.grahp.error :refer [assert!]]
[net.bowmap.client.state :as state]
[net.bowmap.packet :as pk]
[net.bowmap.packet.write :as write])
(:import
[io.netty.buffer ByteBuf Unpooled]))
;; TODO this also looks identical to the serverbound packet ids...
;; It is equally unreadable lmao
(defn 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))
(defn build-packets [registry]
(reduce (fn [acc [packet-id table]]
(assoc acc packet-id (write/writer [:table table])))
{} registry))
(def ^{:spec (s/map-of (s/schema [::state/state ::pk/id]) ::pk/numeric-id)}
packet-ids
"Map of [::state/state ::pk/id] to numeric-id.
Packet ids (::pk/id) could map directly to numeric-ids and be stateless,
but some packets have duplicates across connection states that you'd want to handle in the same way.
If it turns out that I don't end up handling packets in this way, then packet ids could map directly to numeric ids."
(build-packet-ids
{::state/status
[::status-response
::pong-response]
::state/login
[::login-disconnect
::encryption-request
::login-success
::set-compression
::login-plugin-request
::login-cookie-request]}))
(def ^{:spec (s/map-of ::pk/id (s/fspec :args (s/cat :buf #(instance? ByteBuf %) :pk ::pk/packet)
:ret #(instance? ByteBuf %)))}
packets
(build-packets
{::status-response [:response "Response to ::sp/status-request" :json]
::pong-response [:timestamp "The same timestamp as ::sp/ping-request" :long]
::login-success [:uuid :uuid
:username :string
:properties [:list [:table [:name :string
:value :string
:signature [:optional :string]]]]]}))
(defn numeric-id [state id]
(packet-ids {::state/state state ::pk/id id}))
(defn packet-encoder [pk]
(packets (::pk/id pk)))
(defn encode-packet
"Encodes packet 'pk' into a ByteBuf of the packet bytes."
[pk state]
{:pre [(pk/packet? pk)]}
(let [id (assert! (numeric-id state (::pk/id pk)))
encoder (assert! (packet-encoder pk))
data-buf (-> (Unpooled/buffer)
(write/write-varint! id)
(encoder pk))]
;; This buffer only contains the data + a varint, which can never be larger than 5 bytes
(-> (Unpooled/buffer (+ (ByteBuf/.writerIndex data-buf) 5))
(write/write-varint! (ByteBuf/.writerIndex data-buf))
(ByteBuf/.writeBytes data-buf))))