packet_handling.clj

Branches > client-handling-rewrite > ba43832 > files / src / net / bowmap / packet_handling.clj

(ns net.bowmap.packet-handling
  (:require
   [clojure.core.async :refer [<!! io-thread put! timeout]]
   [net.bowmap.network.client :as c]
   [net.bowmap.network.message :as msg]
   [net.bowmap.network.message.client :as client]
   [net.bowmap.network.message.server :as server]
   [net.bowmap.network.registry :as registry]
   [net.bowmap.network.state :as state]))

; "packet handling" is inherently about the low-level protocol level, so either handle stuff at a low level or turn
; packets into high level game events to be sent somewhere else

; It's per-client, but can send stuff into the player
; And it's stateful!

; Performs a side effect and returns the new client?
; TODO nil?
(defmulti handle-packet
  (fn [msg client]
    (::msg/id msg)))

; Returns the client and performs side effects...
; Should be run single-threaded?
; Hmm...
; TODO nil?
(defn handle-packet!! [client msg]
  (handle-packet msg client))

(defn- send-message! [client msg]
  ;; TODO this is bad ofc.
  ;; Clients are kinda intractable?
  ;; TODO uhh can this be async?
  ;; I feel like this should block, no?
  ;; It can't right now because send/rec are in a single alt...
  (put! (::c/messages-out client) msg))

(defmethod handle-packet :default [msg client]
  (prn "Unhandled message:" msg)
  client)

(defmethod handle-packet ::server/handshaking [msg client]
  (assoc client ::state/connection-state (:intent msg)))

(defmethod handle-packet ::server/status-request [_ client]
  (send-message! client
                  {::msg/id ::client/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}})
  client)

(defmethod handle-packet ::server/ping-request [msg client]
  (send-message! client
                  {::msg/id ::client/pong-response
                   :timestamp (:timestamp msg)})
  client)

(defmethod handle-packet ::server/login-start [msg client]
  (send-message! client
                  {::msg/id ::client/login-success
                   :uuid (:uuid msg)
                   :username (:username msg)
                   :properties []})
  client)

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

(defmethod handle-packet ::server/known-packs [_ client]
  (doseq [msg registry/registry-messages]
    (send-message! client msg))
  (send-message! client
                  {::msg/id ::client/finish-config})
  client)

;; TODO Keepalive!
(defmethod handle-packet ::server/finish-config [_ client]
  (send-message! client
                  {::msg/id ::client/login
                   :entity-id 0 ;; TODO magic zero :p
                   :hardcore? false
                   :dimension-names ["world"]
                   :max-players 1000
                   :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})

  (send-message! client
                  {::msg/id ::client/game-event
                   :event :wait-for-chunks})

  ; TODO when the client disconnects?
  (io-thread
    (loop []
      (<!! (timeout 5000))
      (send-message! client {::msg/id ::client/keep-alive
                             :id (long (* (rand) Long/MAX_VALUE))})
      (recur)))

  (assoc client ::state/connection-state ::state/play))