• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mlange-42 / ark / 13665123960

04 Mar 2025 11:39PM CUT coverage: 99.297% (+0.02%) from 99.28%
13665123960

Pull #131

github

web-flow
Merge 2d561f24a into 06677b7f6
Pull Request #131: Check validity of relations when creating tables

8 of 9 new or added lines in 1 file covered. (88.89%)

5794 of 5835 relevant lines covered (99.3%)

35032.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.77
/ecs/world_internal.go
1
package ecs
2

3
import (
4
        "reflect"
5
        "unsafe"
6
)
7

8
func (w *World) newEntityWith(ids []ID, comps []unsafe.Pointer, relations []RelationID) Entity {
510,274✔
9
        w.checkLocked()
510,274✔
10

510,274✔
11
        newTable := w.storage.findOrCreateTable(&w.storage.tables[0], ids, nil, relations)
510,274✔
12
        entity, idx := w.storage.createEntity(newTable.id)
510,274✔
13

510,274✔
14
        if comps != nil {
1,020,474✔
15
                if len(ids) != len(comps) {
510,200✔
16
                        panic("lengths of IDs and components to add do not match")
×
17
                }
18
                for i, id := range ids {
1,024,200✔
19
                        newTable.Set(id, idx, comps[i])
514,000✔
20
                }
514,000✔
21
        }
22
        w.storage.registerTargets(relations)
510,274✔
23
        return entity
510,274✔
24
}
25

26
func (w *World) newEntitiesWith(count int, ids []ID, comps []unsafe.Pointer, relations []RelationID) {
27✔
27
        w.checkLocked()
27✔
28

27✔
29
        newTable := w.storage.findOrCreateTable(&w.storage.tables[0], ids, nil, relations)
27✔
30

27✔
31
        startIdx := newTable.Len()
27✔
32
        w.storage.createEntities(newTable, count)
27✔
33

27✔
34
        if comps != nil {
54✔
35
                if len(ids) != len(comps) {
27✔
36
                        panic("lengths of IDs and components to add do not match")
×
37
                }
38
                for i := range count {
661✔
39
                        for j, id := range ids {
3,380✔
40
                                newTable.Set(id, uint32(startIdx+i), comps[j])
2,746✔
41
                        }
2,746✔
42
                }
43
        }
44
        w.storage.registerTargets(relations)
27✔
45
}
46

47
func (w *World) newEntities(count int, ids []ID, relations []RelationID) (tableID, int) {
115✔
48
        w.checkLocked()
115✔
49

115✔
50
        newTable := w.storage.findOrCreateTable(&w.storage.tables[0], ids, nil, relations)
115✔
51

115✔
52
        startIdx := newTable.Len()
115✔
53
        w.storage.createEntities(newTable, count)
115✔
54
        w.storage.registerTargets(relations)
115✔
55

115✔
56
        return newTable.id, startIdx
115✔
57
}
115✔
58

59
func (w *World) exchange(entity Entity, add []ID, rem []ID, addComps []unsafe.Pointer, relations []RelationID) {
646✔
60
        w.checkLocked()
646✔
61

646✔
62
        if !w.Alive(entity) {
646✔
63
                panic("can't exchange components on a dead entity")
×
64
        }
65
        if len(add) == 0 && len(rem) == 0 {
646✔
66
                if len(relations) > 0 {
×
67
                        panic("exchange operation has no effect, but relations were specified. Use SetRelation(s) instead")
×
68
                }
69
                return
×
70
        }
71

72
        index := &w.storage.entities[entity.id]
646✔
73
        oldTable := &w.storage.tables[index.table]
646✔
74
        oldArchetype := &w.storage.archetypes[oldTable.archetype]
646✔
75

646✔
76
        mask := oldArchetype.mask
646✔
77
        w.storage.getExchangeMask(&mask, add, rem)
646✔
78

646✔
79
        oldIDs := oldArchetype.components
646✔
80

646✔
81
        newTable := w.storage.findOrCreateTable(oldTable, add, rem, relations)
646✔
82
        newIndex := newTable.Add(entity)
646✔
83

646✔
84
        for _, id := range oldIDs {
2,348✔
85
                if mask.Get(id) {
2,414✔
86
                        comp := oldTable.Get(id, uintptr(index.row))
712✔
87
                        newTable.Set(id, newIndex, comp)
712✔
88
                }
712✔
89
        }
90
        if addComps != nil {
973✔
91
                if len(add) != len(addComps) {
327✔
92
                        panic("lengths of IDs and components to add do not match")
×
93
                }
94
                for i, id := range add {
1,046✔
95
                        newTable.Set(id, newIndex, addComps[i])
719✔
96
                }
719✔
97
        }
98

99
        swapped := oldTable.Remove(index.row)
646✔
100

646✔
101
        if swapped {
744✔
102
                swapEntity := oldTable.GetEntity(uintptr(index.row))
98✔
103
                w.storage.entities[swapEntity.id].row = index.row
98✔
104
        }
98✔
105
        w.storage.entities[entity.id] = entityIndex{table: newTable.id, row: newIndex}
646✔
106

646✔
107
        w.storage.registerTargets(relations)
646✔
108
}
109

