apecs 0.1.1.0 → 0.2.0.0
raw patch · 11 files changed
+558/−511 lines, 11 filesdep ~sdl2
Dependency ranges changed: sdl2
Files
- README.md +5/−2
- apecs.cabal +9/−6
- example/RTS.hs +1/−0
- src/Apecs.hs +22/−11
- src/Apecs/Core.hs +0/−377
- src/Apecs/Slice.hs +83/−0
- src/Apecs/Stores.hs +3/−3
- src/Apecs/System.hs +196/−0
- src/Apecs/Types.hs +232/−0
- src/Apecs/Util.hs +4/−109
- tutorials/RTS.md +3/−3
README.md view
@@ -20,7 +20,7 @@ import Apecs import Apecs.Stores import Apecs.Util-import Linear.V2+import Linear -- Component data definitions newtype Velocity = Velocity (V2 Double) deriving (Eq, Show)@@ -58,7 +58,7 @@ game :: System' () game = do -- Create new entities- newEntity (Position 0)+ ety <- newEntity (Position 0) -- Components can be composed using tuples newEntity (Position 0, Velocity 1) -- Tagging one as an enemy is a matter of adding the constructor@@ -68,6 +68,9 @@ liftIO$ putStrLn "Stepping velocities" -- rmap maps a pure function over all entities in its domain rmap $ \(Position p, Velocity v) -> Position (v+p)++ -- Set can be used to (over)write components+ set ety (Position 2, Enemy) -- Print the positions of all enemies cmapM_ $ \(Enemy, Position p) -> liftIO (print p)
apecs.cabal view
@@ -1,5 +1,5 @@ name: apecs-version: 0.1.1.0+version: 0.2.0.0 homepage: https://github.com/jonascarpay/apecs#readme license: BSD3 license-file: LICENSE@@ -17,10 +17,11 @@ src exposed-modules: Apecs,+ Apecs.Types, Apecs.Stores,+ Apecs.System,+ Apecs.Slice, Apecs.Util- other-modules:- Apecs.Core default-language: Haskell2010 build-depends:@@ -43,7 +44,6 @@ default-language: Haskell2010 ghc-options:- -Wall -fno-warn-unused-top-binds executable rts@@ -52,11 +52,14 @@ main-is: RTS.hs build-depends:- base, apecs, sdl2, random, linear+ base,+ apecs,+ sdl2 >= 2.3.0,+ random,+ linear default-language: Haskell2010 ghc-options:- -Wall -Odph -fno-warn-unused-top-binds
example/RTS.hs view
@@ -84,6 +84,7 @@ liftIO$ SDL.rendererDrawColor renderer $= if e then V4 255 255 255 255 else V4 255 0 0 255 SDL.drawPoint renderer (P (round <$> p)) + liftIO$ SDL.rendererDrawColor renderer $= V4 255 255 255 255 r <- readGlobal case r of Dragging a b -> SDL.drawRect renderer (Just $ SDL.Rectangle (P (round <$> a)) (round <$> b-a))
src/Apecs.hs view
@@ -1,30 +1,41 @@ {-# LANGUAGE FlexibleContexts #-} module Apecs (- -- Core- System(..), runSystem, runWith,- Component(..), Entity, Slice, Has(..), Safe(..), cast,+ -- * Types+ System(..),+ Component(..), Entity(..), Slice, Has(..), Safe(..), cast, - -- Initializable+ -- * Initializable initStoreWith, - -- HasMembers+ -- * HasMembers wrapper functions destroy, exists, owners, resetStore, - -- Store- get, set, setMaybe, modify,+ -- * Store wrapper functions+ get, set, setOrDelete, modify, cmap, cmapM, cmapM_, cimapM, cimapM_,- sliceSize,+ rmap', rmap, wmap, wmap', cmap', - -- GlobalRW++ -- * GlobalRW wrapper functions readGlobal, writeGlobal, modifyGlobal, - -- Query+ -- * Query slice, All(..), + -- * Other+ runSystem, runWith,++ -- All slice functions+ module SL,+ -- Reader asks, ask, liftIO, lift, ) where -import Apecs.Core as A import Control.Monad.Reader (asks, ask, liftIO, lift)++import Apecs.Types+import Apecs.System+import Apecs.Slice as SL+
− src/Apecs/Core.hs
@@ -1,377 +0,0 @@-{-# LANGUAGE Strict #-}-{-# LANGUAGE ScopedTypeVariables, RankNTypes #-}-{-# LANGUAGE TypeFamilies, TypeFamilyDependencies #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}-{-# LANGUAGE ConstraintKinds, KindSignatures #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--module Apecs.Core where--import Control.Monad.Reader-import Data.Traversable (for)-import qualified Data.Vector.Unboxed as U---- | A component is defined by the type of its storage--- The storage in turn supplies runtime types for the component.-class Initializable (Storage c) => Component c where- type Storage c = s | s -> c--type ID = Int-type IDVec = U.Vector ID-newtype System w a = System {unSystem :: ReaderT w IO a} deriving (Functor, Monad, Applicative, MonadIO)-newtype Slice c = Slice {unSlice :: U.Vector ID} deriving (Show, Monoid)-newtype Entity c = Entity {unEntity :: ID} deriving (Eq, Num)--{-# INLINE runSystem #-}-runSystem :: System w a -> w -> IO a-runSystem sys = runReaderT (unSystem sys)--{-# INLINE runWith #-}-runWith :: w -> System w a -> IO a-runWith = flip runSystem---- Storage type class hierarchy--- | Common for every storage. Represents a container that can be initialized-class Initializable s where- type InitArgs s- initStoreWith :: InitArgs s -> IO s---- | A store that is indexed by entities-class HasMembers s where- explDestroy :: s -> Int -> IO ()- explExists :: s -> Int -> IO Bool- explMembers :: s -> IO (U.Vector Int)-- {-# INLINE explReset #-}- explReset :: s -> IO ()- explReset s = do- sl <- explMembers s- U.mapM_ (explDestroy s) sl-- explImapM_ :: MonadIO m => s -> (Int -> m a) -> m ()- {-# INLINE explImapM_ #-}- explImapM_ s ma = liftIO (explMembers s) >>= Prelude.mapM_ ma . U.toList-- explImapM :: MonadIO m => s -> (Int -> m a) -> m [a]- {-# INLINE explImapM #-}- explImapM s ma = liftIO (explMembers s) >>= Prelude.mapM ma . U.toList--{-# INLINE imapM_ #-}--- | Monadically iterate a system over all entities that have that component.--- Note that writing to the store while iterating over it is undefined behaviour.-imapM_ :: forall w c. (Has w c, HasMembers (Storage c))- => (Entity c -> System w ()) -> System w ()-imapM_ sys = do s :: Storage c <- getStore- explImapM_ s (sys . Entity)--{-# INLINE imapM #-}--- | Monadically iterate a system over all entities that have that component.--- Note that writing to the store while iterating over it is undefined behaviour.-imapM :: forall w c a. (Has w c, HasMembers (Storage c))- => (Entity c -> System w a) -> System w [a]-imapM sys = do s :: Storage c <- getStore- explImapM s (sys . Entity)---- | Destroys the component @c@ for the given entity-{-# INLINE destroy #-}-destroy :: forall w c. (Has w c, HasMembers (Storage c)) => Entity c -> System w ()-destroy (Entity n) = do s :: Storage c <- getStore- liftIO$ explDestroy s n---- | Returns whether the given entity has component @c@--- For composite components, this indicates whether the component--- has all its constituents-{-# INLINE exists #-}-exists :: forall w c. (Has w c, HasMembers (Storage c)) => Entity c -> System w Bool-exists (Entity n) = do s :: Storage c <- getStore- liftIO$ explExists s n---- | A slice containing all entities with component @c@-{-# INLINE owners #-}-owners :: forall w c. (Has w c, HasMembers (Storage c)) => System w (Slice c)-owners = do s :: Storage c <- getStore- liftIO$ Slice <$> explMembers s--resetStore :: forall w c p. (Has w c, HasMembers (Storage c)) => p c -> System w ()-resetStore _ = do s :: Storage c <- getStore- liftIO$ explReset s---- | Class of storages that associates components with entities.-class HasMembers s => Store s where- type SafeRW s -- ^ Return type for safe reads/writes to the store- type Stores s -- ^ The type of components stored by this Store- -- | Unsafe index to the store. Undefined if the component does not exist- explGetUnsafe :: s -> Int -> IO (Stores s)- -- | Retrieves a component from the store- explGet :: s -> Int -> IO (SafeRW s)- -- | Writes a component- explSet :: s -> Int -> Stores s -> IO ()- -- | Either writes or deletes a component- explSetMaybe :: s -> Int -> SafeRW s -> IO ()-- -- | Modifies an element in the store.- {-# INLINE explModify #-}- explModify :: s -> Int -> (Stores s -> Stores s) -> IO ()- explModify s ety f = do etyExists <- explExists s ety- when etyExists $ explGetUnsafe s ety >>= explSet s ety . f-- -- | Maps over all elements of this store.- -- The default implementation can be replaced by an optimized one- explCmap :: s -> (Stores s -> Stores s) -> IO ()- {-# INLINE explCmap #-}- explCmap s f = do- sl <- explMembers s- U.forM_ sl $ \ety -> do- x :: Stores s <- explGetUnsafe s ety- explSet s ety (f x)-- explCmapM_ :: MonadIO m => s -> (Stores s -> m a) -> m ()- {-# INLINE explCmapM_ #-}- explCmapM_ s sys = do- sl <- liftIO$ explMembers s- U.forM_ sl $ \ety -> do x :: Stores s <- liftIO$ explGetUnsafe s ety- sys x-- explCimapM_ :: MonadIO m => s -> ((Int, Stores s) -> m a) -> m ()- {-# INLINE explCimapM_ #-}- explCimapM_ s sys = do- sl <- liftIO$ explMembers s- U.forM_ sl $ \ety -> do x :: Stores s <- liftIO$ explGetUnsafe s ety- sys (ety,x)-- explCmapM :: MonadIO m => s -> (Stores s -> m a) -> m [a]- {-# INLINE explCmapM #-}- explCmapM s sys = do- sl <- liftIO$ explMembers s- for (U.toList sl) $ \ety -> do- x :: Stores s <- liftIO$ explGetUnsafe s ety- sys x-- explCimapM :: MonadIO m => s -> ((Int, Stores s) -> m a) -> m [a]- {-# INLINE explCimapM #-}- explCimapM s sys = do- sl <- liftIO$ explMembers s- for (U.toList sl) $ \ety -> do- x :: Stores s <- liftIO$ explGetUnsafe s ety- sys (ety,x)---- | A constraint that indicates that the runtime representation of @c@ is @c@-type Runtime c = Stores (Storage c)-type IsRuntime c = (Store (Storage c), Runtime c ~ c)-newtype Safe c = Safe {getSafe :: (SafeRW (Storage c))}---- Setting/Getting-{-# INLINE get #-}-get :: forall w c. (Store (Storage c), Has w c) => Entity c -> System w (Safe c)-get (Entity ety) = do s :: Storage c <- getStore- liftIO$ Safe <$> explGet s ety--{-# INLINE set #-}-set :: forall w c e. (Store (Storage c), Stores (Storage c) ~ c, Has w c) => Entity e -> c -> System w ()-set (Entity ety) x = do- s :: Storage c <- getStore- liftIO$ explSet s ety x--{-# INLINE modify #-}-modify :: forall w c. (IsRuntime c, Has w c) => Entity c -> (c -> c) -> System w ()-modify (Entity ety) f = do- s :: Storage c <- getStore- liftIO$ explModify s ety f--setMaybe :: forall w c. (IsRuntime c, Has w c) => Entity c -> Safe c -> System w ()-setMaybe (Entity ety) (Safe c) = do- s :: Storage c <- getStore- liftIO$ explSetMaybe s ety c--{-# INLINE cmap #-}-cmap :: forall world c. (IsRuntime c, Has world c) => (c -> c) -> System world ()-cmap f = do s :: Storage c <- getStore- liftIO$ explCmap s f--{-# INLINE cmapM_ #-}-cmapM_ :: forall w c. (Has w c, IsRuntime c)- => (c -> System w ()) -> System w ()-cmapM_ sys = do s :: Storage c <- getStore- explCmapM_ s sys--{-# INLINE cimapM_ #-}-cimapM_ :: forall w c. (Has w c, IsRuntime c)- => ((Entity c, c) -> System w ()) -> System w ()-cimapM_ sys = do s :: Storage c <- getStore- explCimapM_ s (\(e,c) -> sys (Entity e,c))--{-# INLINE cmapM #-}-cmapM :: forall w c a. (Has w c, IsRuntime c)- => (c -> System w a) -> System w [a]-cmapM sys = do s :: Storage c <- getStore- explCmapM s sys--{-# INLINE cimapM #-}-cimapM :: forall w c a. (Has w c, IsRuntime c)- => ((Entity c, c) -> System w a) -> System w [a]-cimapM sys = do s :: Storage c <- getStore- explCimapM s (\(e,c) -> sys (Entity e,c))---- | Class of storages for global values-class GlobalRW s c where- {-# MINIMAL explGlobalRead, explGlobalWrite #-}- explGlobalRead :: s -> IO c- explGlobalWrite :: s -> c -> IO ()-- {-# INLINE explGlobalModify #-}- explGlobalModify :: s -> (c -> c) -> IO ()- explGlobalModify s f = do r <- explGlobalRead s- explGlobalWrite s (f r)--{-# INLINE readGlobal #-}-readGlobal :: forall w c. (Has w c, GlobalRW (Storage c) c) => System w c-readGlobal = do s :: Storage c <- getStore- liftIO$ explGlobalRead s--{-# INLINE writeGlobal #-}-writeGlobal :: forall w c. (Has w c, GlobalRW (Storage c) c) => c -> System w ()-writeGlobal c = do s :: Storage c <- getStore- liftIO$ explGlobalWrite s c--{-# INLINE modifyGlobal #-}-modifyGlobal :: forall w c. (Has w c, GlobalRW (Storage c) c) => (c -> c) -> System w ()-modifyGlobal f = do s :: Storage c <- getStore- liftIO$ explGlobalModify s f---- Query-class Query q s where- explSlice :: s -> q -> IO (U.Vector Int)--{-# INLINE slice #-}-slice :: forall w c q. (Query q (Storage c), Has w c) => q -> System w (Slice c)-slice q = do- s :: Storage c <- getStore- liftIO$ Slice <$> explSlice s q--data All = All-instance HasMembers s => Query All s where- {-# INLINE explSlice #-}- explSlice s _ = explMembers s--class Cast a b where cast :: a -> b-instance Cast (Entity a) (Entity b) where- {-# INLINE cast #-}- cast (Entity ety) = Entity ety-instance Cast (Slice a) (Slice b) where- {-# INLINE cast #-}- cast (Slice vec) = Slice vec--class Component c => Has w c where- getStore :: System w (Storage c)--instance Show (Entity c) where- show (Entity e) = "Entity " ++ show e--{-# INLINE sliceFoldM_ #-}-sliceFoldM_ :: (a -> Entity c -> System w a) -> a -> Slice b -> System w ()-sliceFoldM_ f seed (Slice sl) = U.foldM'_ ((.Entity) . f) seed sl---- | Gets the size of a slice (O(n))-{-# INLINE sliceSize #-}-sliceSize :: Slice a -> Int-sliceSize (Slice vec) = U.length vec---- | Tests whether a slice is empty (O(1))-{-# INLINE sliceNull #-}-sliceNull :: Slice a -> Bool-sliceNull (Slice vec) = U.null vec---- | Construct a slice from a list of IDs-{-# INLINE sliceFromList #-}-sliceFromList :: [ID] -> Slice a-sliceFromList = Slice . U.fromList---- | Monadically filter a slice-{-# INLINE sliceFilterM #-}-sliceFilterM :: (Entity c -> System w Bool) -> Slice c -> System w (Slice c)-sliceFilterM fm (Slice vec) = Slice <$> U.filterM (fm . Entity) vec--{-# INLINE sliceConcat #-}-sliceConcat :: Slice a -> Slice b -> Slice c-sliceConcat (Slice a) (Slice b) = Slice (a U.++ b)----- Tuple instances--- (,)-instance (Component a, Component b) => Component (a,b) where- type Storage (a, b) = (Storage a, Storage b)-instance (Has w a, Has w b) => Has w (a,b) where- {-# INLINE getStore #-}- getStore = (,) <$> getStore <*> getStore--instance (Initializable a, Initializable b) => Initializable (a,b) where- type InitArgs (a, b) = (InitArgs a, InitArgs b)- initStoreWith (aa, ab) = (,) <$> initStoreWith aa <*> initStoreWith ab--instance (HasMembers a, HasMembers b) => HasMembers (a,b) where- explMembers (sa,sb) = explMembers sa >>= U.filterM (explExists sb)- explReset (sa,sb) = explReset sa >> explReset sb- explDestroy (sa,sb) ety = explDestroy sa ety >> explDestroy sb ety- explExists (sa,sb) ety = (&&) <$> explExists sa ety <*> explExists sb ety- {-# INLINE explMembers #-}- {-# INLINE explReset #-}- {-# INLINE explDestroy #-}- {-# INLINE explExists #-}--instance (Store a, Store b) => Store (a, b) where- type SafeRW (a, b) = (SafeRW a, SafeRW b)- type Stores (a, b) = (Stores a, Stores b)- explGetUnsafe (sa,sb) ety = (,) <$> explGetUnsafe sa ety <*> explGetUnsafe sb ety- explGet (sa,sb) ety = (,) <$> explGet sa ety <*> explGet sb ety- explSet (sa,sb) ety (wa,wb) = explSet sa ety wa >> explSet sb ety wb- explSetMaybe (sa,sb) ety (wa,wb) = explSetMaybe sa ety wa >> explSetMaybe sb ety wb- {-# INLINE explGetUnsafe #-}- {-# INLINE explGet #-}- {-# INLINE explSet #-}- {-# INLINE explSetMaybe #-}--instance (GlobalRW a ca, GlobalRW b cb) => GlobalRW (a,b) (ca,cb) where- explGlobalRead (sa,sb) = (,) <$> explGlobalRead sa <*> explGlobalRead sb- explGlobalWrite (sa,sb) (wa,wb) = explGlobalWrite sa wa >> explGlobalWrite sb wb- {-# INLINE explGlobalRead #-}- {-# INLINE explGlobalWrite #-}---- (,,)-instance (Component a, Component b, Component c) => Component (a,b,c) where- type Storage (a, b, c) = (Storage a, Storage b, Storage c)-instance (Has w a, Has w b, Has w c) => Has w (a,b,c) where- {-# INLINE getStore #-}- getStore = (,,) <$> getStore <*> getStore <*> getStore--instance (Initializable a, Initializable b, Initializable c) => Initializable (a,b,c) where- type InitArgs (a, b, c) = (InitArgs a, InitArgs b, InitArgs c)- initStoreWith (aa, ab, ac) = (,,) <$> initStoreWith aa <*> initStoreWith ab <*> initStoreWith ac--instance (HasMembers a, HasMembers b, HasMembers c) => HasMembers (a,b,c) where- explMembers (sa,sb,sc) = explMembers sa >>= U.filterM (explExists sb) >>= U.filterM (explExists sc)- explReset (sa,sb,sc) = explReset sa >> explReset sb >> explReset sc- explDestroy (sa,sb,sc) ety = explDestroy sa ety >> explDestroy sb ety >> explDestroy sc ety- explExists (sa,sb,sc) ety = and <$> sequence [explExists sa ety, explExists sb ety, explExists sc ety]- {-# INLINE explMembers #-}- {-# INLINE explReset #-}- {-# INLINE explDestroy #-}- {-# INLINE explExists #-}--instance (Store a, Store b, Store c) => Store (a, b, c) where- type SafeRW (a, b, c) = (SafeRW a, SafeRW b, SafeRW c)- type Stores (a, b, c) = (Stores a, Stores b, Stores c)- explGetUnsafe (sa,sb,sc) ety = (,,) <$> explGetUnsafe sa ety <*> explGetUnsafe sb ety <*> explGetUnsafe sc ety- explGet (sa,sb,sc) ety = (,,) <$> explGet sa ety <*> explGet sb ety <*> explGet sc ety- explSet (sa,sb,sc) ety (wa,wb,wc) = explSet sa ety wa >> explSet sb ety wb >> explSet sc ety wc- explSetMaybe (sa,sb,sc) ety (wa,wb,wc) = explSetMaybe sa ety wa >> explSetMaybe sb ety wb >> explSetMaybe sc ety wc- {-# INLINE explGetUnsafe #-}- {-# INLINE explGet #-}- {-# INLINE explSet #-}- {-# INLINE explSetMaybe #-}--instance (GlobalRW a ca, GlobalRW b cb, GlobalRW c cc) => GlobalRW (a,b,c) (ca,cb,cc) where- explGlobalRead (sa,sb,sc) = (,,) <$> explGlobalRead sa <*> explGlobalRead sb <*> explGlobalRead sc- explGlobalWrite (sa,sb,sc) (wa,wb,wc) = explGlobalWrite sa wa >> explGlobalWrite sb wb >> explGlobalWrite sc wc- {-# INLINE explGlobalRead #-}- {-# INLINE explGlobalWrite #-}
+ src/Apecs/Slice.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}++module Apecs.Slice where++import qualified Data.Vector.Unboxed as U+import Data.Traversable (for)+import Control.Monad.IO.Class++import Apecs.Types++{-# INLINE sliceFoldM_ #-}+sliceFoldM_ :: (a -> Entity c -> System w a) -> a -> Slice b -> System w ()+sliceFoldM_ f seed (Slice sl) = U.foldM'_ ((.Entity) . f) seed sl++-- | Gets the size of a slice (O(n))+{-# INLINE sliceSize #-}+sliceSize :: Slice a -> Int+sliceSize (Slice vec) = U.length vec++-- | Tests whether a slice is empty (O(1))+{-# INLINE sliceNull #-}+sliceNull :: Slice a -> Bool+sliceNull (Slice vec) = U.null vec++-- | Construct a slice from a list of IDs+{-# INLINE sliceFromList #-}+sliceFromList :: [Int] -> Slice a+sliceFromList = Slice . U.fromList++-- | Monadically filter a slice+{-# INLINE sliceFilterM #-}+sliceFilterM :: (Entity c -> System w Bool) -> Slice c -> System w (Slice c)+sliceFilterM fm (Slice vec) = Slice <$> U.filterM (fm . Entity) vec++{-# INLINE sliceConcat #-}+sliceConcat :: Slice a -> Slice b -> Slice c+sliceConcat (Slice a) (Slice b) = Slice (a U.++ b)+-- Slice traversal+{-# INLINE sliceForM_ #-}+sliceForM_ :: Monad m => Slice c -> (Entity c -> m b) -> m ()+sliceForM_ (Slice vec) ma = U.forM_ vec (ma . Entity)++{-# INLINE sliceForM #-}+sliceForM :: Monad m => Slice c -> (Entity c -> m a) -> m [a]+sliceForM (Slice vec) ma = traverse (ma . Entity) (U.toList vec)++{-# INLINE sliceForMC #-}+sliceForMC :: forall w c a. (Store (Storage c), Has w c) => Slice c -> ((Entity c,Safe c) -> System w a) -> System w [a]+sliceForMC (Slice vec) sys = do+ s :: Storage c <- getStore+ for (U.toList vec) $ \e -> do+ r <- liftIO$ explGet s e+ sys (Entity e, Safe r)++{-# INLINE sliceForMC_ #-}+sliceForMC_ :: forall w c a. (Store (Storage c), Has w c) => Slice c -> ((Entity c,Safe c) -> System w a) -> System w ()+sliceForMC_ (Slice vec) sys = do+ s :: Storage c <- getStore+ U.forM_ vec $ \e -> do+ r <- liftIO$ explGet s e+ sys (Entity e, Safe r)++{-# INLINE sliceMapM_ #-}+sliceMapM_ :: Monad m => (Entity c -> m a) -> Slice c -> m ()+sliceMapM_ ma (Slice vec) = U.mapM_ (ma . Entity) vec++{-# INLINE sliceMapM #-}+sliceMapM :: Monad m => (Entity c -> m a) -> Slice c -> m [a]+sliceMapM ma (Slice vec) = traverse (ma . Entity) (U.toList vec)++{-# INLINE sliceMapMC #-}+sliceMapMC :: forall w c a. (Store (Storage c), Has w c) => ((Entity c,Safe c) -> System w a) -> Slice c -> System w [a]+sliceMapMC sys (Slice vec) = do+ s :: Storage c <- getStore+ for (U.toList vec) $ \e -> do+ r <- liftIO$ explGet s e+ sys (Entity e, Safe r)++{-# INLINE sliceMapMC_ #-}+sliceMapMC_ :: forall w c a. (Store (Storage c), Has w c) => ((Entity c, Safe c) -> System w a) -> Slice c -> System w ()+sliceMapMC_ sys vec = sliceForMC_ vec sys+
src/Apecs/Stores.hs view
@@ -9,7 +9,7 @@ module Apecs.Stores ( Map, Set, Flag(..), Cache,- Global, readGlobal, writeGlobal,+ Global, IndexTable, ToIndex(..), ByIndex(..), ByComponent(..), ) where @@ -25,7 +25,7 @@ import GHC.TypeLits import Data.Proxy -import Apecs.Core+import Apecs.Types newtype Map c = Map (IORef (M.IntMap c)) instance Initializable (Map c) where@@ -319,7 +319,7 @@ let indexNew = toIndex x mc <- explGet s ety case mc of- Nothing -> do VM.modify tab (S.insert ety) indexNew+ Nothing -> VM.modify tab (S.insert ety) indexNew Just c -> do let indexOld = toIndex c unless (indexOld == indexNew) $ do VM.modify tab (S.delete ety) indexOld
+ src/Apecs/System.hs view
@@ -0,0 +1,196 @@+{-# LANGUAGE Strict #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies, TypeFamilyDependencies #-}+{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses #-}+{-# LANGUAGE ConstraintKinds #-}++module Apecs.System where++import Control.Monad.Reader+import qualified Data.Vector.Unboxed as U++import Apecs.Types++-- | Run a system with a game world+{-# INLINE runSystem #-}+runSystem :: System w a -> w -> IO a+runSystem sys = runReaderT (unSystem sys)++-- | Run a system with a game world+{-# INLINE runWith #-}+runWith :: w -> System w a -> IO a+runWith = flip runSystem++-- | A slice containing all entities with component @c@+{-# INLINE owners #-}+owners :: forall w c. (Has w c, HasMembers (Storage c)) => System w (Slice c)+owners = do s :: Storage c <- getStore+ liftIO$ Slice <$> explMembers s++-- | Returns whether the given entity has component @c@+-- For composite components, this indicates whether the component+-- has all its constituents+{-# INLINE exists #-}+exists :: forall w c. (Has w c, HasMembers (Storage c)) => Entity c -> System w Bool+exists (Entity n) = do s :: Storage c <- getStore+ liftIO$ explExists s n++-- | Destroys the component @c@ for the given entity+{-# INLINE destroy #-}+destroy :: forall w c. (Has w c, HasMembers (Storage c)) => Entity c -> System w ()+destroy (Entity n) = do s :: Storage c <- getStore+ liftIO$ explDestroy s n++-- | Removes all components. Equivalent to manually iterating and deleting, but usually optimized.+resetStore :: forall w c p. (Has w c, HasMembers (Storage c)) => p c -> System w ()+resetStore _ = do s :: Storage c <- getStore+ liftIO$ explReset s++-- Setting/Getting+-- | Gets the component for a given entity.+-- This is a safe access, because the entity might not have the requested components.+{-# INLINE get #-}+get :: forall w c. (Store (Storage c), Has w c) => Entity c -> System w (Safe c)+get (Entity ety) = do s :: Storage c <- getStore+ liftIO$ Safe <$> explGet s ety++-- | Writes a component to a given entity. Will overwrite existing components.+{-# INLINE set #-}+set :: forall w c e. (Store (Storage c), Stores (Storage c) ~ c, Has w c) => Entity e -> c -> System w ()+set (Entity ety) x = do+ s :: Storage c <- getStore+ liftIO$ explSet s ety x++-- | Same as @set@, but uses Safe to possibly delete a component+setOrDelete :: forall w c. (IsRuntime c, Has w c) => Entity c -> Safe c -> System w ()+setOrDelete (Entity ety) (Safe c) = do+ s :: Storage c <- getStore+ liftIO$ explSetMaybe s ety c++-- | Applies a function if possible. Equivalent to reading, mapping, and writing, but stores can provide optimized implementations.+{-# INLINE modify #-}+modify :: forall w c. (IsRuntime c, Has w c) => Entity c -> (c -> c) -> System w ()+modify (Entity ety) f = do+ s :: Storage c <- getStore+ liftIO$ explModify s ety f++{-# INLINE imapM_ #-}+-- | Monadically iterate a system over all entities that have that component.+-- Note that writing to the store while iterating over it is undefined behaviour.+imapM_ :: forall w c. (Has w c, HasMembers (Storage c))+ => (Entity c -> System w ()) -> System w ()+imapM_ sys = do s :: Storage c <- getStore+ explImapM_ s (sys . Entity)++{-# INLINE imapM #-}+-- | Monadically iterate a system over all entities that have that component.+-- Note that writing to the store while iterating over it is undefined behaviour.+imapM :: forall w c a. (Has w c, HasMembers (Storage c))+ => (Entity c -> System w a) -> System w [a]+imapM sys = do s :: Storage c <- getStore+ explImapM s (sys . Entity)++{-# INLINE cmap #-}+cmap :: forall world c. (IsRuntime c, Has world c) => (c -> c) -> System world ()+cmap f = do s :: Storage c <- getStore+ liftIO$ explCmap s f++{-# INLINE cmapM_ #-}+cmapM_ :: forall w c. (Has w c, IsRuntime c)+ => (c -> System w ()) -> System w ()+cmapM_ sys = do s :: Storage c <- getStore+ explCmapM_ s sys++{-# INLINE cimapM_ #-}+cimapM_ :: forall w c. (Has w c, IsRuntime c)+ => ((Entity c, c) -> System w ()) -> System w ()+cimapM_ sys = do s :: Storage c <- getStore+ explCimapM_ s (\(e,c) -> sys (Entity e,c))++{-# INLINE cmapM #-}+cmapM :: forall w c a. (Has w c, IsRuntime c)+ => (c -> System w a) -> System w [a]+cmapM sys = do s :: Storage c <- getStore+ explCmapM s sys++{-# INLINE cimapM #-}+cimapM :: forall w c a. (Has w c, IsRuntime c)+ => ((Entity c, c) -> System w a) -> System w [a]+cimapM sys = do s :: Storage c <- getStore+ explCimapM s (\(e,c) -> sys (Entity e,c))++cmap' :: forall world c. (Has world c, IsRuntime c)+ => (c -> Safe c) -> System world ()+cmap' f = do s :: Storage c <- getStore+ liftIO$ do sl <- explMembers s+ U.forM_ sl $ \e -> do+ r <- explGetUnsafe s e+ explSetMaybe s e (getSafe . f $ r)++-- | Maps a function over all entities with a @r@, and writes their @w@+{-# INLINE rmap #-}+rmap :: forall world r w. (Has world w, Has world r, IsRuntime w, IsRuntime r)+ => (r -> w) -> System world ()+rmap f = do sr :: Storage r <- getStore+ sc :: Storage w <- getStore+ liftIO$ do sl <- explMembers sr+ U.forM_ sl $ \ e -> do+ r <- explGetUnsafe sr e+ explSet sc e (f r)++-- | Maps a function over all entities with a @r@, and writes or deletes their @w@+{-# INLINE rmap' #-}+rmap' :: forall world r w. (Has world w, Has world r, Store (Storage w), IsRuntime r)+ => (r -> Safe w) -> System world ()+rmap' f = do sr :: Storage r <- getStore+ sw :: Storage w <- getStore+ liftIO$ do sl <- explMembers sr+ U.forM_ sl $ \ e -> do+ r <- explGetUnsafe sr e+ explSetMaybe sw e (getSafe $ f r)++-- | For all entities with a @w@, this map reads their @r@ and writes their @w@+{-# INLINE wmap #-}+wmap :: forall world r w. (Has world w, Has world r, IsRuntime w, IsRuntime r)+ => (Safe r -> w) -> System world ()+wmap f = do sr :: Storage r <- getStore+ sw :: Storage w <- getStore+ liftIO$ do sl <- explMembers sr+ U.forM_ sl $ \ e -> do+ r <- explGet sr e+ explSet sw e (f . Safe $ r)++-- | For all entities with a @w@, this map reads their @r@ and writes or deletes their @w@+{-# INLINE wmap' #-}+wmap' :: forall world r w. (Has world w, Has world r, Store (Storage w), IsRuntime r)+ => (Safe r -> Safe w) -> System world ()+wmap' f = do sr :: Storage r <- getStore+ sw :: Storage w <- getStore+ liftIO$ do sl <- explMembers sr+ U.forM_ sl $ \ e -> do+ r <- explGet sr e+ explSetMaybe sw e (getSafe . f . Safe $ r)+++{-# INLINE slice #-}+slice :: forall w c q. (Query q (Storage c), Has w c) => q -> System w (Slice c)+slice q = do+ s :: Storage c <- getStore+ liftIO$ Slice <$> explSlice s q++{-# INLINE readGlobal #-}+readGlobal :: forall w c. (Has w c, GlobalRW (Storage c) c) => System w c+readGlobal = do s :: Storage c <- getStore+ liftIO$ explGlobalRead s++{-# INLINE writeGlobal #-}+writeGlobal :: forall w c. (Has w c, GlobalRW (Storage c) c) => c -> System w ()+writeGlobal c = do s :: Storage c <- getStore+ liftIO$ explGlobalWrite s c++{-# INLINE modifyGlobal #-}+modifyGlobal :: forall w c. (Has w c, GlobalRW (Storage c) c) => (c -> c) -> System w ()+modifyGlobal f = do s :: Storage c <- getStore+ liftIO$ explGlobalModify s f+
+ src/Apecs/Types.hs view
@@ -0,0 +1,232 @@+{-# LANGUAGE ScopedTypeVariables, RankNTypes #-}+{-# LANGUAGE TypeFamilies, TypeFamilyDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module Apecs.Types where++import Control.Monad.Reader+import Data.Traversable (for)+import qualified Data.Vector.Unboxed as U++-- | An Entity is really just an Int. The type variable is used to keep track of reads and writes, but can be freely cast.+newtype Entity c = Entity {unEntity :: Int} deriving (Eq, Ord, Show)++-- | A slice is a list of entities, represented by a Data.Unbox.Vector of Ints.+newtype Slice c = Slice {unSlice :: U.Vector Int} deriving (Show, Monoid)++-- | A system is a newtype around `ReaderT w IO a`, where `w` is the game world variable.+newtype System w a = System {unSystem :: ReaderT w IO a} deriving (Functor, Monad, Applicative, MonadIO)++-- | A component is defined by the type of its storage+-- The storage in turn supplies runtime types for the component.+-- For the component to be valid, its Storage must be in instance of Initializable.+class Initializable (Storage c) => Component c where+ type Storage c = s | s -> c++-- | A world `Has` a component if it can produce its Storage+class Component c => Has w c where+ getStore :: System w (Storage c)+++-- Storage types+-- | Common for every storage. Represents a container that can be initialized.+class Initializable s where+ type InitArgs s+ initStoreWith :: InitArgs s -> IO s++-- | A store that is indexed by entities.+class HasMembers s where+ -- | Destroys the component for the given index.+ explDestroy :: s -> Int -> IO ()+ -- | Returns whether there is a component for the given index+ explExists :: s -> Int -> IO Bool+ -- | Returns an unboxed vector of member indices+ explMembers :: s -> IO (U.Vector Int)++ -- | Removes all components. Default implementation iterates over members and calls explDestroy.+ {-# INLINE explReset #-}+ explReset :: s -> IO ()+ explReset s = do+ sl <- explMembers s+ U.mapM_ (explDestroy s) sl++ -- | Monadically iterates over member indices+ explImapM_ :: MonadIO m => s -> (Int -> m a) -> m ()+ {-# INLINE explImapM_ #-}+ explImapM_ s ma = liftIO (explMembers s) >>= mapM_ ma . U.toList++ -- | Monadically iterates over member indices+ explImapM :: MonadIO m => s -> (Int -> m a) -> m [a]+ {-# INLINE explImapM #-}+ explImapM s ma = liftIO (explMembers s) >>= mapM ma . U.toList++-- | Represents a safe access to @c@. A safe access is either a read that might fail, or a write that might delete.+newtype Safe c = Safe {getSafe :: SafeRW (Storage c)}++-- | Class of storages that associates components with entities.+class HasMembers s => Store s where+ type SafeRW s -- ^ Return type for safe reads/writes to the store+ type Stores s -- ^ The type of components stored by this Store+ -- | Unsafe index to the store. Undefined if the component does not exist+ explGetUnsafe :: s -> Int -> IO (Stores s)+ -- | Retrieves a component from the store+ explGet :: s -> Int -> IO (SafeRW s)+ -- | Writes a component+ explSet :: s -> Int -> Stores s -> IO ()+ -- | Either writes or deletes a component+ explSetMaybe :: s -> Int -> SafeRW s -> IO ()++ -- | Modifies an element in the store.+ {-# INLINE explModify #-}+ explModify :: s -> Int -> (Stores s -> Stores s) -> IO ()+ explModify s ety f = do etyExists <- explExists s ety+ when etyExists $ explGetUnsafe s ety >>= explSet s ety . f++ -- | Maps over all elements of this store.+ -- The default implementation can be replaced by an optimized one+ explCmap :: s -> (Stores s -> Stores s) -> IO ()+ {-# INLINE explCmap #-}+ explCmap s f = explMembers s >>= U.mapM_ (\ety -> explModify s ety f)++ explCmapM_ :: MonadIO m => s -> (Stores s -> m a) -> m ()+ {-# INLINE explCmapM_ #-}+ explCmapM_ s sys = do+ sl <- liftIO$ explMembers s+ U.forM_ sl $ \ety -> do x :: Stores s <- liftIO$ explGetUnsafe s ety+ sys x++ explCimapM_ :: MonadIO m => s -> ((Int, Stores s) -> m a) -> m ()+ {-# INLINE explCimapM_ #-}+ explCimapM_ s sys = do+ sl <- liftIO$ explMembers s+ U.forM_ sl $ \ety -> do x :: Stores s <- liftIO$ explGetUnsafe s ety+ sys (ety,x)++ explCmapM :: MonadIO m => s -> (Stores s -> m a) -> m [a]+ {-# INLINE explCmapM #-}+ explCmapM s sys = do+ sl <- liftIO$ explMembers s+ for (U.toList sl) $ \ety -> do+ x :: Stores s <- liftIO$ explGetUnsafe s ety+ sys x++ explCimapM :: MonadIO m => s -> ((Int, Stores s) -> m a) -> m [a]+ {-# INLINE explCimapM #-}+ explCimapM s sys = do+ sl <- liftIO$ explMembers s+ for (U.toList sl) $ \ety -> do+ x :: Stores s <- liftIO$ explGetUnsafe s ety+ sys (ety,x)++type Runtime c = Stores (Storage c)+-- | A constraint that indicates that the runtime representation of @c@ is @c@+type IsRuntime c = (Store (Storage c), Runtime c ~ c)+-- | Class of storages for global values+class GlobalRW s c where+ {-# MINIMAL explGlobalRead, explGlobalWrite #-}+ explGlobalRead :: s -> IO c+ explGlobalWrite :: s -> c -> IO ()++ {-# INLINE explGlobalModify #-}+ explGlobalModify :: s -> (c -> c) -> IO ()+ explGlobalModify s f = do r <- explGlobalRead s+ explGlobalWrite s (f r)++-- Query+class Query q s where+ explSlice :: s -> q -> IO (U.Vector Int)++data All = All+instance HasMembers s => Query All s where+ {-# INLINE explSlice #-}+ explSlice s _ = explMembers s++class Cast a b where cast :: a -> b+instance Cast (Entity a) (Entity b) where+ {-# INLINE cast #-}+ cast (Entity ety) = Entity ety+instance Cast (Slice a) (Slice b) where+ {-# INLINE cast #-}+ cast (Slice vec) = Slice vec++-- Tuple Instances+-- (,)+instance (Component a, Component b) => Component (a,b) where+ type Storage (a, b) = (Storage a, Storage b)+instance (Has w a, Has w b) => Has w (a,b) where+ {-# INLINE getStore #-}+ getStore = (,) <$> getStore <*> getStore++instance (Initializable a, Initializable b) => Initializable (a,b) where+ type InitArgs (a, b) = (InitArgs a, InitArgs b)+ initStoreWith (aa, ab) = (,) <$> initStoreWith aa <*> initStoreWith ab++instance (HasMembers a, HasMembers b) => HasMembers (a,b) where+ explMembers (sa,sb) = explMembers sa >>= U.filterM (explExists sb)+ explReset (sa,sb) = explReset sa >> explReset sb+ explDestroy (sa,sb) ety = explDestroy sa ety >> explDestroy sb ety+ explExists (sa,sb) ety = (&&) <$> explExists sa ety <*> explExists sb ety+ {-# INLINE explMembers #-}+ {-# INLINE explReset #-}+ {-# INLINE explDestroy #-}+ {-# INLINE explExists #-}++instance (Store a, Store b) => Store (a, b) where+ type SafeRW (a, b) = (SafeRW a, SafeRW b)+ type Stores (a, b) = (Stores a, Stores b)+ explGetUnsafe (sa,sb) ety = (,) <$> explGetUnsafe sa ety <*> explGetUnsafe sb ety+ explGet (sa,sb) ety = (,) <$> explGet sa ety <*> explGet sb ety+ explSet (sa,sb) ety (wa,wb) = explSet sa ety wa >> explSet sb ety wb+ explSetMaybe (sa,sb) ety (wa,wb) = explSetMaybe sa ety wa >> explSetMaybe sb ety wb+ {-# INLINE explGetUnsafe #-}+ {-# INLINE explGet #-}+ {-# INLINE explSet #-}+ {-# INLINE explSetMaybe #-}++instance (GlobalRW a ca, GlobalRW b cb) => GlobalRW (a,b) (ca,cb) where+ explGlobalRead (sa,sb) = (,) <$> explGlobalRead sa <*> explGlobalRead sb+ explGlobalWrite (sa,sb) (wa,wb) = explGlobalWrite sa wa >> explGlobalWrite sb wb+ {-# INLINE explGlobalRead #-}+ {-# INLINE explGlobalWrite #-}++-- (,,)+instance (Component a, Component b, Component c) => Component (a,b,c) where+ type Storage (a, b, c) = (Storage a, Storage b, Storage c)+instance (Has w a, Has w b, Has w c) => Has w (a,b,c) where+ {-# INLINE getStore #-}+ getStore = (,,) <$> getStore <*> getStore <*> getStore++instance (Initializable a, Initializable b, Initializable c) => Initializable (a,b,c) where+ type InitArgs (a, b, c) = (InitArgs a, InitArgs b, InitArgs c)+ initStoreWith (aa, ab, ac) = (,,) <$> initStoreWith aa <*> initStoreWith ab <*> initStoreWith ac++instance (HasMembers a, HasMembers b, HasMembers c) => HasMembers (a,b,c) where+ explMembers (sa,sb,sc) = explMembers sa >>= U.filterM (explExists sb) >>= U.filterM (explExists sc)+ explReset (sa,sb,sc) = explReset sa >> explReset sb >> explReset sc+ explDestroy (sa,sb,sc) ety = explDestroy sa ety >> explDestroy sb ety >> explDestroy sc ety+ explExists (sa,sb,sc) ety = and <$> sequence [explExists sa ety, explExists sb ety, explExists sc ety]+ {-# INLINE explMembers #-}+ {-# INLINE explReset #-}+ {-# INLINE explDestroy #-}+ {-# INLINE explExists #-}++instance (Store a, Store b, Store c) => Store (a, b, c) where+ type SafeRW (a, b, c) = (SafeRW a, SafeRW b, SafeRW c)+ type Stores (a, b, c) = (Stores a, Stores b, Stores c)+ explGetUnsafe (sa,sb,sc) ety = (,,) <$> explGetUnsafe sa ety <*> explGetUnsafe sb ety <*> explGetUnsafe sc ety+ explGet (sa,sb,sc) ety = (,,) <$> explGet sa ety <*> explGet sb ety <*> explGet sc ety+ explSet (sa,sb,sc) ety (wa,wb,wc) = explSet sa ety wa >> explSet sb ety wb >> explSet sc ety wc+ explSetMaybe (sa,sb,sc) ety (wa,wb,wc) = explSetMaybe sa ety wa >> explSetMaybe sb ety wb >> explSetMaybe sc ety wc+ {-# INLINE explGetUnsafe #-}+ {-# INLINE explGet #-}+ {-# INLINE explSet #-}+ {-# INLINE explSetMaybe #-}++instance (GlobalRW a ca, GlobalRW b cb, GlobalRW c cc) => GlobalRW (a,b,c) (ca,cb,cc) where+ explGlobalRead (sa,sb,sc) = (,,) <$> explGlobalRead sa <*> explGlobalRead sb <*> explGlobalRead sc+ explGlobalWrite (sa,sb,sc) (wa,wb,wc) = explGlobalWrite sa wa >> explGlobalWrite sb wb >> explGlobalWrite sc wc+ {-# INLINE explGlobalRead #-}+ {-# INLINE explGlobalWrite #-}
src/Apecs/Util.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE Strict, ScopedTypeVariables, TypeFamilies #-} {-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-} module Apecs.Util ( -- * Utility@@ -11,13 +12,6 @@ -- * Spatial hashing quantize, flatten, region, inbounds, - -- * Optimized maps- rmap', rmap, wmap, wmap', cmap',-- -- * Slice interation- sliceForM, sliceForM_, sliceForMC, sliceForMC_,- sliceMapM, sliceMapM_, sliceMapMC, sliceMapMC_,- -- * Timing timeSystem, timeSystem_, @@ -26,18 +20,17 @@ import System.Mem (performMajorGC) import Control.Monad.Reader (liftIO) import Control.Applicative (liftA2)-import qualified Data.Vector.Unboxed as U-import Data.Traversable (for) import System.CPUTime -import Apecs.Core+import Apecs.Types import Apecs.Stores+import Apecs.System -- | Initializes a store with (), useful since most stores have () as their initialization argument initStore :: (Initializable s, InitArgs s ~ ()) => IO s initStore = initStoreWith () -newtype EntityCounter = EntityCounter Int+newtype EntityCounter = EntityCounter Int deriving (Num, Eq, Show) instance Component EntityCounter where type Storage EntityCounter = Global EntityCounter @@ -63,104 +56,6 @@ newtype ConcatQueries q = ConcatQueries [q] instance Query q s => Query (ConcatQueries q) s where explSlice s (ConcatQueries qs) = mconcat <$> traverse (explSlice s) qs--cmap' :: forall world c. (Has world c, IsRuntime c)- => (c -> Safe c) -> System world ()-cmap' f = do s :: Storage c <- getStore- liftIO$ do sl <- explMembers s- U.forM_ sl $ \e -> do- r <- explGetUnsafe s e- explSetMaybe s e (getSafe . f $ r)---- | Maps a function over all entities with a @r@, and writes their @w@-{-# INLINE rmap #-}-rmap :: forall world r w. (Has world w, Has world r, IsRuntime w, IsRuntime r)- => (r -> w) -> System world ()-rmap f = do sr :: Storage r <- getStore- sc :: Storage w <- getStore- liftIO$ do sl <- explMembers sr- U.forM_ sl $ \ e -> do- r <- explGetUnsafe sr e- explSet sc e (f r)---- | Maps a function over all entities with a @r@, and writes or deletes their @w@-{-# INLINE rmap' #-}-rmap' :: forall world r w. (Has world w, Has world r, Store (Storage w), IsRuntime r)- => (r -> Safe w) -> System world ()-rmap' f = do sr :: Storage r <- getStore- sw :: Storage w <- getStore- liftIO$ do sl <- explMembers sr- U.forM_ sl $ \ e -> do- r <- explGetUnsafe sr e- explSetMaybe sw e (getSafe $ f r)---- | For all entities with a @w@, this map reads their @r@ and writes their @w@-{-# INLINE wmap #-}-wmap :: forall world r w. (Has world w, Has world r, IsRuntime w, IsRuntime r)- => (Safe r -> w) -> System world ()-wmap f = do sr :: Storage r <- getStore- sw :: Storage w <- getStore- liftIO$ do sl <- explMembers sr- U.forM_ sl $ \ e -> do- r <- explGet sr e- explSet sw e (f . Safe $ r)---- | For all entities with a @w@, this map reads their @r@ and writes or deletes their @w@-{-# INLINE wmap' #-}-wmap' :: forall world r w. (Has world w, Has world r, Store (Storage w), IsRuntime r)- => (Safe r -> Safe w) -> System world ()-wmap' f = do sr :: Storage r <- getStore- sw :: Storage w <- getStore- liftIO$ do sl <- explMembers sr- U.forM_ sl $ \ e -> do- r <- explGet sr e- explSetMaybe sw e (getSafe . f . Safe $ r)----- Slice traversal-{-# INLINE sliceForM_ #-}-sliceForM_ :: Monad m => Slice c -> (Entity c -> m b) -> m ()-sliceForM_ (Slice vec) ma = U.forM_ vec (ma . Entity)--{-# INLINE sliceForM #-}-sliceForM :: Monad m => Slice c -> (Entity c -> m a) -> m [a]-sliceForM (Slice vec) ma = traverse (ma . Entity) (U.toList vec)--{-# INLINE sliceForMC #-}-sliceForMC :: forall w c a. (Store (Storage c), Has w c) => Slice c -> ((Entity c,Safe c) -> System w a) -> System w [a]-sliceForMC (Slice vec) sys = do- s :: Storage c <- getStore- for (U.toList vec) $ \e -> do- r <- liftIO$ explGet s e- sys (Entity e, Safe r)--{-# INLINE sliceForMC_ #-}-sliceForMC_ :: forall w c a. (Store (Storage c), Has w c) => Slice c -> ((Entity c,Safe c) -> System w a) -> System w ()-sliceForMC_ (Slice vec) sys = do- s :: Storage c <- getStore- U.forM_ vec $ \e -> do- r <- liftIO$ explGet s e- sys (Entity e, Safe r)--{-# INLINE sliceMapM_ #-}-sliceMapM_ :: Monad m => (Entity c -> m a) -> Slice c -> m ()-sliceMapM_ ma (Slice vec) = U.mapM_ (ma . Entity) vec--{-# INLINE sliceMapM #-}-sliceMapM :: Monad m => (Entity c -> m a) -> Slice c -> m [a]-sliceMapM ma (Slice vec) = traverse (ma . Entity) (U.toList vec)--{-# INLINE sliceMapMC #-}-sliceMapMC :: forall w c a. (Store (Storage c), Has w c) => ((Entity c,Safe c) -> System w a) -> Slice c -> System w [a]-sliceMapMC sys (Slice vec) = do- s :: Storage c <- getStore- for (U.toList vec) $ \e -> do- r <- liftIO$ explGet s e- sys (Entity e, Safe r)--{-# INLINE sliceMapMC_ #-}-sliceMapMC_ :: forall w c a. (Store (Storage c), Has w c) => ((Entity c, Safe c) -> System w a) -> Slice c -> System w ()-sliceMapMC_ sys vec = sliceForMC_ vec sys -- | The following functions are for spatial hashing. -- The idea is that your spatial hash is defined by two vectors;
tutorials/RTS.md view
@@ -92,10 +92,10 @@ When actually executing the game, we produce a world in the IO monad: ```haskell initWorld = do- positions <- initStore+ positions <- initStore -- initStore = initStoreWith (), used to initialize most stores targets <- initStore selected <- initStore- mouseState <- initStoreWith Rest+ mouseState <- initStoreWith Rest -- A global needs to be initialized with a value counter <- initCounter return $ World positions targets selected counter ```@@ -289,4 +289,4 @@ We'll be taking a look at - How to cache your components for O(1) reads and writes - How to use an IndexTable to add queries to your component storages- - How to use those indextables to get a free spatial hash of our positions+ - How to use those IndexTables to get a free spatial hash of our positions