client.clj

Branches > main > b557414 > files / src / net / bowmap / network / client.clj

(ns net.bowmap.network.client
  (:require
   [net.bowmap.network.packets.clientbound :refer [encode-packet!]]
   [net.bowmap.network.packets.types :as t]
   [net.bowmap.network.state :as state])
  (:import
   [io.netty.buffer ByteBuf Unpooled]
   [java.io
    EOFException
    IOException
    InputStream
    OutputStream]
   [java.net Socket]))

(defn read-byte!!
  "Blocks and returns an unsigned byte from 'input-stream', or throws if EOF is reached"
  [input-stream]
  (let [x (InputStream/.read input-stream)]
    (if (= -1 x)
      (throw (EOFException. "EOF while reading from InputStream"))
      x)))

(defn read-varint!!
  "Blocks and returns a varint from 'input-stream', or throws if one cannot be read"
  [input-stream]
  (loop [num-read 0
         result 0]
    (if (>= num-read t/max-varint-size)
      (throw (IOException. "VarInt too long"))
      (let [b (read-byte!! input-stream)
            result' (bit-or result (bit-shift-left (bit-and b 0x7F) (* 7 num-read)))]
        (if (zero? (bit-and b 0x80))
          result'
          (recur (inc num-read) result'))))))

(defn read-frame!!
  "Reads a frame from 'input-stream', or throws if one cannot be read"
  [input-stream]
  (let [len (read-varint!! input-stream)
        arr (byte-array len)]
    (loop [offset 0]
      (when (< offset len)
        (let [r (InputStream/.read input-stream arr offset (- len offset))]
          (if (= r -1)
            (throw (EOFException. "EOF while reading frame"))
            (recur (+ offset r))))))
    (Unpooled/wrappedBuffer arr)))

(defn write-frame!! [frame output-stream]
  (let [len (ByteBuf/.readableBytes frame)
        arr (byte-array len)]
    (ByteBuf/.readBytes frame arr)
    (OutputStream/.write output-stream arr)
    (OutputStream/.flush output-stream)))

(defn send-clientbound-packet [client packet]
  (println "sending packet out: " packet)
  (write-frame!! (encode-packet! packet (req! client ::state/connection-state))
                 (Socket/.getOutputStream (req! client ::socket)))
  client)

(defn client [socket]
  (agent {::socket socket
          ::state/connection-state ::state/handshaking}
         :error-handler (fn [a ex]
                          (Throwable/.printStackTrace ex *err*)
                          (Socket/.close socket))))