110
func (w *World) exchangeBatch(batch *Batch, add []ID, rem []ID,
111
        addComps []unsafe.Pointer, relations []RelationID, fn func(table tableID, start, len int)) {
80✔
112
        w.checkLocked()
80✔
113

80✔
114
        if len(add) == 0 && len(rem) == 0 {
80✔
115
                if len(relations) > 0 {
×
116
                        panic("exchange operation has no effect, but relations were specified. Use SetRelationBatch instead")
×
117
                }
118
                return
×
119
        }
120

121
        tables := w.storage.getTables(batch)
80✔
122
        lengths := make([]uint32, len(tables))
80✔
123
        var totalEntities uint32 = 0
80✔
124
        for i, table := range tables {
240✔
125
                lengths[i] = uint32(table.Len())
160✔
126
                totalEntities += uint32(table.Len())
160✔
127
        }
160✔
128

129
        for i, table := range tables {
240✔
130
                tableLen := lengths[i]
160✔
131

160✔
132
                if tableLen == 0 {
160✔
133
                        continue
×
134
                }
135
                t, start, len := w.exchangeTable(table, int(tableLen), add, rem, addComps, relations)
160✔
136
                if fn != nil {
240✔
137
                        fn(t, start, len)
80✔
138
                }
80✔
139
        }
140
}
141

142
func (w *World) exchangeTable(oldTable *table, oldLen int, add []ID, rem []ID, addComps []unsafe.Pointer, relations []RelationID) (tableID, int, int) {
160✔
143
        oldArchetype := &w.storage.archetypes[oldTable.archetype]
160✔
144

160✔
145
        mask := oldArchetype.mask
160✔
146
        w.storage.getExchangeMask(&mask, add, rem)
160✔
147

160✔
148
        oldIDs := oldArchetype.components
160✔
149

160✔
150
        newTable := w.storage.findOrCreateTable(oldTable, add, rem, relations)
160✔
151
        startIdx := uintptr(newTable.Len())
160✔
152
        count := uintptr(oldLen)
160✔
153

160✔
154
        var i uintptr
160✔
155
        for i = 0; i < count; i++ {
2,080✔
156
                idx := startIdx + i
1,920✔
157
                entity := oldTable.GetEntity(i)
1,920✔
158
                index := &w.storage.entities[entity.id]
1,920✔
159
                index.table = newTable.id
1,920✔
160
                index.row = uint32(idx)
1,920✔
161
        }
1,920✔
162

163
        newTable.AddAllEntities(oldTable, uint32(oldLen), true)
160✔
164
        for _, id := range oldIDs {
688✔
165
                if mask.Get(id) {
848✔
166
                        oldCol := oldTable.GetColumn(id)
320✔
167
                        newCol := newTable.GetColumn(id)
320✔
168
                        newCol.SetLast(oldCol, uint32(oldLen))
320✔
169
                }
320✔
170
        }
171
        if addComps != nil {
208✔
172
                if len(add) != len(addComps) {
48✔
173
                        panic("lengths of IDs and components to add do not match")
×
174
                }
175
                for i := range count {
624✔
176
                        for j, id := range add {
3,168✔
177
                                newTable.Set(id, uint32(startIdx+i), addComps[j])
2,592✔
178
                        }
2,592✔
179
                }
180
        }
181

182
        oldTable.Reset()
160✔
183
        w.storage.registerTargets(relations)
160✔
184

160✔
185
        return newTable.id, int(startIdx), int(count)
160✔
186
}
187

