packages feed

apecs-0.10.0: src/Apecs/Util.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE Strict #-}
{-# LANGUAGE TypeFamilies #-}
-- For Data.Semigroup compatibility
{-# OPTIONS_GHC -fno-warn-unused-imports #-}

module Apecs.Util
  ( -- * Utility
    runGC
  , global

    -- * EntityCounter
  , EntityCounter (..)
  , nextEntity
  , newEntity
  , newEntity_
  , nextEntityIO
  , Maybify
  ) where

import Control.Applicative (liftA2)
import Control.Monad.IO.Class
import Control.Monad.Trans.Class (lift)
import Data.IORef
import qualified Data.IntSet as IS
import qualified Data.Map.Strict as M
import Data.Monoid
import Data.Semigroup
import qualified Data.Set as S
import System.Mem (performMajorGC)

import Apecs.Core
import Apecs.Stores
import qualified Apecs.Stores.Internal as Stores
import Apecs.System

-- | Convenience entity, for use in places where the entity value does not matter, i.e. a global store.
global :: Entity
global = Entity (-1)

{- | Component used by newEntity to track the number of issued entities.
  Automatically added to any world created with @makeWorld@
-}
newtype EntityCounter = EntityCounter {getCounter :: Sum Int} deriving (Semigroup, Monoid, Eq, Show)

instance Component EntityCounter where
  type Storage EntityCounter = ReadOnly (Global EntityCounter)

{- | Atomically bumps the EntityCounter and yields its value.
  Relatively slower than nextEntity, but unsafeIOToSTM-safe.
-}
{-# INLINE nextEntityIO #-}
nextEntityIO :: (Has w IO EntityCounter) => SystemT w IO Entity
nextEntityIO = do
  Stores.ReadOnly (Stores.Global ecRef) <- getStore
  liftIO $ atomicModifyIORef' ecRef $ \(EntityCounter (Sum c)) ->
    ( EntityCounter (Sum $ c + 1)
    , Entity c
    )

{- | Bumps the EntityCounter and yields its value.

Not thread-safe.
-}
{-# INLINE nextEntity #-}
nextEntity :: (MonadIO m, Get w m EntityCounter) => SystemT w m Entity
nextEntity = do
  EntityCounter n <- get global
  setReadOnly global (EntityCounter $ n + 1)
  return (Entity . getSum $ n)

{- | Writes the given components to a new entity, and yields that entity.
The return value is often ignored.
-}
{-# INLINE newEntity #-}
newEntity
  :: (MonadIO m, Set w m c, Get w m EntityCounter)
  => c -> SystemT w m Entity
newEntity c = do
  ety <- nextEntity
  set ety c
  return ety

{- | Writes the given components to a new entity without yelding the result.
Used mostly for convenience.
-}
{-# INLINE newEntity_ #-}
newEntity_
  :: (MonadIO m, Set world m component, Get world m EntityCounter)
  => component -> SystemT world m ()
newEntity_ component = do
  entity <- nextEntity
  set entity component

-- | Explicitly invoke the garbage collector
runGC :: (MonadIO m) => SystemT w m ()
runGC = liftIO performMajorGC

{- | Wrap tuple elements in Maybe.

This allows to safely `get` component packs generated by @makeInstanceFold mkTupleT@.
-}
type family Maybify t where
  Maybify (Maybe a) = Maybe a
  Maybify () = ()
  Maybify (a, b) =
    (Maybify a, Maybify b)
  Maybify (a, b, c) =
    (Maybify a, Maybify b, Maybify c)
  Maybify (a, b, c, d) =
    (Maybify a, Maybify b, Maybify c, Maybify d)
  Maybify (a, b, c, d, e) =
    (Maybify a, Maybify b, Maybify c, Maybify d, Maybify e)
  Maybify (a, b, c, d, e, f) =
    (Maybify a, Maybify b, Maybify c, Maybify d, Maybify e, Maybify f)
  Maybify (a, b, c, d, e, f, g) =
    (Maybify a, Maybify b, Maybify c, Maybify d, Maybify e, Maybify f, Maybify g)
  Maybify (a, b, c, d, e, f, g, h) =
    (Maybify a, Maybify b, Maybify c, Maybify d, Maybify e, Maybify f, Maybify g, Maybify h)
  Maybify a = Maybe a