core.clj

Branches > main > cdcec40 > files / src / net / bowmap / core.clj

(ns net.bowmap.core
  (:require
   [aleph.tcp :as tcp]
   [manifold.stream :as s]
   [net.bowmap.client.state :as state]
   [net.bowmap.packet-handling :as handle]
   [net.bowmap.packet.client :refer [encode-packet]]
   [net.bowmap.packet.read :as read]
   [net.bowmap.packet.server :as sp])
  (:import
   [io.netty.bootstrap ServerBootstrap]
   [io.netty.buffer ByteBuf Unpooled]
   [io.netty.channel ChannelOption]))

(defn handle-packets
  "Hands all (zero or more) unhandled Minecraft protocol packets the client has sent.
   Returns a new client.
   Packets are parsed and handled synchronously as packet decoding can update
    client state, so is impure."
  [client]
  (let [{::keys [buf stream]} client]
    (loop [client client]
      (if (read/can-read-packet? buf)
        (let [pk (sp/decode-packet! buf (::state/state client))
              {:keys [::state/state response]} (handle/handle-packet pk client)
              client (cond-> client state (assoc ::state/state state))]
          (doseq [pk response]
            (s/put! stream (encode-packet pk (::state/state client))))
          (ByteBuf/.discardReadBytes buf)
          (recur client))
        client))))

(defn handle-bytes
  "Takes a client and a byte array of one TCP packet, and handles all
    unhandled Minecraft protocol packets in the client's buffer.
   Returns the new client state, per 'handle-packets'."
  [client bs]
  (try
    (ByteBuf/.writeBytes (::buf client) bs)
    (handle-packets client)
    (catch Throwable e
      (prn "Exception in bytes:" e)
      (.close (::stream client)))))

(defn client-handler [stream _]
  (let [client {::state/state ::state/handshaking
                ::buf (Unpooled/buffer)
                ::stream stream}]
    (s/reduce #'handle-bytes client stream)))

(defn start-server! []
  (tcp/start-server #'client-handler
                    {:port 25565
                     :bootstrap-transform #(ServerBootstrap/.childOption % ChannelOption/TCP_NODELAY true)}))

(defn -main [& _args]
  (start-server!)
  (println "Server Started"))

(comment
  (def server (start-server!))
  (.close server))