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)