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

mlange-42 / ark / 13842813710

13 Mar 2025 07:15PM CUT coverage: 99.762% (+0.2%) from 99.547%
13842813710

push

github

web-flow
Improve test coverage (#194)

8371 of 8391 relevant lines covered (99.76%)

21363.27 hits per line

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

97.41
/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 {
499,978✔
9
        w.checkLocked()
499,978✔
10

499,978✔
11
        mask := bitMask{}
499,978✔
12
        newTable := w.storage.findOrCreateTable(&w.storage.tables[0], ids, nil, relations, &mask)
499,978✔
13
        entity, idx := w.storage.createEntity(newTable.id)
499,978✔
14

499,978✔
15
        if comps != nil {
999,718✔
16
                if len(ids) != len(comps) {
499,741✔
17
                        panic("lengths of IDs and components to add do not match")
1✔
18
                }
19
                for i, id := range ids {
1,004,721✔
20
                        newTable.Set(id, idx, comps[i])
504,982✔
21
                }
504,982✔
22
        }
23
        w.storage.registerTargets(relations)
499,976✔
24
        return entity
499,976✔
25
}
26

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

41✔
30
        mask := bitMask{}
41✔
31
        newTable := w.storage.findOrCreateTable(&w.storage.tables[0], ids, nil, relations, &mask)
41✔
32

41✔
33
        startIdx := newTable.Len()
41✔
34
        w.storage.createEntities(newTable, count)
41✔
35

41✔
36
        if comps != nil {
82✔
37
                if len(ids) != len(comps) {
42✔
38
                        panic("lengths of IDs and components to add do not match")
1✔
39
                }
40
                for i := range count {
986✔
41
                        for j, id := range ids {
6,740✔
42
                                newTable.Set(id, uint32(startIdx+i), comps[j])
5,794✔
43
                        }
5,794✔
44
                }
45
        }
46
        w.storage.registerTargets(relations)
40✔
47
}
48

49
func (w *World) newEntities(count int, ids []ID, relations []RelationID) (tableID, int) {
155✔
50
        w.checkLocked()
155✔
51

155✔
52
        mask := bitMask{}
155✔
53
        newTable := w.storage.findOrCreateTable(&w.storage.tables[0], ids, nil, relations, &mask)
155✔
54

155✔
55
        startIdx := newTable.Len()
155✔
56
        w.storage.createEntities(newTable, count)
155✔
57
        w.storage.registerTargets(relations)
155✔
58

155✔
59
        return newTable.id, startIdx
155✔
60
}
155✔
61

62
func (w *World) exchange(entity Entity, add []ID, rem []ID, addComps []unsafe.Pointer, relations []RelationID) {
998✔
63
        w.checkLocked()
998✔
64

998✔
65
        if !w.Alive(entity) {
1,034✔
66
                panic("can't exchange components on a dead entity")
36✔
67
        }
68
        if len(add) == 0 && len(rem) == 0 {
964✔
69
                if len(relations) > 0 {
3✔
70
                        panic("exchange operation has no effect, but relations were specified. Use SetRelation(s) instead")
1✔
71
                }
72
                return
1✔
73
        }
74

75
        index := w.storage.entities[entity.id]
960✔
76
        oldTable := &w.storage.tables[index.table]
960✔
77
        oldArchetype := &w.storage.archetypes[oldTable.archetype]
960✔
78

960✔
79
        mask := oldArchetype.mask
960✔
80
        newTable := w.storage.findOrCreateTable(oldTable, add, rem, relations, &mask)
960✔
81
        newIndex := newTable.Add(entity)
960✔
82

960✔
83
        for _, id := range oldArchetype.components {
3,708✔
84
                if mask.Get(id) {
3,476✔
85
                        comp := oldTable.Get(id, uintptr(index.row))
728✔
86
                        newTable.Set(id, newIndex, comp)
728✔
87
                }
728✔
88
        }
89
        if addComps != nil {
1,340✔
90
                if len(add) != len(addComps) {
381✔
91
                        panic("lengths of IDs and components to add do not match")
1✔
92
                }
93
                for i, id := range add {
1,609✔
94
                        newTable.Set(id, newIndex, addComps[i])
1,230✔
95
                }
1,230✔
96
        }
97

98
        swapped := oldTable.Remove(index.row)
959✔
99

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

959✔
106
        w.storage.registerTargets(relations)
959✔
107
}
108

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

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

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

128
        for i, table := range tables {
302✔
129
                tableLen := lengths[i]
201✔
130

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

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

201✔
144
        oldIDs := oldArchetype.components
201✔
145

201✔
146
        mask := oldArchetype.mask
201✔
147
        newTable := w.storage.findOrCreateTable(oldTable, add, rem, relations, &mask)
201✔
148
        startIdx := uintptr(newTable.Len())
201✔
149
        count := uintptr(oldLen)
201✔
150

201✔
151
        var i uintptr
201✔
152
        for i = 0; i < count; i++ {
2,605✔
153
                idx := startIdx + i
2,404✔
154
                entity := oldTable.GetEntity(i)
2,404✔
155
                index := &w.storage.entities[entity.id]
2,404✔
156
                index.table = newTable.id
2,404✔
157
                index.row = uint32(idx)
2,404✔
158
        }
2,404✔
159

160
        newTable.AddAllEntities(oldTable, uint32(oldLen))
201✔
161
        for _, id := range oldIDs {
961✔
162
                if mask.Get(id) {
1,140✔
163
                        oldCol := oldTable.GetColumn(id)
380✔
164
                        newCol := newTable.GetColumn(id)
380✔
165
                        newCol.SetLast(oldCol, newTable.len, uint32(oldLen))
380✔
166
                }
380✔
167
        }
168
        if addComps != nil {
260✔
169
                if len(add) != len(addComps) {
60✔
170
                        panic("lengths of IDs and components to add do not match")
1✔
171
                }
172
                for i := range count {
754✔
173
                        for j, id := range add {
4,320✔
174
                                newTable.Set(id, uint32(startIdx+i), addComps[j])
3,624✔
175
                        }
3,624✔
176
                }
177
        }
178

179
        oldTable.Reset()
200✔
180
        w.storage.registerTargets(relations)
200✔
181

200✔
182
        return newTable.id, int(startIdx), int(count)
200✔
183
}
184

