123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- (ns bombnet.manager
- (:require [bombnet.game :refer :all]
- [clojure.set :refer [difference]]
- [clojure.core.async :refer [>! >!! <! <!! alts! alts!! chan go go-loop timeout]]))
- (defrecord Room [clients players state msg-ch ready-ch])
- (defrecord Manager [rooms])
- (defn new-manager []
- (->Manager (atom {})))
- (defn client-join [room {:keys [id] :as client}]
- (assoc-in room [:clients id] client))
- (defn client-leave [room id]
- (let [new-room (-> room
- (update :clients dissoc id)
- (update :players difference #{id}))]
- (if (not-empty (:clients new-room)) new-room)))
- (defn player-join [room id]
- (let [player-ch (get-in room [:clients id :channel])
- player-count (count (:players room))]
- (if (>= player-count 4)
- (do
- (>!! player-ch {:message "Could not join game. Lobby is full."})
- room)
- (do
- (>!! player-ch {:message "Joined game."})
- (update room :players conj id)))))
- (defn player-leave [room id]
- (update room :players difference #{id}))
- (defn player-action [room id action]
- (if (some? (:state room))
- (let [new-room (update room :state queue-action id action)
- ready-ch (:ready-ch new-room)
- ready (every? :action (get-in new-room [:state :players]))]
- (if ready
- (>!! ready-ch "ready"))
- new-room)))
- (defn schedule-tick [room]
- (let [{:keys [msg-ch ready-ch]} room]
- (go
- (alts! [ready-ch (timeout 5000)])
- (>! msg-ch [:game-tick "tick"]))))
- (defn publish-state [{:keys [clients state]}]
- (doseq [ch (map :channel (vals clients))]
- (>!! ch {:game state})))
- (defn game-start [room]
- (let [clients (map #(dissoc % :channel) (vals (:clients room)))
- players (filter #((:players room) (:id %)) clients)
- new-state (new-game players)
- new-room (assoc room :state new-state)]
- (schedule-tick new-room)
- (publish-state new-room)
- new-room))
- (defn game-tick [room]
- (let [new-state (update-state (:state room))
- new-room (assoc room :state new-state)]
- (if (not (finished? new-state))
- (schedule-tick new-room))
- (publish-state new-room)
- new-room))
- (defn message [room {:keys [id message]}]
- (let [type (:type message)]
- (condp = type
- "start" (game-start room)
- "join" (player-join room id)
- "leave" (player-leave room id)
- "action" (player-action room id (:action message))
- (do
- (println "Unknown message")
- room))))
- (defn start-room [in-ch close-ch]
- (go-loop
- [room (->Room {} #{} nil in-ch (chan))]
- (let [[message-type payload] (<! in-ch)
- new-room (condp = message-type
- :client-join (client-join room payload)
- :client-leave (client-leave room payload)
- :game-start (game-start room)
- :game-tick (game-tick room)
- :message (message room payload))]
- (if (some? new-room)
- (recur new-room)
- (>! close-ch :kill-me-now)))))
- (defn get-room [{:keys [rooms]} room]
- (if-let [room-chan (get @rooms room)]
- room-chan
- (let [room-chan (chan)
- close-chan (chan)]
- (start-room room-chan close-chan)
- (go
- (<! close-chan)
- (swap! rooms dissoc room))
- (swap! rooms assoc room room-chan)
- room-chan)))
|