188
// setRelations sets the target entities for an entity relations.
189
func (w *World) setRelations(entity Entity, relations []RelationID) {
62✔
190
        w.checkLocked()
62✔
191

62✔
192
        if !w.storage.entityPool.Alive(entity) {
70✔
193
                panic("can't set relation for a dead entity")
8✔
194
        }
195
        if len(relations) == 0 {
62✔
196
                panic("no relations specified")
8✔
197
        }
198

199
        index := &w.storage.entities[entity.id]
46✔
200
        oldTable := &w.storage.tables[index.table]
46✔
201

46✔
202
        newRelations, changed := w.storage.getExchangeTargets(oldTable, relations)
46✔
203
        if !changed {
46✔
204
                return
×
205
        }
×
206

207
        oldArch := &w.storage.archetypes[oldTable.archetype]
46✔
208
        newTable, ok := oldArch.GetTable(&w.storage, newRelations)
46✔
209
        if !ok {
60✔
210
                newTable = w.storage.createTable(oldArch, newRelations)
14✔
211
        }
14✔
212
        newIndex := newTable.Add(entity)
45✔
213

45✔
214
        for _, id := range oldArch.components {
153✔
215
                comp := oldTable.Get(id, uintptr(index.row))
108✔
216
                newTable.Set(id, newIndex, comp)
108✔
217
        }
108✔
218

219
        swapped := oldTable.Remove(index.row)
45✔
220

45✔
221
        if swapped {
61✔
222
                swapEntity := oldTable.GetEntity(uintptr(index.row))
16✔
223
                w.storage.entities[swapEntity.id].row = index.row
16✔
224
        }
16✔
225
        w.storage.entities[entity.id] = entityIndex{table: newTable.id, row: newIndex}
45✔
226

45✔
227
        w.storage.registerTargets(relations)
45✔
228
}
229

230
func (w *World) setRelationsBatch(batch *Batch, relations []RelationID, fn func(table tableID, start, len int)) {
17✔
231
        w.checkLocked()
17✔
232

17✔
233
        if len(relations) == 0 {
25✔
234
                panic("no relations specified")
8✔
235
        }
236

237
        tables := w.storage.getTables(batch)
9✔
238
        lengths := make([]uint32, len(tables))
9✔
239
        var totalEntities uint32 = 0
9✔
240
        for i, table := range tables {
18✔
241
                lengths[i] = uint32(table.Len())
9✔
242
                totalEntities += uint32(table.Len())
9✔
243
        }
9✔
244

245
        for i, table := range tables {
18✔
246
                tableLen := lengths[i]
9✔
247

9✔
248
                if tableLen == 0 {
9✔
249
                        continue
×
250
                }
251
                t, start, len := w.setRelationsTable(table, int(tableLen), relations)
9✔
252
                if fn != nil {
18✔
253
                        fn(t, start, len)
9✔
254
                }
9✔
255
        }
256

257
        w.storage.registerTargets(relations)
9✔
258
}
259

260
func (w *World) setRelationsTable(oldTable *table, oldLen int, relations []RelationID) (tableID, int, int) {
9✔
261
        newRelations, changed := w.storage.getExchangeTargets(oldTable, relations)
9✔
262

9✔
263
        if !changed {
9✔
264
                return oldTable.id, 0, oldLen
×
265
        }
×
266
        oldArch := &w.storage.archetypes[oldTable.archetype]
9✔
267
        newTable, ok := oldArch.GetTable(&w.storage, newRelations)
9✔
268
        if !ok {
18✔
269
                newTable = w.storage.createTable(oldArch, newRelations)
9✔
270
        }
9✔
271
        startIdx := newTable.Len()
9✔
272
        w.storage.moveEntities(oldTable, newTable, uint32(oldLen))
9✔
273

9✔
274
        return newTable.id, startIdx, oldLen
9✔
275

276
}
277

278
func (w *World) componentID(tp reflect.Type) ID {
2,766✔
279
        id, newID := w.storage.registry.ComponentID(tp)
2,766✔
280
        if newID {
3,656✔
281
                if w.IsLocked() {
891✔
282
                        w.storage.registry.unregisterLastComponent()
1✔
283
                        panic("attempt to register a new component in a locked world")
1✔
284
                }
285
                w.storage.AddComponent(id)
889✔
286
        }
287
        return ID{id: id}
2,765✔
288
}
289

290
func (w *World) resourceID(tp reflect.Type) ResID {
9✔
291
        id, _ := w.resources.registry.ComponentID(tp)
9✔
292
        return ResID{id: id}
9✔
293
}
9✔
294

295
// lock the world and get the lock bit for later unlocking.
296
func (w *World) lock() uint8 {
1,431✔
297
        return w.locks.Lock()
1,431✔
298
}
1,431✔
299

300
// unlock unlocks the given lock bit.
301
func (w *World) unlock(l uint8) {
1,430✔
302
        w.locks.Unlock(l)
1,430✔
303
}
1,430✔
304

305
// checkLocked checks if the world is locked, and panics if so.
306
func (w *World) checkLocked() {
1,020,787✔
307
        if w.IsLocked() {
1,020,788✔
308
                panic("attempt to modify a locked world")
1✔
309
        }
310
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc