]> git.jsancho.org Git - gacela.git/blob - src/system.scm
Merge tag '0.6' into develop
[gacela.git] / src / system.scm
1 ;;; Gacela, a GNU Guile extension for fast games development
2 ;;; Copyright (C) 2013 by Javier Sancho Fernandez <jsf at jsancho dot org>
3 ;;;
4 ;;; This program is free software: you can redistribute it and/or modify
5 ;;; it under the terms of the GNU General Public License as published by
6 ;;; the Free Software Foundation, either version 3 of the License, or
7 ;;; (at your option) any later version.
8 ;;;
9 ;;; This program is distributed in the hope that it will be useful,
10 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 ;;; GNU General Public License for more details.
13 ;;;
14 ;;; You should have received a copy of the GNU General Public License
15 ;;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17
18 (define-module (gacela system)
19   #:use-module ((bongodb) #:renamer (symbol-prefix-proc 'bongodb:))
20   #:use-module (ice-9 receive)
21   #:use-module (srfi srfi-1)
22   #:use-module (srfi srfi-9)
23   #:use-module (srfi srfi-9 gnu)
24   #:export (define-component
25             export-component
26             get-component-type
27             make-entity-set
28             entity-list
29             entity-count
30             new-entity
31             get-entity
32             remove-entity
33             set-entity
34             set-entity-components
35             remove-entity-components
36             modify-entities
37             entities-changes
38             entities-changes?
39             get-entities-changes
40             find-entities-by-components
41             define-system
42             make-system
43             join-systems
44             thread-systems
45             get-key
46             get-component))
47
48
49 ;;; Component definitions
50
51 (define (symbol-concatenate . args)
52   (string->symbol
53    (string-concatenate
54     (map (lambda (a) (if (symbol? a) (symbol->string a) a)) args))))
55
56 (define-syntax define-component
57   (lambda (x)
58     (define (concat . args)
59       (datum->syntax x
60         (apply symbol-concatenate
61           (map (lambda (a)
62                  (if (string? a)
63                      a
64                      (syntax->datum a)))
65                args))))
66     (define (filtered-args args)
67       (let ((datum (map (lambda (a) (syntax->datum a)) args)))
68         (map (lambda (a) (datum->syntax x a))
69              (map (lambda (a) (if (list? a) (car a) a))
70                   (filter (lambda (a) (not (keyword? a))) datum)))))
71     (syntax-case x ()
72       ((_ name field ...)
73        (with-syntax ((make-name (concat "make-" #'name))
74                      (make-name-record (concat "make-" #'name "-record"))
75                      (name? (concat #'name "?"))
76                      ((field-name ...) (filtered-args #'(field ...)))
77                      ((field-getter ...) (map (lambda (f) (concat #'name "-" f)) (filtered-args #'(field ...))))
78                      ((field-setter ...) (map (lambda (f) (concat "set-" #'name "-" f "!")) (filtered-args #'(field ...)))))
79          #'(begin
80              (define* (make-name field ...)
81                (make-name-record field-name ...))
82              (define-record-type name
83                (make-name-record field-name ...)
84                name?
85                (field-name field-getter field-setter)
86                ...)
87              (set-record-type-printer! name
88                (lambda (record port)
89                  (format port "#<[~a]" 'name)
90                  (format port " ~a: ~a" 'field-name (field-getter record))
91                  ...
92                  (format port ">")))
93              'name))))))
94
95 (define (export-component component)
96   (let ((name (record-type-name component))
97         (m (current-module)))
98     (module-export! m (list
99                        (symbol-concatenate "make-" name)
100                        (symbol-concatenate name "?")))
101     (for-each
102      (lambda (a)
103        (module-export! (current-module)
104                        (list
105                         (symbol-concatenate name "-" a)
106                         (symbol-concatenate "set-" name "-" a "!"))))
107      (record-type-fields component))))
108
109 (define (get-component-type component)
110   (record-type-name (record-type-descriptor component)))
111
112
113 ;;; Entities and components
114
115 (define (make-entity-set . changes)
116   (modify-entities (bongodb:make-collection) changes))
117
118 (define (entity-list entity-set)
119   (bongodb:find entity-set))
120
121 (define (entity-count entity-set)
122   (bongodb:count entity-set))
123
124 (define (normalize-components components)
125   (map
126    (lambda (c)
127      (if (record? c)
128          `(,(get-component-type c) . ,c)
129          c))
130    components))
131
132 (define (entity-ref key entity-set)
133   (hash-get-handle (car entity-set) key))
134
135 (define (new-entity entity-set . new-components)
136   (receive (new-set new-keys) (bongodb:insert entity-set (normalize-components new-components))
137     (values new-set (car new-keys))))
138
139 (define (get-entity entity-set key)
140   (let ((entity (bongodb:find entity-set (bongodb:$eq '_id key))))
141     (and entity (car entity))))
142
143 (define (remove-entity key)
144   (lambda (entity-set)
145     (let ((clist (entity-component-names key entity-set))
146           (entity (entity-ref key entity-set)))
147       (hash-remove! (car entity-set) key)
148       (unregister-components key clist (cdr entity-set))
149       (values
150        entity-set
151        entity))))
152
153 (define (set-entity key . new-components)
154   (lambda (entity-set)
155     (let* ((nc (normalize-components new-components))
156            (clist (entity-component-names key entity-set))
157            (nclist (component-names nc)))
158       (hash-set! (car entity-set) key nc)
159       (register-components key
160        (lset-difference eq? nclist clist)
161        (unregister-components key (lset-difference eq? clist nclist) (cdr entity-set)))
162       (values
163        entity-set
164        (cons key nc)))))
165
166 (define (set-entity-components key . new-components)
167   (lambda (entity-set)
168     (let ((nc (normalize-components new-components))
169           (clist (alist-copy (hash-ref (car entity-set) key))))
170       (for-each
171        (lambda (c)
172          (set! clist (assoc-set! clist (car c) (cdr c))))
173        nc)
174       (hash-set! (car entity-set) key clist)
175       (register-components key (component-names nc) (cdr entity-set))
176       (values
177        entity-set
178        (cons key clist)))))
179
180 (define (remove-entity-components key . old-components)
181   (lambda (entity-set)
182     (let ((clist (alist-copy (hash-ref (car entity-set) key))))
183       (for-each
184        (lambda (c)
185          (set! clist (assoc-remove! clist c)))
186        old-components)
187       (hash-set! (car entity-set) key clist)
188       (unregister-components key old-components (cdr entity-set))
189       (values
190        entity-set
191        (cons key clist)))))
192
193 (define (modify-entities entity-set changes)
194   (cond ((null? changes)
195          entity-set)
196         (else
197          (modify-entities ((car changes) entity-set) (cdr changes)))))
198
199
200 ;;; Making systems
201
202 (define-record-type entities-changes-type
203   (entities-changes changes)
204   entities-changes?
205   (changes get-entities-changes))
206
207 (define (append-changes changes)
208   (entities-changes
209    (apply append
210           (map get-entities-changes changes))))
211
212 (define (find-entities-by-components entity-set clist)
213   (cond ((null? clist) '())
214         (else
215          (let ((e (hash-ref (cdr entity-set) (car clist) '()))
216                (e* (find-entities-by-components entity-set (cdr clist))))
217            (if (null? e*)
218                e
219                (lset-intersection eq? e e*))))))
220
221 (define-syntax make-system
222   (syntax-rules ()
223     ((_ ((name (component-type ...)) ...) form ...)
224      (lambda (entity-set)
225        (let ((name (map (lambda (x)
226                           (cons (car x)
227                                 (filter (lambda (x)
228                                           (memq (car x) '(component-type ...)))
229                                         (cdr x))))
230                         (map (lambda (x)
231                                (entity-ref x entity-set))
232                              (find-entities-by-components entity-set '(component-type ...)))))
233              ...)
234          form
235          ...)))))
236
237 (define-syntax define-system
238   (syntax-rules ()
239     ((_ system-name ((name (component-type ...)) ...) form ...)
240      (define system-name
241        (make-system ((name (component-type ...)) ...)
242          form
243          ...)))))
244
245 (define (composed-systems-result results)
246   (let ((changes (filter (lambda (r) (entities-changes? r)) results)))
247     (cond ((null? changes)
248            (car results))
249           (else
250            (append-changes changes)))))
251
252 (define (join-systems . systems)
253   (lambda (entity-set)
254     (let run ((s systems) (res '()))
255       (cond ((null? s)
256              (composed-systems-result res))
257             (else
258              (run (cdr s) (cons ((car s) entity-set) res)))))))
259
260 (define (thread-systems . systems)
261   (lambda (entity-set)
262     (let run-wait ((thd
263                     (map (lambda (s)
264                            (call-with-new-thread
265                             (lambda () (s entity-set))))
266                          systems))
267                    (res '()))
268       (cond ((null? thd)
269              (composed-systems-result res))
270             (else
271              (run-wait (cdr thd) (cons (join-thread (car thd)) res)))))))
272
273
274 ;;; Entities and components access inside systems
275
276 (define (get-key entity)
277   (car entity))
278
279 (define (get-component component-name entity)
280   (assoc-ref (cdr entity) component-name))