handle.clj

Branches > facts > 9a69ad5 > files / src / net / bowmap / protocol / handle.clj

(ns net.bowmap.protocol.handle
  (:require
   [net.bowmap.protocol.chunk :as chunk]
   [net.bowmap.protocol.client :as client]
   [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.protocol.state :as state]
   [net.bowmap.server.gamemode :as gamemode]
   [net.bowmap.server.player :as player]
   [net.bowmap.server.position :as position]
   [net.bowmap.server.server :as server]
   [net.bowmap.stormbow.fact :as fact]))

(defmulti handle-packet
  (fn [client packet]
    (::pk/id packet)))

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

(defmethod handle-packet ::serverbound/handshaking [client pk]
  (let [{:keys! [intent protocol]} pk]
    {:client (assoc client
                    ::state/connection-state intent
                    :protocol-id protocol)}))

(defmethod handle-packet ::serverbound/status-request [_ _]
  {:facts [{::fact/attribute ::fact/server-list-requested}]})

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

(defmethod handle-packet ::serverbound/login-start [client pk]
  (let [{:keys! [uuid username]} pk]
    {:client (assoc client ::player/uuid uuid)
     :packets [{::pk/id ::clientbound/login-success
                :profile {:uuid uuid
                          :username username
                          :properties []}
                :session-id (random-uuid)}]}))

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

(defmethod handle-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 handle-packet ::serverbound/finish-config [client _]
  ; TODO shit, this is not a pure function!
  ; I'm just going to pretend like this is a pure function for now.
  (let [entity-id (entity/next-entity-id!)
        uuid (req! client ::player/uuid)
        client (assoc client
                      ::state/connection-state ::state/play
                      ::entity/id entity-id
                      ::client/playing? true)
        ; TODO should players have entity ids?
        ; Not sure if that's a player-level concern
        ; Players have associated protocol entities, of course, so this may not be needed
        player {::entity/id entity-id
                ::player/uuid uuid}]
    {:client client
     :facts [{::fact/attribute ::fact/player-joined
              ::fact/entity uuid
              ::fact/value player}]
     :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 #{}}))
     :keep-alive? true}))

; TODO flags are currently unused :p
(defn handle-player-update-movement-packet [{::player/keys! [uuid]}
                                            {:keys [& ::position/x ::position/y ::position/z ::position/yaw ::position/pitch]
                                             :as packet
                                             :select movement}]
  {:facts [(merge {::fact/attribute ::fact/updated-player-movement
                   ::fact/entity uuid
                   ::fact/value movement})]})

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

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

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

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