packages feed

apecs 0.9.1 → 0.9.2

raw patch · 10 files changed

+123/−76 lines, 10 filesdep +exceptionsPVP ok

version bump matches the API change (PVP)

Dependencies added: exceptions

API changes (from Hackage documentation)

+ Apecs.Core: instance Control.Monad.Catch.MonadCatch m => Control.Monad.Catch.MonadCatch (Apecs.Core.SystemT w m)
+ Apecs.Core: instance Control.Monad.Catch.MonadMask m => Control.Monad.Catch.MonadMask (Apecs.Core.SystemT w m)
+ Apecs.Core: instance Control.Monad.Catch.MonadThrow m => Control.Monad.Catch.MonadThrow (Apecs.Core.SystemT w m)

Files

CHANGELOG.md view
@@ -1,3 +1,9 @@+## [0.9.2]+### Changed+- (#68) Add instances of MonadThrow, MonadCatch, and MonadMask to SystemT+- Cleaned up the README+- Small haddock fixes+ ## [0.9.1] ### Changed - (#63) Fixed bug where `modify` on non-existent components crashes
README.md view
@@ -1,37 +1,58 @@-# apecs [![Build Status](https://travis-ci.org/jonascarpay/apecs.svg?branch=master)](https://travis-ci.org/jonascarpay/apecs)-apecs is an _Entity Component System_ (ECS) library inspired by [specs](https://github.com/slide-rs/specs) and [Entitas](https://github.com/sschmid/Entitas-CSharp).-ECS presents a data-driven approach to game development, that elegantly tackles many of the unique issues of game programming.+# apecs +apecs is an [_Entity Component System_](https://en.wikipedia.org/wiki/Entity_component_system) (ECS) library for game development.+ apecs aims to be-* **Fast** - Performance is competitive with Rust ECS libraries (see benchmark results below).-* **Concise** - Game logic is expressed using a small number of powerful combinators.-* **Safe** - The `cmap`/`cfold`-DSL completely hides the dangers of the low-level API.-* **Extensible** - At its heart apecs is just a data manipulation DSL that can be implemented with any number of backends. As a monad transformer it easily integrates into larger applications.+* **Fast** - Performance is competitive with Rust ECS libraries (see benchmark results below)+* **Safe** - Completely hides the dangers of the low-level machinery+* **Concise** - Game logic is expressed using a small number of powerful combinators+* **Flexible** - Easily add new modules or backends * **Cool** -![Benchmarks](apecs/bench/chart.png)+![Benchmarks](https://raw.githubusercontent.com/jonascarpay/apecs/master/apecs/bench/chart.png) -#### Links+### Links - [documentation on hackage](https://hackage.haskell.org/package/apecs/docs/Apecs.html)-- [tutorial](../examples/Shmup.md) and other [examples](../examples/)-- [paper (prepublication)](../apecs/prepub.pdf) (see [#19](https://github.com/jonascarpay/apecs/issues/19))-- [apecs-physics](../apecs-physics/) - 2D physics using the [Chipmunk2D](https://github.com/slembcke/Chipmunk2D) engine-- [apecs-gloss](../apecs-gloss/) - Simple frontend for [gloss](http://hackage.haskell.org/package/gloss)-based rendering-- [apecs-stm](../apecs-stm/) - STM-based stores for easy concurrency+- [tutorial](https://github.com/jonascarpay/apecs/blob/master/examples/Shmup.md) and other [examples](../examples/) -##### By other authors-- [An Introduction to Developing Games in Haskell with Apecs](https://blog.aas.sh/posts/2018-09-10-Making-A-Game-With-Haskell-And-Apecs/) by Ashley Smith+##### Games/articles+- [An Introduction to Developing Games in Haskell with Apecs](https://aas.sh/blog/making-a-game-with-haskell-and-apecs/)+- [mallRL](https://github.com/nmaehlmann/mallRL) - a _grocery shopping roguelike_+- [An implementation of the Unity tutorial project using apecs](https://github.com/mewhhaha/apecs-unity-tutorial-haskell)+- [(Your game here)](https://github.com/jonascarpay/apecs/pulls) -#### Status-| Package | Hackage | Stack LTS | Stack Nightly |-|---|---|---|---|-| [apecs](apecs/) | [![Hackage](https://img.shields.io/hackage/v/apecs.svg)](https://hackage.haskell.org/package/apecs) | [![Stackage](https://www.stackage.org/package/apecs/badge/lts?label=lts)](https://www.stackage.org/package/apecs) | [![Stackage](https://www.stackage.org/package/apecs/badge/nightly?label=nightly)](https://www.stackage.org/package/apecs)-| [apecs-physics](apecs-physics/) |  [![Hackage](https://img.shields.io/hackage/v/apecs-physics.svg)](https://hackage.haskell.org/package/apecs-physics) | [![Stackage](https://www.stackage.org/package/apecs-physics/badge/lts?label=lts)](https://www.stackage.org/package/apecs-physics) | [![Stackage](https://www.stackage.org/package/apecs-physics/badge/nightly?label=nightly)](https://www.stackage.org/package/apecs-physics) |-| [apecs-gloss](apecs-gloss/) | [![Hackage](https://img.shields.io/hackage/v/apecs-gloss.svg)](https://hackage.haskell.org/package/apecs-gloss) | [![Stackage](https://www.stackage.org/package/apecs-gloss/badge/lts?label=lts)](https://www.stackage.org/package/apecs-gloss) | [![Stackage](https://www.stackage.org/package/apecs-gloss/badge/nightly?label=nightly)](https://www.stackage.org/package/apecs-gloss) |-| [apecs-stm](apecs-stm/) | [![Hackage](https://img.shields.io/hackage/v/apecs-stm.svg)](https://hackage.haskell.org/package/apecs-stm) | - | - |-| [examples](examples/) | - | - | - |+##### Packages+- [apecs](https://github.com/jonascarpay/apecs/tree/master/apecs) -#### Example+  [![Hackage](https://img.shields.io/hackage/v/apecs.svg)](https://hackage.haskell.org/package/apecs)+  [![Stackage](https://www.stackage.org/package/apecs/badge/lts?label=lts)](https://www.stackage.org/package/apecs)+  [![Stackage](https://www.stackage.org/package/apecs/badge/nightly?label=nightly)](https://www.stackage.org/package/apecs)+  [![Build Status](https://travis-ci.org/jonascarpay/apecs.svg?branch=master)](https://travis-ci.org/jonascarpay/apecs)++- [apecs-physics](https://github.com/jonascarpay/apecs/tree/master/apecs-physics) - 2D physics using the [Chipmunk2D](https://github.com/slembcke/Chipmunk2D) engine++  [![Hackage](https://img.shields.io/hackage/v/apecs-physics.svg)](https://hackage.haskell.org/package/apecs-physics)+  [![Stackage](https://www.stackage.org/package/apecs-physics/badge/lts?label=lts)](https://www.stackage.org/package/apecs-physics)+  [![Stackage](https://www.stackage.org/package/apecs-physics/badge/nightly?label=nightly)](https://www.stackage.org/package/apecs-physics) +  [![Build Status](https://travis-ci.org/jonascarpay/apecs.svg?branch=master)](https://travis-ci.org/jonascarpay/apecs)++- [apecs-gloss](https://github.com/jonascarpay/apecs/tree/master/apecs-gloss) - Simple frontend for [gloss](http://hackage.haskell.org/package/gloss)-based rendering++  [![Hackage](https://img.shields.io/hackage/v/apecs-gloss.svg)](https://hackage.haskell.org/package/apecs-gloss) +  [![Stackage](https://www.stackage.org/package/apecs-gloss/badge/lts?label=lts)](https://www.stackage.org/package/apecs-gloss) +  [![Stackage](https://www.stackage.org/package/apecs-gloss/badge/nightly?label=nightly)](https://www.stackage.org/package/apecs-gloss) +  [![Build Status](https://travis-ci.org/jonascarpay/apecs.svg?branch=master)](https://travis-ci.org/jonascarpay/apecs)++- [apecs-stm](https://github.com/jonascarpay/apecs/tree/master/apecs-stm) - STM-based stores for easy concurrency++  [![Hackage](https://img.shields.io/hackage/v/apecs-stm.svg)](https://hackage.haskell.org/package/apecs-stm)+  [![Build Status](https://travis-ci.org/jonascarpay/apecs.svg?branch=master)](https://travis-ci.org/jonascarpay/apecs)++- [*examples*](https://github.com/jonascarpay/apecs/tree/master/examples/)++  [![Build Status](https://travis-ci.org/jonascarpay/apecs.svg?branch=master)](https://travis-ci.org/jonascarpay/apecs)++### Example ```haskell {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE MultiParamTypeClasses #-}
apecs.cabal view
@@ -1,5 +1,5 @@ name:                apecs-version:             0.9.1+version:             0.9.2 homepage:            https://github.com/jonascarpay/apecs#readme license:             BSD3 license-file:        LICENSE@@ -44,6 +44,7 @@     base             >= 4.9  && < 5,     array            >= 0.4  && < 0.6,     containers       >= 0.5  && < 0.8,+    exceptions       >= 0.10.0 && < 0.11,     mtl              >= 2.2  && < 2.3,     template-haskell >= 2.12 && < 3,     vector           >= 0.11 && < 0.13
src/Apecs.hs view
@@ -27,12 +27,13 @@     asks, ask, liftIO, lift, Proxy (..) ) where -import Control.Monad.Reader (ask, asks, lift, liftIO)-import Data.Proxy+import           Control.Monad.IO.Class (liftIO)+import           Control.Monad.Reader (ask, asks, lift)+import           Data.Proxy -import Apecs.Components-import Apecs.Core-import Apecs.Stores-import Apecs.System-import Apecs.TH-import Apecs.Util+import           Apecs.Components+import           Apecs.Core+import           Apecs.Stores+import           Apecs.System+import           Apecs.TH+import           Apecs.Util
src/Apecs/Core.hs view
@@ -11,6 +11,8 @@  module Apecs.Core where +import           Control.Monad.Catch+import           Control.Monad.IO.Class import           Control.Monad.Reader import qualified Data.Vector.Unboxed  as U @@ -26,7 +28,7 @@ --   * Allow type-based lookup of a component's store through @getStore@. -- --   * Lift side effects into their host Monad.-newtype SystemT w m a = SystemT {unSystem :: ReaderT w m a} deriving (Functor, Monad, Applicative, MonadTrans, MonadIO)+newtype SystemT w m a = SystemT {unSystem :: ReaderT w m a} deriving (Functor, Monad, Applicative, MonadTrans, MonadIO, MonadThrow, MonadCatch, MonadMask) type System w a = SystemT w IO a  deriving instance Monad m => MonadReader w (SystemT w m)@@ -37,6 +39,7 @@   type Storage c  -- | @Has w m c@ means that world @w@ can produce a @Storage c@.+--   It is parameterized over @m@ to allow stores to be foreign. class (Monad m, Component c) => Has w m c where   getStore :: SystemT w m (Storage c) 
src/Apecs/Experimental/Reactive.hs view
@@ -25,6 +25,8 @@   , IxMap, ixLookup   ) where +import           Control.Monad+import           Control.Monad.IO.Class import           Control.Monad.Reader import qualified Data.Array.IO        as A import qualified Data.IntMap.Strict   as IM
src/Apecs/Stores.hs view
@@ -17,20 +17,22 @@     -- Register, regLookup   ) where +import           Control.Monad+import           Control.Monad.IO.Class import           Control.Monad.Reader+import           Data.Bits                   (shiftL, (.&.)) import qualified Data.IntMap.Strict          as M import           Data.IORef import           Data.Proxy-import           Data.Bits (shiftL, (.&.))-import           Data.Typeable (Typeable, typeRep)+import           Data.Typeable               (Typeable, typeRep) import qualified Data.Vector.Mutable         as VM import qualified Data.Vector.Unboxed         as U import qualified Data.Vector.Unboxed.Mutable as UM import           GHC.TypeLits -import Apecs.Core+import           Apecs.Core --- | A map based on @Data.IntMap.Strict@. O(log(n)) for most operations.+-- | A map based on 'Data.IntMap.Strict'. O(log(n)) for most operations. newtype Map c = Map (IORef (M.IntMap c))  type instance Elem (Map c) = c@@ -66,7 +68,7 @@  -- | A Unique contains zero or one component. --   Writing to it overwrites both the previous component and its owner.---   Its main purpose is to be a @Map@ optimized for when only ever one component inhabits it.+--   Its main purpose is to be a 'Map' optimized for when only ever one component inhabits it. newtype Unique c = Unique (IORef (Maybe (Int, c))) type instance Elem (Unique c) = c instance MonadIO m => ExplInit m (Unique c) where@@ -99,14 +101,14 @@     Nothing -> mempty     Just (ety, _) -> U.singleton ety --- | A @Global@ contains exactly one component.+-- | A 'Global' contains exactly one component. --   The initial value is 'mempty' from the component's 'Monoid' instance.+--   Querying a 'Global' at /any/ Entity yields this one component, effectively sharing the component between /all/ entities. -----   When operating on a Global, any entity arguments are ignored.---   A Global component can be read with @get 0@ or @get 1@ or even @get undefined@.---   This means that you can read and write Globals while @cmap@ping over other components.+--   A Global component can be read with @'get' 0@ or @'get' 1@ or even @'get' undefined@.+--   The convenience entity 'global' is defined as -1, and can be used to make operations on a global more explicit, i.e. 'Time t <- get global'. -----   The integer @global@ is defined as -1, and can be used to make operations on a global explicit, i.e. 'Time t <- get global'.+--   You also can read and write Globals during a 'cmap' over other components. newtype Global c = Global (IORef c) type instance Elem (Global c) = c instance (Monoid c, MonadIO m) => ExplInit m (Global c) where@@ -123,20 +125,26 @@   {-# INLINE explSet #-}   explSet (Global ref) _ c = liftIO$ writeIORef ref c --- | An empty type class indicating that the store behaves like a regular map, and can therefore safely be cached.+-- | Class of stores that behave like a regular map, and can therefore safely be cached.+--   This prevents stores like `Unique` and 'Global', which do /not/ behave like simple maps, from being cached. class Cachable s instance Cachable (Map s) instance (KnownNat n, Cachable s) => Cachable (Cache n s)  -- | A cache around another store.---   Caches store their members in a fixed-size vector, so operations run in O(1).---   Caches can provide huge performance boosts, especially for large numbers of components.+--   Caches store their members in a fixed-size vector, so read/write operations become O(1).+--   Caches can provide huge performance boosts, especially when working with large numbers of components.+-- --   The cache size is given as a type-level argument. -----   Note that iterating over a cache is linear in cache size, so sparsely populated caches might actually decrease performance.+--   Note that iterating over a cache is linear in cache size, so sparsely populated caches might /decrease/ performance. --   In general, the exact size of the cache does not matter as long as it reasonably approximates the number of components present. -----   The cache uses entity (-2) to internally represent missing entities, so be wary when manually manipulating entities.+--   The cache uses entity (-2) internally to represent missing entities.+--   If you manually manipulate Entity values, be careful that you do not use (-2)+--+--   The actual cache is not necessarily the given argument, but the next biggest power of two.+--   This is allows most operations to be expressed as bit masks, for a large potential performance boost. data Cache (n :: Nat) s =   Cache Int (UM.IOVector Int) (VM.IOVector (Elem s)) s @@ -199,8 +207,9 @@     stored <- explMembers s >>= liftIO . U.filterM etyFilter     return $! cached U.++ stored --- | Wrapper that makes a store read-only by hiding its 'ExplSet' and 'ExplDestroy'. Use 'setReadOnly' and 'destroyReadOnly' to override.--- This is used to protect the 'EntityCounter'.+-- | Wrapper that makes a store read-only by hiding its 'ExplSet' and 'ExplDestroy' instances.+--   This is primarily used to protect the 'EntityCounter' from accidental overwrites.+--   Use 'setReadOnly' and 'destroyReadOnly' to override. newtype ReadOnly s = ReadOnly s type instance Elem (ReadOnly s) = Elem s 
src/Apecs/System.hs view
@@ -6,6 +6,7 @@  module Apecs.System where +import           Control.Monad import           Control.Monad.Reader import           Data.Proxy import qualified Data.Vector.Unboxed  as U
src/Apecs/TH.hs view
@@ -8,19 +8,19 @@   , makeMapComponents   ) where -import Control.Monad-import Language.Haskell.TH+import           Control.Monad+import           Language.Haskell.TH -import Apecs.Core-import Apecs.Stores-import Apecs.Util   (EntityCounter)+import           Apecs.Core+import           Apecs.Stores+import           Apecs.Util          (EntityCounter)  genName :: String -> Q Name genName s = mkName . show <$> newName s --- | Same as 'makeWorld', but has no 'EntityCounter'+-- | Same as 'makeWorld', but does not include an 'EntityCounter'+--   You don't typically want to use this, but it's exposed in case you know what you're doing. makeWorldNoEC :: String -> [Name] -> Q [Dec]--- makeWorldNoEC _ [] = do makeWorldNoEC worldName cTypes = do   cTypesNames <- forM cTypes $ \t -> do     rec <- genName "rec"@@ -61,7 +61,7 @@   let ct = return$ ConT comp   head <$> [d| instance Component $ct where type Storage $ct = Map $ct |] --- | Calls 'makeWorld' and 'makeMapComponents', i.e. makes a world and also defines @Component@ instances with a @Map@ store.+-- | Calls 'makeWorld' and 'makeMapComponents', i.e. makes a world and also defines 'Component' instances with a 'Map' store. makeWorldAndComponents :: String -> [Name] -> Q [Dec] makeWorldAndComponents worldName cTypes = do   wdecls <- makeWorld worldName cTypes@@ -70,19 +70,21 @@  {-| -> makeWorld "WorldName" [''Component1, ''Component2, ...]+The typical way to create a @world@ record, associated 'Has' instances, and initialization function. +> makeWorld "MyWorld" [''Component1, ''Component2, ...]+ turns into -> data WorldName = WorldName Component1 Component2 ... EntityCounter-> instance WorldName `Has` Component1 where ...-> instance WorldName `Has` Component2 where ...+> data MyWorld = MyWorld Component1 Component2 ... EntityCounter+> instance MyWorld `Has` Component1 where ...+> instance MyWorld `Has` Component2 where ... > ...-> instance WorldName `Has` EntityCounter where ...+> instance MyWorld `Has` EntityCounter where ... >-> initWorldName :: IO WorldName-> initWorldName = WorldName <$> initStore <*> initStore <*> ... <*> initStore+> initMyWorld :: IO MyWorld+> initMyWorld = MyWorld <$> initStore <*> initStore <*> ... <*> initStore -|-}+-} makeWorld :: String -> [Name] -> Q [Dec] makeWorld worldName cTypes = makeWorldNoEC worldName (cTypes ++ [''EntityCounter])
src/Apecs/Util.hs view
@@ -16,15 +16,16 @@   EntityCounter(..), nextEntity, newEntity, ) where -import Control.Applicative  (liftA2)-import Control.Monad.Reader-import Data.Monoid-import Data.Semigroup-import System.Mem           (performMajorGC)+import           Control.Applicative  (liftA2)+import           Control.Monad.IO.Class+import           Control.Monad.Reader+import           Data.Monoid+import           Data.Semigroup+import           System.Mem           (performMajorGC) -import Apecs.Core-import Apecs.Stores-import Apecs.System+import           Apecs.Core+import           Apecs.Stores+import           Apecs.System  -- | Convenience entity, for use in places where the entity value does not matter, i.e. a global store. global :: Entity