handle.clj

Branches > main > 8ee8c07 > files / src / net / bowmap / protocol / handle.clj

(ns net.bowmap.protocol.handle
  (:require
   [net.bowmap.protocol.chunk :as chunk]
   [net.bowmap.protocol.entity :as entity]
   [net.bowmap.protocol.packet :as pk]
   [net.bowmap.protocol.packet.clientbound :as clientbound]
   [net.bowmap.protocol.packet.registry :as registry]
   [net.bowmap.protocol.packet.serverbound :as serverbound]
   [net.bowmap.protocol.registry :as reg]
   [net.bowmap.server.gamemode :as gamemode]
   [net.bowmap.server.ping :as ping]
   [net.bowmap.server.player :as player]
   [net.bowmap.server.position :as position]
   [net.bowmap.server.server :as server]
   [net.bowmap.stormbow.event :as event]
   [net.bowmap.transport.connection :as connection]))

; Could eventually just be a seq of both packets and events if that would be nicer to read and write.
(defmulti interpret-packet
  "Interprets one serverbound packet, returns a map of [:packets :events].
   Safe to call within a transaction, but may update refs."
  (fn [packet]
    (::pk/id packet)))

(defmethod interpret-packet :default [_]
  {})

(defmethod interpret-packet ::serverbound/status-request [_]
  (let [{::ping/keys! [player-count max-players motd]} (ping/ping-server)]
    {:packets [{::pk/id ::clientbound/status-response
                :response {:version {:name "26.2"
                                     :protocol 776}
                           :players {:online player-count
                                     :max max-players}
                           :description motd
                           :enforcesSecureChat false}}]}))

(defmethod interpret-packet ::serverbound/ping-request [pk]
  {:packets [{::pk/id ::clientbound/pong-response
              :timestamp (:timestamp pk)}]})

(defmethod interpret-packet ::serverbound/login-start [{:keys! [uuid username]}]
  {:events [{::event/id ::event/player-joining
             ::player/uuid uuid
             ::player/username username}]
   :packets [{::pk/id ::clientbound/login-success
              :profile {:uuid uuid
                        :username username
                        :properties []}
              :session-id (random-uuid)}]})

(defmethod interpret-packet ::serverbound/login-acknowledged [_]
  {:packets [{::pk/id ::clientbound/known-packs
              ;; TODO also probably good to unbundle this
              :known-packs [{:namespace "minecraft"
                             :id "core"
                             :version "26.2"}]}]})

(defmethod interpret-packet ::serverbound/known-packs [_]
  {:packets (conj (registry/registry-and-tag-packets)
                  {::pk/id ::clientbound/finish-config})})

; May want this "world" elsewhere, or have some programmatic code to generate it, or implement anvil and go from there?
(defn- build-chunk-packets []
  (let [pk {::pk/id ::clientbound/chunk-data-and-update-light
            :chunk-data
            (let [stone {:block-count (* 16 16 16)
                         :fluid-count 0
                         :block-states {:bpe 0
                                        :palatte 1}
                         :biomes {:bpe 0
                                  :palatte 0}}
                  air {:block-count 0
                       :fluid-count 0
                       :block-states {:bpe 0
                                      :palatte 0}
                       :biomes {:bpe 0
                                :palatte 0}}]
              {:data (cons stone (repeat (- chunk/world-height-chunk-sections 1) air))})
            :light-data {}}]
    (for [x (range -16 17)
          z (range -16 17)]
      (assoc pk
             :chunk-x x
             :chunk-z z))))

(defmethod interpret-packet ::serverbound/finish-config [{:keys! [::player/uuid ::connection/connection]}]
  (let [entity-id (entity/next-entity-id)]
    {:events [{::event/id ::event/player-joined
               ::player/uuid uuid
               ::player/player {::player/uuid uuid
                                ::connection/connection connection
                                ::entity/id entity-id}}]
     :packets (-> [{::pk/id ::clientbound/login
                    :entity-id entity-id
                    :hardcore? false
                    :dimension-names [reg/world-dimension]
                    :max-players server/max-player-count
                    :view-distance 32
                    :simulation-distance 32
                    :reduced-debug-info? false
                    :enable-respawn-screen? false
                    :do-limited-crafting? false
                    :dimension-type-id reg/world-dimension-id
                    :dimension-name reg/world-dimension
                    :hashed-seed 203894
                    :gamemode ::gamemode/adventure
                    :debug-world? false
                    :superflat-world? true
                    :has-death-location? false
                    :portal-cooldown 10
                    :sea-level 60
                    :online-mode? false
                    :enforce-secure-chat? true}
                   {::pk/id ::clientbound/game-event
                    :event :wait-for-chunks}]
                  (into (build-chunk-packets))
                  (conj {::pk/id ::clientbound/synchronize-player-position
                         :teleport-id (rand-int Integer/MAX_VALUE)
                         :x 0
                         :y 16
                         :z 0
                         :velocity-x 0
                         :velocity-y 0
                         :velocity-z 0
                         :yaw 0
                         :pitch 0
                         :flags #{}}))}))

(defn handle-player-update-movement-packet [{:keys! [::player/uuid] :as pk}]
  {:events [{::event/id ::event/updated-player-movement
             ::player/uuid uuid
             ; Flags are currently unused
             ::position/position (select-keys pk [::position/x ::position/y ::position/z ::position/yaw ::position/pitch])}]})

(defmethod interpret-packet ::serverbound/set-player-rotation [pk]
 (handle-player-update-movement-packet pk))

(defmethod interpret-packet ::serverbound/set-player-position-and-rotation [pk]
 (handle-player-update-movement-packet pk))

(defmethod interpret-packet ::serverbound/set-player-position [pk]
 (handle-player-update-movement-packet pk))

(defmethod interpret-packet ::serverbound/set-player-movement-flags [pk]
 (handle-player-update-movement-packet pk))

(defmethod interpret-packet ::serverbound/chat-message [{:keys! [message ::player/uuid]}]
  {:events [{::event/id ::event/player-sent-chat-message
             ::player/uuid uuid
             :message message}]})