aztecs 0.1.0.1 → 0.2.0.0
raw patch · 19 files changed
+870/−1208 lines, 19 filesdep +hspecdep −asyncPVP ok
version bump matches the API change (PVP)
Dependencies added: hspec
Dependencies removed: async
API changes (from Hackage documentation)
- Data.Aztecs: After :: TypeRep -> Constraint
- Data.Aztecs: Before :: TypeRep -> Constraint
- Data.Aztecs: EntityComponent :: Entity -> a -> EntityComponent a
- Data.Aztecs: Schedule :: Map TypeRep (ScheduleNode m) -> Schedule m
- Data.Aztecs: Scheduler :: Map TypeRep (Schedule m) -> Scheduler m
- Data.Aztecs: [AllA] :: Archetype -> Query m a -> Access m [a]
- Data.Aztecs: [AppA] :: Access m (a -> b) -> Access m a -> Access m b
- Data.Aztecs: [BindA] :: Access m a -> (a -> Access m b) -> Access m b
- Data.Aztecs: [CommandA] :: Command m () -> Access m ()
- Data.Aztecs: [GetA] :: Archetype -> Query m a -> Entity -> Access m (Maybe a)
- Data.Aztecs: [LiftA] :: m a -> Access m a
- Data.Aztecs: [MapA] :: (a -> b) -> Access m a -> Access m b
- Data.Aztecs: [PureA] :: a -> Access m a
- Data.Aztecs: access :: System m a => Access m ()
- Data.Aztecs: after :: forall m a. System m a => Constraint
- Data.Aztecs: before :: forall m a. System m a => Constraint
- Data.Aztecs: class (Typeable a) => System m a
- Data.Aztecs: data Constraint
- Data.Aztecs: data EntityComponent a
- Data.Aztecs: data Query m a
- Data.Aztecs: data Schedule m
- Data.Aztecs: data Startup
- Data.Aztecs: data Task m s a
- Data.Aztecs: data Update
- Data.Aztecs: data World
- Data.Aztecs: newtype Scheduler m
- Data.Aztecs: runScheduler :: Scheduler IO -> IO ()
- Data.Aztecs: schedule :: forall l m s. (Typeable l, System m s) => [Constraint] -> Scheduler m
- Data.Aztecs: storage :: Component a => Storage a
- Data.Aztecs.Command: Command :: StateT World m a -> Command m a
- Data.Aztecs.Command: [Insert] :: Component c => Entity -> Proxy c -> Edit
- Data.Aztecs.Command: [Spawn] :: Component c => Entity -> Proxy c -> Edit
- Data.Aztecs.Command: data Edit
- Data.Aztecs.Command: insert :: (Component a, Typeable a) => Entity -> a -> Command IO ()
- Data.Aztecs.Command: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Data.Aztecs.Command.Command m)
- Data.Aztecs.Command: instance GHC.Base.Functor m => GHC.Base.Functor (Data.Aztecs.Command.Command m)
- Data.Aztecs.Command: instance GHC.Base.Monad m => GHC.Base.Applicative (Data.Aztecs.Command.Command m)
- Data.Aztecs.Command: instance GHC.Base.Monad m => GHC.Base.Monad (Data.Aztecs.Command.Command m)
- Data.Aztecs.Command: newtype Command m a
- Data.Aztecs.Command: spawn :: (Component a, Typeable a) => a -> Command IO Entity
- Data.Aztecs.Core: Entity :: Int -> Entity
- Data.Aztecs.Core: EntityComponent :: Entity -> a -> EntityComponent a
- Data.Aztecs.Core: data EntityComponent a
- Data.Aztecs.Core: instance GHC.Classes.Eq Data.Aztecs.Core.Entity
- Data.Aztecs.Core: instance GHC.Classes.Ord Data.Aztecs.Core.Entity
- Data.Aztecs.Core: instance GHC.Show.Show Data.Aztecs.Core.Entity
- Data.Aztecs.Core: instance GHC.Show.Show a => GHC.Show.Show (Data.Aztecs.Core.EntityComponent a)
- Data.Aztecs.Core: newtype Entity
- Data.Aztecs.Query: ReadWrites :: Set TypeRep -> Set TypeRep -> ReadWrites
- Data.Aztecs.Query: [AppQ] :: Query m (a -> b) -> Query m a -> Query m b
- Data.Aztecs.Query: [BindQ] :: Query m a -> (a -> Query m b) -> Query m b
- Data.Aztecs.Query: [EntityQ] :: Query m Entity
- Data.Aztecs.Query: [LiftQ] :: m a -> Query m a
- Data.Aztecs.Query: [MapQ] :: (a -> b) -> Query m a -> Query m b
- Data.Aztecs.Query: [PureQ] :: a -> Query m a
- Data.Aztecs.Query: [ReadQ] :: Component c => Archetype -> Query m c
- Data.Aztecs.Query: [WriteQ] :: Component c => (c -> c) -> Archetype -> Query m c
- Data.Aztecs.Query: all' :: MonadIO m => ArchetypeState -> Query m a -> World -> m [a]
- Data.Aztecs.Query: buildQuery :: Query m a -> Archetype
- Data.Aztecs.Query: data Query m a
- Data.Aztecs.Query: data ReadWrites
- Data.Aztecs.Query: entity :: Query m Entity
- Data.Aztecs.Query: get :: MonadIO m => ArchetypeId -> Query m a -> Entity -> World -> m (Maybe a)
- Data.Aztecs.Query: get' :: MonadIO m => Entity -> ArchetypeState -> Query m a -> World -> m (Maybe a)
- Data.Aztecs.Query: instance GHC.Base.Applicative (Data.Aztecs.Query.Query m)
- Data.Aztecs.Query: instance GHC.Base.Functor (Data.Aztecs.Query.Query m)
- Data.Aztecs.Query: instance GHC.Base.Monad (Data.Aztecs.Query.Query m)
- Data.Aztecs.Query: instance GHC.Base.Monoid Data.Aztecs.Query.ReadWrites
- Data.Aztecs.Query: instance GHC.Base.Semigroup Data.Aztecs.Query.ReadWrites
- Data.Aztecs.Query: read :: forall m c. Component c => Query m c
- Data.Aztecs.Query: write :: forall m c. Component c => (c -> c) -> Query m c
- Data.Aztecs.Schedule: After :: TypeRep -> Constraint
- Data.Aztecs.Schedule: Before :: TypeRep -> Constraint
- Data.Aztecs.Schedule: Schedule :: Map TypeRep (ScheduleNode m) -> Schedule m
- Data.Aztecs.Schedule: ScheduleNode :: Node m -> [Constraint] -> ScheduleNode m
- Data.Aztecs.Schedule: Scheduler :: Map TypeRep (Schedule m) -> Scheduler m
- Data.Aztecs.Schedule: SchedulerGraph :: Map TypeRep [[GraphNode m]] -> SchedulerGraph m
- Data.Aztecs.Schedule: [Node] :: System m a => Proxy a -> Cache -> Node m
- Data.Aztecs.Schedule: after :: forall m a. System m a => Constraint
- Data.Aztecs.Schedule: before :: forall m a. System m a => Constraint
- Data.Aztecs.Schedule: buildScheduler :: Monad m => Scheduler m -> SchedulerGraph m
- Data.Aztecs.Schedule: data Constraint
- Data.Aztecs.Schedule: data Node m
- Data.Aztecs.Schedule: data Schedule m
- Data.Aztecs.Schedule: data ScheduleNode m
- Data.Aztecs.Schedule: data Startup
- Data.Aztecs.Schedule: data Update
- Data.Aztecs.Schedule: instance GHC.Base.Monoid (Data.Aztecs.Schedule.Schedule m)
- Data.Aztecs.Schedule: instance GHC.Base.Monoid (Data.Aztecs.Schedule.Scheduler m)
- Data.Aztecs.Schedule: instance GHC.Base.Semigroup (Data.Aztecs.Schedule.Schedule m)
- Data.Aztecs.Schedule: instance GHC.Base.Semigroup (Data.Aztecs.Schedule.Scheduler m)
- Data.Aztecs.Schedule: newtype Scheduler m
- Data.Aztecs.Schedule: newtype SchedulerGraph m
- Data.Aztecs.Schedule: runSchedule :: [[GraphNode IO]] -> World -> IO ([[GraphNode IO]], World)
- Data.Aztecs.Schedule: runScheduler :: Scheduler IO -> IO ()
- Data.Aztecs.Schedule: runSchedulerGraph :: forall l. Typeable l => SchedulerGraph IO -> World -> IO (SchedulerGraph IO, World)
- Data.Aztecs.Schedule: schedule :: forall l m s. (Typeable l, System m s) => [Constraint] -> Scheduler m
- Data.Aztecs.Storage: Storage :: IO (Storage a) -> (Entity -> a -> IO (Storage a)) -> (Entity -> IO (Maybe (a, a -> Storage a -> IO (Storage a)))) -> IO [EntityComponent a] -> IO [(EntityComponent a, a -> IO ())] -> (Entity -> IO (Storage a)) -> Storage a
- Data.Aztecs.Storage: [empty] :: Storage a -> IO (Storage a)
- Data.Aztecs.Storage: [get] :: Storage a -> Entity -> IO (Maybe (a, a -> Storage a -> IO (Storage a)))
- Data.Aztecs.Storage: [remove] :: Storage a -> Entity -> IO (Storage a)
- Data.Aztecs.Storage: [spawn] :: Storage a -> Entity -> a -> IO (Storage a)
- Data.Aztecs.Storage: [toList'] :: Storage a -> IO [(EntityComponent a, a -> IO ())]
- Data.Aztecs.Storage: [toList] :: Storage a -> IO [EntityComponent a]
- Data.Aztecs.Storage: data Storage a
- Data.Aztecs.Storage: table :: Storage a
- Data.Aztecs.Storage: table' :: [IORef (EntityComponent a)] -> Storage a
- Data.Aztecs.System: Cache :: Map Archetype ArchetypeId -> Cache
- Data.Aztecs.System: [AllA] :: Archetype -> Query m a -> Access m [a]
- Data.Aztecs.System: [AppA] :: Access m (a -> b) -> Access m a -> Access m b
- Data.Aztecs.System: [BindA] :: Access m a -> (a -> Access m b) -> Access m b
- Data.Aztecs.System: [CommandA] :: Command m () -> Access m ()
- Data.Aztecs.System: [GetA] :: Archetype -> Query m a -> Entity -> Access m (Maybe a)
- Data.Aztecs.System: [LiftA] :: m a -> Access m a
- Data.Aztecs.System: [MapA] :: (a -> b) -> Access m a -> Access m b
- Data.Aztecs.System: [PureA] :: a -> Access m a
- Data.Aztecs.System: access :: System m a => Access m ()
- Data.Aztecs.System: all :: Monad m => Query m a -> Access m [a]
- Data.Aztecs.System: class (Typeable a) => System m a
- Data.Aztecs.System: command :: Command m () -> Access m ()
- Data.Aztecs.System: data Access (m :: Type -> Type) a
- Data.Aztecs.System: get :: Monad m => Entity -> Query m a -> Access m (Maybe a)
- Data.Aztecs.System: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Data.Aztecs.System.Access m)
- Data.Aztecs.System: instance GHC.Base.Functor (Data.Aztecs.System.Access m)
- Data.Aztecs.System: instance GHC.Base.Monad m => GHC.Base.Applicative (Data.Aztecs.System.Access m)
- Data.Aztecs.System: instance GHC.Base.Monad m => GHC.Base.Monad (Data.Aztecs.System.Access m)
- Data.Aztecs.System: instance GHC.Base.Monoid Data.Aztecs.System.Cache
- Data.Aztecs.System: instance GHC.Base.Semigroup Data.Aztecs.System.Cache
- Data.Aztecs.System: newtype Cache
- Data.Aztecs.System: runAccess :: Access IO a -> World -> Cache -> IO (Either (Access IO a) a, World, Cache, [Command IO ()])
- Data.Aztecs.System: runAccess' :: Access IO a -> World -> Cache -> IO (a, World, Cache, [Command IO ()])
- Data.Aztecs.System: runSystem :: forall a. System IO a => World -> IO World
- Data.Aztecs.System: runSystem' :: forall a. System IO a => Cache -> World -> IO (Maybe (Access IO ()), Cache, [Command IO ()], World)
- Data.Aztecs.Task: Task :: StateT (s, [Command m ()], World) m a -> Task m s a
- Data.Aztecs.Task: command :: Monad m => Command m () -> Task m a ()
- Data.Aztecs.Task: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Data.Aztecs.Task.Task m s)
- Data.Aztecs.Task: instance GHC.Base.Functor m => GHC.Base.Functor (Data.Aztecs.Task.Task m s)
- Data.Aztecs.Task: instance GHC.Base.Monad m => GHC.Base.Applicative (Data.Aztecs.Task.Task m s)
- Data.Aztecs.Task: instance GHC.Base.Monad m => GHC.Base.Monad (Data.Aztecs.Task.Task m s)
- Data.Aztecs.Task: newtype Task m s a
- Data.Aztecs.Task: runTask :: Functor m => Task m s a -> s -> World -> m (a, s, [Command m ()], World)
- Data.Aztecs.World: EntityComponent :: Entity -> a -> EntityComponent a
- Data.Aztecs.World: class (Typeable a) => Component a
- Data.Aztecs.World: data Entity
- Data.Aztecs.World: data EntityComponent a
- Data.Aztecs.World: get :: forall c. Component c => Entity -> World -> IO (Maybe (c, c -> World -> IO World))
- Data.Aztecs.World: getRow :: Component c => Proxy c -> World -> Maybe (Storage c)
- Data.Aztecs.World: newWorld :: World
- Data.Aztecs.World: remove :: forall c. Component c => Entity -> World -> World
- Data.Aztecs.World: setRow :: forall c. Component c => Storage c -> World -> World
- Data.Aztecs.World: storage :: Component a => Storage a
- Data.Aztecs.World: union :: World -> World -> World
- Data.Aztecs.World.Archetypes: Archetype :: Set ArchetypeComponent -> Archetype
- Data.Aztecs.World.Archetypes: ArchetypeComponents :: Map TypeRep Dynamic -> ArchetypeComponents
- Data.Aztecs.World.Archetypes: ArchetypeId :: Int -> ArchetypeId
- Data.Aztecs.World.Archetypes: ArchetypeState :: Archetype -> Map Entity ArchetypeComponents -> [ArchetypeId] -> ArchetypeState
- Data.Aztecs.World.Archetypes: Archetypes :: IntMap ArchetypeState -> Map TypeRep [ArchetypeId] -> Map Archetype ArchetypeId -> Int -> Archetypes
- Data.Aztecs.World.Archetypes: [ArchetypeComponent] :: Component c => Proxy c -> ArchetypeComponent
- Data.Aztecs.World.Archetypes: archetype :: forall c. Component c => Archetype
- Data.Aztecs.World.Archetypes: data ArchetypeComponent
- Data.Aztecs.World.Archetypes: data ArchetypeState
- Data.Aztecs.World.Archetypes: data Archetypes
- Data.Aztecs.World.Archetypes: getArchetype :: ArchetypeId -> Archetypes -> Maybe ArchetypeState
- Data.Aztecs.World.Archetypes: getArchetypeComponent :: forall c. Component c => ArchetypeComponents -> Maybe (c, c -> IO ())
- Data.Aztecs.World.Archetypes: insert :: forall c. Component c => Entity -> Components -> Archetypes -> Archetypes
- Data.Aztecs.World.Archetypes: insertArchetype :: Archetype -> Components -> Archetypes -> IO (ArchetypeId, Archetypes)
- Data.Aztecs.World.Archetypes: instance GHC.Base.Monoid Data.Aztecs.World.Archetypes.Archetype
- Data.Aztecs.World.Archetypes: instance GHC.Base.Semigroup Data.Aztecs.World.Archetypes.Archetype
- Data.Aztecs.World.Archetypes: instance GHC.Classes.Eq Data.Aztecs.World.Archetypes.Archetype
- Data.Aztecs.World.Archetypes: instance GHC.Classes.Eq Data.Aztecs.World.Archetypes.ArchetypeComponent
- Data.Aztecs.World.Archetypes: instance GHC.Classes.Eq Data.Aztecs.World.Archetypes.ArchetypeId
- Data.Aztecs.World.Archetypes: instance GHC.Classes.Ord Data.Aztecs.World.Archetypes.Archetype
- Data.Aztecs.World.Archetypes: instance GHC.Classes.Ord Data.Aztecs.World.Archetypes.ArchetypeComponent
- Data.Aztecs.World.Archetypes: instance GHC.Classes.Ord Data.Aztecs.World.Archetypes.ArchetypeId
- Data.Aztecs.World.Archetypes: instance GHC.Show.Show Data.Aztecs.World.Archetypes.Archetype
- Data.Aztecs.World.Archetypes: instance GHC.Show.Show Data.Aztecs.World.Archetypes.ArchetypeComponent
- Data.Aztecs.World.Archetypes: instance GHC.Show.Show Data.Aztecs.World.Archetypes.ArchetypeComponents
- Data.Aztecs.World.Archetypes: instance GHC.Show.Show Data.Aztecs.World.Archetypes.ArchetypeId
- Data.Aztecs.World.Archetypes: instance GHC.Show.Show Data.Aztecs.World.Archetypes.ArchetypeState
- Data.Aztecs.World.Archetypes: instance GHC.Show.Show Data.Aztecs.World.Archetypes.Archetypes
- Data.Aztecs.World.Archetypes: newArchetypes :: Archetypes
- Data.Aztecs.World.Archetypes: newtype Archetype
- Data.Aztecs.World.Archetypes: newtype ArchetypeComponents
- Data.Aztecs.World.Archetypes: newtype ArchetypeId
- Data.Aztecs.World.Components: adjust :: Component c => c -> (c -> c) -> Entity -> Components -> IO Components
- Data.Aztecs.World.Components: class (Typeable a) => Component a
- Data.Aztecs.World.Components: data Components
- Data.Aztecs.World.Components: get :: forall c. Component c => Entity -> Components -> IO (Maybe (c, c -> Components -> IO Components))
- Data.Aztecs.World.Components: getRow :: Component c => Proxy c -> Components -> Maybe (Storage c)
- Data.Aztecs.World.Components: insert :: forall c. Component c => Entity -> c -> Components -> IO Components
- Data.Aztecs.World.Components: instance GHC.Show.Show Data.Aztecs.World.Components.Components
- Data.Aztecs.World.Components: newComponents :: Components
- Data.Aztecs.World.Components: remove :: forall c. Component c => Entity -> Components -> Components
- Data.Aztecs.World.Components: setRow :: forall c. Component c => Storage c -> Components -> Components
- Data.Aztecs.World.Components: spawn :: forall c. Component c => c -> Components -> IO (Entity, Components)
- Data.Aztecs.World.Components: storage :: Component a => Storage a
- Data.Aztecs.World.Components: union :: Components -> Components -> Components
+ Data.Aztecs: (:&) :: a -> b -> (:&) a b
+ Data.Aztecs: data EntityID
+ Data.Aztecs: data a :& b
+ Data.Aztecs: runAccess :: Access m a -> World -> m (a, World)
+ Data.Aztecs: type StorageT a = IntMap;
+ Data.Aztecs: }
+ Data.Aztecs.Access: Access :: StateT World m a -> Access m a
+ Data.Aztecs.Access: [unAccess] :: Access m a -> StateT World m a
+ Data.Aztecs.Access: insert :: (Monad m, Component a, Typeable (StorageT a)) => EntityID -> a -> Access m ()
+ Data.Aztecs.Access: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Data.Aztecs.Access.Access m)
+ Data.Aztecs.Access: instance GHC.Base.Functor m => GHC.Base.Functor (Data.Aztecs.Access.Access m)
+ Data.Aztecs.Access: instance GHC.Base.Monad m => GHC.Base.Applicative (Data.Aztecs.Access.Access m)
+ Data.Aztecs.Access: instance GHC.Base.Monad m => GHC.Base.Monad (Data.Aztecs.Access.Access m)
+ Data.Aztecs.Access: newtype Access m a
+ Data.Aztecs.Access: runAccess :: Access m a -> World -> m (a, World)
+ Data.Aztecs.Access: spawn :: (Monad m, Component a, Typeable (StorageT a)) => a -> Access m EntityID
+ Data.Aztecs.Archetype: AnyStorage :: Dynamic -> (Int -> Dynamic -> Dynamic -> Dynamic) -> (Int -> Dynamic -> (Maybe Dynamic, Dynamic)) -> (Int -> Dynamic -> (Maybe AnyStorage, Dynamic)) -> AnyStorage
+ Data.Aztecs.Archetype: Archetype :: Map ComponentID AnyStorage -> Archetype
+ Data.Aztecs.Archetype: [insertDyn] :: AnyStorage -> Int -> Dynamic -> Dynamic -> Dynamic
+ Data.Aztecs.Archetype: [removeAny] :: AnyStorage -> Int -> Dynamic -> (Maybe AnyStorage, Dynamic)
+ Data.Aztecs.Archetype: [removeDyn] :: AnyStorage -> Int -> Dynamic -> (Maybe Dynamic, Dynamic)
+ Data.Aztecs.Archetype: [storageDyn] :: AnyStorage -> Dynamic
+ Data.Aztecs.Archetype: [storages] :: Archetype -> Map ComponentID AnyStorage
+ Data.Aztecs.Archetype: all :: Component a => ComponentID -> Archetype -> [(EntityID, a)]
+ Data.Aztecs.Archetype: anyStorage :: forall s a. Storage s a => s a -> AnyStorage
+ Data.Aztecs.Archetype: data AnyStorage
+ Data.Aztecs.Archetype: empty :: Archetype
+ Data.Aztecs.Archetype: insert :: forall a. Component a => EntityID -> ComponentID -> a -> Archetype -> Archetype
+ Data.Aztecs.Archetype: insertAscList :: forall a. Component a => ComponentID -> [(EntityID, a)] -> Archetype -> Archetype
+ Data.Aztecs.Archetype: instance GHC.Show.Show Data.Aztecs.Archetype.AnyStorage
+ Data.Aztecs.Archetype: instance GHC.Show.Show Data.Aztecs.Archetype.Archetype
+ Data.Aztecs.Archetype: lookup :: forall a. Component a => EntityID -> ComponentID -> Archetype -> Maybe a
+ Data.Aztecs.Archetype: lookupStorage :: Component a => ComponentID -> Archetype -> Maybe (StorageT a a)
+ Data.Aztecs.Archetype: newtype Archetype
+ Data.Aztecs.Archetype: remove :: EntityID -> Archetype -> (Map ComponentID Dynamic, Archetype)
+ Data.Aztecs.Archetype: removeStorages :: EntityID -> Archetype -> (Map ComponentID AnyStorage, Archetype)
+ Data.Aztecs.Core: ComponentID :: Int -> ComponentID
+ Data.Aztecs.Core: Components :: Map TypeRep ComponentID -> ComponentID -> Components
+ Data.Aztecs.Core: EntityID :: Int -> EntityID
+ Data.Aztecs.Core: [componentIds] :: Components -> Map TypeRep ComponentID
+ Data.Aztecs.Core: [nextComponentId] :: Components -> ComponentID
+ Data.Aztecs.Core: [unComponentId] :: ComponentID -> Int
+ Data.Aztecs.Core: [unEntityId] :: EntityID -> Int
+ Data.Aztecs.Core: class (Typeable a, Storage (StorageT a) a) => Component a where {
+ Data.Aztecs.Core: data Components
+ Data.Aztecs.Core: emptyComponents :: Components
+ Data.Aztecs.Core: insertComponentId :: forall c. Component c => Components -> (ComponentID, Components)
+ Data.Aztecs.Core: instance GHC.Classes.Eq Data.Aztecs.Core.ComponentID
+ Data.Aztecs.Core: instance GHC.Classes.Eq Data.Aztecs.Core.EntityID
+ Data.Aztecs.Core: instance GHC.Classes.Ord Data.Aztecs.Core.ComponentID
+ Data.Aztecs.Core: instance GHC.Classes.Ord Data.Aztecs.Core.EntityID
+ Data.Aztecs.Core: instance GHC.Show.Show Data.Aztecs.Core.ComponentID
+ Data.Aztecs.Core: instance GHC.Show.Show Data.Aztecs.Core.Components
+ Data.Aztecs.Core: instance GHC.Show.Show Data.Aztecs.Core.EntityID
+ Data.Aztecs.Core: lookupComponentId :: forall a. Typeable a => Components -> Maybe ComponentID
+ Data.Aztecs.Core: newtype ComponentID
+ Data.Aztecs.Core: newtype EntityID
+ Data.Aztecs.Core: type StorageT a = IntMap;
+ Data.Aztecs.Core: }
+ Data.Aztecs.Entity: (:&) :: a -> b -> (:&) a b
+ Data.Aztecs.Entity: (<&>) :: Entity as -> a -> Entity (a : as)
+ Data.Aztecs.Entity: [ECons] :: t -> Entity ts -> Entity (t ': ts)
+ Data.Aztecs.Entity: [ENil] :: Entity '[]
+ Data.Aztecs.Entity: class Difference (a :: [Type]) (b :: [Type])
+ Data.Aztecs.Entity: class FromEntity a
+ Data.Aztecs.Entity: class Intersect (a :: [Type]) (b :: [Type])
+ Data.Aztecs.Entity: class Split (a :: [Type]) (b :: [Type])
+ Data.Aztecs.Entity: class ToEntity a
+ Data.Aztecs.Entity: concat :: Entity as -> Entity bs -> Entity (ConcatT as bs)
+ Data.Aztecs.Entity: data Entity (ts :: [Type])
+ Data.Aztecs.Entity: data a :& b
+ Data.Aztecs.Entity: difference :: Difference a b => Entity a -> Entity b -> Entity (DifferenceT a b)
+ Data.Aztecs.Entity: fromEntity :: FromEntity a => Entity (EntityT a) -> a
+ Data.Aztecs.Entity: instance (Data.Aztecs.Entity.Difference as (b : bs), Data.Aztecs.Entity.DifferenceT (a : as) (b : bs) GHC.Types.~ (a : Data.Aztecs.Entity.DifferenceT as (b : bs))) => Data.Aztecs.Entity.Difference' 'GHC.Types.False (a : as) (b : bs)
+ Data.Aztecs.Entity: instance (Data.Aztecs.Entity.Difference as b, Data.Aztecs.Entity.DifferenceT (a : as) b GHC.Types.~ Data.Aztecs.Entity.DifferenceT as b) => Data.Aztecs.Entity.Difference' 'GHC.Types.True (a : as) b
+ Data.Aztecs.Entity: instance (Data.Aztecs.Entity.EntityT a GHC.Types.~ '[a]) => Data.Aztecs.Entity.FromEntity a
+ Data.Aztecs.Entity: instance (Data.Aztecs.Entity.EntityT a GHC.Types.~ '[a]) => Data.Aztecs.Entity.ToEntity a
+ Data.Aztecs.Entity: instance (Data.Aztecs.Entity.FromEntity a, Data.Aztecs.Entity.FromEntity b, Data.Aztecs.Entity.EntityT (a Data.Aztecs.Entity.:& b) GHC.Types.~ (a : Data.Aztecs.Entity.EntityT b)) => Data.Aztecs.Entity.FromEntity (a Data.Aztecs.Entity.:& b)
+ Data.Aztecs.Entity: instance (Data.Aztecs.Entity.Intersect as (b : bs), Data.Aztecs.Entity.ElemT a (b : bs) GHC.Types.~ 'GHC.Types.False) => Data.Aztecs.Entity.Intersect' 'GHC.Types.False (a : as) (b : bs)
+ Data.Aztecs.Entity: instance (Data.Aztecs.Entity.Intersect as b, Data.Aztecs.Entity.ElemT a b GHC.Types.~ 'GHC.Types.True) => Data.Aztecs.Entity.Intersect' 'GHC.Types.True (a : as) b
+ Data.Aztecs.Entity: instance (Data.Aztecs.Entity.ToEntity a, Data.Aztecs.Entity.ToEntity b, Data.Aztecs.Entity.EntityT (a Data.Aztecs.Entity.:& b) GHC.Types.~ (a : Data.Aztecs.Entity.EntityT b)) => Data.Aztecs.Entity.ToEntity (a Data.Aztecs.Entity.:& b)
+ Data.Aztecs.Entity: instance (GHC.Show.Show a, Data.Aztecs.Entity.Show' (Data.Aztecs.Entity.Entity as)) => Data.Aztecs.Entity.Show' (Data.Aztecs.Entity.Entity (a : as))
+ Data.Aztecs.Entity: instance (GHC.Show.Show a, Data.Aztecs.Entity.Show' (Data.Aztecs.Entity.Entity as)) => GHC.Show.Show (Data.Aztecs.Entity.Entity (a : as))
+ Data.Aztecs.Entity: instance Data.Aztecs.Entity.Difference '[] b
+ Data.Aztecs.Entity: instance Data.Aztecs.Entity.Difference' (Data.Aztecs.Entity.ElemT a bs) (a : as) bs => Data.Aztecs.Entity.Difference (a : as) bs
+ Data.Aztecs.Entity: instance Data.Aztecs.Entity.FromEntity (Data.Aztecs.Entity.Entity ts)
+ Data.Aztecs.Entity: instance Data.Aztecs.Entity.Intersect '[] b
+ Data.Aztecs.Entity: instance Data.Aztecs.Entity.Intersect' (Data.Aztecs.Entity.ElemT a bs) (a : as) bs => Data.Aztecs.Entity.Intersect (a : as) bs
+ Data.Aztecs.Entity: instance Data.Aztecs.Entity.Show' (Data.Aztecs.Entity.Entity '[])
+ Data.Aztecs.Entity: instance Data.Aztecs.Entity.Split '[] bs
+ Data.Aztecs.Entity: instance Data.Aztecs.Entity.Split as bs => Data.Aztecs.Entity.Split (a : as) (a : bs)
+ Data.Aztecs.Entity: instance Data.Aztecs.Entity.ToEntity (Data.Aztecs.Entity.Entity ts)
+ Data.Aztecs.Entity: instance GHC.Show.Show (Data.Aztecs.Entity.Entity '[])
+ Data.Aztecs.Entity: intersect :: Intersect a b => Entity a -> Entity b -> Entity (IntersectT a b)
+ Data.Aztecs.Entity: split :: Split a b => Entity b -> (Entity a, Entity (SplitT a b))
+ Data.Aztecs.Entity: toEntity :: ToEntity a => a -> Entity (EntityT a)
+ Data.Aztecs.Entity: type family SplitT (a :: [Type]) (b :: [Type]) :: [Type]
+ Data.Aztecs.Query: (<?>) :: (Split a (ConcatT a b), SplitT a (ConcatT a b) ~ b) => Query a -> Query b -> Query (ConcatT a b)
+ Data.Aztecs.Query: Query :: (Components -> (Set ComponentID, Archetype -> ([Entity a], [Entity a] -> Archetype -> Archetype))) -> Query a
+ Data.Aztecs.Query: [runQuery'] :: Query a -> Components -> (Set ComponentID, Archetype -> ([Entity a], [Entity a] -> Archetype -> Archetype))
+ Data.Aztecs.Query: allWorld :: Query a -> World -> [Entity a]
+ Data.Aztecs.Query: fetch :: forall a. (Component a, Typeable (StorageT a)) => Query '[a]
+ Data.Aztecs.Query: instance (Data.Aztecs.Core.Component a, Data.Typeable.Internal.Typeable (Data.Aztecs.Core.StorageT a)) => Data.Aztecs.Query.ToQuery '[a]
+ Data.Aztecs.Query: instance (Data.Aztecs.Core.Component a, Data.Typeable.Internal.Typeable (Data.Aztecs.Core.StorageT a), Data.Aztecs.Query.ToQuery as) => Data.Aztecs.Query.ToQuery (a : as)
+ Data.Aztecs.Query: instance Data.Aztecs.Query.ToQuery '[]
+ Data.Aztecs.Query: map :: forall m i o. (Monad m, FromEntity i, ToEntity o, Intersect (EntityT i) (EntityT o), ToQuery (IntersectT (EntityT i) (EntityT o)), Difference (EntityT i) (EntityT o), ToQuery (DifferenceT (EntityT i) (EntityT o)), ConcatT (DifferenceT (EntityT i) (EntityT o)) (IntersectT (EntityT i) (EntityT o)) ~ EntityT i, IntersectT (EntityT i) (EntityT o) ~ EntityT o) => (i -> o) -> Access m [o]
+ Data.Aztecs.Query: mapWith :: (FromEntity i, ToEntity o, EntityT i ~ ConcatT a b, EntityT o ~ b) => Query a -> Query b -> (i -> o) -> World -> ([o], World)
+ Data.Aztecs.Query: mapWorld :: forall i o. (FromEntity i, ToEntity o, Intersect (EntityT i) (EntityT o), ToQuery (IntersectT (EntityT i) (EntityT o)), Difference (EntityT i) (EntityT o), ToQuery (DifferenceT (EntityT i) (EntityT o)), ConcatT (DifferenceT (EntityT i) (EntityT o)) (IntersectT (EntityT i) (EntityT o)) ~ EntityT i, IntersectT (EntityT i) (EntityT o) ~ EntityT o) => (i -> o) -> World -> ([o], World)
+ Data.Aztecs.Query: newtype Query a
+ Data.Aztecs.Storage: all :: Storage s a => s a -> [(Int, a)]
+ Data.Aztecs.Storage: class (Typeable (s a), Typeable a) => Storage s a
+ Data.Aztecs.Storage: fromAscList :: Storage s a => [(Int, a)] -> s a
+ Data.Aztecs.Storage: insert :: Storage s a => Int -> a -> s a -> s a
+ Data.Aztecs.Storage: instance Data.Typeable.Internal.Typeable a => Data.Aztecs.Storage.Storage Data.IntMap.Internal.IntMap a
+ Data.Aztecs.Storage: lookup :: Storage s a => Int -> s a -> Maybe a
+ Data.Aztecs.Storage: remove :: Storage s a => Int -> s a -> (Maybe a, s a)
+ Data.Aztecs.Storage: singleton :: Storage s a => Int -> a -> s a
+ Data.Aztecs.World: ArchetypeID :: Int -> ArchetypeID
+ Data.Aztecs.World: [archetypeComponents] :: World -> Map ArchetypeID (Set ComponentID)
+ Data.Aztecs.World: [archetypeIds] :: World -> Map (Set ComponentID) ArchetypeID
+ Data.Aztecs.World: [archetypes] :: World -> Map ArchetypeID Archetype
+ Data.Aztecs.World: [components] :: World -> Components
+ Data.Aztecs.World: [entities] :: World -> Map EntityID ArchetypeID
+ Data.Aztecs.World: [nextArchetypeId] :: World -> ArchetypeID
+ Data.Aztecs.World: [nextEntityId] :: World -> EntityID
+ Data.Aztecs.World: [unArchetypeId] :: ArchetypeID -> Int
+ Data.Aztecs.World: despawn :: EntityID -> World -> (Map ComponentID Dynamic, World)
+ Data.Aztecs.World: empty :: World
+ Data.Aztecs.World: insertArchetype :: Set ComponentID -> Archetype -> World -> (ArchetypeID, World)
+ Data.Aztecs.World: insertWithId :: (Component a, Typeable (StorageT a)) => EntityID -> ComponentID -> a -> World -> World
+ Data.Aztecs.World: instance GHC.Classes.Eq Data.Aztecs.World.ArchetypeID
+ Data.Aztecs.World: instance GHC.Classes.Ord Data.Aztecs.World.ArchetypeID
+ Data.Aztecs.World: instance GHC.Show.Show Data.Aztecs.World.ArchetypeID
+ Data.Aztecs.World: newtype ArchetypeID
+ Data.Aztecs.World: spawnEmpty :: World -> (EntityID, World)
+ Data.Aztecs.World: spawnWithArchetypeId :: forall a. (Component a, Typeable (StorageT a)) => a -> ComponentID -> ArchetypeID -> World -> (EntityID, World)
+ Data.Aztecs.World: spawnWithId :: forall a. (Component a, Typeable (StorageT a)) => ComponentID -> a -> World -> (EntityID, World)
- Data.Aztecs: class (Typeable a) => Component a
+ Data.Aztecs: class (Typeable a, Storage (StorageT a) a) => Component a where {
- Data.Aztecs: data Access (m :: Type -> Type) a
+ Data.Aztecs: data Access m a
- Data.Aztecs: data Entity
+ Data.Aztecs: data Entity (ts :: [Type])
- Data.Aztecs.Query: all :: MonadIO m => ArchetypeId -> Query m a -> World -> m [a]
+ Data.Aztecs.Query: all :: forall m a. (Monad m, ToEntity a, FromEntity a, ToQuery (EntityT a)) => Access m [a]
- Data.Aztecs.World: World :: Components -> Archetypes -> World
+ Data.Aztecs.World: World :: Map ArchetypeID Archetype -> Map (Set ComponentID) ArchetypeID -> Map ArchetypeID (Set ComponentID) -> ArchetypeID -> Components -> Map EntityID ArchetypeID -> EntityID -> World
- Data.Aztecs.World: insert :: forall c. Component c => Entity -> c -> World -> IO World
+ Data.Aztecs.World: insert :: forall a. (Component a, Typeable (StorageT a)) => EntityID -> a -> World -> World
- Data.Aztecs.World: spawn :: forall c. Component c => c -> World -> IO (Entity, World)
+ Data.Aztecs.World: spawn :: forall a. (Component a, Typeable (StorageT a)) => a -> World -> (EntityID, World)
Files
- LICENSE +29/−29
- aztecs.cabal +7/−10
- bench/Iter.hs +34/−45
- examples/ECS.hs +33/−47
- src/Data/Aztecs.hs +13/−33
- src/Data/Aztecs/Access.hs +30/−0
- src/Data/Aztecs/Archetype.hs +104/−0
- src/Data/Aztecs/Command.hs +0/−43
- src/Data/Aztecs/Core.hs +60/−5
- src/Data/Aztecs/Entity.hs +162/−0
- src/Data/Aztecs/Query.hs +168/−134
- src/Data/Aztecs/Schedule.hs +0/−207
- src/Data/Aztecs/Storage.hs +25/−65
- src/Data/Aztecs/System.hs +0/−218
- src/Data/Aztecs/Task.hs +0/−29
- src/Data/Aztecs/World.hs +191/−71
- src/Data/Aztecs/World/Archetypes.hs +0/−181
- src/Data/Aztecs/World/Components.hs +0/−87
- test/Main.hs +14/−4
LICENSE view
@@ -1,29 +1,29 @@-Copyright (c) 2024, Matt Hunzinger---Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.-- * Redistributions in binary form must reproduce the above- copyright notice, this list of conditions and the following- disclaimer in the documentation and/or other materials provided- with the distribution.-- * Neither the name of the copyright holder nor the names of its- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+Copyright (c) 2024, Matt Hunzinger + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
aztecs.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: aztecs-version: 0.1.0.1+version: 0.2.0.0 license: BSD-3-Clause license-file: LICENSE maintainer: matt@hunzinger.me@@ -12,23 +12,19 @@ library exposed-modules: Data.Aztecs+ Data.Aztecs.Access+ Data.Aztecs.Archetype Data.Aztecs.Core- Data.Aztecs.Command- Data.Aztecs.Schedule- Data.Aztecs.System- Data.Aztecs.Storage- Data.Aztecs.Task+ Data.Aztecs.Entity Data.Aztecs.Query+ Data.Aztecs.Storage Data.Aztecs.World- Data.Aztecs.World.Archetypes- Data.Aztecs.World.Components hs-source-dirs: src default-language: Haskell2010 ghc-options: -Wall build-depends: base >=4 && <5,- async >=2, containers >=0.7, mtl >=2 @@ -49,7 +45,8 @@ ghc-options: -Wall build-depends: base >=4 && <5,- aztecs+ aztecs,+ hspec >=2 benchmark aztecs-bench type: exitcode-stdio-1.0
bench/Iter.hs view
@@ -1,45 +1,34 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeApplications #-}--import Criterion.Main-import Data.Aztecs-import qualified Data.Aztecs.Query as Q-import Data.Aztecs.System (runSystem)-import qualified Data.Aztecs.System as S-import qualified Data.Aztecs.World as W-import Data.Foldable (foldrM)--newtype Position = Position Int deriving (Show)--instance Component Position--newtype Velocity = Velocity Int deriving (Show)--instance Component Velocity--data S--instance System IO S where- access = do- _ <- S.all $ do- Velocity y <- Q.read- Q.write (\(Position x) -> Position (x + y))- return ()--runner :: World -> IO ()-runner w = do- !_ <- runSystem @S w- return ()--main :: IO ()-main = do- !w <-- foldrM- ( \_ wAcc -> do- (_, wAcc') <- W.spawn (Position 0) wAcc- return wAcc'- )- W.newWorld- [0 :: Int .. 10000]- defaultMain [bench "iter" $ nfIO (runner w)]+{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE MultiParamTypeClasses #-} + +import Criterion.Main +import Data.Aztecs +import Data.Aztecs.Entity +import qualified Data.Aztecs.Query as Q +import Data.Aztecs.World (World) +import qualified Data.Aztecs.World as W + +newtype Position = Position Int deriving (Show) + +instance Component Position + +newtype Velocity = Velocity Int deriving (Show) + +instance Component Velocity + +run :: World -> World +run w = + let !(_, w') = Q.mapWith Q.fetch Q.fetch (\(Velocity v :& Position x) -> Position (x + v)) w + in w' + +main :: IO () +main = do + let w = + foldr + ( \_ wAcc -> + let (e, wAcc') = W.spawn (Position 0) wAcc + in W.insert e (Velocity 1) wAcc' + ) + W.empty + [0 :: Int .. 10000] + defaultMain [bench "iter" $ whnf run w]
examples/ECS.hs view
@@ -1,47 +1,33 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeApplications #-}--module Main where--import Control.Monad.IO.Class-import Data.Aztecs-import qualified Data.Aztecs.Command as C-import qualified Data.Aztecs.Query as Q-import qualified Data.Aztecs.System as S---- Components--newtype Position = Position Int deriving (Show)--instance Component Position--newtype Velocity = Velocity Int deriving (Show)--instance Component Velocity---- Systems--data A--instance System IO A where- access = S.command $ do- e <- C.spawn (Position 0)- C.insert e (Velocity 1)--data B--instance System IO B where- access = do- -- Update all entities with a `Position` and `Velocity` component.- positions <- S.all $ do- Velocity v <- Q.read- Q.write (\(Position p) -> Position (p + v))-- liftIO $ print positions--app :: Scheduler IO-app = schedule @Startup @_ @A [] <> schedule @Update @_ @B []--main :: IO ()-main = runScheduler app+{-# LANGUAGE DataKinds #-} +{-# LANGUAGE MonoLocalBinds #-} + +module Main where + +import Control.Monad.IO.Class (MonadIO (..)) +import Data.Aztecs +import qualified Data.Aztecs.Access as A +import qualified Data.Aztecs.Query as Q +import qualified Data.Aztecs.World as W + +newtype Position = Position Int deriving (Show) + +instance Component Position + +newtype Velocity = Velocity Int deriving (Show) + +instance Component Velocity + +app :: Access IO () +app = do + -- Spawn an entity with position and velocity components + e <- A.spawn (Position 0) + A.insert e (Velocity 1) + + -- Query for and update all matching entities + q <- Q.map (\(Velocity v :& Position x) -> Position (x + v)) + liftIO $ print q + +main :: IO () +main = do + _ <- runAccess app W.empty + return ()
src/Data/Aztecs.hs view
@@ -1,33 +1,13 @@-module Data.Aztecs- ( Entity,- EntityComponent (..),- Component (..),- World,- Query,- Access (..),- Task,- System (..),- Constraint (..),- before,- after,- Schedule (..),- Startup,- Update,- Scheduler (..),- schedule,- runScheduler,- )-where--import Data.Aztecs.Query- ( Query (..),- )-import Data.Aztecs.Schedule-import Data.Aztecs.System-import Data.Aztecs.Task (Task (..))-import Data.Aztecs.World- ( Component (..),- Entity,- EntityComponent (..),- World,- )+module Data.Aztecs + ( Access, + runAccess, + Component (..), + EntityID, + Entity, + (:&) (..), + ) +where + +import Data.Aztecs.Access (Access, runAccess) +import Data.Aztecs.Core (Component (..), EntityID) +import Data.Aztecs.Entity (Entity, (:&) (..))
+ src/Data/Aztecs/Access.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} + +module Data.Aztecs.Access where + +import Control.Monad.IO.Class (MonadIO) +import Control.Monad.State (MonadState (..), StateT (..)) +import Data.Aztecs.Core (Component (..), EntityID) +import Data.Aztecs.World (World) +import qualified Data.Aztecs.World as W +import Data.Data (Typeable) + +newtype Access m a = Access {unAccess :: StateT World m a} + deriving (Functor, Applicative, Monad, MonadIO) + +runAccess :: Access m a -> World -> m (a, World) +runAccess a = runStateT (unAccess a) + +spawn :: (Monad m, Component a, Typeable (StorageT a)) => a -> Access m EntityID +spawn c = Access $ do + w <- get + let (e, w') = W.spawn c w + put w' + return e + +insert :: (Monad m, Component a, Typeable (StorageT a)) => EntityID -> a -> Access m () +insert e c = Access $ do + w <- get + let w' = W.insert e c w + put w'
+ src/Data/Aztecs/Archetype.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} + +module Data.Aztecs.Archetype where + +import Data.Aztecs.Core (Component (..), ComponentID, EntityID (..)) +import qualified Data.Aztecs.Storage as S +import Data.Bifunctor (Bifunctor (..)) +import Data.Dynamic (Dynamic, fromDynamic, toDyn) +import Data.Map (Map) +import qualified Data.Map as Map +import Data.Maybe (fromMaybe) +import Prelude hiding (all, lookup) + +data AnyStorage = AnyStorage + { storageDyn :: Dynamic, + insertDyn :: Int -> Dynamic -> Dynamic -> Dynamic, + removeDyn :: Int -> Dynamic -> (Maybe Dynamic, Dynamic), + removeAny :: Int -> Dynamic -> (Maybe AnyStorage, Dynamic) + } + +instance Show AnyStorage where + show s = "AnyStorage " ++ show (storageDyn s) + +anyStorage :: forall s a. (S.Storage s a) => s a -> AnyStorage +anyStorage s = + AnyStorage + { storageDyn = toDyn s, + insertDyn = \i cDyn sDyn -> + fromMaybe sDyn $ do + s' <- fromDynamic @(s a) sDyn + c <- fromDynamic cDyn + return . toDyn $ S.insert i c s', + removeDyn = \i dyn -> case fromDynamic @(s a) dyn of + Just s' -> let (a, b) = S.remove i s' in (fmap toDyn a, toDyn b) + Nothing -> (Nothing, dyn), + removeAny = \i dyn -> case fromDynamic @(s a) dyn of + Just s' -> let (a, b) = S.remove i s' in (fmap (anyStorage . S.singleton @s i) a, toDyn b) + Nothing -> (Nothing, dyn) + } + +newtype Archetype = Archetype {storages :: Map ComponentID AnyStorage} + deriving (Show) + +empty :: Archetype +empty = Archetype {storages = Map.empty} + +lookupStorage :: (Component a) => ComponentID -> Archetype -> Maybe (StorageT a a) +lookupStorage cId w = do + dynS <- Map.lookup cId (storages w) + fromDynamic (storageDyn dynS) + +insert :: forall a. (Component a) => EntityID -> ComponentID -> a -> Archetype -> Archetype +insert e cId c arch = + let storage = case lookupStorage cId arch of + Just s -> S.insert (unEntityId e) c s + Nothing -> S.singleton @(StorageT a) @a (unEntityId e) c + in arch {storages = Map.insert cId (anyStorage storage) (storages arch)} + +all :: (Component a) => ComponentID -> Archetype -> [(EntityID, a)] +all cId arch = fromMaybe [] $ do + s <- lookupStorage cId arch + return . map (first EntityID) $ S.all s + +lookup :: forall a. (Component a) => EntityID -> ComponentID -> Archetype -> Maybe a +lookup e cId w = lookupStorage cId w >>= S.lookup (unEntityId e) + +insertAscList :: forall a. (Component a) => ComponentID -> [(EntityID, a)] -> Archetype -> Archetype +insertAscList cId as arch = arch {storages = Map.insert cId (anyStorage $ S.fromAscList @(StorageT a) (map (first unEntityId) as)) (storages arch)} + +remove :: EntityID -> Archetype -> (Map ComponentID Dynamic, Archetype) +remove e arch = + foldr + ( \(cId, s) (dynAcc, archAcc) -> + let (dynA, dynS) = removeDyn s (unEntityId e) (storageDyn s) + dynAcc' = case dynA of + Just d -> Map.insert cId d dynAcc + Nothing -> dynAcc + in ( dynAcc', + archAcc {storages = Map.insert cId (s {storageDyn = dynS}) (storages archAcc)} + ) + ) + (Map.empty, arch) + (Map.toList $ storages arch) + +removeStorages :: EntityID -> Archetype -> (Map ComponentID AnyStorage, Archetype) +removeStorages e arch = + foldr + ( \(cId, s) (dynAcc, archAcc) -> + let (dynA, dynS) = removeAny s (unEntityId e) (storageDyn s) + dynAcc' = case dynA of + Just d -> Map.insert cId d dynAcc + Nothing -> dynAcc + in ( dynAcc', + archAcc {storages = Map.insert cId (s {storageDyn = dynS}) (storages archAcc)} + ) + ) + (Map.empty, arch) + (Map.toList $ storages arch)
− src/Data/Aztecs/Command.hs
@@ -1,43 +0,0 @@-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--module Data.Aztecs.Command- ( Command (..),- spawn,- insert,- Edit (..),- )-where--import Control.Monad.IO.Class-import Control.Monad.State (StateT (..))-import qualified Control.Monad.State as S-import Data.Aztecs.World (Component, Entity, World)-import qualified Data.Aztecs.World as W-import Data.Dynamic (Typeable)-import Data.Proxy-import Prelude hiding (all)--data Edit where- Spawn :: (Component c) => Entity -> (Proxy c) -> Edit- Insert :: (Component c) => Entity -> (Proxy c) -> Edit---- | Command to update the `World`.-newtype Command m a = Command (StateT World m a)- deriving (Functor, Applicative, Monad, MonadIO)---- | Spawn a `Component` and return its `Entity`.-spawn :: (Component a, Typeable a) => a -> Command IO Entity-spawn a = Command $ do- w <- S.get- (e, w') <- liftIO $ W.spawn a w- S.put $ w'- return e---- | Insert a `Component` into an `Entity`.-insert :: (Component a, Typeable a) => Entity -> a -> Command IO ()-insert e a = Command $ do- w <- S.get- w' <- liftIO $ W.insert e a w- S.put w'
src/Data/Aztecs/Core.hs view
@@ -1,5 +1,60 @@-module Data.Aztecs.Core (Entity (..), EntityComponent (..)) where--newtype Entity = Entity Int deriving (Eq, Ord, Show)--data EntityComponent a = EntityComponent Entity a deriving (Show)+{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} + +module Data.Aztecs.Core + ( EntityID (..), + Component (..), + ComponentID (..), + Components (..), + emptyComponents, + insertComponentId, + lookupComponentId, + ) +where + +import Data.Aztecs.Storage (Storage) +import Data.IntMap (IntMap) +import Data.Kind (Type) +import Data.Map (Map) +import qualified Data.Map as Map +import Data.Typeable (Proxy (..), TypeRep, Typeable, typeOf) + +newtype EntityID = EntityID {unEntityId :: Int} + deriving (Eq, Ord, Show) + +newtype ComponentID = ComponentID {unComponentId :: Int} + deriving (Eq, Ord, Show) + +class (Typeable a, Storage (StorageT a) a) => Component a where + type StorageT a :: Type -> Type + type StorageT a = IntMap + +data Components = Components + { componentIds :: Map TypeRep ComponentID, + nextComponentId :: ComponentID + } + deriving (Show) + +emptyComponents :: Components +emptyComponents = + Components + { componentIds = mempty, + nextComponentId = ComponentID 0 + } + +lookupComponentId :: forall a. (Typeable a) => Components -> Maybe ComponentID +lookupComponentId cs = Map.lookup (typeOf (Proxy @a)) (componentIds cs) + +insertComponentId :: forall c. (Component c) => Components -> (ComponentID, Components) +insertComponentId cs = + let cId = nextComponentId cs + in ( cId, + cs + { componentIds = Map.insert (typeOf (Proxy @c)) cId (componentIds cs), + nextComponentId = ComponentID (unComponentId cId + 1) + } + )
+ src/Data/Aztecs/Entity.hs view
@@ -0,0 +1,162 @@+{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} + +module Data.Aztecs.Entity + ( Entity (..), + EntityT, + FromEntity (..), + ToEntity (..), + ConcatT, + IntersectT, + Intersect (..), + DifferenceT, + Difference (..), + SplitT, + Split (..), + concat, + (<&>), + (:&) (..), + ) +where + +import Data.Kind (Type) +import Prelude hiding (concat) + +data Entity (ts :: [Type]) where + ENil :: Entity '[] + ECons :: t -> Entity ts -> Entity (t ': ts) + +instance Show (Entity '[]) where + show ENil = "[]" + +instance (Show a, Show' (Entity as)) => Show (Entity (a ': as)) where + show (ECons x xs) = "[" ++ show x ++ showRow xs + +class Show' a where + showRow :: a -> String + +instance Show' (Entity '[]) where + showRow ENil = "]" + +instance (Show a, Show' (Entity as)) => Show' (Entity (a ': as)) where + showRow (ECons x xs) = ", " ++ show x ++ showRow xs + +(<&>) :: Entity as -> a -> Entity (a : as) +(<&>) es c = ECons c es + +type family ConcatT (a :: [Type]) (b :: [Type]) where + ConcatT '[] b = b + ConcatT (a ': as) b = a ': ConcatT as b + +concat :: Entity as -> Entity bs -> Entity (ConcatT as bs) +concat ENil ys = ys +concat (ECons x xs) ys = ECons x (concat xs ys) + +type family SplitT (a :: [Type]) (b :: [Type]) :: [Type] where + SplitT '[] bs = bs + SplitT (a ': as) (a ': bs) = SplitT as bs + +class Split (a :: [Type]) (b :: [Type]) where + split :: Entity b -> (Entity a, Entity (SplitT a b)) + +instance Split '[] bs where + split e = (ENil, e) + +instance forall a as bs. (Split as bs) => Split (a ': as) (a ': bs) where + split (ECons x xs) = + let (as, bs) = split @as xs + in (ECons x as, bs) + +data a :& b = a :& b + +type family EntityT a where + EntityT (a :& b) = a ': EntityT b + EntityT (Entity ts) = ts + EntityT a = '[a] + +class FromEntity a where + fromEntity :: Entity (EntityT a) -> a + +instance {-# OVERLAPS #-} (EntityT a ~ '[a]) => FromEntity a where + fromEntity (ECons a ENil) = a + +instance FromEntity (Entity ts) where + fromEntity = id + +instance (FromEntity a, FromEntity b, EntityT (a :& b) ~ (a ': EntityT b)) => FromEntity (a :& b) where + fromEntity (ECons a rest) = a :& fromEntity rest + +class ToEntity a where + toEntity :: a -> Entity (EntityT a) + +instance {-# OVERLAPS #-} (EntityT a ~ '[a]) => ToEntity a where + toEntity a = ECons a ENil + +instance ToEntity (Entity ts) where + toEntity = id + +instance (ToEntity a, ToEntity b, EntityT (a :& b) ~ (a ': EntityT b)) => ToEntity (a :& b) where + toEntity (a :& b) = ECons a (toEntity b) + +type family ElemT (a :: Type) (b :: [Type]) :: Bool where + ElemT a '[] = 'False + ElemT a (a ': as) = 'True + ElemT a (b ': as) = ElemT a as + +type family If (cond :: Bool) (true :: [Type]) (false :: [Type]) :: [Type] where + If 'True true false = true + If 'False true false = false + +type family IntersectT (a :: [Type]) (b :: [Type]) :: [Type] where + IntersectT '[] b = '[] + IntersectT (a ': as) b = If (ElemT a b) (a ': IntersectT as b) (IntersectT as b) + +class Intersect' (flag :: Bool) (a :: [Type]) (b :: [Type]) where + intersect' :: Entity a -> Entity b -> Entity (IntersectT a b) + +instance (Intersect as b, ElemT a b ~ 'True) => Intersect' 'True (a ': as) b where + intersect' (ECons x xs) ys = ECons x (xs `intersect` ys) + +instance (Intersect as (b ': bs), ElemT a (b ': bs) ~ 'False) => Intersect' 'False (a ': as) (b ': bs) where + intersect' (ECons _ xs) = intersect xs + +class Intersect (a :: [Type]) (b :: [Type]) where + intersect :: Entity a -> Entity b -> Entity (IntersectT a b) + +instance Intersect '[] b where + intersect _ _ = ENil + +instance (Intersect' (ElemT a bs) (a ': as) bs) => Intersect (a ': as) bs where + intersect = intersect' @(ElemT a bs) + +type family DifferenceT (a :: [Type]) (b :: [Type]) :: [Type] where + DifferenceT '[] b = '[] + DifferenceT (a ': as) b = If (ElemT a b) (DifferenceT as b) (a ': DifferenceT as b) + +class Difference' (flag :: Bool) (a :: [Type]) (b :: [Type]) where + difference' :: Entity a -> Entity b -> Entity (DifferenceT a b) + +instance (Difference as b, DifferenceT (a ': as) b ~ DifferenceT as b) => Difference' 'True (a ': as) b where + difference' (ECons _ xs) ys = xs `difference` ys + +instance (Difference as (b ': bs), DifferenceT (a ': as) (b ': bs) ~ (a ': DifferenceT as (b ': bs))) => Difference' 'False (a ': as) (b ': bs) where + difference' (ECons x xs) ys = ECons x (xs `difference` ys) + +class Difference (a :: [Type]) (b :: [Type]) where + difference :: Entity a -> Entity b -> Entity (DifferenceT a b) + +instance Difference '[] b where + difference _ _ = ENil + +instance (Difference' (ElemT a bs) (a ': as) bs) => Difference (a ': as) bs where + difference = difference' @(ElemT a bs)
src/Data/Aztecs/Query.hs view
@@ -1,134 +1,168 @@-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}--module Data.Aztecs.Query- ( ReadWrites (..),- Query (..),- entity,- read,- buildQuery,- all,- all',- get,- get',- write,- )-where--import Control.Monad.IO.Class (MonadIO (..))-import Data.Aztecs.World (Component, Entity, World (..))-import Data.Aztecs.World.Archetypes (Archetype, ArchetypeId, ArchetypeState (..), archetype)-import qualified Data.Aztecs.World.Archetypes as A-import Data.Foldable (foldrM)-import qualified Data.Map as Map-import Data.Set (Set)-import Data.Typeable-import Prelude hiding (all, read)---- | Component IDs to read and write.-data ReadWrites = ReadWrites (Set TypeRep) (Set TypeRep)--instance Semigroup ReadWrites where- ReadWrites rs ws <> ReadWrites rs' ws' = ReadWrites (rs <> rs') (ws <> ws')--instance Monoid ReadWrites where- mempty = ReadWrites mempty mempty---- | Builder for a `Query`.-data Query m a where- PureQ :: a -> Query m a- MapQ :: (a -> b) -> Query m a -> Query m b- AppQ :: Query m (a -> b) -> Query m a -> Query m b- BindQ :: Query m a -> (a -> Query m b) -> Query m b- EntityQ :: Query m Entity- ReadQ :: (Component c) => Archetype -> Query m c- WriteQ :: (Component c) => (c -> c) -> Archetype -> Query m c- LiftQ :: m a -> Query m a--instance Functor (Query m) where- fmap = MapQ--instance Applicative (Query m) where- pure = PureQ- (<*>) = AppQ--instance Monad (Query m) where- (>>=) = BindQ--entity :: Query m Entity-entity = EntityQ---- | Read a `Component`.-read :: forall m c. (Component c) => Query m c-read = ReadQ (archetype @c)---- | Alter a `Component`.-write :: forall m c. (Component c) => (c -> c) -> Query m c-write c = WriteQ c (archetype @c)--buildQuery :: Query m a -> Archetype-buildQuery (PureQ _) = mempty-buildQuery (MapQ _ qb) = buildQuery qb-buildQuery (AppQ f a) = buildQuery f <> buildQuery a-buildQuery EntityQ = mempty-buildQuery (ReadQ a) = a-buildQuery (WriteQ _ a) = a-buildQuery (BindQ a _) = buildQuery a-buildQuery (LiftQ _) = mempty--all :: (MonadIO m) => ArchetypeId -> Query m a -> World -> m [a]-all a qb w@(World _ as) = case A.getArchetype a as of- Just s -> all' s qb w- Nothing -> return []--all' :: (MonadIO m) => ArchetypeState -> Query m a -> World -> m [a]-all' es@(ArchetypeState _ m _) q w =- foldrM- ( \e acc -> do- a <- get' e es q w- return $ case a of- Just a' -> (a' : acc)- Nothing -> acc- )- []- (Map.keys m)--get :: (MonadIO m) => ArchetypeId -> Query m a -> Entity -> World -> m (Maybe a)-get a qb e w@(World _ as) = case A.getArchetype a as of- Just s -> get' e s qb w- Nothing -> return Nothing--get' :: (MonadIO m) => Entity -> ArchetypeState -> Query m a -> World -> m (Maybe a)-get' _ _ (PureQ a) _ = return $ Just a-get' e es (MapQ f qb) w = do- a <- get' e es qb w- return $ fmap f a-get' e es (AppQ fqb aqb) w = do- f <- get' e es fqb w- a <- get' e es aqb w- return $ f <*> a-get' e _ EntityQ _ = return $ Just e-get' e (ArchetypeState _ m _) (ReadQ _) _ = return $ do- cs <- Map.lookup e m- (c, _) <- A.getArchetypeComponent cs- return c-get' e (ArchetypeState _ m _) (WriteQ f _) _ = do- let res = do- cs <- Map.lookup e m- A.getArchetypeComponent cs- case res of- Just (c, g) -> do- let c' = f c- liftIO $ g c'- return $ Just c'- Nothing -> return Nothing-get' e es (BindQ qb f) w = do- a <- get' e es qb w- case a of- Just a' -> get' e es (f a') w- Nothing -> return Nothing-get' _ _ (LiftQ a) _ = fmap Just a+{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} + +module Data.Aztecs.Query + ( Query (..), + (<?>), + fetch, + all, + allWorld, + map, + mapWorld, + mapWith, + ) +where + +import Control.Monad.State (MonadState (..), gets) +import Data.Aztecs.Access (Access (Access)) +import Data.Aztecs.Archetype (Archetype) +import qualified Data.Aztecs.Archetype as A +import Data.Aztecs.Core +import Data.Aztecs.Entity (ConcatT, Difference, DifferenceT, Entity (..), EntityT, FromEntity (..), Intersect, IntersectT, ToEntity (..)) +import qualified Data.Aztecs.Entity as E +import Data.Aztecs.World (World (..)) +import Data.Data (Typeable) +import qualified Data.Map as Map +import Data.Maybe (fromMaybe) +import Data.Set (Set) +import qualified Data.Set as Set +import Prelude hiding (all, map) + +-- | Query into the `World`. +newtype Query a + = Query {runQuery' :: Components -> (Set ComponentID, Archetype -> ([Entity a], [Entity a] -> Archetype -> Archetype))} + +(<?>) :: + (E.Split a (ConcatT a b), E.SplitT a (ConcatT a b) ~ b) => + Query a -> + Query b -> + Query (ConcatT a b) +(Query a) <?> (Query b) = Query $ \cs -> + let (aIds, a') = a cs + (bIds, b') = b cs + in ( aIds <> bIds, + \arch -> + let (a'', aF) = a' arch + (b'', bF) = b' arch + in ( uncurry E.concat <$> zip a'' b'', + \new newArch -> let (as, bs) = unzip $ fmap E.split new in bF bs $ aF as newArch + ) + ) + +fetch :: forall a. (Component a, Typeable (StorageT a)) => Query '[a] +fetch = Query $ \cs -> + let cId = fromMaybe (error "TODO") (lookupComponentId @a cs) + in ( Set.singleton cId, + \arch -> + let as = A.all cId arch + in ( fmap (\x -> ECons (snd x) ENil) as, + A.insertAscList cId . fmap (\((e, _), ECons a ENil) -> (e, a)) . zip as + ) + ) + +all :: forall m a. (Monad m, ToEntity a, FromEntity a, ToQuery (EntityT a)) => Access m [a] +all = Access $ gets (fmap fromEntity . allWorld (query @(EntityT a))) + +allWorld :: Query a -> World -> [Entity a] +allWorld q w = + let (cIds, g) = runQuery' q (components w) + res = do + aId <- Map.lookup cIds (archetypeIds w) + Map.lookup aId (archetypes w) + in case res of + Just arch -> fst $ g arch + Nothing -> [] + +mapWith :: + (FromEntity i, ToEntity o, EntityT i ~ ConcatT a b, EntityT o ~ b) => + Query a -> + Query b -> + (i -> o) -> + World -> + ([o], World) +mapWith a b f w = + let (aCIds, aG) = runQuery' a (components w) + (bCIds, bG) = runQuery' b (components w) + res = do + aId <- Map.lookup (aCIds <> bCIds) (archetypeIds w) + arch <- Map.lookup aId (archetypes w) + return (aId, arch) + in case res of + Just (aId, arch) -> + let (as, _) = aG arch + (bs, bH) = bG arch + es = fmap (\(aE, bE) -> f $ fromEntity (E.concat aE bE)) (zip as bs) + arch' = bH (fmap (toEntity . toEntity) es) arch + in (es, w {archetypes = Map.insert aId arch' (archetypes w)}) + Nothing -> ([], w) + +class ToQuery a where + query :: Query a + +instance ToQuery '[] where + query = Query $ const (Set.empty, const ([], \_ arch' -> arch')) + +instance {-# OVERLAPPING #-} (Component a, Typeable (StorageT a)) => ToQuery '[a] where + query = fetch @a + +instance (Component a, Typeable (StorageT a), ToQuery as) => ToQuery (a ': as) where + query = fetch @a <?> query @as + +mapWorld :: + forall i o. + ( FromEntity i, + ToEntity o, + Intersect (EntityT i) (EntityT o), + ToQuery (IntersectT (EntityT i) (EntityT o)), + Difference (EntityT i) (EntityT o), + ToQuery (DifferenceT (EntityT i) (EntityT o)), + ConcatT (DifferenceT (EntityT i) (EntityT o)) (IntersectT (EntityT i) (EntityT o)) ~ EntityT i, + IntersectT (EntityT i) (EntityT o) ~ EntityT o + ) => + (i -> o) -> + World -> + ([o], World) +mapWorld f w = + let i = query @(IntersectT (EntityT i) (EntityT o)) + o = query @(DifferenceT (EntityT i) (EntityT o)) + (aCIds, aG) = runQuery' i (components w) + (bCIds, bG) = runQuery' o (components w) + res = do + aId <- Map.lookup (aCIds <> bCIds) (archetypeIds w) + arch <- Map.lookup aId (archetypes w) + return (aId, arch) + in case res of + Just (aId, arch) -> + let (as, aH) = aG arch + (bs, _) = bG arch + es = fmap (\(aE, bE) -> f $ fromEntity @i (E.concat bE aE)) (zip as bs) + arch' = aH (fmap (\x -> let e = toEntity x in e) es) arch + in (es, w {archetypes = Map.insert aId arch' (archetypes w)}) + Nothing -> ([], w) + +map :: + forall m i o. + ( Monad m, + FromEntity i, + ToEntity o, + Intersect (EntityT i) (EntityT o), + ToQuery (IntersectT (EntityT i) (EntityT o)), + Difference (EntityT i) (EntityT o), + ToQuery (DifferenceT (EntityT i) (EntityT o)), + ConcatT (DifferenceT (EntityT i) (EntityT o)) (IntersectT (EntityT i) (EntityT o)) ~ EntityT i, + IntersectT (EntityT i) (EntityT o) ~ EntityT o + ) => + (i -> o) -> + Access m [o] +map f = Access $ do + w <- get + let (out, w') = mapWorld @i @o f w + put w' + return out
− src/Data/Aztecs/Schedule.hs
@@ -1,207 +0,0 @@-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}--module Data.Aztecs.Schedule- ( Node (..),- Schedule (..),- ScheduleNode (..),- runSchedule,- Startup,- Update,- Constraint (..),- before,- after,- Scheduler (..),- schedule,- SchedulerGraph (..),- buildScheduler,- runSchedulerGraph,- runScheduler,- )-where--import Control.Concurrent.Async (mapConcurrently)-import Control.Monad.State (StateT (runStateT))-import Data.Aztecs.Command-import Data.Aztecs.System-import Data.Aztecs.World- ( World,- newWorld,- union,- )-import Data.Foldable (foldrM)-import Data.Functor ((<&>))-import Data.List (groupBy, sortBy)-import Data.Map (Map)-import qualified Data.Map as Map-import Data.Proxy-import Data.Set (Set)-import qualified Data.Set as Set-import Data.Typeable-import Prelude hiding (all, read)--data Constraint = Before TypeRep | After TypeRep--before :: forall m a. (System m a) => Constraint-before = Before $ typeOf (Proxy :: Proxy a)--after :: forall m a. (System m a) => Constraint-after = After $ typeOf (Proxy :: Proxy a)--data Node m where- Node :: (System m a) => Proxy a -> Cache -> Node m--data ScheduleNode m = ScheduleNode (Node m) [Constraint]--data Schedule m = Schedule (Map TypeRep (ScheduleNode m))--instance Semigroup (Schedule m) where- Schedule a <> Schedule b = Schedule $ a <> b--instance Monoid (Schedule m) where- mempty = Schedule mempty--data GraphNode m = GraphNode (Node m) (Set TypeRep) (Set TypeRep)--build :: (Monad m) => Schedule m -> [[GraphNode m]]-build (Schedule s) =- let graph =- fmap- ( \(ScheduleNode node constraints) ->- let (deps, befores) =- foldr- ( \c (depAcc, afterAcc) -> case c of- Before i -> (depAcc, [i])- After i -> (depAcc ++ [i], afterAcc)- )- ([], [])- constraints- in GraphNode node (Set.fromList deps) (Set.fromList befores)- )- s- graph' =- foldr- ( \(GraphNode _ _ befores) acc ->- foldr- ( \i acc' ->- Map.adjust- ( \(GraphNode n deps bs) ->- GraphNode n (Set.singleton i <> deps) bs- )- i- acc'- )- acc- befores- )- graph- graph- nodes =- sortBy- ( \(GraphNode _ deps _) (GraphNode _ deps' _) ->- compare (length deps') (length deps)- )- (Map.elems graph')- in groupBy- ( \(GraphNode a deps aBefores) (GraphNode b deps' bBefores) ->- (length deps == length deps')- -- TODO || hasConflict (GraphNode a deps aBefores) (GraphNode b deps' bBefores)- )- nodes--runNode :: Node IO -> World -> IO (Node IO, Maybe (Access IO ()), [Command IO ()], World)-runNode (Node p cache) w =- runSystemProxy p cache w <&> (\(next, a', cmds, w') -> (Node p a', next, cmds, w'))--runSystemProxy :: forall a. (System IO a) => Proxy a -> Cache -> World -> IO (Maybe (Access IO ()), Cache, [Command IO ()], World)-runSystemProxy _ = runSystem' @a---- | Run a `Command`, returning any temporary `Entity`s and the updated `World`.-runCommand :: Command IO () -> World -> IO (World)-runCommand (Command cmd) w = snd <$> runStateT cmd w--runSchedule :: [[GraphNode IO]] -> World -> IO ([[GraphNode IO]], World)-runSchedule nodes w =- foldrM- ( \nodeGroup (nodeAcc, w') -> do- results <-- mapConcurrently- ( \(GraphNode n as bs) -> do- (n', next, cmds, w'') <- runNode n w- return ((next, (GraphNode n' as bs)), cmds, w'')- )- nodeGroup- let (nexts, cmdLists, worlds) =- foldr- ( \(n, b, c) (ns, bs, cs) ->- (n : ns, b : bs, c : cs)- )- ([], [], [])- results- finalWorld = foldr union w' worlds- (cmds, w'') = (concat cmdLists, finalWorld)-- (w''', nodes', cmds') <-- foldrM- ( \(a, (GraphNode (Node p cache) as bs)) (wAcc, nodeAcc', cmdAcc) -> case a of- Just a' -> do- ((), wAcc', cache', cmdAcc') <- runAccess' a' wAcc cache- return (wAcc', (GraphNode (Node p cache') as bs) : nodeAcc', cmdAcc' ++ cmdAcc)- Nothing -> return (w, (GraphNode (Node p cache) as bs) : nodeAcc', cmdAcc)- )- (w'', [], [])- nexts-- w'''' <- foldrM (\cmd wAcc -> runCommand cmd wAcc) w''' (cmds ++ cmds')- return (nodes' : nodeAcc, w'''')- )- ([], w)- nodes--newtype Scheduler m = Scheduler (Map TypeRep (Schedule m))- deriving (Monoid)--instance Semigroup (Scheduler m) where- Scheduler a <> Scheduler b = Scheduler $ Map.unionWith (<>) a b--data Startup--data Update--schedule :: forall l m s. (Typeable l, System m s) => [Constraint] -> Scheduler m-schedule cs =- Scheduler $- Map.singleton- (typeOf (Proxy :: Proxy l))- ( Schedule $- Map.singleton- (typeOf (Proxy :: Proxy s))- (ScheduleNode (Node (Proxy :: Proxy s) mempty) cs)- )--newtype SchedulerGraph m = SchedulerGraph (Map TypeRep [[GraphNode m]])--buildScheduler :: (Monad m) => Scheduler m -> SchedulerGraph m-buildScheduler (Scheduler s) = SchedulerGraph $ fmap build s--runSchedulerGraph :: forall l. (Typeable l) => SchedulerGraph IO -> World -> IO (SchedulerGraph IO, World)-runSchedulerGraph (SchedulerGraph g) w = case Map.lookup (typeOf (Proxy :: Proxy l)) g of- Just s -> do- (nodes, w') <- runSchedule s w- let g' = Map.insert (typeOf (Proxy :: Proxy l)) nodes g- return (SchedulerGraph g', w')- Nothing -> return (SchedulerGraph g, w)--runScheduler :: Scheduler IO -> IO ()-runScheduler s = do- let g = buildScheduler s- (g', w) <- runSchedulerGraph @Startup g newWorld- let go gAcc wAcc = do- (gAcc', wAcc') <- runSchedulerGraph @Update gAcc wAcc- go gAcc' wAcc'- go g' w
src/Data/Aztecs/Storage.hs view
@@ -1,65 +1,25 @@-module Data.Aztecs.Storage (Storage (..), table, table') where--import Control.Monad (filterM)-import Data.Aztecs.Core-import Data.Functor ((<&>))-import Data.IORef (IORef, newIORef, readIORef, writeIORef)-import Data.List (find)--data Storage a = Storage- { empty :: IO (Storage a),- spawn :: Entity -> a -> IO (Storage a),- get :: Entity -> IO (Maybe (a, a -> Storage a -> IO (Storage a))),- toList :: IO [EntityComponent a],- toList' :: IO [(EntityComponent a, a -> IO ())],- remove :: Entity -> IO (Storage a)- }--table' :: [IORef (EntityComponent a)] -> Storage a-table' cs =- Storage- { empty = pure $ table' [],- spawn = \e a -> do- r <- newIORef (EntityComponent e a)- return $ table' (r : cs),- get = \e -> do- cs' <-- mapM- ( \r -> do- a <- readIORef r- return (a, r)- )- cs- return- ( find (\(EntityComponent e' _, _) -> e == e') cs'- <&> \(EntityComponent _ a, r) ->- ( a,- \a' t -> do- writeIORef r (EntityComponent e a')- return t- )- ),- toList = mapM readIORef cs,- toList' =- mapM- ( \r -> do- (EntityComponent e a) <- readIORef r- return- ( (EntityComponent e a),- \a' -> writeIORef r (EntityComponent e a')- )- )- cs,- remove = \e -> do- cs' <-- filterM- ( \r -> do- (EntityComponent e' _) <- readIORef r- return $ e /= e'- )- cs- return $ table' cs'- }--table :: Storage a-table = table' []+{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MonoLocalBinds #-} +{-# LANGUAGE MultiParamTypeClasses #-} + +module Data.Aztecs.Storage (Storage(..)) where + +import Data.Data (Typeable) +import Data.IntMap (IntMap) +import qualified Data.IntMap as IntMap + +class (Typeable (s a), Typeable a) => Storage s a where + singleton :: Int -> a -> s a + all :: s a -> [(Int, a)] + insert :: Int -> a -> s a -> s a + lookup :: Int -> s a -> Maybe a + fromAscList :: [(Int, a)] -> s a + remove :: Int -> s a -> (Maybe a, s a) + +instance (Typeable a) => Storage IntMap a where + singleton = IntMap.singleton + all = IntMap.toList + insert = IntMap.insert + lookup = IntMap.lookup + fromAscList = IntMap.fromAscList + remove i s = (IntMap.lookup i s, IntMap.delete i s) -- TODO remove double lookup
− src/Data/Aztecs/System.hs
@@ -1,218 +0,0 @@-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}--module Data.Aztecs.System- ( Access (..),- runAccess,- runAccess',- all,- get,- command,- System (..),- runSystem,- runSystem',- Cache (..),- )-where--import Control.Monad.IO.Class-import Data.Aztecs.Command-import Data.Aztecs.Query (Query (..))-import qualified Data.Aztecs.Query as Q-import Data.Aztecs.World (Entity, World (..))-import Data.Aztecs.World.Archetypes (Archetype, ArchetypeId)-import qualified Data.Aztecs.World.Archetypes as A-import Data.Foldable (foldrM)-import Data.Kind-import Data.Map (Map)-import qualified Data.Map as Map-import Data.Typeable-import Prelude hiding (all, read)--newtype Cache = Cache (Map Archetype ArchetypeId)- deriving (Semigroup, Monoid)--data Access (m :: Type -> Type) a where- PureA :: a -> Access m a- MapA :: (a -> b) -> Access m a -> Access m b- AppA :: Access m (a -> b) -> Access m a -> Access m b- BindA :: Access m a -> (a -> Access m b) -> Access m b- AllA :: Archetype -> Query m a -> Access m [a]- GetA :: Archetype -> Query m a -> Entity -> Access m (Maybe a)- CommandA :: Command m () -> Access m ()- LiftA :: m a -> Access m a--instance Functor (Access m) where- fmap = MapA--instance (Monad m) => Applicative (Access m) where- pure = PureA- (<*>) = AppA--instance (Monad m) => Monad (Access m) where- (>>=) = BindA--instance (MonadIO m) => MonadIO (Access m) where- liftIO io = LiftA (liftIO io)--runAccess :: Access IO a -> World -> Cache -> IO (Either (Access IO a) a, World, Cache, [Command IO ()])-runAccess (PureA a) w c = return (Right a, w, c, [])-runAccess (MapA f a) w cache = do- (a', w', cache', cmds) <- runAccess a w cache- return- ( case a' of- Left a'' -> Left (MapA f a'')- Right a'' -> Right (f a''),- w',- cache',- cmds- )-runAccess (AppA f a) w cache = do- (f', w', cache', cmds) <- runAccess f w cache- (a', w'', cache'', cmds') <- runAccess a w' cache'- return- ( case (f', a') of- (Right f'', Right a'') -> Right (f'' a'')- (Left f'', _) -> Left (AppA f'' a)- (_, Left a'') -> Left (AppA f a''),- w'',- cache'',- cmds ++ cmds'- )-runAccess (BindA a f) w cache = do- (a', w', cache', cmds) <- runAccess a w cache- case a' of- Left a'' -> return (Left (BindA a'' f), w', cache', cmds)- Right a'' -> runAccess (f a'') w' cache'-runAccess (AllA a qb) w (Cache cache) = case Map.lookup (Q.buildQuery qb) cache of- Just aId -> do- es <- Q.all aId qb w- return (Right es, w, Cache cache, [])- Nothing -> return (Left (AllA a qb), w, Cache cache, [])-runAccess (GetA arch q e) w (Cache cache) = do- case Map.lookup arch cache of- Just aId -> do- a <- Q.get aId q e w- return (Right a, w, Cache cache, [])- Nothing -> return (Left (GetA arch q e), w, Cache cache, [])-runAccess (CommandA cmd) w cache = return (Right (), w, cache, [cmd])-runAccess (LiftA io) w cache = do- a <- liftIO io- return (Right a, w, cache, [])--runAccess' :: Access IO a -> World -> Cache -> IO (a, World, Cache, [Command IO ()])-runAccess' (PureA a) w c = return (a, w, c, [])-runAccess' (MapA f a) w cache = do- (a', w', cache', cmds) <- runAccess' a w cache- return (f a', w', cache', cmds)-runAccess' (AppA f a) w cache = do- (f', w', cache', cmds) <- runAccess' f w cache- (a', w'', cache'', cmds') <- runAccess' a w' cache'- return (f' a', w'', cache'', cmds ++ cmds')-runAccess' (BindA a f) w cache = do- (a', w', cache', cmds) <- runAccess' a w cache- (b, w'', cache'', cmds') <- runAccess' (f a') w' cache'- return (b, w'', cache'', cmds ++ cmds')-runAccess' (AllA _ qb) (World cs as) (Cache cache) = do- (aId, w) <- case Map.lookup (Q.buildQuery qb) cache of- Just q' -> return (q', World cs as)- Nothing -> do- (x, as') <- A.insertArchetype (Q.buildQuery qb) cs as- return (x, World cs as')- es <- Q.all aId qb w- return (es, w, Cache cache, [])-runAccess' (GetA arch q e) (World cs as) (Cache cache) = do- (aId, w) <- case Map.lookup arch cache of- Just q' -> return (q', World cs as)- Nothing -> do- (x, as') <- A.insertArchetype arch cs as- return (x, World cs as')- a <- Q.get aId q e w- return (a, w, Cache cache, [])-runAccess' (CommandA cmd) w cache = return ((), w, cache, [cmd])-runAccess' (LiftA io) w cache = do- a <- liftIO io- return (a, w, cache, [])---- | Query all matches.-all :: (Monad m) => Query m a -> Access m [a]-all q = fst <$> all' mempty q--all' :: (Monad m) => Archetype -> Query m a -> Access m ([a], Archetype)-all' arch (PureQ a) = pure ([a], arch)-all' arch (MapQ f a) = all' arch (f <$> a)-all' arch (AppQ f a) = all' arch (f <*> a)-all' arch (BindQ a f) = do- (a', arch') <- all' arch a- foldrM- ( \q (acc, archAcc) -> do- (as, archAcc') <- all' archAcc (f q)- return (as ++ acc, archAcc')- )- ([], arch')- a'-all' arch (LiftQ m) = do- a <- LiftA m- return ([a], arch)-all' arch (ReadQ arch') = do- let arch'' = (arch <> arch')- as <- AllA arch'' (ReadQ arch')- return (as, arch'')-all' arch (WriteQ f arch') = do- let arch'' = (arch <> arch')- as <- AllA arch'' (WriteQ f arch')- return (as, arch'')-all' arch EntityQ = do- es <- AllA arch EntityQ- return (es, arch)--get :: (Monad m) => Entity -> Query m a -> Access m (Maybe a)-get e q = fst <$> get' mempty e q--get' :: (Monad m) => Archetype -> Entity -> Query m a -> Access m (Maybe a, Archetype)-get' arch _ (PureQ a) = pure (Just a, arch)-get' arch e (MapQ f qb) = get' arch e (f <$> qb)-get' arch e (AppQ f a) = get' arch e (f <*> a)-get' arch e (BindQ a f) = do- (a', arch') <- get' arch e a- case fmap f a' of- Just a'' -> get' arch' e a''- Nothing -> return (Nothing, arch')-get' arch _ (LiftQ m) = do- a <- LiftA m- return (Just a, arch)-get' arch e (ReadQ arch') = do- let arch'' = (arch <> arch')- a <- GetA arch'' (ReadQ arch') e- return (a, arch'')-get' arch e (WriteQ f arch') = do- let arch'' = (arch <> arch')- a <- GetA arch'' (WriteQ f arch') e- return (a, arch'')-get' arch e EntityQ = return (Just e, arch)--command :: Command m () -> Access m ()-command = CommandA--class (Typeable a) => System m a where- access :: Access m ()--runSystem :: forall a. (System IO a) => World -> IO World-runSystem w = do- (_, _, _, w') <- runSystem' @a (Cache mempty) w- return w'--runSystem' :: forall a. (System IO a) => Cache -> World -> IO (Maybe (Access IO ()), Cache, [Command IO ()], World)-runSystem' cache w = do- (result, w', cache', cmds) <- runAccess (access @IO @a) w cache- case result of- Left a -> return (Just a, cache', cmds, w')- Right _ -> return (Nothing, cache', cmds, w')
− src/Data/Aztecs/Task.hs
@@ -1,29 +0,0 @@-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--module Data.Aztecs.Task- ( Task (..),- command,- runTask,- )-where--import Control.Monad.IO.Class-import Control.Monad.State (StateT (..))-import qualified Control.Monad.State as S-import Data.Aztecs.Command-import Data.Aztecs.World (World)-import Prelude hiding (all)---- | System task.-newtype Task m s a = Task (StateT (s, [Command m ()], World) m a)- deriving (Functor, Applicative, Monad, MonadIO)--runTask :: (Functor m) => Task m s a -> s -> World -> m (a, s, [Command m ()], World)-runTask (Task t) s w = fmap (\(a, (s', cmds, w')) -> (a, s', cmds, w')) (S.runStateT t (s, [], w))---- | Queue a `Command` to run after this system is complete.-command :: (Monad m) => Command m () -> Task m a ()-command cmd = Task $ do- (s, cmds, w) <- S.get- S.put $ (s, cmds <> [cmd], w)
src/Data/Aztecs/World.hs view
@@ -1,71 +1,191 @@-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}--module Data.Aztecs.World- ( Entity,- EntityComponent (..),- Component (..),- World (..),- union,- spawn,- insert,- get,- getRow,- newWorld,- setRow,- remove,- )-where--import Data.Aztecs.Core-import Data.Aztecs.Storage (Storage)-import Data.Aztecs.World.Archetypes (Archetypes, newArchetypes)-import qualified Data.Aztecs.World.Archetypes as A-import Data.Aztecs.World.Components (Component, Components, newComponents)-import qualified Data.Aztecs.World.Components as C-import Data.Typeable-import Prelude hiding (read)--data World = World Components Archetypes deriving (Show)--newWorld :: World-newWorld = World newComponents newArchetypes--union :: World -> World -> World-union (World cs as) (World cs' _) = World (C.union cs cs') as--spawn :: forall c. (Component c) => c -> World -> IO (Entity, World)-spawn c (World cs as) = do- (e, cs') <- C.spawn c cs - return (e, World cs' as)--insert :: forall c. (Component c) => Entity -> c -> World -> IO World-insert e c (World cs as) = do- cs' <- C.insert e c cs- return $ World cs' (A.insert @c e cs' as)--getRow :: (Component c) => Proxy c -> World -> Maybe (Storage c)-getRow p (World cs _) = C.getRow p cs--get :: forall c. (Component c) => Entity -> World -> IO (Maybe (c, c -> World -> IO World))-get e (World cs _) = do- res <- C.get e cs- case res of- Just (c, f) ->- return $- Just- ( c,- \c' (World cs' as) -> do- cs'' <- f c' cs'- return $ World cs'' as- )- Nothing -> return Nothing--setRow :: forall c. (Component c) => Storage c -> World -> World-setRow row (World cs as) = World (C.setRow row cs) as--remove :: forall c. (Component c) => Entity -> World -> World-remove e (World cs as) = World (C.remove @c e cs) as+{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} + +module Data.Aztecs.World + ( ArchetypeID (..), + World (..), + empty, + spawn, + spawnWithId, + spawnWithArchetypeId, + spawnEmpty, + insert, + insertWithId, + insertArchetype, + despawn, + ) +where + +import Data.Aztecs.Archetype (Archetype (..)) +import qualified Data.Aztecs.Archetype as A +import Data.Aztecs.Core (Component (..), ComponentID, Components (..), EntityID (..), emptyComponents, insertComponentId) +import Data.Dynamic (Dynamic) +import Data.Map (Map) +import qualified Data.Map as Map +import Data.Set (Set) +import qualified Data.Set as Set +import Data.Typeable (Proxy (..), Typeable, typeOf) + +newtype ArchetypeID = ArchetypeID {unArchetypeId :: Int} + deriving (Eq, Ord, Show) + +-- | World of entities and their components. +data World = World + { archetypes :: Map ArchetypeID Archetype, + archetypeIds :: Map (Set ComponentID) ArchetypeID, + archetypeComponents :: Map ArchetypeID (Set ComponentID), + nextArchetypeId :: ArchetypeID, + components :: Components, + entities :: Map EntityID ArchetypeID, + nextEntityId :: EntityID + } + deriving (Show) + +-- | Empty `World`. +empty :: World +empty = + World + { archetypes = mempty, + archetypeIds = mempty, + archetypeComponents = mempty, + nextArchetypeId = ArchetypeID 0, + components = emptyComponents, + entities = mempty, + nextEntityId = EntityID 0 + } + +-- | Spawn an entity with a component. +spawn :: forall a. (Component a, Typeable (StorageT a)) => a -> World -> (EntityID, World) +spawn c w = case Map.lookup (typeOf (Proxy @a)) (componentIds (components w)) of + Just cId -> spawnWithId cId c w + Nothing -> + let (cId, cs) = insertComponentId @a (components w) + in spawnWithId cId c w {components = cs} + +-- | Spawn an empty entity. +spawnEmpty :: World -> (EntityID, World) +spawnEmpty w = let e = nextEntityId w in (e, w {nextEntityId = EntityID (unEntityId e + 1)}) + +-- | Spawn an entity with a component and its `ComponentID`. +spawnWithId :: + forall a. + (Component a, Typeable (StorageT a)) => + ComponentID -> + a -> + World -> + (EntityID, World) +spawnWithId cId c w = + let (e, w') = spawnEmpty w + in case Map.lookup (Set.singleton cId) (archetypeIds w) of + Just aId -> (e, spawnWithArchetypeId' e aId cId c w') + Nothing -> + ( e, + snd $ + insertArchetype + (Set.singleton cId) + (A.insert e cId c A.empty) + w' {entities = Map.insert e (nextArchetypeId w) (entities w)} + ) + +-- | Spawn an entity with a component and its `ComponentID` directly into an archetype. +spawnWithArchetypeId :: + forall a. + (Component a, Typeable (StorageT a)) => + a -> + ComponentID -> + ArchetypeID -> + World -> + (EntityID, World) +spawnWithArchetypeId c cId aId w = + let (e, w') = spawnEmpty w + in (e, spawnWithArchetypeId' e aId cId c w') + +spawnWithArchetypeId' :: + forall a. + (Component a, Typeable (StorageT a)) => + EntityID -> + ArchetypeID -> + ComponentID -> + a -> + World -> + World +spawnWithArchetypeId' e aId cId c w = + let f = A.insert e cId c + in w + { archetypes = Map.adjust f aId (archetypes w), + entities = Map.insert e aId (entities w) + } + +-- | Insert an archetype by its set of `ComponentID`s. +insertArchetype :: Set ComponentID -> Archetype -> World -> (ArchetypeID, World) +insertArchetype cIds a w = + let aId = nextArchetypeId w + in ( aId, + w + { archetypes = Map.insert aId a (archetypes w), + archetypeIds = Map.insert cIds aId (archetypeIds w), + archetypeComponents = Map.insert aId cIds (archetypeComponents w), + nextArchetypeId = ArchetypeID (unArchetypeId aId + 1) + } + ) + +insert :: forall a. (Component a, Typeable (StorageT a)) => EntityID -> a -> World -> World +insert e c w = + let (cId, components') = insertComponentId @a (components w) + in insertWithId e cId c w {components = components'} + +insertWithId :: (Component a, Typeable (StorageT a)) => EntityID -> ComponentID -> a -> World -> World +insertWithId e cId c w = case Map.lookup e (entities w) of + Just aId -> case Map.lookup aId (archetypeComponents w) of + Just cIds -> + if Set.member cId cIds + then w {archetypes = Map.adjust (A.insert e cId c) aId (archetypes w)} + else + let arch = archetypes w Map.! aId + in case Map.lookup (Set.insert cId cIds) (archetypeIds w) of + Just nextAId -> + let (cs, arch') = A.remove e arch + w' = w {archetypes = Map.insert aId arch' (archetypes w)} + f (itemCId, dyn) archAcc = + archAcc + { A.storages = + Map.adjust + (\s -> s {A.storageDyn = A.insertDyn s (unEntityId e) dyn (A.storageDyn s)}) + itemCId + (A.storages archAcc) + } + in w' + { archetypes = + Map.adjust + (\nextArch -> foldr f nextArch (Map.toList cs)) + nextAId + (archetypes w') + } + Nothing -> + let (s, arch') = A.removeStorages e arch + w' = w {archetypes = Map.insert aId arch' (archetypes w)} + newArch = Archetype {A.storages = s} + newArch' = A.insert e cId c newArch + in snd $ insertArchetype (Set.insert cId cIds) newArch' w' + Nothing -> w + Nothing -> w + +despawn :: EntityID -> World -> (Map ComponentID Dynamic, World) +despawn e w = + let res = do + aId <- Map.lookup e (entities w) + arch <- Map.lookup aId (archetypes w) + return (aId, arch) + in case res of + Just (aId, arch) -> + let (dynAcc, arch') = A.remove e arch + in ( dynAcc, + w + { archetypes = Map.insert aId arch' (archetypes w), + entities = Map.delete e (entities w) + } + ) + Nothing -> (Map.empty, w)
− src/Data/Aztecs/World/Archetypes.hs
@@ -1,181 +0,0 @@-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}--module Data.Aztecs.World.Archetypes- ( Archetype (..),- ArchetypeComponent (..),- ArchetypeComponents (..),- getArchetypeComponent,- ArchetypeState (..),- ArchetypeId (..),- Archetypes (..),- newArchetypes,- archetype,- insertArchetype,- getArchetype,- insert,- )-where--import Data.Aztecs.Core-import qualified Data.Aztecs.Storage as S-import Data.Aztecs.World.Components (Component, Components, getRow)-import qualified Data.Aztecs.World.Components as C-import Data.Dynamic (Dynamic, fromDynamic, toDyn)-import Data.Foldable (foldrM)-import Data.IntMap (IntMap)-import qualified Data.IntMap as IntMap-import Data.Map (Map)-import qualified Data.Map as Map-import Data.Maybe (fromMaybe, isJust)-import Data.Set (Set)-import qualified Data.Set as Set-import Data.Typeable-import Prelude hiding (read)--data ArchetypeComponent where- ArchetypeComponent :: (Component c) => Proxy c -> ArchetypeComponent--instance Eq ArchetypeComponent where- ArchetypeComponent a == ArchetypeComponent b = typeOf a == typeOf b--instance Ord ArchetypeComponent where- ArchetypeComponent a `compare` ArchetypeComponent b = typeOf a `compare` typeOf b--instance Show ArchetypeComponent where- show (ArchetypeComponent p) = show (typeOf p)--newtype Archetype = Archetype (Set ArchetypeComponent)- deriving (Eq, Ord, Show, Monoid, Semigroup)--archetype :: forall c. (Component c) => Archetype-archetype = Archetype . Set.singleton $ ArchetypeComponent (Proxy @c)--newtype ArchetypeId = ArchetypeId Int deriving (Eq, Ord, Show)--newtype ArchetypeComponents = ArchetypeComponents (Map TypeRep Dynamic)- deriving (Show)--getArchetypeComponent :: forall c. (Component c) => ArchetypeComponents -> Maybe (c, c -> IO ())-getArchetypeComponent (ArchetypeComponents m) = do- d <- Map.lookup (typeOf (Proxy @c)) m- fromDynamic d--insertArchetypeComponent :: forall c. (Component c) => c -> (c -> IO ()) -> ArchetypeComponents -> ArchetypeComponents-insertArchetypeComponent c f (ArchetypeComponents m) = ArchetypeComponents $ Map.insert (typeOf (Proxy @c)) (toDyn (c, f)) m--data ArchetypeState = ArchetypeState Archetype (Map Entity ArchetypeComponents) [ArchetypeId]- deriving (Show)--data Archetypes- = Archetypes- (IntMap ArchetypeState)- (Map TypeRep [ArchetypeId])- (Map Archetype ArchetypeId)- Int- deriving (Show)--newArchetypes :: Archetypes-newArchetypes = Archetypes IntMap.empty Map.empty Map.empty 0--insertArchetype :: Archetype -> Components -> Archetypes -> IO (ArchetypeId, Archetypes)-insertArchetype (Archetype a) w (Archetypes es ids as i) = case Map.lookup (Archetype a) as of- Just (ArchetypeId i') -> return (ArchetypeId i, Archetypes es ids as i')- Nothing -> do- (es', ids') <-- foldrM- ( \(ArchetypeComponent p) (eAcc, acc) -> do- cs <- fromMaybe (pure []) $ fmap (\s -> S.toList' s) (getRow p w)- let eAcc' = map (\(EntityComponent e c, f) -> (e, insertArchetypeComponent c f (ArchetypeComponents mempty))) cs- return (eAcc' ++ eAcc, Map.unionWith (<>) (Map.singleton (typeOf p) [ArchetypeId i]) acc)- )- ([], ids)- (Set.toList a)- return (ArchetypeId i, Archetypes (IntMap.insert i (ArchetypeState (Archetype a) (Map.fromList es') []) es) ids' as (i + 1))--getArchetype :: ArchetypeId -> Archetypes -> Maybe ArchetypeState-getArchetype (ArchetypeId i) (Archetypes es _ _ _) = IntMap.lookup i es--insert :: forall c. (Component c) => Entity -> Components -> Archetypes -> Archetypes-insert e cs (Archetypes es ids as j) = case Map.lookup (typeOf (Proxy @c)) ids of- Just (ids') ->- let insertInArchetype :: Int -> IntMap ArchetypeState -> IntMap ArchetypeState- insertInArchetype archetypeId acc =- IntMap.alter (updateArchetypeState archetypeId) archetypeId acc-- updateArchetypeState :: Int -> Maybe ArchetypeState -> Maybe ArchetypeState- updateArchetypeState _ state = case state of- Just (ArchetypeState arch esAcc deps) ->- let isMatch =- all- (\(ArchetypeComponent p) -> isJust $ C.getRow p cs)- (Set.toList $ unwrapArchetype arch)- in if isMatch- then- Just $ ArchetypeState arch (Map.singleton e (ArchetypeComponents mempty) <> esAcc) deps- else state- Nothing -> state-- updateDependencies :: Int -> IntMap ArchetypeState -> IntMap ArchetypeState- updateDependencies archetypeId acc = case IntMap.lookup archetypeId acc of- Just (ArchetypeState _ _ deps) -> foldr updateDependencies (insertInArchetype archetypeId acc) (map getArchetypeId deps)- Nothing -> acc- es' = foldr updateDependencies es (map getArchetypeId ids')- in merge $ Archetypes es' ids as j- Nothing -> Archetypes es ids as j--merge :: Archetypes -> Archetypes-merge archetypes@(Archetypes es _ _ _) =- foldl processArchetype archetypes (IntMap.toList es)- where- processArchetype :: Archetypes -> (Int, ArchetypeState) -> Archetypes- processArchetype acc (parentId, ArchetypeState parentArch _ _) =- foldl (updateDependency parentId parentArch) acc (IntMap.toList es)-- updateDependency :: Int -> Archetype -> Archetypes -> (Int, ArchetypeState) -> Archetypes- updateDependency parentId parentArch acc (childId, ArchetypeState childArch _ _) =- let parentComponents = unwrapArchetype parentArch- childComponents = unwrapArchetype childArch- in if childId /= parentId && Set.isSubsetOf childComponents parentComponents- then mergeWithDeps (ArchetypeId parentId) (ArchetypeId childId) acc- else acc--mergeWithDeps :: ArchetypeId -> ArchetypeId -> Archetypes -> Archetypes-mergeWithDeps parentId childId (Archetypes es ids as nextId) =- case (IntMap.lookup (getArchetypeId parentId) es, IntMap.lookup (getArchetypeId childId) es) of- (Just (ArchetypeState parentArch parentEntities parentDeps), Just (ArchetypeState childArch childEntities childDeps)) ->- let parentComponents = unwrapArchetype parentArch- childComponents = unwrapArchetype childArch-- adjustedChildComponents = Set.intersection parentComponents childComponents- adjustedChildArch = Archetype adjustedChildComponents-- (childId', updatedEs, updatedAs, newNextId) =- if adjustedChildComponents == childComponents- then (childId, es, as, nextId)- else case Map.lookup adjustedChildArch as of- Just existingId -> (existingId, es, as, nextId)- Nothing ->- let newChildId = ArchetypeId nextId- newState = ArchetypeState adjustedChildArch childEntities childDeps- in ( newChildId,- IntMap.insert nextId newState es,- Map.insert adjustedChildArch newChildId as,- nextId + 1- )-- updatedParentDeps = childId' : parentDeps- updatedParentState = ArchetypeState parentArch parentEntities updatedParentDeps- finalEs = IntMap.insert (getArchetypeId parentId) updatedParentState updatedEs- in Archetypes finalEs ids updatedAs newNextId- _ -> Archetypes es ids as nextId--getArchetypeId :: ArchetypeId -> Int-getArchetypeId (ArchetypeId x) = x--unwrapArchetype :: Archetype -> Set ArchetypeComponent-unwrapArchetype (Archetype set) = set
− src/Data/Aztecs/World/Components.hs
@@ -1,87 +0,0 @@-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}--module Data.Aztecs.World.Components- ( Component (..),- Components,- union,- spawn,- insert,- adjust,- get,- getRow,- newComponents,- setRow,- remove,- )-where--import Data.Aztecs.Core-import Data.Aztecs.Storage (Storage, table)-import qualified Data.Aztecs.Storage as S-import Data.Dynamic (Dynamic, fromDynamic, toDyn)-import Data.Map (Map, alter, empty, lookup)-import qualified Data.Map as Map-import Data.Maybe (fromMaybe)-import Data.Typeable-import Prelude hiding (read)--class (Typeable a) => Component a where- storage :: Storage a- storage = table--data Components = Components (Map TypeRep Dynamic) Entity deriving (Show)--newComponents :: Components-newComponents = Components empty (Entity 0)--union :: Components -> Components -> Components-union (Components a e) (Components b _) = Components (Map.union a b) e--spawn :: forall c. (Component c) => c -> Components -> IO (Entity, Components)-spawn c (Components w (Entity e)) = do- w' <- insert (Entity e) c (Components w (Entity $ e + 1))- return (Entity e, w')--insert :: forall c. (Component c) => Entity -> c -> Components -> IO Components-insert e c (Components w e') = do- w' <-- Map.alterF- ( \maybeRow -> do- s <- S.spawn (fromMaybe storage (maybeRow >>= fromDynamic)) e c- return . Just $ toDyn s- )- (typeOf (Proxy :: Proxy c))- w- return $ Components w' e'--adjust :: (Component c) => c -> (c -> c) -> Entity -> Components -> IO Components-adjust a f w = insert w (f a)--getRow :: (Component c) => Proxy c -> Components -> Maybe (Storage c)-getRow p (Components w _) = Data.Map.lookup (typeOf p) w >>= fromDynamic--get :: forall c. (Component c) => Entity -> Components -> IO (Maybe (c, c -> Components -> IO Components))-get e (Components w _) = case Data.Map.lookup (typeOf @(Proxy c) Proxy) w >>= fromDynamic of- Just s -> do- res <- S.get s e- case res of- Just (c, f) ->- return $- Just- ( c,- \c' (Components w' e') ->- return $ Components (alter (\row -> Just . toDyn $ f c' (fromMaybe storage (row >>= fromDynamic))) (typeOf @(Proxy c) Proxy) w') e'- )- Nothing -> return Nothing- Nothing -> return Nothing--setRow :: forall c. (Component c) => Storage c -> Components -> Components-setRow cs (Components w e') = Components (Map.insert (typeOf @(Proxy c) Proxy) (toDyn cs) w) e'--remove :: forall c. (Component c) => Entity -> Components -> Components-remove e (Components w e') = Components (alter (\row -> row >>= f) (typeOf @(Proxy c) Proxy) w) e'- where- f row = fmap (\row' -> toDyn $ S.remove @c row' e) (fromDynamic row)
test/Main.hs view
@@ -1,4 +1,14 @@-module Main (main) where--main :: IO ()-main = putStrLn "Test suite not yet implemented."+module Main (main) where + +import Data.Aztecs + +newtype X = X Int deriving (Eq, Show) + +instance Component X + +newtype Y = Y Int deriving (Eq, Show) + +instance Component Y + +main :: IO () +main = return ()