Skip to content

Commit cef602e

Browse files
committed
WIP on EM threadsafety, tidying.
1 parent 900ebeb commit cef602e

File tree

2 files changed

+120
-98
lines changed

2 files changed

+120
-98
lines changed

lib/ruby/entity_manager.rb

Lines changed: 112 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ def initialize
2121
@ids_to_tags = Hash.new
2222
@tags_to_ids = Hash.new
2323

24-
# "Stores" hash: key=component class, value=a component store
25-
# Each "component store" hash: key=entity UUID, value=an array of components
26-
@component_stores = Hash.new
24+
@component_stores = Hash.new { |h, k| h[k] = {} }
25+
26+
@mutex = Mutex.new
2727
end
2828

2929
def all_entities
@@ -40,152 +40,155 @@ def create_tagged_entity(human_readable_tag)
4040
raise ArgumentError, "Must specify tag" if human_readable_tag.nil?
4141
raise ArgumentError, "Tag '-' is reserved and cannot be used" if human_readable_tag=='-'
4242

43-
uuid=create_basic_entity
44-
@ids_to_tags[uuid]=human_readable_tag
45-
if @tags_to_ids.has_key? human_readable_tag
46-
@tags_to_ids[human_readable_tag]<<uuid
47-
else
48-
@tags_to_ids[human_readable_tag]=[uuid]
43+
@mutex.synchronize do
44+
uuid=create_basic_entity
45+
@ids_to_tags[uuid]=human_readable_tag
46+
if @tags_to_ids.has_key? human_readable_tag
47+
@tags_to_ids[human_readable_tag]<<uuid
48+
else
49+
@tags_to_ids[human_readable_tag]=[uuid]
50+
end
51+
52+
return uuid
4953
end
54+
end
5055

51-
return uuid
56+
def get_all_entities_with_tag(tag)
57+
@tags_to_ids[tag]
5258
end
5359

54-
# def set_tag(entity_uuid, human_readable_tag)
55-
# raise ArgumentError, "UUID and tag must be specified" if entity_uuid.nil? || human_readable_tag.nil?
60+
# def set_tag(entity, human_readable_tag)
61+
# raise ArgumentError, "UUID and tag must be specified" if entity.nil? || human_readable_tag.nil?
5662

57-
# @ids_to_tags[entity_uuid]=human_readable_tag
63+
# @ids_to_tags[entity]=human_readable_tag
5864
# @tags_to_ids[]
59-
# @tags_to_ids[human_readable_tag]<<entity_uuid
65+
# @tags_to_ids[human_readable_tag]<<entity
6066
# end
6167

62-
def get_tag(entity_uuid)
63-
raise ArgumentError, "UUID must be specified" if entity_uuid.nil?
68+
def get_tag(entity)
69+
raise ArgumentError, "UUID must be specified" if entity.nil?
6470

65-
@ids_to_tags[entity_uuid]
71+
@ids_to_tags[entity]
6672
end
6773

68-
def get_all_entities_with_tag(tag)
69-
@tags_to_ids[tag]
70-
end
74+
def has_component?(entity, component)
75+
raise ArgumentError, "UUID and component must be specified" if entity.nil? || component.nil?
7176

72-
def kill_entity(entity_uuid)
73-
raise ArgumentError, "UUID must be specified" if entity_uuid.nil?
74-
75-
@component_stores.each_value do |store|
76-
store.delete(entity_uuid)
77-
end
78-
@tags_to_ids.each_key do |tag|
79-
if @tags_to_ids[tag].include? entity_uuid
80-
@tags_to_ids[tag].delete entity_uuid
81-
end
77+
store = @component_stores[component.class]
78+
if store.nil?
79+
return false # NOBODY has this component type
80+
else
81+
return store.has_key?(entity) && store[entity].include?(component)
8282
end
83+
end
8384

84-
if @ids_to_tags.delete(entity_uuid)==nil
85-
return false
85+
def has_component_of_type?(entity, component_class)
86+
raise ArgumentError, "UUID and component class must be specified" if entity.nil? || component_class.nil?
87+
88+
store = @component_stores[component_class]
89+
if store.nil?
90+
return false # NOBODY has this component type
8691
else
87-
return true
92+
return store.has_key?(entity) && store[entity].size > 0
8893
end
8994
end
9095

