message_application.clj

Branches > client-handling-rewrite > 0fa6363 > files / src / net / bowmap / message_application.clj

(ns net.bowmap.message-application
  (:require
   [clojure.alpha.spec :as s]
   [dev.grahp.spec :as S]
   [net.bowmap.network.client :as client]
   [net.bowmap.network.message :as msg]
   [net.bowmap.network.message.client :as cl]
   [net.bowmap.network.message.server :as srv]
   [net.bowmap.network.registry :as registry]
   [net.bowmap.network.state :as state]))

(S/def ::messages (s/coll-of ::msg/message :type vector?))
(S/def ::process-id qualified-keyword?)
(S/def ::process-timeout pos-int?)
(S/def ::process (s/schema [::process-id ::process-timeout]))
(S/def ::processes (s/coll-of ::process :type vector?))
(S/def ::response (s/schema [::client/client ::messages ::processes])) ; And eventually game events as well :)

(defmulti apply-process
  (fn [process client]
    (::process-id process)))

(defmulti apply-message
  "'Applies' a message to a client state. Returns the new client state, as well
   as the effects that should occur from this message (see the spec)."
  (fn [msg client]
    (::msg/id msg)))
(s/fdef apply-message :args (s/cat ::msg/message ::client/client) :ret ::response)

(defmethod apply-message :default [msg client]
  (prn "Unhandled message:" msg)
  {::client/client client})

(defmethod apply-message ::srv/handshaking [msg client]
  {::client/client (assoc client ::state/connection-state (:intent msg))})

(defmethod apply-message ::srv/status-request [_ client]
  {::client/client client
   ::messages
   [{::msg/id ::cl/status-response
     ;; Should be un-bundled yeah.
     :response {:version {:name "1.21.11"
                          :protocol 774}
                :players {:online 0
                          :max 1000}
                ; Is this a text component? Could make that interface
                :description {:text "Bowmap!"}
                :enforcesSecureChat false}}]})

(defmethod apply-message ::srv/ping-request [msg client]
  {::client/client client
   ::messages
   [{::msg/id ::cl/pong-response
     :timestamp (:timestamp msg)}]})

(defmethod apply-message ::srv/login-start [msg client]
  {::client/client client
   ::messages
   [{::msg/id ::cl/login-success
     :uuid (:uuid msg)
     :username (:username msg)
     :properties []}]})

(defmethod apply-message ::srv/login-acknowledged [_ client]
  {::client/client (assoc client ::state/connection-state ::state/config)
   ::messages
   [{::msg/id ::cl/known-packs
     ;; TODO also probably good to unbundle this
     :known-packs [{:namespace "minecraft"
                    :id "core"
                    :version "1.12.11"}]}]})

(defmethod apply-message ::srv/known-packs [_ client]
  {::client/client client
   ::messages
   (conj (vec registry/registry-messages)
         {::msg/id ::cl/finish-config})})

(defmethod apply-message ::srv/finish-config [_ client]
  {::client/client (assoc client ::state/connection-state ::state/play)
   ::messages
   [{::msg/id ::cl/login
     ;; TODO magic zero :p
     ;; I think you can keep a registry of entity ids and stick this on the client
     :entity-id 0
     :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 :creative
     :previous-gamemode :unknown
     :debug-world? false
     :superflat-world? true
     :has-death-location? false
     :portal-cooldown 10
     :sea-level 60
     :enforce-secure-chat? true}

    {::msg/id ::cl/game-event
     :event :wait-for-chunks}]
   ::processes [{::process-id ::keep-alive
                 ::process-timeout 20000}]})

(defmethod apply-process ::keep-alive [process client]
  {::client/client client
   ::processes [process]
   ::messages
   [{::msg/id ::cl/keep-alive
     :id (long (* (rand) Long/MAX_VALUE))}]})