serverbound.clj

Branches > main > 080b6b8 > files / src / net / bowmap / network / packets / serverbound.clj

(ns net.bowmap.network.packets.serverbound
  (:require
   [net.bowmap.network.packet :as pk]
   [net.bowmap.network.packets.readers
    :refer [->bit-mask ->boolean ->byte ->double ->enum ->float ->identifier
            ->int ->list ->long ->optional ->location ->short ->string ->ubyte
            ->uuid ->varint coerce]]
   [net.bowmap.network.state :as state]))

(def packet-order
  "Map of connection states to vectors of serverbound packet ids in the order the appear in the protocol."
  {::state/handshaking
   [::handshaking]
   ::state/status
   [::status-request
    ::ping-request]
   ::state/login
   [::login-start
    ::encryption-response
    ::login-plugin-response
    ::login-acknowledged
    ::cookie-response]
   ::state/config
   [::client-information
    ::cookie-response
    ::plugin-message
    ::finish-config
    ::keep-alive
    ::pong
    ::resource-pack-response
    ::known-packs
    ::custom-click-action
    ::accept-code-of-conduct]
   ::state/play
   [::confirm-teleportation
    ::attack
    ::query-block-entity-tag
    ::bundle-item-selected
    ::change-difficulty
    ::change-game-mode
    ::acknowledge-message
    ::chat-command
    ::signed-chat-command
    ::chat-message
    ::player-session
    ::chunk-batch-received
    ::client-status
    ::client-tick-end
    ::client-information
    ::command-suggestions-request
    ::acknowledge-configuration
    ::click-container-button
    ::click-container
    ::close-container
    ::change-container-slot-state
    ::cookie-response
    ::plugin-message
    ::debug-subscription-request
    ::edit-book
    ::query-entity-tag
    ::interact
    ::jigsaw-generate
    ::keep-alive
    ::lock-difficulty
    ::set-player-position
    ::set-player-position-and-rotation
    ::set-player-rotation
    ::set-player-movement-flags
    ::move-vehicle
    ::paddle-boat
    ::pick-item-from-block
    ::pick-item-from-entity
    ::ping-request
    ::place-recipe
    ::player-abilities
    ::player-action
    ::player-command
    ::player-input
    ::player-loaded
    ::pong
    ::change-recipe-book-settings
    ::set-seen-recipe
    ::rename-item
    ::resource-pack-response
    ::seen-advancements
    ::select-trade
    ::set-beacon-effect
    ::select-hotbar-slot
    ::program-command-block
    ::program-command-block-minecart
    ::set-creative-mode-slot
    ::set-game-rules
    ::program-jigsaw-block
    ::program-structure-block
    ::set-test-block
    ::update-sign
    ::spectator-action
    ::swing-arm
    ::teleport-to-entity
    ::test-instance-block-action
    ::use-item-on
    ::use-item
    ::custom-click-action]})

(def unused-fields [])

