...

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

Documentation: code.rocketnine.space/tslocum/gohan

     1  package gohan
     2  
     3  import (
     4  	"reflect"
     5  )
     6  
     7  // componentID is a component identifier. Each Component is assigned a unique ID
     8  // via world.NewComponentID, and implements a componentID method returning its ID.
     9  type componentID int
    10  
    11  // newComponentID returns the next available componentID.
    12  func newComponentID() componentID {
    13  	w.maxComponentID++
    14  
    15  	for i := Entity(1); i <= w.maxEntityID; i++ {
    16  		w.components[i] = append(w.components[i], nil)
    17  	}
    18  
    19  	return w.maxComponentID
    20  }
    21  
    22  func componentIDByValue(v interface{}) componentID {
    23  	sV := reflect.ValueOf(v)
    24  	sT := reflect.TypeOf(v)
    25  	if sV.Kind() == reflect.Ptr {
    26  		sV = sV.Elem()
    27  		sT = sT.Elem()
    28  	}
    29  
    30  	componentName := sV.Type().String()
    31  	return componentIDByName(componentName)
    32  }
    33  
    34  func componentIDByName(name string) componentID {
    35  	if len(name) == 0 {
    36  		return 0
    37  	}
    38  
    39  	if name[0:1] == "*" {
    40  		name = name[1:]
    41  	}
    42  
    43  	if !w.haveSystemComponentName[name] {
    44  		w.systemComponentNames = append(w.systemComponentNames, name)
    45  		w.haveSystemComponentName[name] = true
    46  		id := newComponentID()
    47  		return id
    48  	}
    49  
    50  	for i, savedName := range w.systemComponentNames {
    51  		if savedName == name {
    52  			return componentID(i)
    53  		}
    54  	}
    55  
    56  	return 0
    57  }
    58  
    59  // AddComponent adds a Component to an Entity.
    60  func (e Entity) AddComponent(component interface{}) {
    61  	w.componentMutex.Lock()
    62  	defer w.componentMutex.Unlock()
    63  
    64  	id := componentIDByValue(component)
    65  	w.components[e][id] = component
    66  
    67  	w.entityMutex.Lock()
    68  	defer w.entityMutex.Unlock()
    69  	w.modifiedEntities = append(w.modifiedEntities, e)
    70  }
    71  
    72  // With accepts any function which takes one or more components as arguments.
    73  // This function will block until the provided function returns.
    74  func (e Entity) With(f interface{}) {
    75  	components := w.components[e]
    76  	if components == nil {
    77  		return
    78  	}
    79  
    80  	t := reflect.TypeOf(f)
    81  	v := reflect.ValueOf(f)
    82  	numIn := t.NumIn()
    83  
    84  	if t.Kind() != reflect.Func || v.IsNil() || numIn == 0 {
    85  		panic("component.With() must be provided with a function containing one or more components as arguments")
    86  	}
    87  
    88  	args := make([]reflect.Value, numIn)
    89  	for i := 0; i < numIn; i++ {
    90  		id := componentIDByName(t.In(i).String())
    91  
    92  		arg := reflect.ValueOf(components[id])
    93  		if components[id] == nil {
    94  			arg = reflect.New(t.In(i)).Elem()
    95  		}
    96  
    97  		args[i] = arg
    98  	}
    99  
   100  	v.Call(args)
   101  }
   102  
   103  // getComponent gets a Component of an Entity.
   104  func (e Entity) getComponent(componentID componentID) interface{} {
   105  	components := w.components[e]
   106  	if components == nil {
   107  		return nil
   108  	}
   109  	return components[componentID]
   110  }
   111  

View as plain text