Archive for March, 2013|Monthly archive page

making roguelikes with Clojure

Just a quick post to remind me and anyone else what writing RLs in Clojure looks like today (of course the Java landscape here is vast, so I’ll just cherry pick a couple of Java libraries).

libs

(for Clojurescript)

http://tapiov.net/unicodetiles.js/

http://ondras.github.com/rot.js/hp/

(for Clojure)

https://code.google.com/p/blacken/

https://github.com/SquidPony/SquidLib

https://github.com/sjl/clojure-lanterna/

https://github.com/mikera/orculje

games

https://github.com/thomcc/dunjeon

http://stevelosh.com/blog/2012/07/caves-of-clojure-01/

https://github.com/mikera/alchemy (see the README for a link to the blog and a great series of blog posts in media 7DRL)

Who was Rolindar?

I’ve been going through my collection of mud areas lately (you can download areas freely from various codebase repositories). Many are entertaining reads in their own right; over 20 years of mud development is bound to produce at least a few highly skilled wielders of OLC (or an area format file, whatever the case may be). One of my favorite builders is Rolindar.

In my experience Rolindar’s areas are not to everyone’s taste (he has a distinct style), but his flair for writing, the interactivity in his zones, and feel for creating a coherent zone make him a standout in my opinion. If you’re curious you can search for some of his areas here, http://www.smaugmuds.org/index.php?a=files&s=search .

There must be other builders, past (and perhaps) present of Rolindar’s caliber; there surely are thousands of original mud areas out there. I wonder who would be in the ‘top 5 builders of all time’. I find it a little sad that such a question will never be answered, if only that it deprives future builders of knowing who came before them. Maybe it isn’t a necessary question to answer in a hobby like this. No (well, let’s say most) builders don’t create areas to make a name for themselves, but out of a sense of contributing to a greater whole.

It does seem lacking though to have to create without a past.

server in Clojure, fourth attempt

Getting closer! Continue reading

server in Clojure, third attempt, solely for posterity’s sake

This is not what you want to do.

(ns exvarcis.core
  (:import [java.net InetAddress InetSocketAddress Socket]
           [java.nio ByteBuffer]
           [java.nio.charset Charset]
           [java.nio.channels
            ServerSocketChannel SocketChannel Selector SelectionKey]))

(defn buf-seq
  ([buf] (buf-seq buf 0))
  ([buf i]
   (lazy-seq  
      (when (< i (.limit buf))
        (cons (.get buf i) (buf-seq buf (inc i)))))))

(let [c (atom 0)]
  (defn new-connection []
    {:id (swap! c inc)
     :writebuf (ByteBuffer/allocate 8096)
     :readbuf (ByteBuffer/allocate 8096)}))

(defmulti select-op
  (fn [k] (.readyOps k)))

(defmethod select-op :default [k]
  (println "Default op."))

(defmethod select-op (SelectionKey/OP_ACCEPT) [k]
  (-> 
    (.accept (.channel k))
    (.configureBlocking false)
    (.register (.selector k) (SelectionKey/OP_READ))
    (.attach (new-connection)))
  (println "Client registered"))

(defn close-connection [k]
  (println "closing connection" (.channel k))
  (.cancel k)
  (.close (.channel k)))

(defmethod select-op (SelectionKey/OP_READ) [k]
  (let
    [readbuf (-> (.attachment k) (:readbuf))]
    (.read (.channel k) readbuf)
    (.flip readbuf)
    (when (= (.limit readbuf) 0)
      (close-connection k))))

(defn select! [selector]
  (.select selector)
  (let [ks (.selectedKeys selector)]
    (doseq [k ks]
      (select-op k))
    (.clear ks)))

(defn get-lines [buf]
  (let
    [original-limit (.limit buf)
    last-newline (.lastIndexOf (vec (buf-seq buf)) 10)]
    (when-not (= last-newline -1)
      (.limit buf (inc last-newline))
      (let
        [lines (->>
                  buf
                  (.decode (Charset/defaultCharset))
                  .toString
                  clojure.string/split-lines)]
        (.limit buf original-limit)
        (.compact buf)
        lines))))

(defn write-lines [lines buf k]
  (let
      [encoded-lines (->> lines (.encode (Charset/defaultCharset)))]
    (when (> (.limit encoded-lines) 0)
      (.put buf encoded-lines)
      (.flip buf)
      (.write (.channel k) buf)
      (.compact buf)
      (println buf))))

(defn write-all [ks]
  (let
    [bufs (map #(-> (.attachment %) (:readbuf)) ks)
     lines (mapcat get-lines bufs)
     lines-with-crlf (str (clojure.string/join "\r\n" lines) "\r\n")
     bufs (map #(-> (.attachment %) (:writebuf)) ks)]
    (doseq [[buf k] (map list bufs ks)]
      (write-lines lines-with-crlf buf k))))

(defn run [selector]
 (while true
   (select! selector)
   (write-all (filter #(.attachment %) (.keys selector)))
   (Thread/sleep 3000)))

(defn start [handler]
  (let [selector (Selector/open)
        acceptor (->
                  (ServerSocketChannel/open)
                  (.configureBlocking false)
                  (.bind (InetSocketAddress. "127.0.0.1" 0)))]
    (.register acceptor selector (SelectionKey/OP_ACCEPT))
    (println "Starting Ex v Arcis on" (.getLocalAddress acceptor))
    (handler selector)))

(defn -main []
  (start run)
)