server.clj
Branches > client-handling-rewrite > da5bbf0 > 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})))