core.clj
Branches > namespace-reorganization > cbd1421 > 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.codecs.read :as read]
[net.bowmap.packet-handling :as handle]
[net.bowmap.packets.client :refer [encode-packet!]]
[net.bowmap.packets.server :as sp])
(:import
[io.netty.buffer ByteBuf Unpooled]
[io.netty.channel ChannelOption]))
;; TODO disconnect client on exception
(defn handle-bytes
"Handles a byte array of one TCP packet, splits it into Minecraft protocol packets,
sends response packets to the client, and returns a new client.
Packets are parsed and handled synchronously as packet decoding can update
client state, so is impure."
[client bs]
(let [{::keys [buf stream]} client
buf (ByteBuf/.writeBytes buf bs)]
(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))]
(when response
(let [pk (encode-packet! response (::state/state client))]
(s/put! stream pk)))
(ByteBuf/.discardReadBytes buf)
(recur client))
client))))
(defn client-handler [s _]
(let [client {::state/state ::state/handshaking
::buf (Unpooled/buffer)
::stream s}]
(s/reduce #'handle-bytes client s)))
(defn start-server! []
(tcp/start-server #'client-handler
{:port 25565
:bootstrap-transform (fn [bootstrap]
(.childOption bootstrap ChannelOption/TCP_NODELAY true)
bootstrap)}))
(defn -main [& _args]
(start-server!)
(println "Server Started"))
(comment
(def server (start-server!))
(.close server))