91-
def add_component(entity_uuid, component)
92-
raise ArgumentError, "UUID and component must be specified" if entity_uuid.nil? || component.nil?
96+
97+
def add_component(entity, component)
98+
raise ArgumentError, "UUID and component must be specified" if entity.nil? || component.nil?
9399

94100
# Get the store for this component class.
95101
# If it doesn't exist, make it.
96102
store = @component_stores[component.class]
97-
if store.nil?
98-
store = Hash.new
99-
@component_stores[component.class]=store
100-
end
103+
@mutex.synchronize do
104+
if store.nil?
105+
store = Hash.new
106+
@component_stores[component.class]=store
107+
end
101108

102-
if store.has_key? entity_uuid
103-
store[entity_uuid] << component unless store[entity_uuid].include? component
104-
else
105-
store[entity_uuid] = [component]
109+
if store.has_key? entity
110+
store[entity] << component unless store[entity].include? component
111+
else
112+
store[entity] = [component]
113+
end
106114
end
107115
end
108116

109-
def has_component(entity_uuid, component)
110-
raise ArgumentError, "UUID and component must be specified" if entity_uuid.nil? || component.nil?
111-
112-
store = @component_stores[component.class]
113-
if store.nil?
114-
return false # NOBODY has this component type
115-
else
116-
return store.has_key?(entity_uuid) && store[entity_uuid].include?(component)
117+
def add_singleton_component(entity, component)
118+
if has_component_of_type?(entity, component.class.to_s.to_sym)
119+
$logger.warn "Denied attempt to add more than one singleton #{component} to #{entity}"
120+
return false
117121
end
122+
123+
add_component(entity, component)
118124
end
119125

120-
def has_component_of_type(entity_uuid, component_class)
121-
raise ArgumentError, "UUID and component class must be specified" if entity_uuid.nil? || component_class.nil?
126+
def remove_component(entity, component)
127+
raise ArgumentError, "UUID and component must be specified" if entity.nil? || component.nil?
122128

123-
store = @component_stores[component_class]
124-
if store.nil?
125-
return false # NOBODY has this component type
126-
else
127-
return store.has_key?(entity_uuid) && store[entity_uuid].size > 0
129+
Thread.exclusive do
130+
store = @component_stores[component.class]
131+
return nil if store.nil?
132+
133+
components = store[entity]
134+
return nil if components.nil?
135+
136+
result = components.delete(component)
137+
if result.nil?
138+
raise ArgumentError, "Entity #{entity} did not possess #{component} to remove"
139+
else
140+
store.delete(entity) if store[entity].empty?
141+
return true
142+
end
128143
end
129144
end
130145

131-
def get_component_of_type(entity_uuid, component_class)
132-
raise ArgumentError, "UUID and component class must be specified" if entity_uuid.nil? || component_class.nil?
146+
def get_component_of_type(entity, component_class)
147+
raise ArgumentError, "UUID and component class must be specified" if entity.nil? || component_class.nil?
133148

134-
# return nil unless has_component_of_type(entity_uuid, component.class)
149+
# return nil unless has_component_of_type?(entity, component.class)
135150
store = @component_stores[component_class]
136151
return nil if store.nil?
137152

138-
components = store[entity_uuid]
153+
components = store[entity]
139154
return nil if components.nil? || components.empty?
140155

141156
if components.size != 1
142-
puts "Warning: you probably expected #{entity_uuid} to have just one #{component_class.to_s} but it had #{components.size}...returning first."
157+
puts "Warning: you probably expected #{entity} to have just one #{component_class.to_s} but it had #{components.size}...returning first."
143158
end
144159

145160
return components.first
146161
end
147162

148-
def get_components_of_type(entity_uuid, component_class)
149-
raise ArgumentError, "UUID and component class must be specified" if entity_uuid.nil? || component_class.nil?
163+
def get_components_of_type(entity, component_class)
164+
raise ArgumentError, "UUID and component class must be specified" if entity.nil? || component_class.nil?
150165

151-
# return nil unless has_component_of_type(entity_uuid, component.class)
166+
# return nil unless has_component_of_type?(entity, component.class)
152167
store = @component_stores[component_class]
153168
return nil if store.nil?
154169

155-
components = store[entity_uuid]
170+
components = store[entity]
156171
return nil if components.nil? || components.empty?
157172

