blob: d31406578c08194878209e32a8e8af53eaf5588b (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
package coffee.liz.ecs
import kotlin.reflect.KClass
/**
* World implementation that executes systems in dependency order using a DAG.
*/
class DAGWorld(systems: List<System>) : World {
private val entities = mutableSetOf<Entity>()
private val componentCache = mutableMapOf<KClass<out Component>, MutableSet<Entity>>()
private val systemExecutionOrder: List<System>
private var nextEntityId = 0
init {
systemExecutionOrder = buildExecutionOrder(systems)
}
override fun createEntity(): Entity {
val entity = Entity(nextEntityId++)
entities.add(entity)
return entity
}
override fun destroyEntity(entity: Entity) {
// Remove from all component caches
entity.componentTypes().forEach { componentType ->
componentCache[componentType]?.remove(entity)
}
entities.remove(entity)
}
override fun query(vararg componentTypes: KClass<out Component>): Set<Entity> {
if (componentTypes.isEmpty()) return entities.toSet()
// Start with entities that have the first component type
val firstType = componentTypes[0]
val candidates = componentCache[firstType] ?: return emptySet()
// Filter to entities that have all component types
return candidates.filter { entity ->
entity.hasAll(*componentTypes)
}.toSet()
}
override fun update(deltaTime: Float) {
// Update component cache based on current entity state
updateComponentCache()
// Execute systems in dependency order
systemExecutionOrder.forEach { system ->
system.update(this, deltaTime)
}
}
/**
* Rebuild the component cache based on current entity components.
* Call this after components have been added/removed from entities.
*/
private fun updateComponentCache() {
componentCache.clear()
entities.forEach { entity ->
entity.componentTypes().forEach { componentType ->
componentCache.getOrPut(componentType) { mutableSetOf() }.add(entity)
}
}
}
/**
* Build a topologically sorted execution order from system dependencies.
* Uses Kahn's algorithm for topological sorting.
*/
private fun buildExecutionOrder(systems: List<System>): List<System> {
if (systems.isEmpty()) return emptyList()
val systemMap = systems.associateBy { it::class }
val inDegree = mutableMapOf<KClass<out System>, Int>()
val adjacencyList = mutableMapOf<KClass<out System>, MutableSet<KClass<out System>>>()
// Initialize graph
systems.forEach { system ->
val systemClass = system::class
inDegree[systemClass] = 0
adjacencyList[systemClass] = mutableSetOf()
}
// Build dependency graph (reversed - dependencies point to dependents)
systems.forEach { system ->
system.dependencies.forEach { dependency ->
if (systemMap.containsKey(dependency)) {
adjacencyList[dependency]!!.add(system::class)
inDegree[system::class] = inDegree[system::class]!! + 1
}
}
}
// Kahn's algorithm
val queue = ArrayDeque<KClass<out System>>()
val result = mutableListOf<System>()
// Add all systems with no dependencies to queue
inDegree.forEach { (systemClass, degree) ->
if (degree == 0) {
queue.add(systemClass)
}
}
while (queue.isNotEmpty()) {
val currentClass = queue.removeFirst()
result.add(systemMap[currentClass]!!)
// Process dependents
adjacencyList[currentClass]?.forEach { dependent ->
inDegree[dependent] = inDegree[dependent]!! - 1
if (inDegree[dependent] == 0) {
queue.add(dependent)
}
}
}
// Check for cycles
if (result.size != systems.size) {
throw IllegalArgumentException("Circular dependency detected in systems")
}
return result
}
}
|