083e2e7
Branches > main > 083e2e7 > files / src / net / bowmap
Use a ref for client state
Commit: 083e2e72d5aec9c4f2e40f4387f9365685e914d4
Parent: 108a097
3 changed files:
src/net/bowmap/network/client.clj view file
@@ -1,32 +1,23 @@ (ns net.bowmap.network.client (:require [net.bowmap.network.packets.clientbound :refer [encode-packet!]]+ [net.bowmap.network.packets.serverbound :refer [decode-packet!]] [net.bowmap.network.state :as state]- [net.bowmap.network.streams :as streams]- [net.bowmap.server.entity :as entity]- [net.bowmap.server.server :as server])+ [net.bowmap.network.streams :as streams]) (:import [java.net Socket])) -(defn send-clientbound-packet [client packet]- #_(println "sending packet out: " packet)- (streams/write-frame!! (encode-packet! packet (req! client ::state/connection-state))- (Socket/.getOutputStream (req! client ::socket)))- client)--(defn leave! [client-agent]- (try- (dosync- (when-let [uuid (::entity/uuid @client-agent)]- (commute server/players dissoc uuid)))- (finally- (Socket/.close (req! @client-agent ::socket)))))- (defn client [socket]- (agent {::socket socket- ::state/connection-state ::state/handshaking}- :error-handler (fn [a ex]- (try- (Throwable/.printStackTrace ex *err*)- (finally- (leave! a))))))+ (ref {::state/connection-state ::state/handshaking+ ::socket socket}))++(defn read-packet! [client-ref]+ (let [{:keys! [::socket ::state/connection-state]} @client-ref+ frame (streams/read-frame!! (Socket/.getInputStream socket))]+ (decode-packet! frame connection-state)))++(defn send-packet! [client-ref packet]+ (let [{:keys! [::socket ::state/connection-state]} @client-ref]+ #_(println "sending packet out: " packet)+ (streams/write-frame!! (encode-packet! packet connection-state) (Socket/.getOutputStream socket))+ client))src/net/bowmap/network/handle.clj view file
@@ -1,187 +1,42 @@ (ns net.bowmap.network.handle (:require- [net.bowmap.network.chunk :as chunk]- [net.bowmap.network.client :as client]+ [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 :refer [decode-packet!]]- [net.bowmap.network.registry :as reg]+ [net.bowmap.network.packets.serverbound :as serverbound] [net.bowmap.network.state :as state]- [net.bowmap.server.entity :as entity]- [net.bowmap.server.gamemode :as gamemode] [net.bowmap.server.server :as server])) -; Handles incoming packets from clients--(defmulti handler+(defmulti handle-packet (fn [client packet] (::pk/id packet))) -(defmethod handler :default [client pk]- #_(prn "Unhandled packet:" pk)- client)+(defmethod handle-packet :default [client _]+ {:client client}) -(defmethod handler ::serverbound/handshaking [client pk]- (assoc client- ::state/connection-state (req! pk :intent)- :protocol-id (req! pk :protocol)))+(defmethod handle-packet ::serverbound/handshaking [client pk]+ (let [{:keys! [intent protocol]} pk]+ {:client (assoc client+ ::state/connection-state intent+ :protocol-id protocol)})) ; TODO move to a fact and the game logic handler :)-(defmethod handler ::serverbound/status-request [client _]- (client/send-clientbound-packet client- {::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/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 handler ::serverbound/ping-request [client pk]- (client/send-clientbound-packet- client- {::pk/id ::clientbound/pong-response- :timestamp (:timestamp pk)}))--(defmethod handler ::serverbound/login-start [client pk]- (let [{:keys! [uuid username]} pk]- (client/send-clientbound-packet- (assoc client ::entity/uuid uuid)- {::pk/id ::clientbound/login-success- :profile {:uuid uuid- :username username- :properties []}- :session-id (random-uuid)})))--(defmethod handler ::serverbound/login-acknowledged [client _]- (client/send-clientbound-packet- (assoc client ::state/connection-state ::state/config)- {::pk/id ::clientbound/known-packs- ;; TODO also probably good to unbundle this- :known-packs [{:namespace "minecraft"- :id "core"- :version "26.2"}]}))--(defmethod handler ::serverbound/known-packs [client _]- (doseq [pk (registry/registry-packets)]- (client/send-clientbound-packet client pk))-- ; TODO ofc move these to the registry :p- (client/send-clientbound-packet client {::pk/id ::clientbound/update-tags- :tagged-registries [{:registry "minecraft:block"- :tags [{:tag-name "bowmap:infiniburn"- :entries []}]}]})- (client/send-clientbound-packet client {::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 []}]}]})- (client/send-clientbound-packet client {::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 []}]}]})--- (client/send-clientbound-packet client {::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 handler ::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}]- (client/send-clientbound-packet client- {::pk/id ::clientbound/login- :entity-id entity-id- :hardcore? false- ; TODO use the registry id, show that connection somehow because this is a reference to that id- :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})-+(defn handle!! [client-ref packet]+ (let [{:keys [client packets]} (handle-packet @client-ref packet)] (dosync- (if (@server/players uuid)- (throw (Exception. "Player tried to join with an existing UUID"))- (commute server/players assoc uuid player)))-- (client/send-clientbound-packet client- {::pk/id ::clientbound/game-event- :event :wait-for-chunks})-- ; TODO can't enqueue keepalive because I have no access to the queue!-- (doseq [pk (build-chunk-packets)]- (client/send-clientbound-packet client pk))- (client/send-clientbound-packet client- {::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 process-serverbound-frame [client frame]- (let [packet (decode-packet! frame (req! client ::state/connection-state))]- ; TODO make handler return packets and send them out here- (handler client packet)))+ (ref-set client-ref client))+ (doseq [pk packets]+ (send-packet! client-ref pk))+ client))src/net/bowmap/network/server.clj view file
@@ -1,9 +1,8 @@ (ns net.bowmap.network.server (:require [clojure.core.async :refer [io-thread]]- [net.bowmap.network.client :as client :refer [leave!]]- [net.bowmap.network.handle :as handle]- [net.bowmap.network.streams :as streams])+ [net.bowmap.network.client :as client]+ [net.bowmap.network.handle :as handle]) (:import [java.io EOFException] [java.net ServerSocket Socket SocketException]))@@ -12,17 +11,15 @@ (let [server (ServerSocket. port)] (loop [] (let [socket (ServerSocket/.accept server)- input-stream (Socket/.getInputStream socket)- client-agent (client/client socket)]+ client-ref (client/client socket)] (io-thread (try (loop [] ; TODO this is another way the client can leave!- (let [frame (streams/read-frame!! input-stream)]- (send client-agent handle/process-serverbound-frame frame))+ (handle/handle!! client-ref (client/read-packet! client-ref)) (recur)) (catch EOFException _) (catch SocketException _) (finally- (leave! client-agent)))))+ (Socket/.close socket))))) (recur))))