Skip to content

Instantly share code, notes, and snippets.

@danneu

danneu/mud.clj

Created May 5, 2014
Embed
What would you like to do?
(ns mud.sandbox
(:require [schema.core :as s]
[clojure.java.io :as io]
[clojure.string :as str]
[clojure.set :as set]
[mud.ansi :as ansi])
(:import [java.net ServerSocket]
[java.io PrintWriter]))
(defn prompt
([] (prompt ""))
([msg]
(print (str msg "\n> "))
(flush)
(read-line)))
(def locs
{1 {:name "Workstation"
:id 1
:short-desc "You're sitting in a chair staring at your laptop.\n\nYour to-do list is in front of you. The only item you've managed to cross off so far is \"Make to-do list\". Your lack of progress overwhelms you with guilt and dread.\n\nMaybe you need to take a break and walk around the office."
:exits {:stand 2}}
2 {:name "Living Room"
:id 2
:short-desc "Your co-workers are sitting at one, big, shared table and staring lifelessly into their terminals, idly clacking their keyboards."
:exits {:up 3
:sit 1}}
3 {:name "Upstairs"
:short-desc "You are on a mezzanine overlooking your co-workers in the living room below.\n\nYou're surrounded by unlabeled doors. Behind one of them, a cat meows incessantly. Behind another, you hear the muffled ramblings of someone talking about Bitcoin."
:id 3
:exits {:down 2}
}})
(defn get-curr-loc [session]
(get locs (:curr-loc-id @session)))
;; Commands ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmulti command (fn [server session line]
(keyword (first (str/split line #" ")))))
(defmethod command :default
[_ session _]
(println "What?"))
(defmethod command :look
[_ session _]
(let [curr-loc (get-curr-loc session)]
(println (str \newline
(-> (:name curr-loc)
(ansi/style :cyan))))
(println (:short-desc curr-loc))
(println (str "\nExits:\n" (->> (for [[exit-dir exit-id] (:exits curr-loc)
:let [exit-loc (get locs exit-id)]]
(str " [" (name exit-dir) "] " (:name exit-loc)))
(str/join ", ")))))
;(println "You're sitting at your desk in front of your laptop.")
)
;; "Go back to :prev-loc. Can only go back if current-loc has :prev-loc
;; as an exit."
(defmethod command :back
[server session _]
(if-let [prev-loc-id (:prev-loc-id @session)]
(let [curr-loc (get-curr-loc session)]
(if-let [direction (get (set/map-invert (:exits curr-loc)) prev-loc-id)]
(command server session (str "go " (name direction)))
(println "You can't go back that way.")))
(println "You can't go back.")))
(defn println->session [session msg]
(.println (:out @session) msg))
(defmethod command :say
[server session line]
(let [msg (->> (str/split line #" ")
(drop 1)
(str/join " "))]
(if (str/blank? msg)
(println "Supply a message.")
;; Print msg to other sessions in same room
(do
(println (str "You say \"" msg "\""))
(let [curr-loc (get-curr-loc session)]
(doseq [other-session (as-> (:sessions server) _
(deref _)
(filter #(= (:id curr-loc)
(:curr-loc-id @%))
_)
(remove #(= session %) _))]
(println->session other-session
(str (:name @session) " says \"" msg "\"")))
)))))
(defn get-other-sessions-in-loc [loc server session]
(let [curr-loc (get-curr-loc session)]
(->> (:sessions server)
(deref)
(filter #(= (:id loc) (:curr-loc-id @%)))
(remove #(= session %)))))
(defmethod command :go
[server session line]
(let [curr-loc (get-curr-loc session)
direction (keyword (second (str/split line #" ")))]
(swap! session assoc :prev-loc-id (:id curr-loc))
(swap! session assoc :curr-loc-id (get (:exits curr-loc) direction))
(doseq [other-session (get-other-sessions-in-loc curr-loc server session)]
(println->session other-session (str (:short-desc @session)
" left the room.")))
(let [new-loc (get-curr-loc session)]
;; Find other player sessions in the new location so we can announce to
;; them that someone entered.
(doseq [other-session (get-other-sessions-in-loc new-loc server session)]
(println->session other-session (str (:short-desc @session)
" enters the room."))))
(command server session "look")))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn start-repl [server session]
(binding [*in* (:in @session)
*out* (:out @session)]
(println (str \newline "Welcome to Agave Lab."
\newline
"Players: " (count @(:sessions server))
\newline
"Commands: look, go <EXIT>, say <MESSAGE>"
\newline
"Try 'look' right now." \newline))
(loop [line (prompt "You awake from a day-dream and remember that you're at work.")]
;(prn @session)
(when-not (or (str/blank? line) (= line "quit"))
(command server session line)
(recur (prompt))))))
;; Pass in an in-stream and out-stream.
;; PlayerSession atom: {:in java.io.BufferedReader
;; :out java.io.PrintWriter}
(defn make-session [in out]
(atom {:in (io/reader in)
:out (PrintWriter. out true)
:curr-loc-id 1
:prev-loc-id 1
:name (rand-nth ["Dan" "Edgar" "Magaly" "Fab" "Ernesto" "Antonio" "Tara" "Andy" "Daisy the Dog" "Armando" "Martha" "Juan" "Graham" "Eric"])
:short-desc (rand-nth ["a gringo" "a cool person" "a magical wizard" "a badass" "a lazy person" "a grue"])}))
(defn handle-client
"1. Creates a PlayerSession
2. Assocs it to Server's :sessions set
3. Begins player repl
4. Dissocs PlayerSession from :sessions when player disconnects or idles out."
[server client-sock]
(println "Client connected.")
(with-open [in-stream (io/input-stream client-sock)
out-stream (io/output-stream client-sock)]
;; 1. Create PlayerSession atom
(let [session (make-session in-stream out-stream)]
(println (str (:name @session) " has entered the world.") )
;; 2. Assoc PlayerSession to Server :sessions
(swap! (:sessions server) conj session)
;; 3. Start player repl
(start-repl server session)
;; 4. Remove PlayerSession when player disconnects or idles out.
(swap! (:sessions server) disj session)
(.close client-sock)
(println "A player disconnected."))))
;; Server: {:socket java.net.ServerSocket
;; :sessions #{}}
(defn start-server [port]
(let [server {:socket (ServerSocket. port)
:sessions (atom #{})}]
(println (str "Listening on " port "..."))
(while true
(let [client-sock (.accept (:socket server))]
(.start (Thread. #(handle-client server client-sock)))))))
(defn -main [& args]
;(println (ansi/style-test-page))
(start-server 5000))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.