handle.clj

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

(ns net.bowmap.network.handle
  (:require
   [net.bowmap.network.chunk :as chunk]
   [net.bowmap.network.client :refer [send-packet!]]
   [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]
   [net.bowmap.network.registry :as reg]
   [net.bowmap.network.state :as state]
   [net.bowmap.server.entity :as entity]
   [net.bowmap.server.gamemode :as gamemode]
   [net.bowmap.server.server :as server]))

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

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

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

; TODO move to a fact and the game logic handler :)
(defmethod handle-packet ::serverbound/status-request [client _]
  {:client client
   :packets [{::pk/id ::clientbound/status-response
              ;; Should be un-bundled yeah.
              :response {:version {:name "26.2"
                                   :protocol 776}
                         :players {:online (count @server/players)
                                   :max server/max-player-count}
                         ; Is this a text component? Could make that interface
                         :description {:text "Bowmap!"}
                         :enforcesSecureChat false}}]})

(defmethod handle-packet ::serverbound/ping-request [client pk]
  {:client client
   :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 ::entity/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"}]}]})

; TODO how to update state here?
; Yield facts from this as well and send 'em in!
; Or well, this can be retried so just use the dosync
(defmethod handle-packet ::serverbound/known-packs [client _]
  {:client client
   :packets (conj (vec (registry/registry-packets))
                  {::pk/id ::clientbound/update-tags
                   :tagged-registries [{:registry "minecraft:block"
                                        :tags [{:tag-name "bowmap:infiniburn"
                                                :entries []}]}]}

                  {::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 []}]}]}

                  {::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 []}]}]}

                  {::pk/id ::clientbound/finish-config})})


(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 _]
  (let [entity-id (entity/next-entity-id!)
        uuid (req! client ::entity/uuid)
        client (assoc client
                      ::state/connection-state ::state/play
                      ::entity/id entity-id)
        player {::entity/id entity-id
                ::entity/uuid uuid}]
    ; Bad state here, yield a fact instead, but that's later ofc
    (dosync
      (if (@server/players uuid)
        (throw (Exception. "Player tried to join with an existing UUID"))
        (commute server/players assoc uuid player)))
    {:client client
     :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 #{}}))}))

; TODO keepalive

(defn handle!! [client-ref packet]
  (let [{:keys! [client packets]} (handle-packet @client-ref packet)]
    (dosync
      (ref-set client-ref client))
    (doseq [pk packets]
      (send-packet! client-ref pk))
    client))