├── .ackrc ├── test └── clecs │ ├── core_test.clj │ ├── test │ └── checkers.clj │ ├── util_test.clj │ ├── world_test.clj │ ├── component_test.clj │ ├── world │ └── validate_test.clj │ ├── query_test.clj │ ├── system_test.clj │ └── backend │ └── atom_world_test.clj ├── .gitignore ├── .travis.yml ├── src └── clecs │ ├── util.clj │ ├── world │ └── validate.clj │ ├── test │ └── mock.clj │ ├── system.clj │ ├── component.clj │ ├── backend │ └── atom_world.clj │ ├── query.clj │ └── world.clj ├── resources └── ga.inc ├── README.md ├── codox └── writer.clj ├── LICENSE ├── project.clj ├── doc └── index.adoc └── COPYING /.ackrc: -------------------------------------------------------------------------------- 1 | --ignore-dir=target 2 | -------------------------------------------------------------------------------- /test/clecs/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.core-test) 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /lib 3 | /classes 4 | /checkouts 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | .lein-deps-sum 10 | .lein-failures 11 | .lein-plugins 12 | .lein-repl-history 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: clojure 2 | lein: lein2 3 | script: 4 | - lein2 all midje 5 | # - lein2 all eastwood 6 | - lein2 docs 7 | jdk: 8 | - openjdk6 9 | - openjdk7 10 | - oraclejdk7 11 | -------------------------------------------------------------------------------- /src/clecs/util.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.util) 2 | 3 | 4 | (defn map-values [f coll] 5 | (let [key-vals (map (fn [[k v]] [k (f v)]) coll)] 6 | (if (seq key-vals) 7 | (apply conj (empty coll) key-vals) 8 | coll))) 9 | -------------------------------------------------------------------------------- /test/clecs/test/checkers.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.test.checkers 2 | (:require [midje.sweet :refer :all])) 3 | 4 | 5 | (defn implements-protocols [& protocols] 6 | (checker [obj] 7 | (let [t (type obj)] 8 | (every? #(extends? % t) protocols)))) 9 | -------------------------------------------------------------------------------- /resources/ga.inc: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # clecs 2 | 3 | Entity-component-system for Clojure. 4 | 5 | [![Build Status](https://travis-ci.org/muhuk/clecs.svg?branch=master)](https://travis-ci.org/muhuk/clecs) 6 | [![API Documentation](http://b.repl.ca/v1/doc-API-blue.png)](http://clecs.muhuk.com/latest/api/index.html) 7 | [![User Guide](http://b.repl.ca/v1/doc-user_guide-blue.png)](http://clecs.muhuk.com/latest/user_guide/index.html) 8 | 9 | [![Clojars Project](http://clojars.org/clecs/latest-version.svg)](http://clojars.org/clecs) 10 | 11 | 12 | ## License 13 | 14 | Copyright (C) 2015 Atamert Ölçgen 15 | 16 | This program is distributed under GNU GPL v3 license. See `LICENSE` file. 17 | -------------------------------------------------------------------------------- /test/clecs/util_test.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.util-test 2 | (:require [clecs.util :refer :all] 3 | [midje.sweet :refer :all])) 4 | 5 | 6 | 7 | (facts "About map-values" 8 | (let [coll {:a 2 :b 3 :c 5}] 9 | (fact "Map values with nil." 10 | (map-values ..f.. nil) => nil) 11 | 12 | (fact "Map values with an empty collection." 13 | (map-values ..f.. {}) => {}) 14 | 15 | 16 | (fact "Map values using identity." 17 | (map-values identity coll) => coll) 18 | 19 | 20 | (fact "Map values using custom function." 21 | (map-values (fn [i] (* i i)) coll) => {:a 4 :b 9 :c 25}))) 22 | -------------------------------------------------------------------------------- /codox/writer.clj: -------------------------------------------------------------------------------- 1 | (ns ^:no-doc writer 2 | (require [codox.writer.html :as html-writer] 3 | [clojure.java.io :refer [file]])) 4 | 5 | 6 | (defn html-file? [f] 7 | (.endsWith (.getName f) ".html")) 8 | 9 | 10 | (defn add-google-analytics [f] 11 | (let [matcher (re-matcher #"^(?s)(.*)(.*)$" (slurp f)) 12 | [_ head tail] (re-find matcher) 13 | ga-code (slurp "resources/ga.inc")] 14 | (spit f (str head ga-code tail)))) 15 | 16 | 17 | (defn write-docs 18 | [project] 19 | (html-writer/write-docs project) 20 | (doseq [f (->> (:output-dir project) 21 | (file) 22 | (file-seq) 23 | (filter html-file?))] 24 | (add-google-analytics f))) 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015 Atamert Ölçgen 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see . 15 | 16 | Additional permission under GNU GPL version 3 section 7 17 | 18 | If you modify this Program, or any covered work, by linking or 19 | combining it with clojure (or a modified version of that 20 | library), containing parts covered by the terms of EPL, the licensors 21 | of this Program grant you additional permission to convey the resulting 22 | work. Corresponding source for a non-source form of such a combination 23 | shall include the source code for the parts of clojure used as well as 24 | that of the covered work. 25 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject clecs "2.1.0-SNAPSHOT" 2 | :description "Entity-component-system for Clojure." 3 | :url "https://github.com/muhuk/clecs" 4 | :license {:name "GNU GPL v3" 5 | :url "http://www.gnu.org/licenses/gpl-3.0.en.html"} 6 | :dependencies [[org.clojure/clojure "1.7.0-alpha5"]] 7 | :plugins [[codox "0.8.10"] 8 | [jonase/eastwood "0.2.1"] 9 | [lein-asciidoctor "0.1.14"] 10 | [lein-cloverage "1.0.2"] 11 | [lein-midje "3.1.3"]] 12 | :profiles {:dev {:dependencies [[midje "1.6.3"]]} 13 | :codox {:source-paths ["codox"]} 14 | :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]}} 15 | :codox {:defaults {:doc/format :markdown} 16 | :src-dir-uri "http://github.com/muhuk/clecs/blob/master/" 17 | :src-linenum-anchor-prefix "L" 18 | :output-dir "target/doc/api" 19 | :writer writer/write-docs} 20 | :asciidoctor {:extract-css true 21 | :format :html5 22 | :source-highlight true 23 | :sources "doc/*.adoc" 24 | :to-dir "target/doc/user_guide" 25 | :toc :left} 26 | :deploy-repositories {"releases" :clojars 27 | "snapshots" :clojars} 28 | :aliases {"all" ["with-profile" "dev:dev,1.6"] 29 | "docs" ["do" 30 | "with-profile" "codox" "doc," 31 | "asciidoctor," 32 | "cloverage" "-o" "target/doc/coverage"]}) 33 | -------------------------------------------------------------------------------- /src/clecs/world/validate.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.world.validate 2 | (:require [clojure.set :refer [difference superset? union]])) 3 | 4 | 5 | (declare -validate-components 6 | -validate-systems 7 | fail-when-extra) 8 | 9 | 10 | (defn validate-world 11 | "Performs following checks: 12 | 13 | - Components validation: 14 | - At least one component must be defined. 15 | - Systems validation: 16 | - At least one system must be defined. 17 | - Each component must be used by at least one system. 18 | - Systems can't access to unknown components." 19 | [components systems supported-types] 20 | (-validate-components components supported-types) 21 | (-validate-systems components systems)) 22 | 23 | 24 | (defn ^:no-doc -validate-components [components supported-types] 25 | (when (empty? components) 26 | (throw (RuntimeException. 27 | "You must provide at least one component."))) 28 | (let [types-used (set (mapcat #(-> % (:params) (vals)) components)) 29 | supported-types (set supported-types)] 30 | (when-not (superset? supported-types types-used) 31 | (throw (RuntimeException. (format "Parameter types %s are not supported." 32 | (difference types-used supported-types))))))) 33 | 34 | 35 | (defn ^:no-doc -validate-systems [components systems] 36 | (when (empty? systems) 37 | (throw (RuntimeException. 38 | "You must provide at least one system."))) 39 | (let [components-defined (set (map :name components)) 40 | components-by-system (into {} 41 | (map (fn [s] 42 | [(:name s) 43 | (union (:reads s) (:writes s))]) 44 | systems)) 45 | components-used (apply union (vals components-by-system))] 46 | (fail-when-extra components-defined 47 | components-used 48 | "These components are not used by any system: %s") 49 | (doseq [[s cs] components-by-system] 50 | (fail-when-extra cs 51 | components-defined 52 | (str s " is using unknown components: %s"))))) 53 | 54 | 55 | (defn- fail-when-extra [a b error-message] 56 | (when-let [diff (seq (difference a b))] 57 | (throw (RuntimeException. (format error-message diff))))) 58 | -------------------------------------------------------------------------------- /test/clecs/world_test.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.world-test 2 | (:require [clecs.component :refer [validate]] 3 | [clecs.test.mock :as mock] 4 | [clecs.world :refer :all] 5 | [clecs.world.validate :refer [validate-world]] 6 | [midje.sweet :refer :all])) 7 | 8 | 9 | 10 | (fact "set-component validates component names." 11 | (let [w (mock/mock-editable-world)] 12 | (set-component w ..eid.. ..cname.. ..cdata..) => (throws RuntimeException 13 | #"Unknown") 14 | (provided (mock/-component w ..cname..) => nil))) 15 | 16 | 17 | (fact "set-component validates component data." 18 | (let [w (mock/mock-editable-world)] 19 | (set-component w ..eid.. ..cname.. ..cdata..) => nil 20 | (provided (mock/-component w ..cname..) => ..c.. 21 | (validate ..c.. ..cdata..) => nil 22 | (mock/-set-component w ..eid.. ..cname.. ..cdata..) => anything))) 23 | 24 | 25 | (fact "world validates its parameters, creates a new world and runs initializer." 26 | (let [initializer-called-with (atom nil) 27 | w (reify 28 | IWorld 29 | (-run [this _ _ f dt] (f ..editable-world.. dt) this)) 30 | initializer (fn [w] (reset! initializer-called-with w)) 31 | components [{:name :c1} 32 | {:name :c2} 33 | {:name :c3}] 34 | systems [{:name :s1} 35 | {:name :s2} 36 | {:name :s3}] 37 | supported-types [:c1 :c2 :c3]] 38 | (world mock/mock-world-factory {:components components 39 | :initializer initializer 40 | :systems systems}) => w 41 | (provided (mock/-supported-types mock/mock-world-factory) => supported-types 42 | (validate-world components systems supported-types) => nil 43 | (mock/-world mock/mock-world-factory 44 | {:c1 {:name :c1} 45 | :c2 {:name :c2} 46 | :c3 {:name :c3}} 47 | {:s1 {:name :s1} 48 | :s2 {:name :s2} 49 | :s3 {:name :s3}} 50 | {}) => w) 51 | @initializer-called-with => ..editable-world..)) 52 | -------------------------------------------------------------------------------- /test/clecs/component_test.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.component-test 2 | (:require [clecs.component :refer :all] 3 | [midje.sweet :refer :all])) 4 | 5 | 6 | (fact "A component with no parameters always validate." 7 | (validate (component :Foo {}) {}) => nil) 8 | 9 | 10 | (future-fact "Component names must be keywords.") 11 | 12 | 13 | (fact "Number of parameters are validated." 14 | (let [c (component :Foo {:a Integer :b String})] 15 | (validate c {}) => (throws RuntimeException #"parameters") 16 | (validate c {:a anything}) => (throws RuntimeException #"parameters") 17 | (validate c {:a anything 18 | :b anything}) =not=> (throws RuntimeException #"parameters") 19 | (validate c {:a anything 20 | :b anything 21 | :c anything}) => (throws RuntimeException #"parameters"))) 22 | 23 | 24 | (fact "Parameter keys are validated." 25 | (let [c (component :Bar {:a String :b Boolean})] 26 | (validate c {:a anything :b anything}) =not=> (throws RuntimeException #"parameters") 27 | (validate c {:c anything :b anything}) => (throws RuntimeException #"parameters") 28 | (validate c {:a anything :d anything}) => (throws RuntimeException #"parameters") 29 | (validate c {:c anything :d anything}) => (throws RuntimeException #"parameters"))) 30 | 31 | 32 | (facts "Parameter values are validated." 33 | (fact "Booleans are validated." 34 | (let [c (component :Baz {:x Boolean})] 35 | (validate c {:x true}) => nil 36 | (validate c {:x false}) => nil 37 | (validate c {:x 3}) => (throws RuntimeException #"not a valid") 38 | (validate c {:x "Fubar"}) => (throws RuntimeException #"not a valid"))) 39 | 40 | (fact "Integers are validated." 41 | (let [c (component :Baz {:x Integer})] 42 | (validate c {:x 3}) => nil 43 | (validate c {:x true}) => (throws RuntimeException #"not a valid") 44 | (validate c {:x false}) => (throws RuntimeException #"not a valid") 45 | (validate c {:x "Fubar"}) => (throws RuntimeException #"not a valid"))) 46 | (fact "Strings are validated." 47 | (let [c (component :Baz {:x String})] 48 | (validate c {:x "Fubar"}) => nil 49 | (validate c {:x true}) => (throws RuntimeException #"not a valid") 50 | (validate c {:x false}) => (throws RuntimeException #"not a valid") 51 | (validate c {:x 3}) => (throws RuntimeException #"not a valid")))) 52 | -------------------------------------------------------------------------------- /src/clecs/test/mock.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.test.mock 2 | "Mock worlds that delegate their operations to functions. 3 | 4 | Because protocol method can't be mocked using `with-redefs` 5 | etc. systems and world backends are difficult to unit test. 6 | Paying one level of indirection, protocols can be mocked 7 | using functions defined in this module. 8 | 9 | #### Examples: 10 | 11 | (def foo-system [w dt] 12 | (doseq [eid (world/query w (all :FooComponent 13 | (any :BarComponent 14 | :BazComponent)))] 15 | (world/remove-component w eid :FooComponent))) 16 | 17 | (fact \"foo-system does stuff.\" 18 | (let [w (mock/mock-editable-world) 19 | q (all :FooComponent 20 | (any :BarComponent 21 | :BazComponent))] 22 | (foo-system w anything) => anything 23 | (provided (mock/query w q) => [..e1.. ..e2..] 24 | (mock/remove-component w ..e1.. :FooComponent) => nil 25 | (mock/remove-component w ..e2.. :FooComponent) => nil))) 26 | " 27 | (:require [clecs.world :refer [IEditableWorld 28 | IQueryableWorld 29 | IWorld 30 | IWorldFactory]])) 31 | 32 | 33 | (defmacro ^:private def-mock-fn [n] 34 | `(defn ~n [~'& ~'_] 35 | (throw (RuntimeException. ~(str n " is called directly."))))) 36 | 37 | 38 | (def-mock-fn -component) 39 | (def-mock-fn -run) 40 | (def-mock-fn -set-component) 41 | (def-mock-fn -supported-types) 42 | (def-mock-fn -world) 43 | (def-mock-fn add-entity) 44 | (def-mock-fn component) 45 | (def-mock-fn process!) 46 | (def-mock-fn query) 47 | (def-mock-fn remove-component) 48 | (def-mock-fn remove-entity) 49 | 50 | 51 | (defn mock-editable-world [] 52 | (reify 53 | IEditableWorld 54 | (-set-component [this eid cname cdata] (-set-component this eid cname cdata)) 55 | (add-entity [this] (add-entity this)) 56 | (remove-component [this eid cname] (remove-component this eid cname)) 57 | (remove-entity [this eid] (remove-entity this eid)) 58 | IQueryableWorld 59 | (-component [this cname] (-component this cname)) 60 | (component [this eid cname] (component this eid cname)) 61 | (query [this q] (query this q)))) 62 | 63 | 64 | (defn mock-world [] 65 | (reify 66 | IWorld 67 | (-run [this reads writes f dt] (-run this reads writes f dt)) 68 | (process! [this dt] (process! this dt)))) 69 | 70 | 71 | (def mock-world-factory 72 | (reify 73 | IWorldFactory 74 | (-supported-types [this] (-supported-types this)) 75 | (-world [this components systems extra-config] (-world this 76 | components 77 | systems 78 | extra-config)))) 79 | -------------------------------------------------------------------------------- /test/clecs/world/validate_test.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.world.validate-test 2 | (:require [clecs.component :refer [component] :rename {component c}] 3 | [clecs.system :refer [system]] 4 | [clecs.world.validate :refer :all] 5 | [midje.sweet :refer :all])) 6 | 7 | 8 | 9 | (fact "validate-world validates components then systems." 10 | (validate-world ..components.. ..systems.. ..suported-types..) => nil 11 | (provided (-validate-components ..components.. ..suported-types..) => nil 12 | (-validate-systems ..components.. ..systems..) => nil)) 13 | 14 | 15 | (fact "-validate-components fails if there are no components." 16 | (-validate-components [] nil) => 17 | (throws RuntimeException 18 | "You must provide at least one component.")) 19 | 20 | 21 | (facts "-validate-components fails if unsupported parameter types are used." 22 | (-validate-components [(c :Foo nil)] nil) => nil 23 | (-validate-components [(c :Foo {:a Integer})] nil) => (throws RuntimeException 24 | #"not supported" 25 | #"Integer") 26 | (-validate-components [(c :Foo {:a Integer})] [Integer]) => nil 27 | (-validate-components [(c :Foo {:a Integer 28 | :b Boolean})] [Integer]) => (throws RuntimeException 29 | #"not supported" 30 | #"Boolean")) 31 | 32 | 33 | (fact "-validate-systems fails if there are no systems." 34 | (-validate-systems [..component..] []) => (throws RuntimeException 35 | "You must provide at least one system.")) 36 | 37 | 38 | (fact "-validate-systems rejects components no system is using." 39 | (-validate-systems [(c :Foo nil) 40 | (c :Bar nil)] 41 | [(system {:name :FooSystem 42 | :process-fn identity 43 | :reads #{:Foo}})]) => (throws RuntimeException 44 | #"These components are not used by any system: " 45 | #":Bar")) 46 | 47 | 48 | (fact "-validate-systems rejects systems associated with unknown components." 49 | (-validate-systems [(c :Foo nil)] 50 | [(system {:name :FooSystem 51 | :process-fn identity 52 | :reads #{:Foo}}) 53 | (system {:name :BarSystem 54 | :process-fn identity 55 | :reads #{:Bar}})]) => (throws RuntimeException 56 | #":BarSystem" 57 | #"is using unknown components" 58 | #":Bar")) 59 | -------------------------------------------------------------------------------- /src/clecs/system.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.system 2 | (:require [clojure.set :refer [difference]] 3 | [clojure.string :as s]) 4 | (:refer-clojure :exclude [name])) 5 | (ns-unmap *ns* 'System) 6 | 7 | 8 | (declare validate-components 9 | validate-name 10 | validate-process) 11 | 12 | 13 | (defrecord System [name process process-fn reads writes] 14 | clojure.lang.IFn 15 | (invoke [_ w dt] (process-fn w dt))) 16 | 17 | 18 | (defn system 19 | "Create a system. 20 | 21 | Takes a map with the following elements: 22 | 23 | :name 24 | : A keyword to refer to this system later. 25 | 26 | :process 27 | : Namespaced symbol of the system's 28 | function. 29 | If `:process-fn` is provided, :process 30 | can be omitted. However the system 31 | won't be serializable in this case. 32 | 33 | :process-fn 34 | : System's function itself. 35 | `:process-fn` will be resolved automatically 36 | if `:process` is provided. 37 | 38 | :reads 39 | : Components this system will read. 40 | 41 | :writes 42 | : Components this system will read and write. 43 | 44 | Components not specified in either `:reads` or 45 | `:writes` won't be accessible to the system. 46 | " 47 | [s] 48 | (-> s 49 | (validate-name) 50 | (validate-process) 51 | (validate-components) 52 | (map->System))) 53 | 54 | 55 | (defn- assoc-process-fn [s] 56 | (let [p-var (:process s) 57 | ns-sym (symbol (s/replace (str p-var) #"/.*$" ""))] 58 | (require ns-sym) 59 | (assoc s :process-fn (var-get (find-var p-var))))) 60 | 61 | 62 | (defn- validate-components [s] 63 | (when-not (or (contains? s :reads) (contains? s :writes)) 64 | (throw (IllegalArgumentException. 65 | "Either :reads or :writes must be specified"))) 66 | (when (and (empty? (:reads s)) (empty? (:writes s))) 67 | (throw (IllegalArgumentException. 68 | "At least one component must be specified"))) 69 | (let [writes (set (:writes s)) 70 | reads (difference (set (:reads s)) writes)] 71 | (-> s 72 | (assoc :reads reads) 73 | (assoc :writes writes)))) 74 | 75 | 76 | (defn- validate-name [s] 77 | (if-not (contains? s :name) 78 | (throw (IllegalArgumentException. 79 | ":name is required for systems")) 80 | s)) 81 | 82 | 83 | (defn- validate-process [s] 84 | (when-not (or (contains? s :process) 85 | (contains? s :process-fn)) 86 | (throw (IllegalArgumentException. "Either :process or :process-fn must be specified"))) 87 | (when (and (contains? s :process) 88 | (not (symbol? (:process s)))) 89 | (throw (IllegalArgumentException. ":process must be a symbol"))) 90 | (when (and (contains? s :process-fn) 91 | (not (fn? (:process-fn s)))) 92 | (throw (IllegalArgumentException. ":process-fn must be a function."))) 93 | (cond-> s 94 | (not (contains? s :process-fn)) (assoc-process-fn))) 95 | 96 | 97 | ;; Hide internals from documentation generator. 98 | (doseq [v [#'->System 99 | #'map->System]] 100 | (alter-meta! v assoc :no-doc true)) 101 | -------------------------------------------------------------------------------- /src/clecs/component.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.component 2 | (:require [clojure.string :refer [join]]) 3 | (:refer-clojure :exclude [name])) 4 | 5 | 6 | (declare make-validator) 7 | 8 | 9 | (defrecord Component [name params validate]) 10 | 11 | 12 | (defmacro component 13 | "Create a component definition. 14 | 15 | #### Parameters: 16 | 17 | cname 18 | : A keyword to be used to refer to this component 19 | later. 20 | 21 | cdef 22 | : A map of component parameter names to their types. 23 | 24 | #### Examples: 25 | 26 | ;; A marker component. 27 | (component Renderable nil) 28 | 29 | ;; Components can have any number of parameters. 30 | (component HitPoints {hp Integer}) 31 | (component Point {x Int y Integer}) 32 | (component Player {name String 33 | team Integer 34 | alive Boolean}) 35 | 36 | See also [[clecs.world/world]]." 37 | [name params] 38 | `(->Component ~name 39 | ~params 40 | ~(make-validator name params))) 41 | 42 | 43 | (defn- make-validator [cname cdef] 44 | (let [parameter-count (count cdef) 45 | parameter-names (keys cdef) 46 | wrong-parameters-error (str cname 47 | " takes [" 48 | (join ", " parameter-names) 49 | "] parameters, you have passed [")] 50 | `(fn [~'cdata] 51 | (when-not (and (= (count ~'cdata) ~parameter-count) 52 | ~@(map (fn [param-name] `(contains? ~'cdata ~param-name)) 53 | parameter-names)) 54 | (throw (RuntimeException. (str ~wrong-parameters-error 55 | (join ", " (keys ~'cdata)) 56 | "]")))) 57 | ~@(for [[parameter-name parameter-type] cdef 58 | :let [error-message (str parameter-name 59 | " is not a valid " 60 | parameter-type)]] 61 | `(when-not (validate-param ~parameter-type (~'cdata ~parameter-name)) 62 | (throw (RuntimeException. ~error-message)))) 63 | nil))) 64 | 65 | 66 | (defn validate 67 | "Validate component data against a component definition. 68 | 69 | Throws `RuntimeException` if validation fails. 70 | 71 | #### Parameters: 72 | 73 | c 74 | : Component definition to validate against. 75 | 76 | cdata 77 | : A map containing component data. 78 | " 79 | [^Component c cdata] 80 | ((.validate c) cdata)) 81 | 82 | 83 | (defmulti validate-param 84 | "Validates a parameter. 85 | 86 | #### Parameters: 87 | 88 | parameter-type 89 | : Parameter type to validate against. 90 | 91 | parameter-value 92 | : Parameter value to validate. 93 | " 94 | (fn [parameter-type _] parameter-type)) 95 | (defmethod validate-param Boolean [_ v] (or (true? v) (false? v))) 96 | (defmethod validate-param Integer [_ v] (integer? v)) 97 | (defmethod validate-param String [_ v] (string? v)) 98 | 99 | 100 | ;; Hide internals from documentation generator. 101 | (doseq [v [#'->Component 102 | #'map->Component]] 103 | (alter-meta! v assoc :no-doc true)) 104 | -------------------------------------------------------------------------------- /src/clecs/backend/atom_world.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.backend.atom-world 2 | "Reference implementation of clecs API. 3 | 4 | `AtomWorld` stores it's data in-memory. It is backed by an 5 | `clojure.core/atom` internally. 6 | 7 | Currently systems run sequentially." 8 | (:require [clecs.query :refer [accessed]] 9 | [clecs.util :refer [map-values]] 10 | [clecs.world :refer [-run 11 | IEditableWorld 12 | IQueryableWorld 13 | IWorld 14 | IWorldFactory]] 15 | [clojure.set :refer [difference 16 | subset? 17 | union]])) 18 | 19 | 20 | (def ^:no-doc initial_state {:components {} 21 | :entities {} 22 | :last-entity-id 0}) 23 | 24 | 25 | (def ^{:dynamic true 26 | :no-doc true} *state*) 27 | 28 | 29 | (deftype AtomEditableWorld [readables writables] 30 | IEditableWorld 31 | (add-entity [_] 32 | (let [state *state* 33 | eid (inc (:last-entity-id state))] 34 | (var-set #'*state* 35 | (-> state 36 | (assoc-in [:entities eid] #{}) 37 | (assoc :last-entity-id eid))) 38 | eid)) 39 | (remove-component [this eid cname] 40 | (when-not (contains? writables cname) 41 | (throw (RuntimeException. (str "Unknown component " cname)))) 42 | (var-set #'*state* 43 | (-> *state* 44 | (update-in [:entities eid] disj cname) 45 | (update-in [:components cname] dissoc eid))) 46 | this) 47 | (remove-entity [this eid] 48 | (let [state *state*] 49 | (var-set #'*state* 50 | (-> state 51 | (update-in [:entities] dissoc eid) 52 | (update-in [:components] 53 | (partial map-values #(dissoc % eid)))))) 54 | this) 55 | (-set-component [this eid cname cdata] 56 | (var-set #'*state* 57 | (-> *state* 58 | (update-in [:entities eid] conj cname) 59 | (update-in [:components cname] #(or % {})) 60 | (update-in [:components cname] conj [eid cdata]))) 61 | this) 62 | IQueryableWorld 63 | (-component [_ cname] (writables cname)) 64 | (component [_ eid cname] 65 | (when-not (contains? readables cname) 66 | (throw (RuntimeException. (str "Unknown component " cname)))) 67 | (get-in *state* [:components cname eid])) 68 | (query [_ q] 69 | (when-not (subset? (accessed q) readables) 70 | (throw (RuntimeException. (str "Unknown components " (difference (accessed q) 71 | readables))))) 72 | (reduce-kv (fn [coll k v] 73 | (if (q (seq v)) 74 | (conj coll k) 75 | coll)) 76 | (seq []) 77 | (:entities *state*)))) 78 | 79 | 80 | (deftype AtomWorld [components systems state] 81 | IWorld 82 | (-run [this readables writables f dt] 83 | (swap! (.state this) 84 | (fn [state] 85 | (binding [*state* state] 86 | (f (->AtomEditableWorld readables writables) dt) 87 | *state*))) 88 | this) 89 | (process! [this dt] 90 | (doseq [s (vals systems) 91 | :let [readables (union (:reads s) (:writes s)) 92 | writables (select-keys components (:writes s))]] 93 | (-run this readables writables s dt)) 94 | this)) 95 | 96 | 97 | (def atom-world-factory 98 | " 99 | Factory for creating atom-world's. 100 | 101 | See also [[clecs.world/world]]. 102 | " 103 | (reify 104 | IWorldFactory 105 | (-supported-types [_] #{Boolean Integer String}) 106 | (-world [_ components systems _] (->AtomWorld components 107 | systems 108 | (atom initial_state))))) 109 | 110 | 111 | ;; Hide internals from documentation generator. 112 | (doseq [v [#'->AtomWorld 113 | #'->AtomEditableWorld]] 114 | (alter-meta! v assoc :no-doc true)) 115 | -------------------------------------------------------------------------------- /test/clecs/query_test.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.query-test 2 | (:require [clecs.query :refer :all] 3 | [midje.sweet :refer :all]) 4 | (:import [clecs.query All Any Query])) 5 | 6 | 7 | (facts "Query primitives return an IQuery." 8 | (all :a) => (partial satisfies? IQueryNode) 9 | (any :a) => (partial satisfies? IQueryNode) 10 | (all :a :b :c) => (partial satisfies? IQueryNode) 11 | (any :a :b :c) => (partial satisfies? IQueryNode)) 12 | 13 | 14 | (fact "all inlines other `all` children." 15 | (all :a (all :b :c) :d) => (just (Query. (All. #{:a :b :c :d}) irrelevant))) 16 | 17 | 18 | (fact "all doesn't inline `any` children." 19 | (all :a (any :b :c) :d) => (just (Query. (All. #{:a 20 | (Any. #{:b :c}) 21 | :d}) 22 | irrelevant))) 23 | 24 | 25 | (fact "any inlines other `any` children." 26 | (any :a (any :b :c) :d) => (just (Query. (Any. #{:a :b :c :d}) irrelevant))) 27 | 28 | 29 | (fact "any doesn't inline `all` children." 30 | (any :a (all :b :c) :d) => (just (Query. (Any. #{:a 31 | (All. #{:b :c}) 32 | :d}) 33 | irrelevant))) 34 | 35 | 36 | (fact "Multiple levels of nesting is allowed." 37 | (any :a 38 | (all :b 39 | (any :c 40 | (all :d 41 | (any :e :f))))) => 42 | (just (Query. (Any. #{:a 43 | (All. #{:b 44 | (Any. #{:c 45 | (All. #{:d 46 | (Any. #{:e :f})})})})}) 47 | irrelevant))) 48 | 49 | 50 | (facts "Queries can be build from sub-queries." 51 | (let [sub-query-one (all :x :y) 52 | sub-query-two (all :u :v :w)] 53 | (any sub-query-one sub-query-two) => (just (Query. (Any. #{(All. #{:x :y}) 54 | (All. #{:u :v :w})}) 55 | irrelevant))) 56 | (let [sub-query-one (any :x :y) 57 | sub-query-two (any :u :v :w)] 58 | (all sub-query-one sub-query-two) => (just (Query. (All. #{(Any. #{:x :y}) 59 | (Any. #{:u :v :w})}) 60 | irrelevant)))) 61 | 62 | 63 | (facts "Single element sub-queries are eliminated." 64 | (all :a) => (just (Query. (All. #{:a}) irrelevant)) 65 | (any :a) => (just (Query. (Any. #{:a}) irrelevant)) 66 | (all :a (any :b) :c) => (just (Query. (All. #{:a :b :c}) irrelevant)) 67 | (any :a (all :b) :c) => (just (Query. (Any. #{:a :b :c}) irrelevant)) 68 | (any (all :a :b :c)) => (just (Query. (All. #{:a :b :c}) irrelevant)) 69 | (all (any :a :b :c)) => (just (Query. (Any. #{:a :b :c}) irrelevant)) 70 | (all :a (any (all :b :c)) :d) => (just (Query. (All. #{:a :b :c :d}) irrelevant)) 71 | (any :a (all (any :b :c)) :d) => (just (Query. (Any. #{:a :b :c :d}) irrelevant))) 72 | 73 | 74 | (fact "A query must have at least one criteria." 75 | (all) => (throws IllegalArgumentException) 76 | (any) => (throws IllegalArgumentException)) 77 | 78 | 79 | (facts "Queries are callable, checking if given components satisfy them." 80 | (let [q (all :a :b)] 81 | (q nil) => falsey 82 | (q [:a]) => falsey 83 | (q [:b]) => falsey 84 | (q [:c]) => falsey 85 | (q [:a :c]) => falsey 86 | (q [:b :c]) => falsey 87 | (q [:a :b]) => truthy 88 | (q [:a :b :c]) => truthy) 89 | (let [q (any :a :b)] 90 | (q nil) => falsey 91 | (q [:a]) => truthy 92 | (q [:b]) => truthy 93 | (q [:c]) => falsey 94 | (q [:a :c]) => truthy 95 | (q [:b :c]) => truthy 96 | (q [:a :b]) => truthy 97 | (q [:a :b :c]) => truthy) 98 | (let [q (all :a (any :b :c))] 99 | (q nil) => falsey 100 | (q [:a]) => falsey 101 | (q [:b]) => falsey 102 | (q [:c]) => falsey 103 | (q [:a :c]) => truthy 104 | (q [:b :c]) => falsey 105 | (q [:a :b]) => truthy 106 | (q [:a :b :c]) => truthy 107 | (q [:a :b :c :d]) => truthy) 108 | (let [q (any :a (all :b :c))] 109 | (q nil) => falsey 110 | (q [:a]) => truthy 111 | (q [:b]) => falsey 112 | (q [:c]) => falsey 113 | (q [:a :c]) => truthy 114 | (q [:b :c]) => truthy 115 | (q [:a :b]) => truthy 116 | (q [:a :b :c]) => truthy 117 | (q [:a :b :c :d]) => truthy)) 118 | 119 | 120 | (facts "IQueryNode/accessed returns all components in the query tree." 121 | (accessed (all :a :b)) => #{:a :b} 122 | (accessed (any :a :b)) => #{:a :b} 123 | (accessed (all :a (any :b :c))) => #{:a :b :c} 124 | (accessed (any :a (all :b :c))) => #{:a :b :c}) 125 | -------------------------------------------------------------------------------- /src/clecs/query.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.query 2 | "Primitives for query criteria. 3 | 4 | `query` method of [[clecs.world.queryable/IQueryableWorld]] 5 | is used to find entities based on their components. 6 | 7 | #### Examples: 8 | 9 | ;; Return a seq containing entity id's that 10 | ;; has FooComponent: 11 | (query queryable-world (all FooComponent)) 12 | 13 | ;; Same as the previous one: 14 | (query queryable-world (any FooComponent)) 15 | 16 | ;; Entities with both FooComponent and BarComponent 17 | (query queryable-world 18 | (all FooComponent BarComponent)) 19 | 20 | ;; Entities with either FooComponent or BarComponent 21 | (query queryable-world 22 | (any FooComponent BarComponent)) 23 | 24 | ;; Entities with FooComponent and either 25 | ;; BazComponent or BatComponent 26 | (query queryable-world 27 | (all FooComponent (any BazComponent 28 | BatComponent))) 29 | 30 | ;; Entities with either FooComponent and 31 | ;; BarComponent or BazComponent and BatComponent 32 | (query queryable-world 33 | (any (all FooComponent BarComponent) 34 | (all BazComponent BatComponent))) 35 | 36 | You can nest primitive calls infinitely. However 37 | it is considered bad design to query too many 38 | components at once. 39 | 40 | See also [[clecs.world.queryable/IQueryableWorld]]." 41 | (:import [clojure.lang Keyword])) 42 | 43 | 44 | (declare recursively-components 45 | simplify-children) 46 | 47 | 48 | (defprotocol ^:no-doc IQueryNode 49 | (satisfies [this components]) 50 | (simplify [this])) 51 | 52 | 53 | (defrecord Query [root components] 54 | clojure.lang.IFn 55 | (invoke [this cs] (satisfies this (set cs))) 56 | IQueryNode 57 | (satisfies [_ cs] (satisfies root cs)) 58 | (simplify [_] 59 | (let [new-root (simplify root) 60 | cs (recursively-components new-root)] 61 | (->Query new-root cs)))) 62 | 63 | 64 | (defrecord All [children] 65 | IQueryNode 66 | (satisfies [_ components] (loop [[head & tail] (seq children)] 67 | (if (nil? head) 68 | true 69 | (if (satisfies head components) 70 | (recur tail) 71 | false)))) 72 | (simplify [_] (simplify-children All ->All children))) 73 | 74 | 75 | (defrecord Any [children] 76 | IQueryNode 77 | (satisfies [_ components] (loop [[head & tail] (seq children)] 78 | (if (nil? head) 79 | false 80 | (if (satisfies head components) 81 | true 82 | (recur tail))))) 83 | (simplify [_] (simplify-children Any ->Any children))) 84 | 85 | 86 | (extend-protocol IQueryNode 87 | Keyword 88 | (satisfies [this components] (contains? components this)) 89 | (simplify [this] this)) 90 | 91 | 92 | (defn- query [constructor elems] 93 | (if (seq elems) 94 | (-> (set elems) 95 | (constructor) 96 | (->Query nil) 97 | (simplify)) 98 | (throw (IllegalArgumentException. "You cannot create an empty query")))) 99 | 100 | 101 | (defn accessed [q] 102 | (:components q)) 103 | 104 | 105 | (def ^{:deprecated "2.1.0"} accesses accessed) 106 | 107 | 108 | (defn all [& elems] 109 | (query ->All elems)) 110 | 111 | 112 | (defn any [& elems] 113 | (query ->Any elems)) 114 | 115 | 116 | (defn- recursively-components 117 | "Recursively walking root, Return a set of all component names used." 118 | [root] 119 | (->> root 120 | (tree-seq (partial satisfies? IQueryNode) :children) 121 | (filter keyword?) 122 | (set))) 123 | 124 | 125 | (defn- simplify-children 126 | "Simplify, flatten & inline children with respect to node-type." 127 | [node-type constructor children] 128 | (let [f (fn [acc elem] 129 | (cond 130 | ;; Not a query, add as is. 131 | (not (instance? Query elem)) (conj acc elem) 132 | ;; A query with root node with the same type, inline its children. 133 | (instance? node-type (:root elem)) (into acc (get-in elem [:root :children])) 134 | ;; A query with a root node with one element, inline the element. 135 | (= (count (get-in elem [:root :children])) 1) (conj acc (first (get-in elem [:root :children]))) 136 | ;; A query with root node of different type add the root node. 137 | :else (conj acc (:root elem)))) 138 | new-children (->> children 139 | (map simplify) 140 | (reduce f #{}))] 141 | (if (and (= (count new-children) 1) 142 | (not (keyword? (first (seq new-children)))) 143 | (satisfies? IQueryNode (first (seq new-children)))) 144 | (first new-children) 145 | (constructor new-children)))) 146 | 147 | 148 | ;; Hide internals from documentation generator. 149 | (doseq [v [#'->All 150 | #'map->All 151 | #'->Any 152 | #'map->Any 153 | #'->Query 154 | #'map->Query]] 155 | (alter-meta! v assoc :no-doc true)) 156 | -------------------------------------------------------------------------------- /test/clecs/system_test.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.system-test 2 | (:require [clecs.system :refer :all] 3 | [midje.sweet :refer :all])) 4 | 5 | 6 | (defn mock-system [_ _] nil) 7 | 8 | 9 | (fact "system requires :name, :process or :process-fn and :reads or :writes." 10 | (let [s {:name :FooSystem 11 | :process 'clecs.system-test/mock-system 12 | :process-fn mock-system 13 | :reads #{:BarComponent} 14 | :writes #{:BazComponent}}] 15 | (system nil) => (throws IllegalArgumentException) 16 | (system (select-keys s [:name :process])) => (throws IllegalArgumentException 17 | "Either :reads or :writes must be specified") 18 | (system (select-keys s [:name :process-fn])) => (throws IllegalArgumentException 19 | "Either :reads or :writes must be specified") 20 | (system (select-keys s [:name :reads])) => (throws IllegalArgumentException 21 | "Either :process or :process-fn must be specified") 22 | (system (select-keys s [:name :writes])) => (throws IllegalArgumentException 23 | "Either :process or :process-fn must be specified") 24 | (system (select-keys s [:process :reads])) => (throws IllegalArgumentException 25 | ":name is required for systems") 26 | (system (select-keys s [:process :writes])) => (throws IllegalArgumentException 27 | ":name is required for systems") 28 | (system (select-keys s [:process-fn :reads])) => (throws IllegalArgumentException 29 | ":name is required for systems") 30 | (system (select-keys s [:process-fn :writes])) => (throws IllegalArgumentException 31 | ":name is required for systems"))) 32 | 33 | 34 | (fact ":process must be a symbol." 35 | (let [s {:name :FooSystem 36 | :reads #{:FooComponent}}] 37 | (system (assoc s :process nil)) => (throws IllegalArgumentException 38 | ":process must be a symbol") 39 | (system (assoc s :process 1)) => (throws IllegalArgumentException 40 | ":process must be a symbol") 41 | (system (assoc s :process true)) => (throws IllegalArgumentException 42 | ":process must be a symbol") 43 | (system (assoc s :process (fn [& _] nil))) => (throws IllegalArgumentException 44 | ":process must be a symbol"))) 45 | 46 | 47 | (fact ":process-fn must be a function." 48 | (let [s {:name :FooSystem 49 | :reads #{:FooComponent}}] 50 | (system (assoc s :process-fn nil)) => (throws IllegalArgumentException 51 | ":process-fn must be a function.") 52 | (system (assoc s :process-fn 1)) => (throws IllegalArgumentException 53 | ":process-fn must be a function.") 54 | (system (assoc s :process-fn true)) => (throws IllegalArgumentException 55 | ":process-fn must be a function.") 56 | (system (assoc s :process-fn 'foo)) => (throws IllegalArgumentException 57 | ":process-fn must be a function."))) 58 | 59 | 60 | (fact "If :process-fn is missing resolve it using :process." 61 | (system {:name :FooSystem 62 | :process 'clecs.system-test/mock-system 63 | :reads #{:FooComponent}}) => (contains [[:process-fn (exactly mock-system)]])) 64 | 65 | 66 | (fact "At least one component must be specified in :reads or :writes." 67 | (let [s {:name :FooSystem 68 | :process-fn (fn [& _] nil) 69 | :reads nil 70 | :writes nil} 71 | s-reads (assoc s :reads #{:BarComponent}) 72 | s-writes (assoc s :writes #{:BazComponent}) 73 | s-reads-writes (-> s 74 | (assoc :reads #{:BarComponent}) 75 | (assoc :writes #{:BazComponent}))] 76 | (system s) => (throws IllegalArgumentException 77 | "At least one component must be specified") 78 | (system s-reads) =not=> (throws IllegalArgumentException 79 | "At least one component must be specified") 80 | (system s-writes) =not=> (throws IllegalArgumentException 81 | "At least one component must be specified") 82 | (system s-reads-writes) =not=> (throws IllegalArgumentException 83 | "At least one component must be specified"))) 84 | 85 | 86 | (fact "Any component in :writes implies :reads, remove them from :reads." 87 | (system {:name :FooSystem 88 | :process-fn (fn [& _] nil) 89 | :reads #{:BarComponent :BazComponent} 90 | :writes #{:BazComponent}}) => (contains [[:reads #{:BarComponent}] 91 | [:writes #{:BazComponent}]])) 92 | 93 | 94 | (fact ":reads & :writes are converted to sets." 95 | (let [s {:name :FooSystem 96 | :process-fn (fn [& _] nil)}] 97 | (system (assoc s :reads [:FooComponent])) => (contains [[:reads #{:FooComponent}]]) 98 | (system (assoc s :reads '(:FooComponent))) => (contains [[:reads #{:FooComponent}]]) 99 | (system (assoc s :writes [:FooComponent])) => (contains [[:writes #{:FooComponent}]]) 100 | (system (assoc s :writes '(:FooComponent))) => (contains [[:writes #{:FooComponent}]]))) 101 | 102 | 103 | (future-fact "serializable?") 104 | 105 | 106 | ;; Deserialize is just calling system on the result of serialize. 107 | (future-fact "serialize") 108 | -------------------------------------------------------------------------------- /doc/index.adoc: -------------------------------------------------------------------------------- 1 | Clecs Documentation 2 | =================== 3 | Atamert Ölçgen 4 | 2.0.x 5 | :toc: left 6 | :numbered: 7 | :source-highlighter: pygments 8 | :pygments-style: friendly 9 | 10 | Entity-component-system for Clojure. 11 | 12 | 13 | image:https://travis-ci.org/muhuk/clecs.svg?branch=master["Build Status", link=https://travis-ci.org/muhuk/clecs] 14 | 15 | Goals of this project are: 16 | 17 | * Be game-engine agnostic. 18 | 19 | * Support massive games as well as fast-paced ones. 20 | 21 | * Transparently optimize operations. 22 | 23 | 24 | Latest Release 25 | -------------- 26 | 27 | Leiningen 28 | ~~~~~~~~~ 29 | 30 | Add this to your `:dependencies` in `project.clj`: 31 | 32 | image:http://clojars.org/clecs/latest-version.svg["Clojars Project", link=http://clojars.org/clecs] 33 | 34 | 35 | Usage 36 | ----- 37 | 38 | Clecs is not a fully featured game engine. A game engine typically provides 39 | user input, sound and video output and a main loop. Then the application 40 | code is written to glue these features to form a game. Clecs helps organize 41 | the application code by restricting the data model and state management. 42 | 43 | Clecs is built on these five concepts; entities, components, systems, worlds 44 | & queries. See http://clecs.muhuk.com/latest/api/clecs.world.html[clecs.world] 45 | documentation for more info on these concepts and how to create a world. 46 | It would be helpful to read that first. 47 | 48 | Once you have your world initialized, it is quite easy to use it in your 49 | main loop: 50 | 51 | [source, Clojure] 52 | ---- 53 | ;; Initialize your world 54 | (def w (clecs.world/world ...)) 55 | 56 | ;; Call process! on it in your main loop 57 | (game-engine/main-loop (fn [dt] (clecs.world/process! w dt))) 58 | ---- 59 | 60 | `process!` returns the world. World interacts with the game engine via 61 | systems. For example a rendering system would be written like: 62 | 63 | [source, Clojure] 64 | ---- 65 | ;; render-background & render-entity use 66 | ;; game engine's methods on screen to do 67 | ;; actual rendering. 68 | (defn rendering-system [screen] 69 | (fn [w dt] 70 | (render-background screen) 71 | (doseq [eid (clecs.world/query w renderable-entities)] 72 | (render-entity w eid screen)))) 73 | ---- 74 | 75 | This way other systems in the world would have no knowledge 76 | about the game engine doing any rendering. It might as well 77 | be that there are multiple game engindes running or none. 78 | 79 | 80 | 81 | Examples 82 | -------- 83 | 84 | Official demos are link:https://github.com/muhuk/clecs-examples[here]. 85 | 86 | 87 | API Documentation 88 | ----------------- 89 | 90 | API documentation is link:http://clecs.muhuk.com/2.0.x/api/[here]. 91 | 92 | 93 | Changelog 94 | --------- 95 | 96 | .Changes Since Version 2.0.0 97 | 98 | * Added validation to `atom-world`'s `query` implementation. It doesn't allow 99 | unknown (unreadable by currently running system) components to be queries anymore. 100 | 101 | * `clecs.query/accesses` is deprecated. Use `accessed` instead. 102 | 103 | .Changes Since Version 1.1.0 104 | 105 | * `ITransactableWorld` and transaction concept are removed. 106 | + 107 | [source, Clojure] 108 | ---- 109 | ;; A systems process used to call transaction! 110 | ;; to get an editable world: 111 | (comment 112 | (fn [w dt] 113 | (transaction! w (fn [w] (set-component! w ...)))) 114 | 115 | ;; Now process receives an editable world and can modify 116 | ;; the world directly: 117 | (fn [w dt] 118 | (set-component! w ...)) 119 | ---- 120 | 121 | * Components and systems are registered with worlds during 122 | initialization. Use `clecs.world/world` to create a new world. 123 | 124 | ** `clecs.backend.atom-world/make-world` is removed. Use 125 | `clecs.world/world` with 126 | `clecs.world.backend.atom-world/atom-world-factory` instead: 127 | + 128 | [source, Clojure] 129 | ---- 130 | (world atom-world-factory 131 | {:components [(component ...) 132 | (component ...) 133 | (component ...) 134 | ...] 135 | :initializer (fn [w] ...) 136 | :systems [(system ...) 137 | (system ...) 138 | ...]}) 139 | ---- 140 | 141 | ** `ISystemManager` is renamed as `IWorld`. 142 | + 143 | Following functions are removed from this protocol: 144 | 145 | *** `remove-system!` 146 | 147 | *** `set-system!` 148 | 149 | *** `systems` 150 | 151 | * `world/set-component` accepts entity-id and component type explicitly. 152 | + 153 | [source, Clojure] 154 | ---- 155 | ;; Old way: 156 | (comment 157 | (def c (->SomeComponent eid x y)) 158 | (world/set-component w c)) 159 | 160 | ;; New way: 161 | (world/set-component w eid :my.ns/SomeComponent {:x x :y y}) 162 | ---- 163 | 164 | * `world/set-component` validates its input. 165 | 166 | * Queries are constructed using component type keywords instead 167 | of records: 168 | + 169 | [source, Clojure] 170 | ---- 171 | ;; Old way: 172 | (comment 173 | (defrecord SomeComponent [eid]) 174 | (def q (all SomeComponent))) 175 | 176 | ;; New way: 177 | (def q (all :my.ns/SomeComponent)) 178 | ---- 179 | 180 | * `clecs.core/make-world` is removed. 181 | 182 | * Following deprecated functions are removed: 183 | 184 | ** `clecs.component/component?` 185 | 186 | ** `clecs.component/component-label` 187 | 188 | ** `clecs.component/component-type?` 189 | 190 | ** `clecs.component/defcomponent` 191 | 192 | * Removed support for systems as functions. 193 | 194 | * Deprecated `IComponent` is removed. 195 | 196 | 197 | .Changes Since Version 1.0.0 198 | 199 | * Components are no longer records. The same API still works but since 200 | a java class is not generated anymore you are likely to get an error 201 | like: 202 | + 203 | [source, Java] 204 | ---- 205 | java.lang.ClassNotFoundException: java_path.to.YourComponent 206 | ---- 207 | + 208 | To solve this problem declare components in a `:require` instead 209 | of `:import`. (Examples: 210 | link:https://github.com/muhuk/clecs-examples/commit/a965ab138b888d3137742aa290be87d9e1528bd1[muhuk/clecs-examples@a965ab1] 211 | & link:https://github.com/muhuk/clecs-examples/commit/22de34f592ca6cf3609e0822b9fd2ce6bf30afd0[muhuk/clecs-examples@22de34f]) 212 | 213 | * Added suport for systems as maps. Assuming `sys-fn` is an old 214 | style system you can write a system as: 215 | + 216 | [source, Clojure] 217 | ---- 218 | {:process sys-fn} 219 | ---- 220 | 221 | 222 | .Changes Since Version 0.2.0 223 | 224 | * Replaced function based queries with data driven queries. See `clecs.query`. 225 | 226 | 227 | Previous Versions 228 | ----------------- 229 | 230 | * link:http://clecs.muhuk.com/1.1.x/user_guide/[v1.1.x] 231 | 232 | 233 | See Also 234 | -------- 235 | 236 | * link:http://gamadu.com/artemis/[Artemis] 237 | * link:https://github.com/markmandel/brute[brute] 238 | * link:https://bitbucket.org/mludwig/entreri/overview[entreri] 239 | 240 | 241 | License 242 | ------- 243 | 244 | Copyright (C) 2015 Atamert Ölçgen 245 | 246 | This program is distributed under GNU GPL v3 license. See `LICENSE` file. 247 | 248 | 249 | ++++ 250 | 260 | ++++ 261 | -------------------------------------------------------------------------------- /src/clecs/world.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.world 2 | "Protocols that define clecs API. 3 | 4 | #### Definitions 5 | 6 | World 7 | : Worlds are top level containers. Entities, 8 | components are stored in worlds and systems 9 | are run within the context of worlds. 10 | 11 | Entity 12 | : Objects in the world. Entities are merely 13 | identifiers, they do not store any information. 14 | This is an important difference from object 15 | oriented data structures, where all information 16 | related to an object is stored with the object. 17 | 18 | Clecs refers to entities as `entity-id` or 19 | `eid`. An entity-id can be an integer or a 20 | UUID or any other type depending on the 21 | world implementation. 22 | 23 | Component 24 | : Components encode a single aspect about an 25 | entity. Entities can have any number of 26 | components and components can be added or 27 | removed from an entity during its lifetime. 28 | Therefore there is not necessarily a static 29 | set components for a set of entities. This 30 | is another difference from the object oriented 31 | approach which enforces a static template of 32 | aspects for each type of entity. 33 | 34 | System 35 | : Systems are either maps (new style) or (old style) 36 | callables that define operations 37 | over the world. Each system should ideally 38 | deal with a unique aspect of the application. 39 | 40 | Systems may primarily deal with a single 41 | component but it is not a requirement. 42 | 43 | Query 44 | : There are two kinds of queries in clecs: 45 | 46 | 1. Queries for entities associated with certain 47 | components. 48 | 1. Queries of a certain entity for its individual 49 | components. 50 | 51 | Queries can be only be run by systems. 52 | 53 | 54 | #### Relationships Between Concepts 55 | 56 | +++++++++++++++++++++++++++++++++++++ 57 | | | 58 | | Main +++++++++++++++++++++++++ | 59 | | Loop | | | 60 | | | World | | 61 | | | | | 62 | | | +++++++++++++++++++ | | 63 | | | | | | | 64 | | | | Components | | | 65 | | | | | | | 66 | | | +++++++++++++++++++ | | 67 | | | | | | | 68 | | | | Systems | | | 69 | | | | | | | 70 | | | | +++++++++++++ | | | 71 | | | | | | | | | 72 | | | | | Queries | | | | 73 | | | | | | | | | 74 | | | | +++++++++++++ | | | 75 | | | | | | | 76 | | | +++++++++++++++++++ | | 77 | | | | | 78 | | +++++++++++++++++++++++++ | 79 | | | 80 | +++++++++++++++++++++++++++++++++++++ 81 | " 82 | (:require [clecs.component :refer [validate]] 83 | [clecs.world.validate :refer [validate-world]])) 84 | 85 | 86 | (defprotocol IEditableWorld 87 | (-set-component 88 | [this eid cname cdata] 89 | "Sets a component without validating. 90 | 91 | Use [[set-component]] instead of calling this directly. 92 | 93 | #### Parameters: 94 | 95 | eid 96 | : Entity id. 97 | 98 | cname 99 | : Component name. 100 | 101 | cdata 102 | : Component data as a map. 103 | ") 104 | (add-entity 105 | [this] 106 | "Create a new entity in the world and return its id.") 107 | (remove-component 108 | [this eid cname] 109 | "Remove the component of type `cname` that is associated 110 | with `eid` and return `nil`. 111 | 112 | This method is a no-op if there is no relevant component. 113 | 114 | #### Parameters: 115 | 116 | eid 117 | : Entity id. 118 | 119 | cname 120 | : Component name. 121 | ") 122 | (remove-entity 123 | [this eid] 124 | "Delete the entity with `eid`, all components 125 | associated with it and return `nil`. 126 | 127 | This method is a no-op if there is no relevant entity. 128 | 129 | #### Parameters: 130 | 131 | eid 132 | : Entity id. 133 | ")) 134 | 135 | 136 | (defprotocol IQueryableWorld 137 | (-component 138 | [this cname] 139 | "Return component definition for `cname` or `nil` 140 | if none found. 141 | 142 | Use [[component]] to query a component for an entity. 143 | 144 | #### Parameters: 145 | 146 | cname 147 | : Component name. 148 | ") 149 | (component 150 | [this eid cname] 151 | "Return the component of type `cname` associated with 152 | `eid`, or `nil` if none found. 153 | 154 | #### Parameters: 155 | 156 | eid 157 | : Entity id. 158 | 159 | cname 160 | : Component name. 161 | ") 162 | (query 163 | [this q] 164 | "Return a sequence of entity id's using `q` as 165 | filter criteria. 166 | 167 | #### Parameters: 168 | 169 | q 170 | : A query object. See [[clecs.query]] for more 171 | info. 172 | ")) 173 | 174 | 175 | (defprotocol IWorld 176 | (-run 177 | [this reads writes f dt] 178 | "Run `f` passing it an editable world and `dt`. Return world. 179 | 180 | Use [[process!]] instead of calling `-run` directly. 181 | 182 | #### Parameters: 183 | 184 | reads 185 | : Components that are readable for `f`. 186 | 187 | writes 188 | : Components that are readable & writable for `f`. 189 | 190 | f 191 | : A function that takes an editable world and 192 | a time increment as parameters. 193 | 194 | dt 195 | : Time passed since this function is last run. 196 | ") 197 | (process! 198 | [this dt] 199 | "Run systems using `dt` as time increment. 200 | 201 | This is the function that will be called in 202 | the main loop. 203 | 204 | #### Parameters: 205 | 206 | dt 207 | : Time passed since process! was called last 208 | time. This value is passed to the systems. 209 | It is recommended to use miliseconds as 210 | unit. 211 | ")) 212 | 213 | 214 | (defprotocol IWorldFactory 215 | (-supported-types 216 | [this] 217 | "Component parameter types supported by this backend.") 218 | (-world 219 | [this components systems extra-config] 220 | "Creates a world. 221 | 222 | #### Parameters: 223 | 224 | components 225 | : A map of component names to components. 226 | 227 | systems 228 | : A map of system names to systems. 229 | 230 | extra-config 231 | : A map containing other elements passed 232 | to [[world]]. Some backends may require 233 | certains parameters other than components 234 | and systems. 235 | 236 | Use [[world]] instead of calling this directly.")) 237 | 238 | 239 | (defn set-component 240 | "Set `eid`'s `cname` component as `cdata` and return 241 | `nil`. 242 | 243 | #### Parameters: 244 | 245 | world 246 | : World. 247 | 248 | eid 249 | : Entity id. 250 | 251 | cname 252 | : Component type. 253 | 254 | cdata 255 | : Component data as a map." 256 | [world eid cname cdata] 257 | (if-some [c (-component world cname)] 258 | (do 259 | (validate c cdata) 260 | (-set-component world eid cname cdata) 261 | nil) 262 | (throw (RuntimeException. (str "Unknown component " cname))))) 263 | 264 | 265 | (defn world 266 | "Creates a world. 267 | 268 | #### Parameters: 269 | 270 | world-factory 271 | : A instance of [[IWorldFactory]]. 272 | 273 | params 274 | : A map of world parameters. Keys recognized by 275 | all backends are described below: 276 | 277 | :components 278 | : A sequence of [[clecs.component/component]] 279 | instances. At least one component must be 280 | specified. 281 | 282 | :initializer 283 | : A function that takes an editable world and 284 | run as soon as the world is created. 285 | 286 | :systems 287 | : A sequence of [[clecs.system/system]] 288 | instances. At least one system must be 289 | specified. 290 | 291 | Any other keys will be passed to the factory. 292 | See the backend's documentation for recognized 293 | parameters. 294 | 295 | 296 | #### Examples: 297 | 298 | (clecs.world/world atom-world-factory 299 | {:components [(component ...) 300 | (component ...) 301 | (component ...) 302 | ...] 303 | :initializer (fn [w] ...) 304 | :systems [(system ...) 305 | (system ...) 306 | ...]}) 307 | " 308 | [world-factory 309 | {components :components 310 | initializer :initializer 311 | systems :systems :as params}] 312 | (validate-world components 313 | systems 314 | (-supported-types world-factory)) 315 | (let [systems-map (->> systems 316 | (map (juxt :name identity)) 317 | (into {})) 318 | components-map (->> components 319 | (map (juxt :name identity)) 320 | (into {})) 321 | extra-config (dissoc params 322 | :components 323 | :initializer 324 | :systems) 325 | w (-world world-factory 326 | components-map 327 | systems-map 328 | extra-config)] 329 | (if initializer 330 | (-run w 331 | components-map 332 | components-map 333 | (fn [w _] (initializer w)) 334 | nil) 335 | w))) 336 | -------------------------------------------------------------------------------- /test/clecs/backend/atom_world_test.clj: -------------------------------------------------------------------------------- 1 | (ns clecs.backend.atom-world-test 2 | (:require [clecs.backend.atom-world :refer :all] 3 | [clecs.component :refer [component]] 4 | [clecs.query :refer [accessed]] 5 | [clecs.system :refer [system]] 6 | [clecs.test.checkers :refer :all] 7 | [clecs.world :as world] 8 | [midje.sweet :refer :all])) 9 | 10 | 11 | (def editable-world-like (implements-protocols world/IEditableWorld 12 | world/IQueryableWorld)) 13 | 14 | 15 | ;; World Initialization. 16 | 17 | 18 | (fact "Supported types for atom-world." 19 | (world/-supported-types atom-world-factory) => (contains [Boolean 20 | Integer 21 | String] 22 | :in-any-order)) 23 | 24 | 25 | (fact "Atom world implements IWorld." 26 | (world/-world atom-world-factory 27 | nil 28 | nil 29 | nil) => (implements-protocols world/IWorld)) 30 | 31 | 32 | ;; Processing 33 | 34 | (fact "world/-run runs arbitrary code with an editable world, returns world." 35 | (let [w (world/-world atom-world-factory nil nil nil)] 36 | (world/-run w ..reads.. ..writes.. --f-- ..dt..) => w 37 | (provided (--f-- editable-world-like ..dt..) => anything))) 38 | 39 | 40 | (fact "process! returns the world." 41 | (let [w (world/-world atom-world-factory nil nil nil)] 42 | (world/process! w ..dt..) => w)) 43 | 44 | 45 | (fact "process! calls each system with the world and delta time." 46 | (let [calls (atom []) 47 | s-one (system {:name :s-one 48 | :process-fn (fn [& args] (swap! calls 49 | conj 50 | [:one-called args])) 51 | :reads #{:Foo}}) 52 | s-two (system {:name :s-two 53 | :process-fn (fn [& args] (swap! calls 54 | conj 55 | [:two-called args])) 56 | :reads #{:Foo}}) 57 | w (world/-world atom-world-factory 58 | nil 59 | {:s-one s-one 60 | :s-two s-two} 61 | nil)] 62 | (world/process! w ..dt..) => irrelevant 63 | (set (map first @calls)) => (set [:one-called :two-called]) 64 | (-> @calls first second first) => editable-world-like 65 | (-> @calls first second second) => ..dt.. 66 | (-> @calls second second first) => editable-world-like 67 | (-> @calls second second second) => ..dt..)) 68 | 69 | 70 | (fact "process! calls -run with components filtered by system." 71 | (let [components {:Foo (component :Foo nil) 72 | :Bar (component :Bar nil) 73 | :Baz (component :Baz nil)} 74 | systems {:s1 (system {:name :s1 75 | :process-fn (fn [w dt] (--s1-- w dt)) 76 | :reads #{:Bar} 77 | :writes #{:Foo}}) 78 | :2 (system {:name :s2 79 | :process-fn (fn [w dt] (--s2-- w dt)) 80 | :reads #{:Baz} 81 | :writes #{:Bar}})} 82 | w (world/-world atom-world-factory 83 | components 84 | systems 85 | nil)] 86 | (world/process! w ..dt..) => irrelevant 87 | (provided (->AtomEditableWorld #{:Foo :Bar} 88 | {:Foo (:Foo components)}) => ..editable-1.. 89 | (->AtomEditableWorld #{:Bar :Baz} 90 | {:Bar (:Bar components)}) => ..editable-2.. 91 | (--s1-- ..editable-1.. ..dt..) => irrelevant 92 | (--s2-- ..editable-2.. ..dt..) => irrelevant))) 93 | 94 | 95 | ;; Editable world. 96 | 97 | (fact "world/-component returns the component definition." 98 | (let [w (->AtomEditableWorld nil 99 | {::TestComponentA ..a.. 100 | ::TestComponentB ..b..})] 101 | (world/-component w ::TestComponentA) => ..a.. 102 | (world/-component w ::TestComponentB) => ..b..)) 103 | 104 | 105 | (fact "world/-component returns nil if component is not recognized." 106 | (let [w (->AtomEditableWorld nil {::TestComponentA ..c..})] 107 | (world/-component w ::TestComponentC) => nil)) 108 | 109 | 110 | (fact "world/-set-component adds the component if entity doesn't have one." 111 | (let [eid 1 112 | cdata {} 113 | w (->AtomEditableWorld #{::TestComponentA} nil) 114 | initial-state {:components {} 115 | :entities {eid #{}}} 116 | expected-state {:components {::TestComponentA {eid cdata}} 117 | :entities {eid #{::TestComponentA}}}] 118 | (binding [*state* initial-state] 119 | (world/-set-component w eid ::TestComponentA cdata) => w 120 | *state* => expected-state))) 121 | 122 | 123 | (fact "world/-set-component replaces existing components." 124 | (let [eid 1 125 | c-old {:a ..a.. :b ..b..} 126 | c-new {:a ..c.. :b ..d..} 127 | w (->AtomEditableWorld #{::TestComponentB} nil) 128 | initial-state {:components {::TestComponentB {eid c-old}} 129 | :entities {eid #{::TestComponentB}}} 130 | expected-state {:components {::TestComponentB {eid c-new}} 131 | :entities {eid #{::TestComponentB}}}] 132 | (binding [*state* initial-state] 133 | (world/-set-component w eid ::TestComponentB c-new) => w 134 | *state* => expected-state))) 135 | 136 | 137 | (fact "world/add-entity returns a new entity id." 138 | (binding [*state* {:last-entity-id 0}] 139 | (world/add-entity (->AtomEditableWorld nil nil)) => 1) 140 | (binding [*state* {:last-entity-id 41}] 141 | (world/add-entity (->AtomEditableWorld nil nil)) => 42)) 142 | 143 | 144 | (fact "world/add-entity adds the new entity-id to the entity index." 145 | (binding [*state* {:last-entity-id 0}] 146 | (let [eid (world/add-entity (->AtomEditableWorld nil nil))] 147 | (get-in *state* [:entities eid]) => #{}))) 148 | 149 | 150 | (fact "world/add-entity updates entity counter." 151 | (binding [*state* {:last-entity-id 0}] 152 | (world/add-entity (->AtomEditableWorld nil nil)) 153 | (:last-entity-id *state*) => 1)) 154 | 155 | 156 | (fact "world/add-entity returns different values each time it's called." 157 | (let [w (->AtomEditableWorld nil nil)] 158 | (binding [*state* {:entities {} 159 | :last-entity-id 0}] 160 | (repeatedly 42 #(world/add-entity w)) => (comp #(= % 42) count set)))) 161 | 162 | 163 | (fact "world/component resolves queried component." 164 | (binding [*state* {:components {::TestComponentA {..eid.. ..component..}}}] 165 | (let [w (->AtomEditableWorld #{::TestComponentA} nil)] 166 | (world/component w ..eid.. ::TestComponentA) => ..component..))) 167 | 168 | 169 | (fact "world/component rejects unknown components." 170 | (let [w (->AtomEditableWorld {::TestComponentA ..c..} nil)] 171 | (world/component w ..eid.. ::TestComponentB) => (throws RuntimeException 172 | #"Unknown component" 173 | #"TestComponentB"))) 174 | 175 | 176 | (fact "world/query calls the query with a seq of component labels." 177 | (binding [*state* {:entities {..e1.. #{..c1.. ..c2..} 178 | ..e2.. #{..c2..} 179 | ..e3.. #{..c3..}}}] 180 | (world/query (->AtomEditableWorld nil nil) --q--) => (just [..e1.. ..e3..] :in-any-order) 181 | (provided (accessed anything) => #{} 182 | (--q-- (just [..c1.. ..c2..] :in-any-order)) => true 183 | (--q-- [..c2..]) => false 184 | (--q-- [..c3..]) => true))) 185 | 186 | 187 | (fact "world/query rejects queries trying to access unknown components." 188 | (world/query (->AtomEditableWorld #{::TestComponentA 189 | ::TestComponentC} 190 | nil) ..q..) => (throws RuntimeException 191 | #"Unknown components" 192 | #"TestComponentB") 193 | (provided (accessed ..q..) => #{::TestComponentB 194 | ::TestComponentC})) 195 | 196 | 197 | (fact "world/remove-component works." 198 | (let [w (->AtomEditableWorld nil {..cname.. ..c..}) 199 | initial-state {:components {..cname.. {..eid.. ..component..}} 200 | :entities {..eid.. #{..cname..}}} 201 | expected-state {:components {..cname.. {}} 202 | :entities {..eid.. #{}}}] 203 | (binding [*state* initial-state] 204 | (world/remove-component w ..eid.. ..cname..) => w 205 | *state* => expected-state))) 206 | 207 | 208 | (fact "world/remove-component rejects unknown component." 209 | (let [w (->AtomEditableWorld nil {::TestComponentA ..c..})] 210 | (world/remove-component w ..eid.. ::TestComponentB) => (throws RuntimeException 211 | #"Unknown component" 212 | #"TestComponentB"))) 213 | 214 | 215 | (fact "world/remove-entity removes entity-id from entity index." 216 | (let [w (->AtomEditableWorld nil nil)] 217 | (binding [*state* {:components {} 218 | :entities {1 #{}} 219 | :last-entity-id 1}] 220 | (world/remove-entity w 1) => w 221 | *state* => {:components {} 222 | :entities {} 223 | :last-entity-id 1}))) 224 | 225 | 226 | (fact "world/remove-entity removes entity's components." 227 | (let [cname ::TestComponentA 228 | w (->AtomEditableWorld nil nil) 229 | initial-state {:components {cname {..eid.. ..i.. ..other-eid.. ..j..}} 230 | :entities {}} 231 | expected-state {:components {cname {..other-eid.. ..j..}} 232 | :entities {}}] 233 | (binding [*state* initial-state] 234 | (world/remove-entity w ..eid..) => w 235 | *state* => expected-state))) 236 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------