1 package gohan
2
3 import (
4 "fmt"
5 "log"
6 "os"
7 "reflect"
8 "strconv"
9 "strings"
10 "sync"
11 "time"
12
13 "github.com/hajimehoshi/ebiten/v2"
14 )
15
16 var debug int
17
18 func init() {
19 debugEnv := os.Getenv("GOHAN_DEBUG")
20 debugEnv = strings.TrimSpace(debugEnv)
21 debugEnv = strings.ToLower(debugEnv)
22
23 i, err := strconv.Atoi(debugEnv)
24 if err == nil {
25 debug = i
26 return
27 }
28
29 if debugEnv == "t" || debugEnv == "y" || debugEnv == "on" || debugEnv == "yes" || debugEnv == "true" {
30 debug = 1
31 }
32 }
33
34 var w = newWorld()
35
36
37 type world struct {
38 maxEntityID Entity
39
40 maxComponentID componentID
41
42 components [][]interface{}
43
44 allEntities []Entity
45
46 modifiedEntities []Entity
47 removedEntities []Entity
48
49 handledModifiedEntities map[Entity]bool
50
51
52
53 availableEntities []Entity
54
55 systems []System
56 systemEntities [][]Entity
57 systemNeeds [][]componentID
58 systemUses [][]componentID
59 systemComponentIDs [][]componentID
60 systemComponentFields [][]reflect.Value
61
62 systemReceivesUpdate []bool
63 systemReceivesDraw []bool
64
65 systemUpdatedEntities int
66 systemUpdatedEntitiesV int
67 systemUpdatedEntitiesT time.Time
68
69 systemDrawnEntities int
70 systemDrawnEntitiesV int
71 systemDrawnEntitiesT time.Time
72
73 systemComponentNames []string
74 haveSystemComponentName map[string]bool
75
76 cacheTime time.Duration
77
78 entityMutex sync.Mutex
79 componentMutex sync.Mutex
80
81 sync.Mutex
82 }
83
84
85 func newWorld() *world {
86 w := &world{
87 cacheTime: time.Second,
88
89 handledModifiedEntities: make(map[Entity]bool),
90 haveSystemComponentName: make(map[string]bool),
91 }
92
93
94 w.components = append(w.components, nil)
95 w.systemComponentNames = append(w.systemComponentNames, "")
96
97 return w
98 }
99
100
101 func AddSystem(system System) {
102 w.Lock()
103 defer w.Unlock()
104
105 systemIndex := len(w.systems)
106
107 w.systems = append(w.systems, system)
108 w.systemReceivesUpdate = append(w.systemReceivesUpdate, true)
109 w.systemReceivesDraw = append(w.systemReceivesDraw, true)
110 w.systemEntities = append(w.systemEntities, nil)
111 w.systemComponentFields = append(w.systemComponentFields, nil)
112
113 w.entityMutex.Lock()
114 defer w.entityMutex.Unlock()
115 w.modifiedEntities = append(w.modifiedEntities, w.allEntities...)
116
117 sV := reflect.ValueOf(system)
118 sT := reflect.TypeOf(system)
119 if sV.Kind() == reflect.Ptr {
120 sV = sV.Elem()
121 sT = sT.Elem()
122 }
123 if sV.Kind() != reflect.Struct {
124 panic("system must be a struct type")
125 }
126
127 var usedComponentIDs []componentID
128 var neededComponentIDs []componentID
129 w.systemComponentIDs = append(w.systemComponentIDs, nil)
130 for i := 0; i < sT.NumField(); i++ {
131 field := sV.Field(i)
132
133 if !field.CanSet() {
134 continue
135 }
136
137 tag := sT.Field(i).Tag.Get("gohan")
138 if tag == "-" {
139 continue
140 }
141
142
143 w.systemComponentFields[systemIndex] = append(w.systemComponentFields[systemIndex], field)
144
145 id := componentIDByName(field.Type().String())
146 if tag == "?" {
147 usedComponentIDs = append(usedComponentIDs, id)
148 } else {
149 neededComponentIDs = append(neededComponentIDs, id)
150 }
151
152 w.systemComponentIDs[systemIndex] = append(w.systemComponentIDs[systemIndex], id)
153 }
154
155 w.systemNeeds = append(w.systemNeeds, neededComponentIDs)
156 w.systemUses = append(w.systemUses, usedComponentIDs)
157 }
158
159
171
172 func (w *world) setSystemComponentFields(e Entity, i int) {
173
174
175 for j, field := range w.systemComponentFields[i] {
176
177 id := w.systemComponentIDs[i][j]
178
179 if w.components[e][id] == nil {
180 field.Set(reflect.Zero(field.Type()))
181 } else {
182 field.Set(reflect.ValueOf(w.components[e][id]))
183 }
184 }
185 }
186
187 func (w *world) updateSystem(i int) (int, error) {
188 w.propagateEntityChanges()
189
190 updated := 0
191 for _, entity := range w.systemEntities[i] {
192 w.setSystemComponentFields(entity, i)
193
194 err := w.systems[i].Update(entity)
195 if err != nil {
196 if err == ErrUnregister {
197
198 w.systemReceivesUpdate[i] = false
199 return 0, nil
200 }
201 return 0, fmt.Errorf("failed to update %s for entity %d: %+v", w.systemName(i), entity, err)
202 }
203 updated++
204 }
205 return updated, nil
206 }
207
208 func (w *world) _handleRemovedEntities() {
209 for _, entity := range w.removedEntities {
210
211 REMOVED:
212 for i := range w.systemEntities {
213 for j, e := range w.systemEntities[i] {
214 if e == entity {
215 w.systemEntities[i] = _removeAt(w.systemEntities[i], j)
216 continue REMOVED
217 }
218 }
219 }
220 }
221
222
223 w.availableEntities = append(w.availableEntities, w.removedEntities...)
224
225 w.removedEntities = w.removedEntities[:0]
226 }
227
228
229
230 func (w *world) _handleModifiedEntities() {
231 if len(w.modifiedEntities) == 0 {
232 return
233 }
234
235 for _, entity := range w.modifiedEntities {
236 if w.handledModifiedEntities[entity] {
237 continue
238 }
239 w.handledModifiedEntities[entity] = true
240
241 for i := range w.systems {
242 systemEntityIndex := -1
243 for j, systemEntity := range w.systemEntities[i] {
244 if systemEntity == entity {
245 systemEntityIndex = j
246 break
247 }
248 }
249
250 var skip bool
251 for _, componentID := range w.systemNeeds[i] {
252 c := entity.getComponent(componentID)
253 if c == nil {
254 skip = true
255 break
256 }
257 }
258 if !skip {
259 if systemEntityIndex != -1 {
260
261 continue
262 }
263
264 w.systemEntities[i] = append(w.systemEntities[i], entity)
265
266 if debug > 1 {
267 log.Printf("Attached entity %d to %s.", entity, w.systemName(i))
268 }
269 } else if systemEntityIndex != -1 {
270
271 w.systemEntities[i] = _removeAt(w.systemEntities[i], systemEntityIndex)
272 }
273 }
274 }
275
276 for k := range w.handledModifiedEntities {
277 delete(w.handledModifiedEntities, k)
278 }
279
280 w.modifiedEntities = w.modifiedEntities[:0]
281 }
282
283 func (w *world) propagateEntityChanges() {
284 w.entityMutex.Lock()
285 defer w.entityMutex.Unlock()
286
287 w._handleRemovedEntities()
288 w._handleModifiedEntities()
289 }
290
291
292 func Update() error {
293 w.Lock()
294 defer w.Unlock()
295
296 var t time.Time
297 if debug != 0 {
298 t = time.Now()
299 }
300
301 var entitiesUpdated int
302 for i, registered := range w.systemReceivesUpdate {
303 if !registered {
304 continue
305 }
306
307 updated, err := w.updateSystem(i)
308 if err != nil {
309 return err
310 }
311
312 entitiesUpdated += updated
313
314 if debug != 0 {
315 log.Printf("- %s: %d updated in %.2fms.", w.systemName(i), updated, float64(time.Since(t).Microseconds())/1000)
316 }
317 }
318 w.systemUpdatedEntities = entitiesUpdated
319
320 if debug != 0 {
321 log.Printf("Handled %d entity updates in %.2fms.", entitiesUpdated, float64(time.Since(t).Microseconds())/1000)
322 }
323 return nil
324 }
325
326
327
328
329 func CurrentUpdates() int {
330 if time.Since(w.systemUpdatedEntitiesT) >= w.cacheTime {
331 w.systemUpdatedEntitiesV = w.systemUpdatedEntities
332 w.systemUpdatedEntitiesT = time.Now()
333 }
334 return w.systemUpdatedEntitiesV
335 }
336
337 func (w *world) drawSystem(i int, screen *ebiten.Image) (int, error) {
338 w.propagateEntityChanges()
339
340 var drawn int
341 for _, entity := range w.systemEntities[i] {
342 w.setSystemComponentFields(entity, i)
343
344 err := w.systems[i].Draw(entity, screen)
345 if err != nil {
346 if err == ErrUnregister {
347
348 w.systemReceivesDraw[i] = false
349 return 0, nil
350 }
351 return 0, fmt.Errorf("failed to draw %s for entity %d: %+v", w.systemName(i), entity, err)
352 }
353 drawn++
354 }
355 return drawn, nil
356 }
357
358
359 func Draw(screen *ebiten.Image) error {
360 w.Lock()
361 defer w.Unlock()
362
363 var t time.Time
364 if debug != 0 {
365 t = time.Now()
366 }
367
368 var entitiesDrawn int
369 for i, registered := range w.systemReceivesDraw {
370 if !registered {
371 continue
372 }
373
374 drawn, err := w.drawSystem(i, screen)
375 if err != nil {
376 return err
377 }
378
379 entitiesDrawn += drawn
380
381 if debug != 0 {
382 log.Printf("- %s: %d drawn in %.2fms.", w.systemName(i), drawn, float64(time.Since(t).Microseconds())/1000)
383 }
384 }
385 w.systemDrawnEntities = entitiesDrawn
386
387 if debug != 0 {
388 log.Printf("Handled %d entity draws in %.2fms.", entitiesDrawn, float64(time.Since(t).Microseconds())/1000)
389 }
390 return nil
391 }
392
393
394
395
396 func CurrentDraws() int {
397 if time.Since(w.systemDrawnEntitiesT) >= w.cacheTime {
398 w.systemDrawnEntitiesV = w.systemDrawnEntities
399 w.systemDrawnEntitiesT = time.Now()
400 }
401 return w.systemDrawnEntitiesV
402 }
403
404
405
406
407
408
409 func Preallocate(entities int) {
410 if len(w.availableEntities) >= entities {
411 return
412 }
413
414 e := make([]Entity, entities)
415 for i := 0; i < entities; i++ {
416 e[i] = NewEntity()
417 }
418 for i := 0; i < entities; i++ {
419 e[i].Remove()
420 }
421 }
422
423 func uniqueComponentIDs(v []componentID) []componentID {
424 var list []componentID
425 keys := make(map[componentID]bool)
426 for _, entry := range v {
427 if _, value := keys[entry]; !value {
428 keys[entry] = true
429 list = append(list, entry)
430 }
431 }
432 return list
433 }
434
435 func (w *world) componentName(id componentID) string {
436 if int(id) < len(w.systemComponentNames) {
437 return w.systemComponentNames[id]
438 }
439 return strconv.Itoa(int(id))
440 }
441
442 func (w *world) systemName(i int) string {
443 if i < len(w.systems) {
444 return getName(w.systems[i])
445 }
446 return strconv.Itoa(i)
447 }
448
449 func getName(v interface{}) string {
450 t := reflect.TypeOf(v)
451 if t.Kind() == reflect.Ptr {
452 return strings.Title(t.Elem().Name())
453 } else if t.Kind() == reflect.Struct {
454 return strings.Title(t.Name())
455 }
456 return ""
457 }
458
459
460
461
462 func Query(f interface{}) {
463 t := reflect.TypeOf(f)
464 v := reflect.ValueOf(f)
465 numIn := t.NumIn()
466
467 const expectedFirstArg = "gohan.Entity"
468 if t.Kind() != reflect.Func || v.IsNil() || numIn < 2 || t.In(0).String() != expectedFirstArg {
469 panic("component.With() must be provided with a function which takes an entity and one or more components as arguments")
470 }
471
472 var needIDs []componentID
473 for i := 1; i < numIn; i++ {
474 id := componentIDByName(t.In(i).String())
475
476 needIDs = append(needIDs, id)
477 }
478
479 args := make([]reflect.Value, numIn)
480 QUERY:
481 for _, entity := range w.allEntities {
482 for _, id := range needIDs {
483 c := entity.getComponent(id)
484 if c == nil {
485 continue QUERY
486 }
487 }
488
489 args[0] = reflect.ValueOf(entity)
490 for i := 1; i < numIn; i++ {
491 id := componentIDByName(t.In(i).String())
492
493 arg := reflect.ValueOf(w.components[entity][id])
494 if w.components[entity][id] == nil {
495 arg = reflect.New(t.In(i)).Elem()
496 }
497 args[i] = arg
498 }
499 v.Call(args)
500 }
501 }
502
503
504 func Reset() {
505 old := w
506 old.Lock()
507
508 w = newWorld()
509
510 old.Unlock()
511 }
512
View as plain text