185
// setRelations sets the target entities for an entity relations.
186
func (w *World) setRelations(entity Entity, relations []RelationID) {
73✔
187
        w.checkLocked()
73✔
188

73✔
189
        if !w.storage.entityPool.Alive(entity) {
85✔
190
                panic("can't set relation for a dead entity")
12✔
191
        }
192
        if len(relations) == 0 {
73✔
193
                panic("no relations specified")
12✔
194
        }
195

196
        index := &w.storage.entities[entity.id]
49✔
197
        oldTable := &w.storage.tables[index.table]
49✔
198

49✔
199
        newRelations, changed := w.storage.getExchangeTargets(oldTable, relations)
49✔
200
        if !changed {
49✔
201
                return
×
202
        }
×
203

204
        oldArch := &w.storage.archetypes[oldTable.archetype]
49✔
205
        newTable, ok := oldArch.GetTable(&w.storage, newRelations)
49✔
206
        if !ok {
66✔
207
                newTable = w.storage.createTable(oldArch, newRelations)
17✔
208
        }
17✔
209
        newIndex := newTable.Add(entity)
49✔
210

49✔
211
        for _, id := range oldArch.components {
199✔
212
                comp := oldTable.Get(id, uintptr(index.row))
150✔
213
                newTable.Set(id, newIndex, comp)
150✔
214
        }
150✔
215

216
        swapped := oldTable.Remove(index.row)
49✔
217

49✔
218
        if swapped {
65✔
219
                swapEntity := oldTable.GetEntity(uintptr(index.row))
16✔
220
                w.storage.entities[swapEntity.id].row = index.row
16✔
221
        }
16✔
222
        w.storage.entities[entity.id] = entityIndex{table: newTable.id, row: newIndex}
49✔
223

49✔
224
        w.storage.registerTargets(relations)
49✔
225
}
226

227
func (w *World) setRelationsBatch(batch *Batch, relations []RelationID, fn func(table tableID, start, len int)) {
25✔
228
        w.checkLocked()
25✔
229

25✔
230
        if len(relations) == 0 {
37✔
231
                panic("no relations specified")
12✔
232
        }
233

234
        tables := w.storage.getTables(batch)
13✔
235
        lengths := make([]uint32, len(tables))
13✔
236
        var totalEntities uint32 = 0
13✔
237
        for i, table := range tables {
26✔
238
                lengths[i] = uint32(table.Len())
13✔
239
                totalEntities += uint32(table.Len())
13✔
240
        }
13✔
241

242
        for i, table := range tables {
26✔
243
                tableLen := lengths[i]
13✔
244

13✔
245
                if tableLen == 0 {
13✔
246
                        continue
×
247
                }
248
                t, start, len := w.setRelationsTable(table, int(tableLen), relations)
13✔
249
                if fn != nil {
26✔
250
                        fn(t, start, len)
13✔
251
                }
13✔
252
        }
253

254
        w.storage.registerTargets(relations)
13✔
255
}
256

257
func (w *World) setRelationsTable(oldTable *table, oldLen int, relations []RelationID) (tableID, int, int) {
13✔
258
        newRelations, changed := w.storage.getExchangeTargets(oldTable, relations)
13✔
259

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

13✔
271
        return newTable.id, startIdx, oldLen
13✔
272

273
}
274

275
func (w *World) componentID(tp reflect.Type) ID {
3,390✔
276
        id, newID := w.storage.registry.ComponentID(tp)
3,390✔
277
        if newID {
4,712✔
278
                if w.IsLocked() {
1,323✔
279
                        w.storage.registry.unregisterLastComponent()
1✔
280
                        panic("attempt to register a new component in a locked world")
1✔
281
                }
282
                w.storage.AddComponent(id)
1,321✔
283
        }
284
        return ID{id: id}
3,389✔
285
}
286

287
func (w *World) resourceID(tp reflect.Type) ResID {
15✔
288
        id, _ := w.resources.registry.ComponentID(tp)
15✔
289
        return ResID{id: id}
15✔
290
}
15✔
291

292
// lock the world and get the lock bit for later unlocking.
293
func (w *World) lock() uint8 {
1,524✔
294
        return w.locks.Lock()
1,524✔
295
}
1,524✔
296

297
// unlock unlocks the given lock bit.
298
func (w *World) unlock(l uint8) {
1,523✔
299
        w.locks.Unlock(l)
1,523✔
300
}
1,523✔
301

302
// checkLocked checks if the world is locked, and panics if so.
303
func (w *World) checkLocked() {
999,847✔
304
        if w.IsLocked() {
999,848✔
305
                panic("attempt to modify a locked world")
1✔
306
        }
307
}
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