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

mlange-42 / arche-serde / 7577714645

19 Jan 2024 12:28AM CUT coverage: 84.483% (+0.3%) from 84.141%
7577714645

push

github

web-flow
Fix for opaque Arche 0.10 component and resource IDs (#7)

* Fix for opaque Arche entities and component and resource IDs
* add more interesting package example

196 of 232 relevant lines covered (84.48%)

19.97 hits per line

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

77.32
/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.LoadEntities(&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
        allComps := ecs.ComponentIDs(world)
3✔
46
        for _, id := range allComps {
9✔
47
                if info, ok := ecs.ComponentInfo(world, id); ok {
12✔
48
                        infos[id] = info
6✔
49
                        ids[info.Type.String()] = id
6✔
50
                }
6✔
51
        }
52

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

59
        for i, comps := range deserial.Components {
68✔
60
                entity := deserial.World.Entities[deserial.World.Alive[i]]
65✔
61

65✔
62
                mp := map[string]entry{}
65✔
63

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

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

79
                        id := ids[tpName]
69✔
80
                        info := infos[id]
69✔
81

69✔
82
                        if info.IsRelation {
71✔
83
                                targetComp = id
2✔
84
                        }
2✔
85

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

106
func deserializeResources(world *ecs.World, deserial *deserializer) error {
3✔
107
        resTypes := map[ecs.ResID]reflect.Type{}
3✔
108
        resIds := map[string]ecs.ResID{}
3✔
109
        allRes := ecs.ResourceIDs(world)
3✔
110
        for _, id := range allRes {
5✔
111
                if tp, ok := ecs.ResourceType(world, id); ok {
4✔
112
                        resTypes[id] = tp
2✔
113
                        resIds[tp.String()] = id
2✔
114
                }
2✔
115
        }
116

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

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

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

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

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