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

mlange-42 / arche-serde / 7596945134

20 Jan 2024 09:32PM CUT coverage: 93.939% (+9.5%) from 84.483%
7596945134

push

github

web-flow
Add tests for errors, remove dead code (#9)

217 of 231 relevant lines covered (93.94%)

24.39 hits per line

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

100.0
/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 {
14✔
25
        deserial := deserializer{}
14✔
26
        if err := json.Unmarshal(jsonData, &deserial); err != nil {
16✔
27
                return err
2✔
28
        }
2✔
29

30
        world.LoadEntities(&deserial.World)
12✔
31

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

39
        return nil
4✔
40
}
41

42
func deserializeComponents(world *ecs.World, deserial *deserializer) error {
12✔
43
        infos := map[ecs.ID]ecs.CompInfo{}
12✔
44
        ids := map[string]ecs.ID{}
12✔
45
        allComps := ecs.ComponentIDs(world)
12✔
46
        for _, id := range allComps {
53✔
47
                if info, ok := ecs.ComponentInfo(world, id); ok {
82✔
48
                        infos[id] = info
41✔
49
                        ids[info.Type.String()] = id
41✔
50
                }
41✔
51
        }
52

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

59
        if len(deserial.Components) != len(deserial.World.Alive) {
12✔
60
                return fmt.Errorf("found components for %d entities, but world has %d alive entities", len(deserial.Components), len(deserial.World.Alive))
1✔
61
        }
1✔
62

63
        for i, comps := range deserial.Components {
85✔
64
                entity := deserial.World.Entities[deserial.World.Alive[i]]
75✔
65

75✔
66
                mp := map[string]entry{}
75✔
67

75✔
68
                if err := json.Unmarshal(comps.Bytes, &mp); err != nil {
76✔
69
                        return err
1✔
70
                }
1✔
71

72
                target := ecs.Entity{}
74✔
73
                var targetComp ecs.ID
74✔
74
                components := []ecs.Component{}
74✔
75
                for tpName, value := range mp {
161✔
76
                        if tpName == targetTag {
90✔
77
                                if err := json.Unmarshal(value.Bytes, &target); err != nil {
4✔
78
                                        return err
1✔
79
                                }
1✔
80
                                continue
2✔
81
                        }
82

83
                        id := ids[tpName]
84✔
84
                        info := infos[id]
84✔
85

84✔
86
                        if info.IsRelation {
86✔
87
                                targetComp = id
2✔
88
                        }
2✔
89

90
                        component := reflect.New(info.Type).Interface()
84✔
91
                        if err := json.Unmarshal(value.Bytes, &component); err != nil {
85✔
92
                                return err
1✔
93
                        }
1✔
94
                        components = append(components, ecs.Component{
83✔
95
                                ID:   id,
83✔
96
                                Comp: component,
83✔
97
                        })
83✔
98
                }
99
                builder := ecs.NewBuilderWith(world, components...)
72✔
100
                if target.IsZero() {
143✔
101
                        builder.Add(entity)
71✔
102
                } else {
72✔
103
                        builder = builder.WithRelation(targetComp)
1✔
104
                        builder.Add(entity, target)
1✔
105
                }
1✔
106
        }
107
        return nil
7✔
108
}
109

110
func deserializeResources(world *ecs.World, deserial *deserializer) error {
7✔
111
        resTypes := map[ecs.ResID]reflect.Type{}
7✔
112
        resIds := map[string]ecs.ResID{}
7✔
113
        allRes := ecs.ResourceIDs(world)
7✔
114
        for _, id := range allRes {
12✔
115
                if tp, ok := ecs.ResourceType(world, id); ok {
10✔
116
                        resTypes[id] = tp
5✔
117
                        resIds[tp.String()] = id
5✔
118
                }
5✔
119
        }
120

121
        for tpName, res := range deserial.Resources {
13✔
122
                resID, ok := resIds[tpName]
6✔
123
                if !ok {
7✔
124
                        return fmt.Errorf("resource type is not registered: %s", tpName)
1✔
125
                }
1✔
126
                tp := resTypes[resID]
5✔
127

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

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

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