handle.clj

Bowmap · bowmap.git · Tickets

Branches > main > 445a50b > files / src / net / bowmap / server / game / handle.clj

(ns net.bowmap.server.game.handle
  (:require
   [com.wotbrew.relic :as rel]
   [net.bowmap.server.game.player :as player]
   [net.bowmap.server.game.server :as server]
   [net.bowmap.server.network.connection :as connection]
   [net.bowmap.server.packet.clientbound :as clientbound]
   [net.bowmap.server.packet.component :as component]
   [net.bowmap.server.packet.entity :as entity]
   [net.bowmap.server.packet.gamemode :as gamemode]
   [net.bowmap.server.packet.registry :as registry]
   [net.bowmap.server.packet.schema :as p]
   [net.bowmap.server.packet.schema.minecraft :as mc]
   [net.bowmap.server.packet.serverbound :as serverbound]))

(defmulti handle-packet
  "Handles a packet from a player given their connection uuid. Takes the game
   state, and returns a vector of nil or a state value, followed by 0 or more
   response packets."
  (fn [state connection-uuid packet]
    (::p/id packet)))

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

(defmethod handle-packet ::serverbound/status-request [state _ _]
  [nil
   {::p/id ::clientbound/status-response
    ::p/version-name "26.3"
    ::p/version-id 777
    ::p/motd {::component/text "Bowmap!"
              ::component/color "#9f9f9f"}
    ::p/online-player-count (count (state ::server/players))
    ::p/max-player-count server/max-player-count}])

(defmethod handle-packet ::serverbound/ping-request [_ _ {::p/keys! [timestamp]}]
  [nil
   {::p/id ::clientbound/pong-response
    ::p/timestamp timestamp}])

(defmethod handle-packet ::serverbound/login-start [state connection-uuid {::p/keys! [uuid username]}]
  [(rel/transact state
                 [:insert ::server/players
                  {::player/uuid uuid
                   ::player/username username
                   ::connection/connection-uuid connection-uuid}])
   {::p/id ::clientbound/login-success
    ::p/game-profile {::p/uuid uuid
                      ::p/username username
                      ::p/game-profile-properties []}
    ::p/session-uuid connection-uuid}])

(defmethod handle-packet ::serverbound/login-acknowledged [_ _ _]
  [nil
   {::p/id ::clientbound/known-packs
    ::p/known-packs [registry/core-data-pack]}])

(defmethod handle-packet ::serverbound/known-packs [state _ {::p/keys! [known-packs]}]
  (if (= [registry/core-data-pack] known-packs)
    (cons state
          (conj registry/registry-packets
                {::p/id ::clientbound/finish-config}))
    (throw (ex-info "Client does not support the core datapack"
                    {:known-packs known-packs}))))

(defmethod handle-packet ::serverbound/finish-config [state connection-uuid _]
  (let [entity-id (entity/next-entity-id)]
    [(rel/transact state
                   [:update ::server/players
                    {::server/playing? true
                     ::entity/id entity-id}
                    [= ::connection/connection-uuid connection-uuid]])
     {::p/id ::clientbound/login
      ::entity/id entity-id
      :dimensions [registry/world-dimension]
      ::p/max-player-count server/max-player-count
      :render-distance 32
      :simulation-distance 32
      :current-dimension-registry-id (registry/registry-id ::mc/dimension_type registry/world-dimension)
      :current-dimension registry/world-dimension
      :first-8-bytes-of-the-sha-256-hash-of-the-world's-seed 0
      ::gamemode/gamemode ::gamemode/adventure
      :superflat-world? true
      :portal-cooldown-ticks 0
      :sea-level 60
      :enforce-secure-chat? true}
     {::p/id ::clientbound/game-event
      ::p/game-event-id ::p/wait-for-chunk-event}]))