158173
return components
159174
end
160175

161-
def remove_component(entity_uuid, component)
162-
raise ArgumentError, "UUID and component must be specified" if entity_uuid.nil? || component.nil?
163-
164-
store = @component_stores[component.class]
165-
return nil if store.nil?
166-
167-
components = store[entity_uuid]
168-
return nil if components.nil?
169-
170-
result = components.delete(component)
171-
if result.nil?
172-
raise ArgumentError, "Entity #{entity_uuid} did not possess #{component} to remove"
173-
else
174-
store.delete(entity_uuid) if store[entity_uuid].empty?
175-
return true
176-
end
176+
def get_all_components_of_type(component_sym)
177+
Set.new(@component_stores[component_sym].values).flatten
177178
end
178179

179-
def get_all_components(entity_uuid)
180-
raise ArgumentError, "UUID must be specified" if entity_uuid.nil?
180+
def get_all_components(entity)
181+
raise ArgumentError, "UUID must be specified" if entity.nil?
181182

182-
components = []
183-
@component_stores.values.each do |store|
184-
if store[entity_uuid]
185-
components += store[entity_uuid]
183+
@mutex.synchronize do
184+
components = []
185+
@component_stores.values.each do |store|
186+
if store[entity]
187+
components += store[entity]
188+
end
186189
end
190+
components
187191
end
188-
components
189192
end
190193

191194
def get_all_entities_with_component_of_type(component_class)
@@ -209,6 +212,25 @@ def get_all_entities_with_components_of_type(component_classes)
209212
return entities
210213
end
211214

215+
def kill_entity(entity)
216+
raise ArgumentError, "UUID must be specified" if entity.nil?
217+
218+
@component_stores.each_value do |store|
219+
store.delete(entity)
220+
end
221+
@tags_to_ids.each_key do |tag|
222+
if @tags_to_ids[tag].include? entity
223+
@tags_to_ids[tag].delete entity
224+
end
225+
end
226+
227+
if @ids_to_tags.delete(entity)==nil
228+
return false
229+
else
230+
return true
231+
end
232+
end
233+
212234
def dump_details
213235
output = to_s
214236
all_entities.each do |e|

test/entity_manager_test.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def test_add_component_and_test_for_its_existence
155155
id=@em.create_tagged_entity('blah')
156156
@em.add_component(id, @comp)
157157

158-
assert_equal(true, @em.has_component(id, @comp))
158+
assert_equal(true, @em.has_component?(id, @comp))
159159

160160
assert_equal(@comp, @em.get_all_components(id)[0])
161161

@@ -166,23 +166,23 @@ def test_add_and_remove_component
166166
id=@em.create_tagged_entity('blah')
167167
@em.add_component(id, @comp)
168168

169-
assert(@em.has_component(id,@comp))
170-
assert(@em.has_component_of_type(id,Component))
169+
assert(@em.has_component?(id,@comp))
170+
assert(@em.has_component_of_type?(id,Component))
171171
assert_equal(1, @em.get_all_components(id).size)
172172

173173
@em.remove_component(id, @comp)
174174

175175
assert_equal(0, @em.get_all_components(id).size)
176176

177-
refute(@em.has_component(id,@comp))
178-
refute(@em.has_component_of_type(id,Component))
177+
refute(@em.has_component?(id,@comp))
178+
refute(@em.has_component_of_type?(id,Component))
179179
end
180180

181181
def test_get_component_of_type
182182
id=@em.create_tagged_entity('blah')
183183
@em.add_component(id, @comp)
184184

185-
assert(@em.has_component(id,@comp))
185+
assert(@em.has_component?(id,@comp))
186186
assert_equal(1, @em.get_all_components(id).size)
187187

188188
comp = @em.get_component_of_type(id, Component)
@@ -198,8 +198,8 @@ def test_get_components_of_type
198198
@comp3 = String.new
199199
@em.add_component(id, @comp3)
200200

201-
assert(@em.has_component(id,@comp))
202-
assert(@em.has_component(id,@comp2))
201+
assert(@em.has_component?(id,@comp))
202+
assert(@em.has_component?(id,@comp2))
203203
assert_equal(3, @em.get_all_components(id).size)
204204

205205
comps = @em.get_components_of_type(id, Component)

0 commit comments

Comments
 (0)