server.clj

Branches > client-handling-rewrite > 0fa6363 > files / src / net / bowmap / network / message / server.clj

(ns net.bowmap.network.message.server
  (:require
   [clojure.alpha.spec :as s]
   [dev.grahp.build :refer [build]]
   [dev.grahp.error :refer [adorn raise]]
   [net.bowmap.network.message :as msg]
   [net.bowmap.network.packet :as pk]
   [net.bowmap.network.packet.read :refer [->bit-mask ->byte ->double ->enum
                                           ->float ->long ->short ->string
                                           ->table ->ubyte ->uuid ->varint]]
   [net.bowmap.network.state :as state])
  (:import
   [io.netty.buffer ByteBuf]))

; TODO docstring, rename?
(defn build-message-readers [registry]
  (reduce (fn [acc [packet-id table]]
            (assoc acc packet-id
                   (let [reader (->table table)]
                     (fn [buf]
                       (assoc (reader buf)
                              ::msg/id packet-id)))))
          {} registry))

; TODO docstring, rename?
; TODO spec for sure
(defn build-message-id-map [registry]
  (build [[state packet-ids] registry
          [i id] (map-indexed vector packet-ids)]
    [{::state/connection-state state
      ::pk/id i}
     id]))

; TODO document why ids are enumerated like this?
(def ^{:spec (s/map-of (s/schema [::state/connection-state ::pk/id]) ::msg/id)}
  packet-ids
  (build-message-id-map
    {::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
      ::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
      ::set-held-item
      ::program-command-block
      ::program-command-block-minecart
      ::set-creative-mode-slot
      ::program-jigsaw-block
      ::program-structure-block
      ::set-test-block
      ::update-sign
      ::swing-arm
      ::teleport-to-entity
      ::test-instance-block-action
      ::use-item-on
      ::use-item
      ::custom-click-action]}))

(def ^{:spec (s/map-of ::msg/id (s/fspec :args (s/cat :buf #(instance? ByteBuf %))
                                         :ret ::msg/message))}
  packets
  (build-message-readers
    {::handshaking [:protocol ->varint
                    :address ->string
                    :port ->short
                    :intent (->enum ->varint {1 ::state/status 2 ::state/login 3 ::state/transfer})]
     ::status-request []
     ::ping-request [:timestamp ->long]
     ::login-start [:username ->string
                    :uuid ->uuid]
     ::login-acknowledged []
     ;; This packet has more fields I just don't care about them :p
     ::plugin-message []
     ;; This packet also has a bunch of fields that I probably do care about!
     ::client-information []
     ::known-packs []
     ::finish-config []
     ::keep-alive [:id ->long]
     ::client-tick-end []
     ::player-loaded []
     ::player-input [:flags (->bit-mask ->ubyte [:forward :backward :left :right :jump :sneak :sprint])]
     ::set-player-rotation [:yaw ->float
                            :pitch ->float
                            :flags ->byte]
     ::set-player-position [:x ->double
                            :feet-y ->double
                            :z ->double
                            :flags ->byte]
     ::set-player-position-and-rotation [:x ->double
                                         :feet-y ->double
                                         :z ->double
                                         :yaw ->float
                                         :pitch ->float
                                         :flags ->byte]}))

(defn packet->message
  ; TODO more references in docstrings
  "Decodes/deserializes one packet (::net.bowmap.packet) from framed ByteBuf 'buf'.
   The initial length field of the packet should be stripped from the buffer.
   Can throw if:
    1. No such packet with the numeric id and state exists
    2. No schema exists for that packet id
    3. 'buf' is not properly framed
    4. The contents of 'buf' are not a valid packet"
  [packet-buf state]
  (-> (let [numeric-id (->varint packet-buf)
            packet-id (packet-ids {::state/connection-state state ::pk/id numeric-id})
            reader (packets packet-id)]
        (if reader
          (adorn (reader packet-buf)
                 "Error while calling packet reader" {:numeric-id numeric-id :packet-id packet-id :reader reader})
          (raise "Failed to read message, no such message with that id." {:id numeric-id :state state :packet-id packet-id})))
      (adorn "Error while turning serverbound packet into message" {:buf packet-buf :state state})))