client.clj
Branches > namespace-reorganization > e0255cb > files / src / net / bowmap / packet / client.clj
(ns net.bowmap.packet.client
"Clientbound packets"
(:require
[clojure.alpha.spec :as s]
[dev.grahp.build :refer [build]]
[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]))
(defn build-packet-ids [registry]
(build [[state packet-ids] registry
[i id] (map-indexed vector packet-ids)]
[{::state/state state
::pk/id id}
i]))
(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 ;; Uses JSON instead of a text component :p
::encryption-request
::login-success
::set-compression
::login-plugin-request
::login-cookie-request]
::state/config
[::cookie-request
::client-plugin-message
::disconnect
::finish-config
::keep-alive
::ping
::reset-chat
::registry-data
::remove-resource-pack
::add-resource-pack
::store-cookie
::transfer
::feature-flags
::update-tags
::client-known-packs
::custom-report-details
::server-links
::clear-dialog
::show-dialog
::code-of-conduct]}))
(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))))