615c075

Branches > main > 615c075 > files

Potentially working networking rewrite idk

Grahp, 2026-07-10

Commit: 615c0754ee264e3aa08d9b4632a37c92f512d892
Parent: a216cd1

4 changed files:

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

@@ -1,13 +1,13 @@ (ns net.bowmap.core   (:require-   [net.bowmap.minecraft-server :as mc]))+   [net.bowmap.network.minecraft-server :as mc]))  (defn -main [& _args]-  (let [shutdown (mc/start-server! {:port 25565})]+  (let [shutdown (mc/start-minecraft-server!)]     (println "Server Started!")     @shutdown))  (comment   (binding [*out* ((requiring-resolve 'clojure.java.io/writer) System/out)]-    (def shutdown (mc/start-server! {:port 25565})))+    (def shutdown (mc/start-minecraft-server!)))   (deliver shutdown :shutdown))

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

@@ -11,20 +11,19 @@    [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/keys :opt [::process-id ::process-timeout]))-(s/def ::processes (s/coll-of ::process :type vector?))-(s/def ::response (s/keys :opt [::client/client ::messages ::processes])) ; And eventually game events as well :)+(s/def ::response (s/keys :opt [::client/client ::messages]))  ; 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))) +; Can also shell out to the server and just send a fact in that'll get handled, asynchronously I guess?+; Because this only deals in messages and new client state returned right now.++; TODO rename :p+; Just return effects instead?+; Handle them at the client level (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)."@@ -146,9 +145,9 @@    ::processes [{::process-id ::keep-alive                  ::process-timeout 20000}]}) -(defmethod apply-process ::keep-alive [process client]-  {::client/client client-   ::processes [process]-   ::messages-   [{::msg/id ::clientbound/keep-alive-     :id (long (* (rand) Long/MAX_VALUE))}]})+#_(defmethod apply-process ::keep-alive [process client]+    {::client/client client+     ::processes [process]+     ::messages+     [{::msg/id ::clientbound/keep-alive+       :id (long (* (rand) Long/MAX_VALUE))}]})

src/net/bowmap/minecraft_server.clj deleted (view file)

@@ -1,89 +0,0 @@-(ns net.bowmap.minecraft-server-  (:require-   [clojure.core.async :refer [<!! >!! alt!! chan close! io-thread put!-                               timeout]]-   [net.bowmap.message-application :as a]-   [net.bowmap.network.client :as client]-   [net.bowmap.network.protocol.message.clientbound :as clientbound]-   [net.bowmap.network.protocol.message.serverbound :as serverbound]-   [net.bowmap.network.protocol.state :as state]-   [net.bowmap.network.server :as server]))--; The logical server, holds a set of clients connected--(defn run-effects! [messages-out process-chan result]-  (let [{::a/keys! [processes messages] ::client/keys! [client]} result]-    (doseq [process processes]-      (io-thread-        (<!! (timeout (::a/process-timeout process)))-        (>!! process-chan process)))-    (doseq [msg messages]-      (put! messages-out msg))-    client))--(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 messages-out process-chan]-  (when-some [msg (serverbound/packet->message pk (::state/connection-state client))]-    (run-effects! messages-out process-chan (a/apply-message msg client))))--(defn handle-process! [process client messages-out process-chan]-  (run-effects! messages-out process-chan (a/apply-process process client)))--; TODO I don't really like all these channels...-(defn handle-packets-and-messages! [client packets-in messages-out packets-out process-chan]-  (alt!!-    process-chan ([process]-                  (handle-process! process client messages-out process-chan))-    messages-out ([msg]-                  (when msg-                    (assert (>!! packets-out (clientbound/message->packet msg (::state/connection-state client))))-                    client))-    packets-in ([pk]-                (when pk-                  (read-and-handle-packet! pk client messages-out process-chan)))))--(defn connect! [client packets-in packets-out]-  (let [messages-out (chan 1024)-        process-chan (chan 64)]-    ; TODO error handling and disconnect-    (io-thread-      (loop [client client]-        (when-some [client (handle-packets-and-messages! client packets-in messages-out packets-out process-chan)]-          ; Otherwise, stop?-          (recur client))))-    nil))--(defn client-  "Returns a new client value"-  []-  {::state/connection-state ::state/handshaking})--(defn closable-promise-  "Returns a promise that closes 'ch' when it is resolved."-  [ch]-  (let [p (promise)]-    (io-thread-      @p-      (close! ch))-    p))--; TODO run all of this through a var so it's reloadable, and clearly so-; TODO this is networking, and so should be in net.bowmap.server.network-(defn start-server!-  "Starts the Minecraft server at the specified port.-   Returns a promise that will deliver when the server closes.-   Delivering the promise will also close the server."-  [{:keys! [port]}]-  (let [clients (server/start-tcp-server! {:port port})-        shutdown (closable-promise clients)]-    ; Loop over the instance?-    (io-thread-      (loop []-        (if-some [{::server/keys! [packets-in packets-out]} (<!! clients)]-          (do (connect! (client) packets-in packets-out)-              (recur))-          (deliver shutdown :shutdown))))-    shutdown))

src/net/bowmap/network/minecraft_server.clj added (view file)

@@ -0,0 +1,63 @@+(ns net.bowmap.network.minecraft-server +  (:require+   [clojure.core.async :refer [<!! >!! alt!! chan close! io-thread put!]]+   [dev.grahp.error :refer [raise]]+   [net.bowmap.message-application :as a]+   [net.bowmap.network.client :as client]+   [net.bowmap.network.protocol.message.clientbound :as clientbound]+   [net.bowmap.network.protocol.message.serverbound :as serverbound]+   [net.bowmap.network.protocol.state :as state]+   [net.bowmap.network.server :as server]))++;;;++(defn handle-incoming-packet [client packet outgoing-messages]+  (let [message (serverbound/packet->message packet (::state/connection-state client))+        {:keys [::client/client ::a/messages]} (a/apply-message message client)]+    (doseq [message messages]+      (put! outgoing-messages message))+    client))++(defn handle-packets!! [client outgoing-messages]+  (let [{::server/keys! [packets-in packets-out]} client]+    (alt!!+      packets-in ([packet]+                  (if (some? packet)+                    (handle-incoming-packet client packet outgoing-messages)+                    (raise "client incoming packet channel closed!")))+      outgoing-messages ([message]+                         (if (some? message)+                           (do (>!! packets-out (clientbound/message->packet message (::state/connection-state client)))+                               client)+                           (raise "client outgoing message channel closed!"))))))++(defn connect! [client]+  (let [outgoing-messages (chan 1024)]+    (io-thread+      (loop [client client]+        (recur (or (handle-packets!! client outgoing-messages) client))))+    nil))++(defn closable-promise+  "Returns a promise that closes 'ch' when it is resolved."+  [ch]+  (let [p (promise)]+    (io-thread+      @p+      (close! ch))+    p))++(defn start-minecraft-server!+  "Starts the Minecraft server.+   Returns a promise that will deliver when the server closes.+   Delivering the promise will also close the server."+  []+  (let [clients (server/start-tcp-server! {:port 25565})+        shutdown (closable-promise clients)]+    (io-thread+      (loop []+        (if-some [client (<!! clients)]+          (do (connect! client)+              (recur))+          (deliver shutdown :shutdown))))+    shutdown))