src/clojurecraft/core.clj @ b1007d01decd
Whitespace.
| author | Steve Losh <steve@stevelosh.com> |
|---|---|
| date | Tue, 16 Aug 2011 20:22:42 -0400 |
| parents | 7b3c5af9949d |
| children | d8fa70c91585 |
(ns clojurecraft.core (:use [clojurecraft.mappings]) (:use [clojurecraft.in]) (:use [clojurecraft.out]) (:use [clojurecraft.util]) (:use [clojure.contrib.pprint :only (pprint)]) (:require [clojurecraft.chunks :as chunks]) (:require [clojurecraft.physics :as physics]) (:require [clojurecraft.actions :as act]) (:require (clojurecraft.data)) (:import [clojurecraft.data Location Entity Block Chunk World Bot]) (:import (java.net Socket) (java.io DataOutputStream DataInputStream) (java.util.concurrent LinkedBlockingQueue TimeUnit))) (def STARTING-LOC (Location. 0 0 0 0 0 0 false)) ; Worlds --------------------------------------------------------------------------- (def *worlds* (ref {})) (defn get-world [server] (dosync (ensure *worlds*) (let [world (@*worlds* server)] (if world world (do (alter *worlds* assoc server (World. server (ref {}) (ref {}) (ref 0))) (@*worlds* server)))))) ; Connections ---------------------------------------------------------------------- (defn- random-username [] (apply str (repeatedly 10 #(rand-nth "abcdefghijklmnopqrstuvwxyz")))) (defn login [bot username] ; Send handshake (write-packet bot :handshake {:username username}) ; Get handshake (read-packet bot nil nil nil) ; Send login (write-packet bot :login {:version 14 :username username}) ; Get login (get (read-packet bot nil nil nil) 1)) (defn input-handler [bot] (let [conn (:connection bot)] (loop [prevs [nil nil nil]] (when (nil? (:exit @conn)) (recur (read-packet bot (get prevs 0) (get prevs 1) (get prevs 2)))))) (println "done - input handler")) (defn update-location [bot] (when (chunks/current bot) (dosync (let [player (:player bot) loc (:loc @player) [bounds-min bounds-max] (physics/player-bounds loc) new-data-y (physics/update-loc-y bot bounds-min bounds-max) {new-y :y new-onground :onground new-velocity :vel} new-data-y] (alter player assoc :velocity new-velocity) (alter player assoc-in [:loc :y] new-y) (alter player assoc-in [:loc :stance] (+ new-y physics/CHAR-HEIGHT-EYES)) (alter player assoc-in [:loc :onground] new-onground))))) (defn location-handler [bot] (let [conn (:connection bot) outqueue ^LinkedBlockingQueue (:outqueue bot)] (while (nil? (:exit @conn)) (let [player (:player bot) location (:loc @player)] (when (not (nil? location)) (.put outqueue [:playerpositionlook location]) (update-location bot)) (Thread/sleep 50)))) (println "done - location handler")) (defn output-handler [bot] (let [conn (:connection bot) outqueue ^LinkedBlockingQueue (:outqueue bot)] (while (nil? (:exit @conn)) (let [packet (.poll outqueue 1 TimeUnit/SECONDS)] (when packet (let [[packet-type, payload] packet] (write-packet bot packet-type payload)))))) (println "done - output handler")) (defn action-handler [bot] (let [conn (:connection bot) actionqueue ^LinkedBlockingQueue (:actionqueue bot)] (while (nil? (:exit @conn)) (let [action (.poll actionqueue 1 TimeUnit/SECONDS)] (when action (force action))))) (println "done - action handler")) (defn connect [server username] (let [username (or username (random-username)) socket (Socket. (:name server) (:port server)) in (DataInputStream. (.getInputStream socket)) out (DataOutputStream. (.getOutputStream socket)) conn (ref {:in in :out out}) outqueue ^LinkedBlockingQueue (LinkedBlockingQueue.) actionqueue (LinkedBlockingQueue.) world (get-world server) bot (Bot. conn username outqueue actionqueue nil world (ref {}) (atom {}) (atom {}))] (println "connecting") ; We need to log in to find out our bot's entity ID, so we delay creation of the ; player until then. (let [player-id (:eid (login bot username)) player (ref (Entity. player-id nil false 0.0)) bot (assoc bot :player player)] ; Theoretically another connected bot could fill in the player's entity entry ; in the world before we get here, but in practice it probably doesn't matter ; because we're going to fill it in anyway. ; ; The fact that there could be a ref thrown away is troubling. ; ; TODO: Think more about this. (dosync (alter (:entities world) assoc player-id player)) (println "connected and logged in") (println "queueing initial keepalive packet") (.put outqueue [:keepalive {}]) (println "starting read handler") (.start (Thread. #(input-handler bot))) (println "starting write handler") (.start (Thread. #(output-handler bot))) (println "starting location updating handler") (.start (Thread. #(location-handler bot))) (println "starting action handler") (.start (Thread. #(action-handler bot))) (println "all systems go!") bot))) (defn disconnect [bot] (dosync (alter (:connection bot) merge {:exit true}))) (def minecraft-local {:name "localhost" :port 25565})