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

mlange-42 / ark / 13730551907

07 Mar 2025 10:34PM CUT coverage: 99.424% (+0.001%) from 99.423%
13730551907

push

github

web-flow
Component IDs of entities are returned immutable (#156)

13 of 13 new or added lines in 2 files covered. (100.0%)

6387 of 6424 relevant lines covered (99.42%)

27582.11 hits per line

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

93.16
/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 {
486,511✔
9
        w.checkLocked()
486,511✔
10

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

486,511✔
15
        if comps != nil {
972,850✔
16
                if len(ids) != len(comps) {
486,339✔
17
                        panic("lengths of IDs and components to add do not match")
×
18
                }
19
                for i, id := range ids {
976,494✔
20
                        newTable.Set(id, idx, comps[i])
490,155✔
21
                }
490,155✔
22
        }
23
        w.storage.registerTargets(relations)
486,511✔
24
        return entity
486,511✔
25
}
26

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

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

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

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

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

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

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

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

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

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

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

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

760✔
81
        mask := Mask{}
760✔
82
        newTable := w.storage.findOrCreateTable(oldTable, add, rem, relations, &mask)
760✔
83
        newIndex := newTable.Add(entity)
760✔
84

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

100
        swapped := oldTable.Remove(index.row)
760✔
101

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

760✔
108
        w.storage.registerTargets(relations)
760✔
109
}
110

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

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

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

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

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

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

160✔
146
        oldIDs := oldArchetype.components
160✔
147

160✔
148
        mask := Mask{}
160✔
149
        newTable := w.storage.findOrCreateTable(oldTable, add, rem, relations, &mask)
160✔
150
        startIdx := uintptr(newTable.Len())
160✔
151
        count := uintptr(oldLen)
160✔
152

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

275
}
276

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

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

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

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

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