message_application.clj
Branches > namespace-reorganization > b544284 > 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.chunk :as chunk]
[net.bowmap.network.client :as client]
[net.bowmap.network.message :as msg]
[net.bowmap.network.message.client :as cl]
[net.bowmap.network.message.registry :as registry]
[net.bowmap.network.message.server :as srv]
[net.bowmap.network.state :as state]
[net.bowmap.server.gamemode :as gamemode]))
(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 :)
; TODO
; I do think having these processes/timeouts/whatever be data with a deadline and just run the things by deadline order is better
; Right now they're scheduled and then you don't even know where they are and have no way to access them or associate them with
; a client...
(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})})
(defn- build-chunk-messages []
(let [pk {::msg/id ::cl/chunk-data-and-update-light
:chunk-data
(let [stone {:block-count (* 16 16 16)
:block-states {:bpe 0
:palatte 1}
:biomes {:bpe 0
:palatte 0}}
air {:block-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 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 ::gamemode/creative
: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}]
(into (build-chunk-messages))
(conj {::msg/id ::cl/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 #{}}))
::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))}]})