1 import Base 2 import ./deferred_command.bend as Deferred 3 import ./trace_facts.bend as TraceFacts 4 import entity_key.bend as Key 5 import ./world.bend as W 6 import ./transaction.bend as T 7 import ./commands.bend as Cmd 8 import ./relation_commands.bend as A 9 import ./relation_types.bend as G 10 import ./relation_cleanup.bend as Cleanup 11 import ./string_equality.bend as Text 12 13 # Lifetime metadata is separate from component owners and never proves liveness. 14 type Scope<-S:Data> is Data: 15 Scope{namespace:U32,name:String} 16 type Member is Data: 17 Member{name:String,id:Key.Key} 18 type Store<-S:Data,C:Type> is Type: 19 Store{components:C,members:List<&2,Member>} 20 def empty(~S:Data,~C:Type,components:C) -> Store: 21 Store{components,[]} 22 def 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) 28 def 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) 34 def assigned(~S:Data,~C:Type,store:A.Store>,name:String,+id:Key.Key) -> A.Store> & 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{}) 37 def observed(~S:Data,~C:Type,store:A.Store>,name:String) -> A.Store> & List<&2,Key.Key>: 38 match store: 39 case A.Store{Store{components,+members},graph}:(A.Store{Store{components,members},graph},select(members,name)) 40 def scope_returned(~S:Data,~C:Type,~R:Type,~E:Data,name:String,seen:W.World>,R,A.Notice> & U32) -> W.World>,R,A.Notice> & Scope: 41 match seen: 42 case (world,namespace):(world,Scope{namespace,name}) 43 def scope(~S:Data,~C:Type,~R:Type,~E:Data,world:W.World>,R,A.Notice>,name:String) -> W.World>,R,A.Notice> & Scope: 44 scope_returned(~S,~C,~R,~E,name,W.namespace(~S,~A.Store>,~R,~A.Notice,world)) 45 def cleared(~S:Data,~C:Type,~E:Data,members:List<&2,Member>,seen:C & List<&2,A.Notice>) -> Store & List<&2,A.Notice>: 46 match seen: 47 case (components,records):(Store{components,members},records) 48 def clear_components(~S:Data,~C:Type,~E:Data,~clear:C -> W.Handle -> C & List<&2,A.Notice>,store:Store,handle:W.Handle) -> Store & List<&2,A.Notice>: 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})) 51 def cleanup_result(~S:Data,~C:Type,~R:Type,~E:Data,result:Cleanup.CleanupResult,R,E>) -> W.World>,R,A.Notice>: 52 match result: 53 case Cleanup.Complete{world}:world 54 case Cleanup.Incomplete{world,frames}:W.event_publish(~S,~A.Store>,~R,~A.Notice,world,[A.CleanupPaused{frames}]) 55 def cleanup_members(~S:Data,~C:Type,~R:Type,~E:Data,~clear:C -> W.Handle -> C & List<&2,A.Notice>,~record:W.Handle -> A.Notice,~descriptors:List<&2,G.Descriptor>,members:List<&2,Key.Key>,world:W.World>,R,A.Notice>,+namespace:U32) -> W.World>,R,A.Notice>: 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,~R,~E,~clear_components(~S,~C,~E,~clear),~record,world,W.Handle{namespace,id},descriptors)),namespace) 59 def cleanup_observed(~S:Data,~C:Type,~R:Type,~E:Data,~clear:C -> W.Handle -> C & List<&2,A.Notice>,~record:W.Handle -> A.Notice,~descriptors:List<&2,G.Descriptor>,namespace:U32,seen:W.World>,R,A.Notice> & List<&2,Key.Key>) -> W.World>,R,A.Notice>: 60 match seen: 61 case (world,members):cleanup_members(~S,~C,~R,~E,~clear,~record,~descriptors,members,world,namespace) 62 def cleanup_apply(~S:Data,~C:Type,~R:Type,~E:Data,~clear:C -> W.Handle -> C & List<&2,A.Notice>,~record:W.Handle -> A.Notice,~descriptors:List<&2,G.Descriptor>,world:W.World>,R,A.Notice>,scope:Scope) -> W.World>,R,A.Notice>: 63 match scope: 64 case Scope{namespace,name}:cleanup_observed(~S,~C,~R,~E,~clear,~record,~descriptors,namespace,W.with_store(~S,~A.Store>,~R,~A.Notice,~List<&2,Key.Key>,store => observed(~S,~C,store,name),world)) 65 def cleanup_checked(~S:Data,~C:Type,~R:Type,~E:Data,~clear:C -> W.Handle -> C & List<&2,A.Notice>,~record:W.Handle -> A.Notice,~descriptors:List<&2,G.Descriptor>,same:Bool,tx:T.Tx>,R,A.Notice>,A.Notice>,scope:Scope) -> T.Tx>,R,A.Notice>,A.Notice> & A.QueueStatus: 66 match same: 67 case False{}:(tx,A.MissingEntity{}) 68 case True{}:(T.stage(~W.World>,R,A.Notice>,~A.Notice,tx,world => cleanup_apply(~S,~C,~R,~E,~clear,~record,~descriptors,world,scope)),A.Queued{}) 69 def cleanup_namespaced(~S:Data,~C:Type,~R:Type,~E:Data,~clear:C -> W.Handle -> C & List<&2,A.Notice>,~record:W.Handle -> A.Notice,~descriptors:List<&2,G.Descriptor>,seen:W.World>,R,A.Notice> & U32,scope:Scope,undo:List<(W.World>,R,A.Notice> -> W.World>,R,A.Notice>)>,commands:List>,R,A.Notice>>>,events:List<&2,A.Notice>,trace:TraceFacts.State) -> T.Tx>,R,A.Notice>,A.Notice> & 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}) 72 def tx_cleanup(~S:Data,~C:Type,~R:Type,~E:Data,~clear:C -> W.Handle -> C & List<&2,A.Notice>,~record:W.Handle -> A.Notice,~descriptors:List<&2,G.Descriptor>,tx:T.Tx>,R,A.Notice>,A.Notice>,scope:Scope) -> T.Tx>,R,A.Notice>,A.Notice> & 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>,~R,~A.Notice,world),scope,undo,commands,events,trace) 75 type SpawnResult is Type: 76 Spawned{handle:W.Handle} 77 SpawnRejected{payload:P,error:W.WorldError} 78 def populate_store(~S:Data,~C:Type,~P:Type,~populate:C -> W.Handle -> P -> C,store:A.Store>,handle:W.Handle,payload:P,name:String) -> A.Store> & 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{}) 81 def populate_returned(~S:Data,~C:Type,~R:Type,~E:Data,seen:W.World>,R,A.Notice> & Unit) -> W.World>,R,A.Notice>: 82 match seen: 83 case (world,_):world 84 def populate_world(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle -> P -> C,world:W.World>,R,A.Notice>,+handle:W.Handle,payload:P,name:String) -> W.World>,R,A.Notice>: 85 populate_returned(~S,~C,~R,~E,W.with_store(~S,~A.Store>,~R,~A.Notice,~Unit,store => populate_store(~S,~C,~P,~populate,store,handle,payload,name),world)) 86 def spawn_activated(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle -> P -> C,seen:W.World>,R,A.Notice> & W.Status,handle:W.Handle,payload:P,name:String) -> W.World>,R,A.Notice>: 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 90 def spawn_apply(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle -> P -> C,world:W.World>,R,A.Notice>,+handle:W.Handle,payload:P,name:String) -> W.World>,R,A.Notice>: 91 spawn_activated(~S,~C,~R,~E,~P,~populate,W.activate(~S,~A.Store>,~R,~A.Notice,world,handle),handle,payload,name) 92 def spawn_reserved(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle -> P -> C,seen:W.World>,R,A.Notice> & W.Reservation,undo:List<(W.World>,R,A.Notice> -> W.World>,R,A.Notice>)>,commands:List>,R,A.Notice>>>,events:List<&2,A.Notice>,trace:TraceFacts.State,payload:P,name:String) -> T.Tx>,R,A.Notice>,A.Notice> & SpawnResult: 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>,~R,~A.Notice,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}) 96 def spawn_checked(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle -> P -> C,same:Bool,world:W.World>,R,A.Notice>,undo:List<(W.World>,R,A.Notice> -> W.World>,R,A.Notice>)>,commands:List>,R,A.Notice>>>,events:List<&2,A.Notice>,trace:TraceFacts.State,payload:P,name:String) -> T.Tx>,R,A.Notice>,A.Notice> & SpawnResult: 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>,~R,~A.Notice,world),undo,commands,events,trace,payload,name) 100 def spawn_namespaced(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle -> P -> C,seen:W.World>,R,A.Notice> & U32,scope:Scope,undo:List<(W.World>,R,A.Notice> -> W.World>,R,A.Notice>)>,commands:List>,R,A.Notice>>>,events:List<&2,A.Notice>,trace:TraceFacts.State,payload:P) -> T.Tx>,R,A.Notice>,A.Notice> & SpawnResult: 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) 103 def tx_spawn_in(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:C -> W.Handle -> P -> C,tx:T.Tx>,R,A.Notice>,A.Notice>,scope:Scope,payload:P) -> T.Tx>,R,A.Notice>,A.Notice> & SpawnResult: 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>,~R,~A.Notice,world),scope,undo,commands,events,trace,payload)