Line data Source code
1 : import 'dart:async'; 2 : 3 : import 'package:uuid/uuid.dart'; 4 : 5 : import 'component.dart'; 6 : import 'entity.dart'; 7 : import 'extensions/extensions.dart'; 8 : import 'mixins.dart'; 9 : 10 : // A function that takes JSON data and returns a component. 11 : typedef ComponentDeserializerFunction = Component? Function(Map<String, dynamic> data); 12 : 13 : // A function that takes a component and returns JSON-esque data. 14 : typedef ComponentSerializerFunction = Map<String, dynamic> Function(SerializableComponent component); 15 : 16 : // A function that takes in an entity and returns JSON-esque data. 17 : typedef EntityToJsonFunction = Map<String, dynamic> Function(Entity entity); 18 : 19 : // A function that takes in JSON and returns an Entity; 20 : typedef EntityFromJsonFunction = Entity Function( 21 : Map<String, dynamic> json, 22 : EntitySystem system, 23 : ); 24 : 25 : /// This system is for keeping track of all created entities. 26 : class EntitySystem { 27 : final uuid = const Uuid(); 28 : 29 : // The listener that updates the public list of entities. 30 : late StreamSubscription _listener; 31 : 32 1 : EntitySystem() { 33 : // When entities updates, map over and update the public entity list. 34 5 : _listener = _entities.stream.listen((ents) { 35 2 : final list = ents.values.toList(); 36 2 : entities.add(list); 37 : }); 38 : } 39 : 40 : /// Call this to release resources and workers. 41 0 : void dispose() { 42 : /// Clear entities and cancel subscription. 43 0 : _listener.cancel(); 44 0 : _entities.close(); 45 0 : entities.close(); 46 : } 47 : 48 : /// Remove all entities. 49 0 : void flush() { 50 0 : if (_entities.hasValue) { 51 0 : _entities.value.forEach((guid, e) => e.destroy()); 52 0 : _entities.value.clear(); 53 : } 54 : } 55 : 56 : /// A list of registered deserializer functions. 57 : List<ComponentDeserializerFunction> deserializers = [ 58 : componentFromJson, 59 : ]; 60 : 61 : /// A list of registered serializer functions. 62 : List<ComponentSerializerFunction> serializers = [ 63 : componentToJson, 64 : ]; 65 : 66 : /// The function called to turn an entity into JSON. 67 : EntityToJsonFunction entityToJsonFunction = defaultEntityToJsonFunction; 68 : 69 : /// The function called to turn JSON into an entity; 70 : EntityFromJsonFunction entityFromJsonFunction = defaultEntityFromJsonFunction; 71 : 72 : /// The internal registry of all created entities. 73 : final _entities = <String, Entity>{}.bs; 74 : 75 : /// An observable list of entities. 76 : final entities = <Entity>[].bs; 77 : 78 : /// Register a deserializer function. 79 0 : void registerDeserializer(ComponentDeserializerFunction deserializer) { 80 0 : if (!deserializers.contains(deserializer)) { 81 0 : deserializers.add(deserializer); 82 : } 83 : } 84 : 85 : /// Register a serializer function. 86 0 : void registerSerializer(ComponentSerializerFunction serializer) { 87 0 : if (!serializers.contains(serializer)) { 88 0 : serializers.add(serializer); 89 : } 90 : } 91 : 92 : /// The way a new entity should be created. 93 1 : Entity create() { 94 3 : final entity = Entity(uuid.v4(), this); 95 1 : return setEntity(entity); 96 : } 97 : 98 : /// Deserializing entities from JSON. 99 0 : Entity createFromJson(Map<String, dynamic> json) { 100 0 : final entity = entityFromJsonFunction(json, this); 101 0 : return setEntity(entity); 102 : } 103 : 104 1 : Entity setEntity(Entity entity) { 105 2 : final current = _entities.value; 106 7 : final newList = Map.fromIterables([...current.keys, entity.guid], [...current.values, entity]); 107 2 : _entities.add(newList); 108 : // Return the entity. 109 4 : return _entities.value[entity.guid]!; 110 : } 111 : 112 : /// Standard serializer that turns an entity into JSON. 113 0 : Map<String, dynamic> entityToJson(Entity entity) { 114 0 : return entityToJsonFunction(entity); 115 : } 116 : 117 : /// When an entity has been destroyed, we remove it from the list 118 : /// and that will automatically sync that to the observable list. 119 0 : void destroyed(Entity entity) { 120 0 : final current = _entities.value; 121 : // Drop the guid and remove the entity from the list. 122 0 : final newList = Map.fromIterables([...current.keys], [...current.values].where((e) => e != entity)); 123 : // Set the new list. 124 0 : _entities.add(newList); 125 : } 126 : } 127 : 128 : /// Standard serializer that turns an entity into JSON. 129 0 : Map<String, dynamic> defaultEntityToJsonFunction(Entity entity) { 130 0 : List<Map<String, dynamic>> componentData = []; 131 0 : entity.components().forEach( 132 0 : (type, component) { 133 0 : if (component is SerializableComponent) { 134 0 : componentData.add({'type': type.toString(), 'data': (component as SerializableComponent).toJson()}); 135 : } 136 : }, 137 : ); 138 0 : return <String, dynamic>{ 139 : 'components': componentData, 140 : }; 141 : } 142 : 143 0 : Entity defaultEntityFromJsonFunction(Map<String, dynamic> json, EntitySystem system) { 144 0 : var e = Entity(json['guid'], system); 145 0 : if (json['components'] != null) { 146 0 : final comps = json['components'] as List<dynamic>; 147 0 : for (var compData in comps) { 148 : final data = compData as Map<String, dynamic>; 149 0 : Component? c = componentFromJson(data); 150 : // if not contained in default system, check serializers. 151 : if (c == null) { 152 0 : for (var serializer in system.deserializers) { 153 0 : c = serializer.call(data); 154 : } 155 : } 156 : 157 : /// If we actually got a component, add it. 158 : if (c != null) { 159 0 : e += c; 160 : } 161 : } 162 : } 163 : 164 : return e; 165 : } 166 : 167 : /// A map of strings to types. 168 0 : Component? componentFromJson(Map<String, dynamic> json) { 169 : return null; 170 : } 171 : 172 0 : Map<String, dynamic> componentToJson(SerializableComponent component) { 173 0 : return component.toJson(); 174 : }