lifetime_scope.bend

bendvy-ecs-core@0.1.0.0 · 105 lines · 0 definitions

1import Base
2import bendvy-ecs-core@0.1.0.0/deferred_command.bend as Deferred
3import bendvy-ecs-core@0.1.0.0/trace_facts.bend as TraceFacts
4import entity_key.bend as Key
5import bendvy-ecs-core@0.1.0.0/world.bend as W
6import bendvy-ecs-core@0.1.0.0/transaction.bend as T
7import bendvy-ecs-core@0.1.0.0/commands.bend as Cmd
8import bendvy-ecs-core@0.1.0.0/relation_commands.bend as A
9import bendvy-ecs-core@0.1.0.0/relation_types.bend as G
10import bendvy-ecs-core@0.1.0.0/relation_cleanup.bend as Cleanup
11import bendvy-ecs-core@0.1.0.0/string_equality.bend as Text
12
13# Lifetime metadata is separate from component owners and never proves liveness.
14type Scope<-S:Data> is Data:
15  Scope{namespace:U32,name:String}
16type Member is Data:
17  Member{name:String,id:Key.Key}
18type Store<-S:Data,C:Type> is Type:
19  Store{components:C,members:List<&2,Member>}
20def empty(~S:Data,~C:Type,components:C) -> Store<S,C>:
21  Store{components,[]}
22def without(members:List<&2,Member>,+id:Key.Key) -> List<&2,Member>:
23  match members:
24    case []:[]
25    case Member{+name,+mid} <> rest:
26      +remaining = without(rest,id)
27      Bool.pick(List<&2,Member>,Key.equal(mid,id),remaining,Member{name,mid} <> remaining)
28def select(members:List<&2,Member>,+name:String) -> List<&2,Key.Key>:
29  match members:
30    case []:[]
31    case Member{+key,+id} <> rest:
32      +remaining = select(rest,name)
33      Bool.pick(List<&2,Key.Key>,Text.equal(key,name),id <> remaining,remaining)
34def assigned(~S:Data,~C:Type,store:A.Store<S,Store<S,C>>,name:String,+id:Key.Key) -> A.Store<S,Store<S,C>> & Unit:
35  match store:
36    case A.Store{Store{components,members},graph}:(A.Store{Store{components,List.append(&2,Member,without(members,id),[Member{name,id}])},graph},Unit{})
37def observed(~S:Data,~C:Type,store:A.Store<S,Store<S,C>>,name:String) -> A.Store<S,Store<S,C>> & List<&2,Key.Key>:
38  match store:
39    case A.Store{Store{components,+members},graph}:(A.Store{Store{components,members},graph},select(members,name))
40def scope_returned(~S:Data,~C:Type,~R:Type,~E:Data,name:String,seen:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> & U32) -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> & Scope<S>:
41  match seen:
42    case (world,namespace):(world,Scope{namespace,name})
43def scope(~S:Data,~C:Type,~R:Type,~E:Data,world:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,name:String) -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> & Scope<S>:
44  scope_returned(~S,~C,~R,~E,name,W.namespace(~S,~A.Store<S,Store<S,C>>,~R,~A.Notice<S,E>,world))
45def cleared(~S:Data,~C:Type,~E:Data,members:List<&2,Member>,seen:C & List<&2,A.Notice<S,E>>) -> Store<S,C> & List<&2,A.Notice<S,E>>:
46  match seen:
47    case (components,records):(Store{components,members},records)
48def clear_components(~S:Data,~C:Type,~E:Data,~clear:C -> W.Handle<S> -> C & List<&2,A.Notice<S,E>>,store:Store<S,C>,handle:W.Handle<S>) -> Store<S,C> & List<&2,A.Notice<S,E>>:
49  match store handle:
50    case Store{components,members} W.Handle{+namespace,+id}:cleared(~S,~C,~E,without(members,id),clear(components,W.Handle{namespace,id}))
51def cleanup_result(~S:Data,~C:Type,~R:Type,~E:Data,result:Cleanup.CleanupResult<S,Store<S,C>,R,E>) -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>:
52  match result:
53    case Cleanup.Complete{world}:world
54    case Cleanup.Incomplete{world,frames}:W.event_publish(~S,~A.Store<S,Store<S,C>>,~R,~A.Notice<S,E>,world,[A.CleanupPaused{frames}])
55def cleanup_members(~S:Data,~C:Type,~R:Type,~E:Data,~clear:C -> W.Handle<S> -> C & List<&2,A.Notice<S,E>>,~record:W.Handle<S> -> A.Notice<S,E>,~descriptors:List<&2,G.Descriptor<S>>,members:List<&2,Key.Key>,world:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,+namespace:U32) -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>:
56  match members:
57    case []:world
58    case id <> rest:cleanup_members(~S,~C,~R,~E,~clear,~record,~descriptors,rest,cleanup_result(~S,~C,~R,~E,Cleanup.automatic(~S,~Store<S,C>,~R,~E,~clear_components(~S,~C,~E,~clear),~record,world,W.Handle{namespace,id},descriptors)),namespace)
59def cleanup_observed(~S:Data,~C:Type,~R:Type,~E:Data,~clear:C -> W.Handle<S> -> C & List<&2,A.Notice<S,E>>,~record:W.Handle<S> -> A.Notice<S,E>,~descriptors:List<&2,G.Descriptor<S>>,namespace:U32,seen:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> & List<&2,Key.Key>) -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>:
60  match seen:
61    case (world,members):cleanup_members(~S,~C,~R,~E,~clear,~record,~descriptors,members,world,namespace)
62def cleanup_apply(~S:Data,~C:Type,~R:Type,~E:Data,~clear:C -> W.Handle<S> -> C & List<&2,A.Notice<S,E>>,~record:W.Handle<S> -> A.Notice<S,E>,~descriptors:List<&2,G.Descriptor<S>>,world:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,scope:Scope<S>) -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>:
63  match scope:
64    case Scope{namespace,name}:cleanup_observed(~S,~C,~R,~E,~clear,~record,~descriptors,namespace,W.with_store(~S,~A.Store<S,Store<S,C>>,~R,~A.Notice<S,E>,~List<&2,Key.Key>,store => observed(~S,~C,store,name),world))
65def cleanup_checked(~S:Data,~C:Type,~R:Type,~E:Data,~clear:C -> W.Handle<S> -> C & List<&2,A.Notice<S,E>>,~record:W.Handle<S> -> A.Notice<S,E>,~descriptors:List<&2,G.Descriptor<S>>,same:Bool,tx:T.Tx<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,A.Notice<S,E>>,scope:Scope<S>) -> T.Tx<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,A.Notice<S,E>> & A.QueueStatus:
66  match same:
67    case False{}:(tx,A.MissingEntity{})
68    case True{}:(T.stage(~W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,~A.Notice<S,E>,tx,world => cleanup_apply(~S,~C,~R,~E,~clear,~record,~descriptors,world,scope)),A.Queued{})
69def cleanup_namespaced(~S:Data,~C:Type,~R:Type,~E:Data,~clear:C -> W.Handle<S> -> C & List<&2,A.Notice<S,E>>,~record:W.Handle<S> -> A.Notice<S,E>,~descriptors:List<&2,G.Descriptor<S>>,seen:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> & U32,scope:Scope<S>,undo:List<(W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>)>,commands:List<Deferred.Command<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>>>,events:List<&2,A.Notice<S,E>>,trace:TraceFacts.State) -> T.Tx<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,A.Notice<S,E>> & A.QueueStatus:
70  match seen scope:
71    case (world,current) Scope{+namespace,+name}:cleanup_checked(~S,~C,~R,~E,~clear,~record,~descriptors,U32.is_eq(namespace,current),T.Tx{world,undo,commands,events,trace},Scope{namespace,name})
72def tx_cleanup(~S:Data,~C:Type,~R:Type,~E:Data,~clear:C -> W.Handle<S> -> C & List<&2,A.Notice<S,E>>,~record:W.Handle<S> -> A.Notice<S,E>,~descriptors:List<&2,G.Descriptor<S>>,tx:T.Tx<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,A.Notice<S,E>>,scope:Scope<S>) -> T.Tx<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,A.Notice<S,E>> & A.QueueStatus:
73  match tx:
74    case T.Tx{world,undo,commands,events,trace}:cleanup_namespaced(~S,~C,~R,~E,~clear,~record,~descriptors,W.namespace(~S,~A.Store<S,Store<S,C>>,~R,~A.Notice<S,E>,world),scope,undo,commands,events,trace)
75type SpawnResult<S:Data,P:Type> is Type:
76  Spawned{handle:W.Handle<S>}
77  SpawnRejected{payload:P,error:W.WorldError}
78def populate_store(~S:Data,~C:Type,~P:Type,~populate:C -> W.Handle<S> -> P -> C,store:A.Store<S,Store<S,C>>,handle:W.Handle<S>,payload:P,name:String) -> A.Store<S,Store<S,C>> & Unit:
79  match store handle:
80    case A.Store{Store{components,members},graph} W.Handle{+ns,+id}:(A.Store{Store{populate(components,W.Handle{ns,id},payload),List.append(&2,Member,without(members,id),[Member{name,id}])},graph},Unit{})
81def populate_returned(~S:Data,~C:Type,~R:Type,~E:Data,seen:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> & Unit) -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>:
82  match seen:
83    case (world,_):world
84def populate_world(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle<S> -> P -> C,world:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,+handle:W.Handle<S>,payload:P,name:String) -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>:
85  populate_returned(~S,~C,~R,~E,W.with_store(~S,~A.Store<S,Store<S,C>>,~R,~A.Notice<S,E>,~Unit,store => populate_store(~S,~C,~P,~populate,store,handle,payload,name),world))
86def spawn_activated(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle<S> -> P -> C,seen:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> & W.Status,handle:W.Handle<S>,payload:P,name:String) -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>:
87  match seen:
88    case (world,W.Accepted{}):populate_world(~S,~C,~R,~E,~P,~populate,world,handle,payload,name)
89    case (world,W.Rejected{_}):world
90def spawn_apply(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle<S> -> P -> C,world:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,+handle:W.Handle<S>,payload:P,name:String) -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>:
91  spawn_activated(~S,~C,~R,~E,~P,~populate,W.activate(~S,~A.Store<S,Store<S,C>>,~R,~A.Notice<S,E>,world,handle),handle,payload,name)
92def spawn_reserved(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle<S> -> P -> C,seen:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> & W.Reservation<S>,undo:List<(W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>)>,commands:List<Deferred.Command<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>>>,events:List<&2,A.Notice<S,E>>,trace:TraceFacts.State,payload:P,name:String) -> T.Tx<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,A.Notice<S,E>> & SpawnResult<S,P>:
93  match seen:
94    case (world,W.ReservationRejected{error}):(T.Tx{world,undo,commands,events,trace},SpawnRejected{payload,error})
95    case (world,W.Reserved{+handle}):(Cmd.track_pending(~S,~A.Store<S,Store<S,C>>,~R,~A.Notice<S,E>,T.Tx{world,undo,Deferred.Raw{(world => spawn_apply(~S,~C,~R,~E,~P,~populate,world,handle,payload,name))} <> commands,events,trace},handle),Spawned{handle})
96def spawn_checked(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle<S> -> P -> C,same:Bool,world:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,undo:List<(W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>)>,commands:List<Deferred.Command<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>>>,events:List<&2,A.Notice<S,E>>,trace:TraceFacts.State,payload:P,name:String) -> T.Tx<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,A.Notice<S,E>> & SpawnResult<S,P>:
97  match same:
98    case False{}:(T.Tx{world,undo,commands,events,trace},SpawnRejected{payload,W.MissingEntity{}})
99    case True{}:spawn_reserved(~S,~C,~R,~E,~P,~populate,W.reserve_id(~S,~A.Store<S,Store<S,C>>,~R,~A.Notice<S,E>,world),undo,commands,events,trace,payload,name)
100def spawn_namespaced(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle<S> -> P -> C,seen:W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> & U32,scope:Scope<S>,undo:List<(W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>> -> W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>)>,commands:List<Deferred.Command<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>>>,events:List<&2,A.Notice<S,E>>,trace:TraceFacts.State,payload:P) -> T.Tx<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,A.Notice<S,E>> & SpawnResult<S,P>:
101  match seen scope:
102    case (world,current) Scope{namespace,name}:spawn_checked(~S,~C,~R,~E,~P,~populate,U32.is_eq(current,namespace),world,undo,commands,events,trace,payload,name)
103def tx_spawn_in(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle<S> -> P -> C,tx:T.Tx<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,A.Notice<S,E>>,scope:Scope<S>,payload:P) -> T.Tx<W.World<S,A.Store<S,Store<S,C>>,R,A.Notice<S,E>>,A.Notice<S,E>> & SpawnResult<S,P>:
104  match tx:
105    case T.Tx{world,undo,commands,events,trace}:spawn_namespaced(~S,~C,~R,~E,~P,~populate,W.namespace(~S,~A.Store<S,Store<S,C>>,~R,~A.Notice<S,E>,world),scope,undo,commands,events,trace,payload)