...

Source file src/code.rocketnine.space/tslocum/gohan/world.go

Documentation: code.rocketnine.space/tslocum/gohan

     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  // world represents a collection of AllEntities, components and Systems.
    37  type world struct {
    38  	maxEntityID Entity
    39  
    40  	maxComponentID componentID
    41  
    42  	components [][]interface{} // components[Entity][componentID]Component
    43  
    44  	allEntities []Entity
    45  
    46  	modifiedEntities []Entity
    47  	removedEntities  []Entity
    48  
    49  	handledModifiedEntities map[Entity]bool
    50  
    51  	// availableEntities is the set of EntityIDs available because they were
    52  	// removed from the game.
    53  	availableEntities []Entity
    54  
    55  	systems               []System
    56  	systemEntities        [][]Entity        // Slice of entities matching each system.
    57  	systemNeeds           [][]componentID   // Slice of ComponentIDs needed by each system.
    58  	systemUses            [][]componentID   // Slice of ComponentIDs used by each system.
    59  	systemComponentIDs    [][]componentID   // Slice of ComponentIDs needed or used by each system.
    60  	systemComponentFields [][]reflect.Value // Slice of component struct fields used by each system.
    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  // NewWorld returns a new world.
    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  	// Pad slices to match IDs starting with 1.
    94  	w.components = append(w.components, nil)
    95  	w.systemComponentNames = append(w.systemComponentNames, "")
    96  
    97  	return w
    98  }
    99  
   100  // AddSystem registers a system to start receiving Update and Draw calls.
   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  		//log.Println("SET FIELD", systemIndex, field.String(), tag, field.CanSet())
   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  /*
   160  // AddSystemAfter registers a system to start receiving Update and Draw calls
   161  // after the specified system (or systems) are called first.
   162  func AddSystemAfter(system System, after ...System) {
   163  	gameSystems = append(gameSystems, system)
   164  	gameSystemReceivesUpdate = append(gameSystemReceivesUpdate, true)
   165  	gameSystemReceivesDraw = append(gameSystemReceivesDraw, true)
   166  	gameSystemEntities = append(gameSystemEntities, nil)
   167  
   168  	attachEntitiesToSystem(system)
   169  }
   170  */
   171  
   172  func (w *world) setSystemComponentFields(e Entity, i int) {
   173  	//log.Println(len(w.systemComponentFields[i]))
   174  	//log.Println(w.systemComponentFields[i])
   175  	for j, field := range w.systemComponentFields[i] {
   176  		//log.Println(j, field, field.String())
   177  		id := w.systemComponentIDs[i][j]
   178  		//log.Println("SYSTEM", i, "FIELD", j, "ID", id)
   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  				// Unregister system from Update events.
   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  		// Remove from attached systems.
   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  	// Mark EntityID as available.
   223  	w.availableEntities = append(w.availableEntities, w.removedEntities...)
   224  
   225  	w.removedEntities = w.removedEntities[:0]
   226  }
   227  
   228  // _handleModifiedEntities handles changes to entity components by attaching
   229  // and detaching modified entities from affected systems.
   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  					// Already attached.
   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  				// Detach from system.
   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  // Update updates the game state.
   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  // CurrentUpdates returns the number of System Update calls required to update
   327  // the game state. Because entities may be handled by more than one System,
   328  // this number may be higher than the number of active entities.
   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  				// Unregister system from Draw events.
   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  // Draw draws the game on to the screen.
   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  // CurrentDraws returns the number of System Draw calls required to draw the
   394  // game on to the screen. Because entities may be handled by more than one
   395  // System, this number may be higher than the number of active entities.
   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  // Preallocate creates and then immediately removes the specified number of entities.
   405  // Because Gohan reuses removed entities, this has the effect of pre-allocating
   406  // the memory used later to create entities normally. Pre-allocating enough
   407  // entities to run your application after its systems has been added, but
   408  // before any entities are created, will provide the greatest performance boost.
   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  // Query accepts any function which takes an entity and one or more components
   460  // as arguments. The provided function will be called once for each matching
   461  // entity. This function will block until the provided function returns.
   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  // Reset removes all entities, components and systems.
   504  func Reset() {
   505  	old := w
   506  	old.Lock()
   507  
   508  	w = newWorld()
   509  
   510  	old.Unlock()
   511  }
   512  

View as plain text