logic.clj

Branches > facts > 89e8252 > files / src / net / bowmap / server / logic.clj

(ns net.bowmap.server.logic
  (:require
   [net.bowmap.server.player :as player]
   [net.bowmap.server.position :as position]
   [net.bowmap.server.server :as server]
   [net.bowmap.stormbow.effect :as effect]
   [net.bowmap.stormbow.fact :as fact]))

; TODO should also be able to return new facts :)
(defmulti handle-fact
  "Returns a sequence of effects from a fact."
  (fn [fact]
    (::fact/attribute fact)))

(defmethod handle-fact :default [_])

(defmethod handle-fact ::fact/server-list-requested [_]
  (dosync
    [{::effect/id ::effect/send-server-list
      :server-list-response {:version {:name "26.2"
                                       :protocol 776}
                             ; Gets the number of players playing, which is what you want
                             ; And there are no side-effects!
                             #_(count (fact ::fact/playing))
                             :players {:online (count (fact/fact ::fact/playing))
                                       :max server/max-player-count}
                             :description {:text "Bowmap!"}
                             :enforcesSecureChat false}}]))

(defmethod handle-fact ::fact/player-joined [{::fact/keys! [value]}]
  (dosync
    (let [{::player/keys! [uuid]} value]
      (if (contains? (fact/fact ::fact/playing) uuid)
        (throw (Exception. "Player tried to join with an existing UUID"))
        [{::fact/attribute ::fact/playing
          ::fact/entity uuid}]))))

; TODO this is never actually used :p
; So the player list and re-joining are broken
(defmethod handle-fact ::fact/player-left [{::fact/keys! [entity]}]
  [{::fact/attribute ::fact/playing
    ::fact/mode ::fact/retract
    ::fact/entity entity}])

#_(defmethod handle-fact ::fact/updated-player-movement [{:keys! [::player/uuid ::position/position]}]
    (dosync
      (alter (@server/players uuid) update ::position/position merge position)
      []))

; TODO move this comment to the fact definition itself somewhere
; Called when the player actually moves, not just when a position is sent
(defmethod handle-fact ::fact/player-moved [_ _])

(defmethod handle-fact ::fact/player-sent-chat-message [{::fact/keys! [entity value]}]
  [{::fact/attribute ::fact/message
    ::fact/entity (random-uuid)
    ::fact/value {:message value
                  :author entity}}])

(defn apply-rules
  "Applies rules recursively, returns a map containing :facts and :effects.
   :facts being the seqs of all facts that were asserted/retracted,
   :effects being the seq of all effects that are implied from that fact."
  ([fact] (apply-rules {:facts [fact] :effects []} fact))
  ([acc fact]
   (let [results (handle-fact fact)]
     (reduce (fn [acc result]
               (if (::fact/attribute result)
                 (apply-rules (update acc :facts conj result) result)
                 (update acc :effects conj result)))
             acc results))))