handle.clj

Branches > facts > 242aed7 > files / src / net / bowmap / network / handle.clj

(ns net.bowmap.network.handle
  (:require
   [net.bowmap.network.chunk :as chunk]
   [net.bowmap.network.client :as client]
   [net.bowmap.network.packet :as pk]
   [net.bowmap.network.packets.clientbound :as clientbound]
   [net.bowmap.network.packets.registry :as registry]
   [net.bowmap.network.packets.serverbound :as serverbound :refer [decode-packet!]]
   [net.bowmap.network.state :as state]
   [net.bowmap.server.gamemode :as gamemode]))

; Handles incoming packets from clients

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

(defmethod handler :default [client pk]
  (prn "Unhandled packet:" pk)
  client)

(defmethod handler ::serverbound/handshaking [client pk]
  (assoc client
         ::state/connection-state (req! pk :intent)
         :protocol-id (req! pk :protocol)))

(defmethod handler ::serverbound/status-request [client _]
  (client/send-clientbound-packet client
        {::pk/id ::clientbound/status-response
         ;; Should be un-bundled yeah.
         :response {:version {:name "26.2"
                              :protocol 776}
                    :players {:online 0
                              :max 1000}
                    ; Is this a text component? Could make that interface
                    :description {:text "Bowmap!"}
                    :enforcesSecureChat false}})
  client)

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

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

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

(defmethod handler ::serverbound/known-packs [client _]
  (doseq [pk (registry/registry-packets)]
    (client/send-clientbound-packet client pk))

  (client/send-clientbound-packet client {::pk/id ::clientbound/update-tags
                                          :tagged-registries [{:registry "minecraft:block"
                                                               :tags [{:tag-name "minecraft:infiniburn_overworld"
                                                                       :entries []}]}]})
  (client/send-clientbound-packet client {::pk/id ::clientbound/update-tags
                                          :tagged-registries [{:registry "minecraft:damage_type"
                                                               :tags [{:tag-name "minecraft:is_fire"
                                                                       :entries []}
                                                                      {:tag-name "minecraft:is_explosion"
                                                                       :entries []}
                                                                      {:tag-name "minecraft:bypasses_shield"
                                                                       :entries []}]}]})
  (client/send-clientbound-packet client {::pk/id ::clientbound/update-tags
                                          :tagged-registries [{:registry "minecraft:banner_pattern"
                                                               :tags [{:tag-name "minecraft:pattern_item/bordure_indented" :entries []}
                                                                      {:tag-name "minecraft:pattern_item/creeper" :entries []}
                                                                      {:tag-name "minecraft:pattern_item/field_masoned" :entries []}
                                                                      {:tag-name "minecraft:pattern_item/flow" :entries []}
                                                                      {:tag-name "minecraft:pattern_item/flower" :entries []}
                                                                      {:tag-name "minecraft:pattern_item/globe" :entries []}
                                                                      {:tag-name "minecraft:pattern_item/guster" :entries []}
                                                                      {:tag-name "minecraft:pattern_item/mojang" :entries []}
                                                                      {:tag-name "minecraft:pattern_item/piglin" :entries []}
                                                                      {:tag-name "minecraft:pattern_item/skull" :entries []}]}]})


  (client/send-clientbound-packet client {::pk/id ::clientbound/finish-config})
  client)

(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 handler ::serverbound/finish-config [client _]
  (let [client (assoc client ::state/connection-state ::state/play)]
    (client/send-clientbound-packet client
                                    {::pk/id ::clientbound/login
                                     ;; TODO magic one :p
                                     ;; I think you can keep a registry of entity ids and stick this on the client
                                     :entity-id 1
                                     :hardcore? false
                                     :dimension-names ["world"]
                                     :max-players 1000 ; TODO extract :p
                                     :view-distance 32
                                     :simulation-distance 32
                                     :reduced-debug-info? false
                                     :enable-respawn-screen? false
                                     :do-limited-crafting? false
                                     :dimension-type-id 0
                                     :dimension-name "world"
                                     :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})

    (client/send-clientbound-packet client
                                    {::pk/id ::clientbound/game-event
                                     :event :wait-for-chunks})
    (doseq [pk (build-chunk-packets)]
      (client/send-clientbound-packet client pk))
    (client/send-clientbound-packet client
                                    {::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 #{}})
    client))

; TODO keepalive

(defn process-serverbound-frame [client frame]
  (let [packet (decode-packet! frame (req! client ::state/connection-state))]
    ; TODO make handler return packets and send them out here
    (handler client packet)))