aztecs-0.14.0: src/Aztecs/Internal.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UndecidableInstances #-}
module Aztecs.Internal
( AztecsT (..),
runAztecsT,
runAztecsT_,
)
where
import Aztecs.ECS.Bundle
import Aztecs.ECS.Bundle.Class
import Aztecs.ECS.Class
import Aztecs.ECS.Commands
import Aztecs.ECS.Component (Component (ComponentStorage, componentHooks), Hooks (..))
import Aztecs.ECS.HSet (AdjustM, HSet (..), Lookup (..))
import qualified Aztecs.ECS.HSet as HS
import Aztecs.ECS.Query
import Aztecs.ECS.Query.Internal
import Aztecs.ECS.R
import qualified Aztecs.ECS.Scheduler as Scheduler
import Aztecs.ECS.W
import qualified Aztecs.Entity as E
import Aztecs.Storage
import qualified Aztecs.Storage as S
import Aztecs.World (SparseStorage, WorldComponents)
import qualified Aztecs.World as W
import qualified Aztecs.World.Entities as E
import Control.Monad.Primitive
import Control.Monad.State.Strict
import qualified Data.IntMap.Strict as IntMap
import qualified Data.Map.Strict as Map
import Data.Maybe
import Data.Proxy
import qualified Data.Set as Set
import Data.Typeable
import Prelude hiding (Read, lookup)
newtype AztecsT cs m a = AztecsT {unAztecsT :: StateT (W.World m cs) m a}
deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
instance MonadTrans (AztecsT cs) where
lift = AztecsT . lift
{-# INLINE lift #-}
instance (PrimMonad m) => ECS (AztecsT cs m) where
type Entity (AztecsT cs m) = E.Entity
type Task (AztecsT cs m) = (Commands (AztecsT cs) m)
spawn b = do
w <- AztecsT get
let (e, counter) = E.mkEntityWithCounter (W.worldEntities w)
AztecsT $ put w {W.worldEntities = counter}
runBundle b e
return e
{-# INLINE spawn #-}
insert e b = runBundle b e
{-# INLINE insert #-}
remove e = AztecsT $ do
w <- get
w' <- lift $ W.remove e w
put w'
{-# INLINE remove #-}
task = runCommands
{-# INLINE task #-}
instance
( PrimMonad m,
Typeable c,
Component (AztecsT cs m) c,
AdjustM m (SparseStorage m c) (WorldComponents m cs)
) =>
Bundleable c (AztecsT cs m)
where
bundle c = Bundle $ \entity -> do
w <- AztecsT get
let entityIdx = fromIntegral $ E.entityIndex entity
componentType = typeOf c
go = S.insertStorage entity c
hooks = componentHooks (Proxy :: Proxy c)
cs <- lift . HS.adjustM @_ @(SparseStorage m c) go $ W.worldComponents w
let entityComponents' =
IntMap.insertWith
Map.union
entityIdx
(Map.singleton componentType (W.removeComponent' @m @c entity))
(W.worldEntityComponents w)
AztecsT $ put w {W.worldComponents = cs, W.worldEntityComponents = entityComponents'}
onInsert hooks entity
{-# INLINE bundle #-}
runAztecsT :: (Monad m) => AztecsT cs m a -> W.World m cs -> m (a, W.World m cs)
runAztecsT (AztecsT m) = runStateT m
{-# INLINE runAztecsT #-}
runAztecsT_ :: (Monad m) => AztecsT cs m a -> W.World m cs -> m a
runAztecsT_ (AztecsT m) = evalStateT m
{-# INLINE runAztecsT_ #-}
instance (PrimMonad m) => Queryable (AztecsT cs m) E.Entity where
type QueryableAccess E.Entity = '[]
queryable = AztecsT $ do
w <- get
return . Query . map pure . E.entities $ W.worldEntities w
{-# INLINE queryable #-}
instance
( PrimMonad m,
Lookup (ComponentStorage m a a) (WorldComponents m cs),
Storage m (ComponentStorage m a)
) =>
Queryable (AztecsT cs m) (With a)
where
type QueryableAccess (With a) = '[With a]
queryable = AztecsT $ do
w <- get
withComponent <-
lift
. S.queryStorageR
. HS.lookup @(ComponentStorage m a a)
$ W.worldComponents w
return . fmap (const With) $ withComponent
{-# INLINE queryable #-}
instance
( PrimMonad m,
Lookup (ComponentStorage m a a) (WorldComponents m cs),
Storage m (ComponentStorage m a)
) =>
Queryable (AztecsT cs m) (Without a)
where
type QueryableAccess (Without a) = '[Without a]
queryable = AztecsT $ do
w <- get
(Query cs) <-
lift
. S.queryStorageR
. HS.lookup @(ComponentStorage m a a)
$ W.worldComponents w
let go m = case m of
Just v -> Nothing
Nothing -> Just Without
return . Query $ fmap go cs
{-# INLINE queryable #-}
instance
( PrimMonad m,
Lookup (ComponentStorage m a a) (WorldComponents m cs),
Storage (AztecsT cs m) (ComponentStorage m a)
) =>
Queryable (AztecsT cs m) (R a)
where
type QueryableAccess (R a) = '[Read a]
queryable = do
w <- AztecsT get
S.queryStorageR . HS.lookup @(ComponentStorage m a a) $ W.worldComponents w
{-# INLINE queryable #-}
instance
( PrimMonad m,
PrimState m ~ s,
Lookup (ComponentStorage m a a) (WorldComponents m cs),
Storage m (ComponentStorage m a)
) =>
Queryable (AztecsT cs m) (W (Commands (AztecsT cs) m) a)
where
type QueryableAccess (W (Commands (AztecsT cs) m) a) = '[Write a]
queryable = AztecsT $ do
w <- get
Query results <-
lift
. S.queryStorageW
. HS.lookup @(ComponentStorage m a a)
$ W.worldComponents w
let liftToCommands m = Commands $ (,pure ()) <$> m
go (W r wf mf) =
W
(liftToCommands r)
(liftToCommands . wf)
(liftToCommands . mf)
return . Query $ map (fmap go) results
{-# INLINE queryable #-}
-- Additional instance for direct AztecsT usage in scheduler
instance
( PrimMonad m,
PrimState m ~ s,
Lookup (ComponentStorage m a a) (WorldComponents m cs),
Storage m (ComponentStorage m a)
) =>
Queryable (AztecsT cs m) (W (AztecsT cs m) a)
where
type QueryableAccess (W (AztecsT cs m) a) = '[Write a]
queryable = AztecsT $ do
w <- get
Query results <-
lift
. S.queryStorageW
. HS.lookup @(ComponentStorage m a a)
$ W.worldComponents w
let liftToAztecs (W r wf mf) =
W
(AztecsT $ lift r)
(AztecsT . lift . wf)
(AztecsT . lift . mf)
return . Query $ map (fmap liftToAztecs) results
{-# INLINE queryable #-}