7014976

Branches > namespace-reorganization > 7014976 > files / src / user.clj

Updates

Grahp, 2026-06-02

Commit: 7014976933fe9b1f558dc202b97f0be1f4582ace
Parent: 0880c86

9 changed files:

Notes.md added (view file)

@@ -0,0 +1,3 @@+Not tickets yet :p++- clean up core main logic until I'm happy with it

src/dev/grahp/error.clj modified (view file)

@@ -40,6 +40,8 @@                  ~m))      x))) +#_(defmacro when! [])+ ;; TODO these have the arguments in a different order than assert! ;; And keywords are assumed to be specs even if you want to ensure a key is present in a map... (defmacro ensure!

src/net/bowmap/core.clj modified (view file)

@@ -9,40 +9,51 @@    [net.bowmap.network.message.server :as server]    [net.bowmap.server :as server-process])) +;; TODO server should just provide messages in/out++(defn read-and-handle-packet+  "Reads/deserializes one packet into a message, and handles it.+   Because packet reading is dependent on the state of the client, and handling a packet can update the state of the client,+   Reading and handling must be synchronized!"+  [pk client]+  (when-some [msg (server/packet->message pk (::state/connection-state client))]+    (handle/handle-packet!! client msg)))++ ; TODO bad positional arguments, use a map? ; TODO only send in things to the server, the rest is stateful-(defn handle-packets-and-messages [client packets-in messages-in messages-out packets-out]+(defn handle-packets-and-messages [client packets-in messages-out packets-out]   (alt!!     messages-out ([msg]-                  ; Make one of these keepalive-                  ; How does keepalive being though?-                  (assert (>!! packets-out (client/message->packet msg (::state/state client))))-                  client)+                  (when msg+                    ; Make one of these keepalive+                    ; How does keepalive begin though?+                    (assert (>!! packets-out (client/message->packet msg (::state/connection-state client))))+                    client))     ; These could be agents around clients with vectors as well instead of immutable maps holding channels     ; Put this onto messages-in after translating?     ; Check messages-out and send to uhh yeah     ;; TODO do you need to send messages out before reading them?-    packets-in ([buf]-                (when buf-                  (when-some [msg (server/packet->message buf (::state/state client))]-                    (handle/handle-packet!! client msg))))))+    packets-in ([pk]+                (when pk+                  (read-and-handle-packet pk client)))))  ; Do I need to close out?+; So clients should be managed by the server and they're not really data are they (defn client-handler [packets-in packets-out]-  (let [messages-out (chan)-        messages-in (chan)-        client {::state/state ::state/handshaking-                ::c/messages-out messages-out}]+  (let [messages-out (chan) ; Use like 1024 so it can block but only when bad things are happening bad things are afoot+        client-state {::state/connection-state ::state/handshaking+                      ; TODO this is used but get rid of it/make it generic and pass it around not a key of player+                      ::c/messages-out messages-out}]     ; Client is not data-    (try (loop [client client]-           (when-some [client (handle-packets-and-messages client packets-in messages-in messages-out packets-out)]-             (recur client)))-         (catch Throwable e-           ; Disconnect/cleanup?-           (println "In client handler: " e)))))+    (loop [client client-state]+      ; TODO this can just... stop the loop+      (when-some [client (handle-packets-and-messages client packets-in messages-out packets-out)]+        (recur client)))))  (defn start-server []   (server-process/start-server! {:handler #'client-handler+                                 :logger-fn prn                                  :port 25565}))  (defn -main [& _args]

src/net/bowmap/network/message/client.clj modified (view file)

@@ -21,7 +21,7 @@ (defn build-packet-ids [registry]   (build [[state packet-ids] registry           [i id] (map-indexed vector packet-ids)]-    [{::state/state state+    [{::state/connection-state state       ::msg/id id}      i])) @@ -30,7 +30,7 @@             (assoc acc packet-id (table-> table)))           {} registry)) -(def ^{:spec (s/map-of (s/schema [::state/state ::msg/id]) ::pk/id)}+(def ^{:spec (s/map-of (s/schema [::state/connection-state ::msg/id]) ::pk/id)}   packet-ids   ; TODO docstrings shouldn't contain these yeag...   ; Update@@ -260,7 +260,7 @@                    :value (or-> float-> 0)]}))  (defn- numeric-id [pk state]-  (assert! (packet-ids {::state/state state ::msg/id (::msg/id pk)})+  (assert! (packet-ids {::state/connection-state state ::msg/id (::msg/id pk)})            {:pk pk :state state}            "No such packet with that id.")) @@ -285,8 +285,6 @@                          (varint-> (numeric-id pk state))                          (encode-body pk))))) -; TODO also update, see sp/decode-packet-; message->packet (defn message->packet   "Encodes packet 'pk' into a ByteBuf of the packet bytes.    Like decoding, encoding a packet also requires the connection state, as

src/net/bowmap/network/message/server.clj modified (view file)

@@ -27,12 +27,12 @@ (defn build-message-id-map [registry]   (build [[state packet-ids] registry           [i id] (map-indexed vector packet-ids)]-    [{::state/state state+    [{::state/connection-state state       ::pk/id i}      id]))  ; TODO document why ids are enumerated like this?-(def ^{:spec (s/map-of (s/schema [::state/state ::pk/id]) ::msg/id)}+(def ^{:spec (s/map-of (s/schema [::state/connection-state ::pk/id]) ::msg/id)}   packet-ids   (build-message-id-map     {::state/handshaking@@ -162,8 +162,8 @@                                          :pitch ->float                                          :flags ->byte]})) -; TODO is this pure/no external effects besides the buf? (defn packet->message+  ; TODO more references in docstrings   "Decodes/deserializes one packet (::net.bowmap.packet) from framed ByteBuf 'buf'.    The initial length field of the packet should be stripped from the buffer.    Can throw if:@@ -173,7 +173,7 @@     4. The contents of 'buf' are not a valid packet"   [packet-buf state]   (-> (let [numeric-id (->varint packet-buf)-            packet-id (packet-ids {::state/state state ::pk/id numeric-id})+            packet-id (packet-ids {::state/connection-state state ::pk/id numeric-id})             reader (packets packet-id)]         (if reader           (adorn (reader packet-buf)

src/net/bowmap/network/registry.clj modified (view file)

@@ -88,7 +88,7 @@     [id])  ;; TODO this shouldn't be here :p-(def registry-packets+(def registry-messages   (for [[id m] (partition 2 registries)]     {::msg/id ::client/registry-data      :registry-id id

src/net/bowmap/network/state.clj modified (view file)

@@ -2,6 +2,7 @@   (:require    [dev.grahp.spec :as S])) -(S/def ::state+; TODO rename to connection state I think+(S/def ::connection-state   "Network connection state, see https://minecraft.wiki/w/Java_Edition_protocol/Packets"   #{::handshaking ::status ::login ::config ::play})

src/net/bowmap/packet_handling.clj modified (view file)

@@ -15,6 +15,7 @@ ; And it's stateful!  ; Performs a side effect and returns the new client?+; TODO nil? (defmulti handle-packet   (fn [msg client]     (::msg/id msg)))@@ -22,6 +23,7 @@ ; Returns the client and performs side effects... ; Should be run single-threaded? ; Hmm...+; TODO nil? (defn handle-packet!! [client msg]   (handle-packet msg client)) @@ -38,7 +40,7 @@   client)  (defmethod handle-packet ::server/handshaking [msg client]-  (assoc client ::state/state (:intent msg)))+  (assoc client ::state/connection-state (:intent msg)))  (defmethod handle-packet ::server/status-request [_ client]   (send-message! client@@ -74,10 +76,10 @@                    :known-packs [{:namespace "minecraft"                                   :id "core"                                   :version "1.12.11"}]})-  (assoc client ::state/state ::state/config))+  (assoc client ::state/connection-state ::state/config))  (defmethod handle-packet ::server/known-packs [_ client]-  (doseq [msg registry/registry-packets]+  (doseq [msg registry/registry-messages]     (send-message! client msg))   (send-message! client                   {::msg/id ::client/finish-config})@@ -120,4 +122,4 @@                              :id (long (* (rand) Long/MAX_VALUE))})       (recur))) -  (assoc client ::state/state ::state/play))+  (assoc client ::state/connection-state ::state/play))

src/net/bowmap/server.clj modified (view file)

@@ -1,8 +1,11 @@ (ns net.bowmap.server   (:require    [aleph.tcp :as tcp]+   [clojure.alpha.spec :as spec]    [clojure.core.async :refer [chan io-thread]]+   [dev.grahp.spec :as S]    [manifold.stream :as s]+   [net.bowmap.network.packet :as pk]    [net.bowmap.network.packet.read :as read :refer [->varint]])   (:import    [io.netty.bootstrap ServerBootstrap]@@ -27,18 +30,27 @@              (recur res'))            res)))))) -(defn- handle-client [handler stream info]-  (let [in (chan 1 packet-framer-xf)-        out (chan)]-    (s/connect stream in)-    (s/connect out stream)-    (io-thread (handler in out))))+(defn logging-handler [handler logger-fn]+  (fn [in out]+    (try+      (handler in out)+      (catch Throwable t+        (logger-fn t)))))++(defn- handle-client [handler logger-fn stream info]+  (let [packets-in (chan 1 packet-framer-xf)+        packets-out (chan)+        handler (logging-handler handler logger-fn)]+    (s/connect stream packets-in)+    (s/connect packets-out stream)+    (io-thread (handler packets-in packets-out))))  ; Could return a channel of clients?+; Make sure this is just kept an interface, it really only needs to know about packets-in, packets-out, and then maybe how to close/disconnect? (defn start-server!   "Starts a server at the port, calling 'handler' with an 'in' and 'out' channel of packets."-  [& {:keys [handler port]}]+  [& {:keys [handler port logger-fn]}]   (tcp/start-server (fn [stream info]-                      (handle-client handler stream info))+                      (handle-client handler logger-fn stream info))                     {:port port                      :bootstrap-transform #(ServerBootstrap/.childOption % ChannelOption/TCP_NODELAY true)}))