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

mlange-42 / arche-serde / 7494943208

11 Jan 2024 09:50PM CUT coverage: 84.141% (+0.2%) from 83.929%
7494943208

push

github

web-flow
Entity builder for relations (#4)

Use an `ecs.Builder` to create entities with targets in one command.

This should (?) result in an initial entity/query order exactly like in the serialized world.
Over time, however, the query iteration order will deviate in both worlds.

191 of 227 relevant lines covered (84.14%)

30.83 hits per line

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

76.84
/deserialize.go
1
package archeserde
2

3
import (
4
        "encoding/json"
5
        "fmt"
6
        "reflect"
7

8
        "github.com/mlange-42/arche/ecs"
9
)
10

11
// Deserialize an Arche [ecs.World] from JSON.
12
//
13
// The world must be prepared the following way:
14
//   - The world must not contain any alive or dead entities (i.e. a new or [ecs.World.Reset] world)
15
//   - All required component types must be registered using [ecs.ComponentID]
16
//   - All required resources must be added as dummies using [ecs.AddResource]
17
//
18
// # Query iteration order
19
//
20
// After deserialization, it is not guaranteed that entity iteration order in queries is the same as before.
21
// More precisely, it should at first be the same as before, but will likely deviate over time from what would
22
// happen when continuing the original, serialized run. Multiple worlds deserialized from the same source should,
23
// however, behave exactly the same.
24
func Deserialize(jsonData []byte, world *ecs.World) error {
3✔
25
        deserial := deserializer{}
3✔
26
        if err := json.Unmarshal(jsonData, &deserial); err != nil {
3✔
27
                return err
×
28
        }
×
29

30
        world.SetEntityData(&deserial.World)
3✔
31

3✔
32
        if err := deserializeComponents(world, &deserial); err != nil {
3✔
33
                return err
×
34
        }
×
35
        if err := deserializeResources(world, &deserial); err != nil {
3✔
36
                return err
×
37
        }
×
38

39
        return nil
3✔
40
}
41

42
func deserializeComponents(world *ecs.World, deserial *deserializer) error {
3✔
43
        infos := map[ecs.ID]ecs.CompInfo{}
3✔
44
        ids := map[string]ecs.ID{}
3✔
45
        for i := 0; i < ecs.MaskTotalBits; i++ {
771✔
46
                if info, ok := ecs.ComponentInfo(world, ecs.ID(i)); ok {
773✔
47
                        infos[ecs.ID(i)] = info
5✔
48
                        ids[info.Type.String()] = ecs.ID(i)
5✔
49
                }
5✔
50
        }
51

52
        for _, tp := range deserial.Types {
8✔
53
                if _, ok := ids[tp]; !ok {
5✔
54
                        return fmt.Errorf("component type is not registered: %s", tp)
×
55
                }
×
56
        }
57

58
        for i, comps := range deserial.Components {
8✔
59
                entity := deserial.World.Entities[deserial.World.Alive[i]]
5✔
60

5✔
61
                mp := map[string]entry{}
5✔
62

5✔
63
                if err := json.Unmarshal(comps.Bytes, &mp); err != nil {
5✔
64
                        return err
×
65
                }
×
66

67
                target := ecs.Entity{}
5✔
68
                var targetComp ecs.ID
5✔
69
                components := []ecs.Component{}
5✔
70
                for tpName, value := range mp {
16✔
71
                        if tpName == targetTag {
13✔
72
                                if err := json.Unmarshal(value.Bytes, &target); err != nil {
2✔
73
                                        return err
×
74
                                }
×
75
                                continue
2✔
76
                        }
77

78
                        id := ids[tpName]
9✔
79
                        info := infos[id]
9✔
80

9✔
81
                        if info.IsRelation {
11✔
82
                                targetComp = id
2✔
83
                        }
2✔
84

85
                        component := reflect.New(info.Type).Interface()
9✔
86
                        if err := json.Unmarshal(value.Bytes, &component); err != nil {
9✔
87
                                return err
×
88
                        }
×
89
                        components = append(components, ecs.Component{
9✔
90
                                ID:   id,
9✔
91
                                Comp: component,
9✔
92
                        })
9✔
93
                }
94
                builder := ecs.NewBuilderWith(world, components...)
5✔
95
                if target.IsZero() {
9✔
96
                        builder.Add(entity)
4✔
97
                } else {
5✔
98
                        builder = builder.WithRelation(targetComp)
1✔
99
                        builder.Add(entity, target)
1✔
100
                }
1✔
101
        }
102
        return nil
3✔
103
}
104

105
func deserializeResources(world *ecs.World, deserial *deserializer) error {
3✔
106
        resTypes := map[ecs.ResID]reflect.Type{}
3✔
107
        resIds := map[string]ecs.ResID{}
3✔
108
        for i := 0; i < ecs.MaskTotalBits; i++ {
771✔
109
                if tp, ok := ecs.ResourceType(world, ecs.ResID(i)); ok {
770✔
110
                        resTypes[ecs.ResID(i)] = tp
2✔
111
                        resIds[tp.String()] = ecs.ResID(i)
2✔
112
                }
2✔
113
        }
114

115
        for tpName, res := range deserial.Resources {
5✔
116
                resID, ok := resIds[tpName]
2✔
117
                if !ok {
2✔
118
                        return fmt.Errorf("resource type is not registered: %s", tpName)
×
119
                }
×
120

121
                tp := resTypes[resID]
2✔
122
                resource := reflect.New(tp).Interface()
2✔
123
                if err := json.Unmarshal(res.Bytes, &resource); err != nil {
2✔
124
                        return err
×
125
                }
×
126

127
                resLoc := world.Resources().Get(resID)
2✔
128
                if resLoc == nil {
2✔
129
                        return fmt.Errorf("resource type registered but nil: %s", tpName)
×
130
                }
×
131

132
                rValue := reflect.ValueOf(resLoc)
2✔
133
                ptr := rValue.UnsafePointer()
2✔
134
                value := reflect.NewAt(tp, ptr).Interface()
2✔
135

2✔
136
                if err := json.Unmarshal(res.Bytes, &value); err != nil {
2✔
137
                        return err
×
138
                }
×
139
        }
140
        return nil
3✔
141
}
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