packets.clj

Branches > main > 4813198 > 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)))