packets.clj
Branches > main > cb928b0 > files / src / net / bowmap / transport / packets.clj
(ns net.bowmap.transport.packets
(:require
[net.bowmap.protocol.packet :as pk]
[net.bowmap.protocol.packet.clientbound :as clientbound]
[net.bowmap.transport.readers :as readers]
[net.bowmap.protocol.packet.serverbound :as serverbound]
[net.bowmap.transport.writers :as writers]
[net.bowmap.transport.phase :as phase])
(:import
[io.netty.buffer Unpooled]))
; TODO docstrings
(defn build-clientbound-packet-id-index
[registry]
(into {}
(for [[phase packet-ids] registry
[i id] (map-indexed vector packet-ids)]
[{::phase/phase phase
::pk/id id}
i])))
(defn build-packet-writers
[registry]
(reduce (fn [acc [packet-id table]]
(assoc acc packet-id (writers/coerce table)))
{} registry))
(def clientbound-packet-ids (build-clientbound-packet-id-index clientbound/packet-order))
(def packet-encoders (build-packet-writers clientbound/packets))
(defn encode-packet!
"Encodes a packet into a frame.
Requires the connection phase, as packet ids are not unique across phases.
Several packets are logically the same across phases.
Throws when the packet cannot be written."
[packet phase]
(let [payload-writer (req! packet-encoders (::pk/id packet))]
(writers/length-prefixed (-> (Unpooled/buffer)
(writers/varint-> (req! clientbound-packet-ids {::pk/id (::pk/id packet) ::phase/phase phase}))
(payload-writer packet)))))
; Serverbound
(defn build-serverbound-packet-id-index [registry]
(into {}
(for [[phase packet-ids] registry
[i id] (map-indexed vector packet-ids)]
[{::phase/phase phase
::pk/numeric-id i}
id])))
(defn build-packet-readers [registry]
(reduce (fn [acc [packet-id table]]
; TODO coerce isn't a very good name
(assoc acc packet-id (readers/coerce table)))
{} registry))
(def serverbound-packet-ids (build-serverbound-packet-id-index serverbound/packet-order))
(def packet-decoders (build-packet-readers serverbound/packets))
; TODO may want to just make this return only the id when there is no schema
(defn decode-packet!
"Decodes a packet from a frame.
Will throw if no such packet exists with that id.
May return an empty packet if the packet schema is not known/nil."
[frame phase]
(let [numeric-id (readers/->varint frame)
packet-id (req! serverbound-packet-ids {::phase/phase phase ::pk/numeric-id numeric-id})]
(assoc (when-let [decode-fn (packet-decoders packet-id)]
(decode-fn frame))
::pk/id packet-id)))