(def packets
  "Map of packet ids to schemas"
  ; Handshaking
  {::handshaking [:protocol ->varint
                  :address ->string
                  :port ->short
                  :intent (->enum ->varint {1 ::state/status 2 ::state/login 3 ::state/transfer})]

   ; Status
   ::status-request []
   ::ping-request [:timestamp ->long]

   ; Login
   ::login-start [:username ->string
                  :uuid ->uuid]
   ::encryption-response [:shared-secret (->list ->byte)]
   ::login-plugin-response [:message ->varint
                            :data (fn [buf] (throw (Exception. "Fuck you")))]
   ::login-acknowledged []
   ::cookie-response [:key ->identifier
                      :payload (->optional (->list ->byte))]

   ; Config

   ::client-information unused-fields
   ::plugin-message unused-fields
   ::finish-config []
   ::pong [:id ->int]
   ::resource-pack-response [:uuid ->uuid
                             :result (->enum ->varint {0 :sucessfully-downloaded
                                                       1 :declined
                                                       2 :failed-to-download
                                                       3 :accepted
                                                       4 :downloaded
                                                       5 :invalid-url
                                                       6 :failed-to-reload
                                                       7 :discarded})]
   ; The wiki is too damn confusing
   ::known-packs unused-fields
   #_::custom-click-action
   #_::accept-code-of-conduct

   ;; This packet has more fields I just don't care about them :p
   ;; This packet also has a bunch of fields that I probably do care about!

   ; Play

   ::confirm-teleportation [:teleport-id ->varint]
   #_::query-block-entity-tag
   #_::bundle-item-selected
   #_::change-difficulty
   #_::change-game-mode
   #_::acknowledge-message
   #_::chat-command
   #_::signed-chat-command
   #_::chat-message
   #_::player-session
   #_::chunk-batch-received
   #_::client-status
   ::client-tick-end []
   #_::command-suggestions-request
   #_::acknowledge-configuration
   #_::click-container-button
   #_::click-container
   ::close-container [:window-id ->varint]
   #_::change-container-slot-state
   #_::debug-subscription-request
   #_::edit-book
   #_::query-entity-tag
   #_::interact
   #_::jigsaw-generate
   ::keep-alive [:id ->long]
   #_::lock-difficulty

   ::set-player-rotation [:yaw ->float
                          :pitch ->float
                          :flags ->byte]
   ::set-player-position-and-rotation [:x ->double
                                       :feet-y ->double
                                       :z ->double
                                       :yaw ->float
                                       :pitch ->float
                                       :flags ->byte]
   ::set-player-position [:x ->double
                          :feet-y ->double
                          :z ->double
                          :flags ->byte]

   ::set-player-movement-flags [:flags (->bit-mask ->byte [:on-ground :pushing-against-wall])]
   #_::move-vehicle
   #_::paddle-boat
   #_::pick-item-from-block
   #_::pick-item-from-entity
   ; TODO is this different?
   #_[::ping-request]
   #_::place-recipe
   ::player-abilities [:flags (->bit-mask ->byte {1 :flying?})] ; is this right?
   ::player-action [:status (->enum ->varint [:started-digging :canceled-digging :finished-digging
                                              :drop-item-stack :drop-item :update-item-state :swap-item-in-hand])
                    ; TODO aren't positions and locations different things?
                    :location ->location
                    ; TODO block face might be something logical
                    :face (->enum ->byte [:bottom :top :north :south :west :east])
                    :sequence ->varint]
   ::player-command [:entity-id ->varint
                     :action-id (->enum ->varint [:leave-bed :start-sprinting :stop-sprinting :start-jumping-with-horse
                                                  :stop-jumping-with-horse :open-vehicle-inventory :start-flying-with-elytra])
                     :jump-boost ->varint]
   ::player-input [:flags (->bit-mask ->ubyte [:forward :backward :left :right :jump :sneak :sprint])]
   ::player-loaded []
   ::change-recipe-book-settings [:id (->enum ->varint [:crafting :furnace :blast-furnace :smoker])
                                  :book-open? ->boolean
                                  :filter-active? ->boolean]
   #_::set-seen-recipe
   #_::rename-item
   #_::seen-advancements
   #_::select-trade
   #_::set-beacon-effect
   ::select-hotbar-slot [:slot "Slots 0-8" ->varint]
   #_::program-command-block
   #_::program-command-block-minecart
   #_::set-creative-mode-slot
   #_::program-jigsaw-block
   #_::program-structure-block
   #_::set-test-block
   #_::update-sign
   ::swing-arm [:hand (->enum ->varint [:main-hand :off-hand])]
   #_::teleport-to-entity
   #_::test-instance-block-action
   #_::use-item-on
   #_::use-item})

; TODO is this called "registry"?
(defn build-packet-decode-functions [registry]
  (reduce (fn [acc [packet-id table]]
            (assoc acc packet-id
                   (let [reader (coerce table)]
                     (fn [buf]
                       (assoc (reader buf)
                              ::pk/id packet-id)))))
          {} registry))

(defn build-packet-id-index
  "Takes a map of connection states to vectors of packet ids and returns a map of [connection-state numeric-id] to packet ids"
  [registry]
  (into {}
        (for [[state packet-ids] registry
                [i id] (map-indexed vector packet-ids)]
               [{::state/connection-state state
                 ::pk/numeric-id i}
                id])))

(def packet-ids (build-packet-id-index packet-order))
(def packet-decoders (build-packet-decode-functions packets))

(defn decode-packet!
  "Decodes a packet from a frame."
  [frame connection-state]
  (let [numeric-id (->varint frame)
        packet-id (req! packet-ids {::state/connection-state connection-state ::pk/numeric-id numeric-id})
        decode-fn (req! packet-decoders packet-id)]
    (decode-fn frame)))