1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
|
-- | Server and client game state types and operations.
module Game.LambdaHack.Server.State
( StateServer(..), ActorTime, ActorPushedBy
, emptyStateServer, updateActorTime, lookupActorTime, ageActor
#ifdef EXPOSE_INTERNAL
, GearOfTeams
#endif
) where
import Prelude ()
import Game.LambdaHack.Core.Prelude
import Data.Binary
import qualified Data.EnumMap.Strict as EM
import qualified Data.EnumSet as ES
import qualified Data.HashMap.Strict as HM
import qualified Data.IntMap.Strict as IM
import qualified System.Random.SplitMix32 as SM
import Game.LambdaHack.Common.Analytics
import Game.LambdaHack.Common.Item
import Game.LambdaHack.Common.Perception
import Game.LambdaHack.Common.State
import Game.LambdaHack.Common.Time
import Game.LambdaHack.Common.Types
import Game.LambdaHack.Content.FactionKind (TeamContinuity)
import Game.LambdaHack.Content.ItemKind (ItemKind)
import Game.LambdaHack.Definition.Defs
import Game.LambdaHack.Server.Fov
import Game.LambdaHack.Server.ItemRev
import Game.LambdaHack.Server.ServerOptions
-- | State with server-specific data, including a copy of each client's
-- basic game state, but not the server's basic state.
data StateServer = StateServer
{ sactorTime :: ActorTime -- ^ absolute times of actors next actions
, strajTime :: ActorTime -- ^ and same for actors with trajectories
, strajPushedBy :: ActorPushedBy -- ^ culprits for actors with trajectories
, steamGear :: GearOfTeams -- ^ metagame persistent personal
-- characteristics and favourite gear
-- of each numbered continued team member
, steamGearCur :: GearOfTeams -- ^ gear preferences to be taken into
-- account in the current game
, stcounter :: EM.EnumMap TeamContinuity Int
-- ^ stores next continued team character
-- identity index number in this game
, sfactionAn :: FactionAnalytics
-- ^ various past events data for factions
, sactorAn :: ActorAnalytics -- ^ various past events data for actors
, sgenerationAn :: GenerationAnalytics
-- ^ item creation statistics, by item lore
, sactorStasis :: ES.EnumSet ActorId
-- ^ actors currently in time stasis,
-- invulnerable to time warps until move
, sdiscoKindRev :: DiscoveryKindRev
-- ^ reverse map, used for item creation
, suniqueSet :: UniqueSet -- ^ already generated unique items
, sitemRev :: ItemRev -- ^ reverse id map, used for item creation
, sflavour :: FlavourMap -- ^ association of flavour to item kinds
, sacounter :: ActorId -- ^ stores next actor index
, sicounter :: ItemId -- ^ stores next item index
, snumSpawned :: EM.EnumMap LevelId Int
-- ^ how many spawned so far on the level
, sbandSpawned :: IM.IntMap Int -- ^ how many times such group spawned
, sundo :: () -- [CmdAtomic] -- ^ atomic commands performed to date
, sclientStates :: EM.EnumMap FactionId State
-- ^ each faction state, as seen by clients
, smetaBackup :: EM.EnumMap TeamContinuity DiscoveryKind
-- ^ discovery info for absent factions
, sperFid :: PerFid -- ^ perception of all factions
, sperValidFid :: PerValidFid -- ^ perception validity for all factions
, sperCacheFid :: PerCacheFid -- ^ perception cache of all factions
, sfovLucidLid :: FovLucidLid -- ^ ambient or shining light positions
, sfovClearLid :: FovClearLid -- ^ clear tiles positions
, sfovLitLid :: FovLitLid -- ^ ambient light positions
, sarenas :: ES.EnumSet LevelId
-- ^ the set of active arenas
, svalidArenas :: Bool -- ^ whether active arenas valid
, srandom :: SM.SMGen -- ^ current random generator
, srngs :: RNGs -- ^ initial random generators
, sbreakLoop :: Bool -- ^ exit game loop after clip's end;
-- usually no game save follows
, sbreakASAP :: Bool -- ^ exit game loop ASAP; usually with save
, swriteSave :: Bool -- ^ write savegame to file after loop exit
, soptions :: ServerOptions -- ^ current commandline options
, soptionsNxt :: ServerOptions -- ^ options for the next game
}
deriving Show
-- | Position in time for each actor, grouped by level and by faction.
type ActorTime =
EM.EnumMap FactionId (EM.EnumMap LevelId (EM.EnumMap ActorId Time))
-- | Record who last propelled a given actor with trajectory.
type ActorPushedBy = EM.EnumMap ActorId ActorId
-- | Per-team, per-actor metagame persistent favourite organs and gear.
type GearOfTeams = EM.EnumMap
TeamContinuity
(IM.IntMap [(GroupName ItemKind, ContentId ItemKind)])
-- | Initial, empty game server state.
emptyStateServer :: StateServer
emptyStateServer =
StateServer
{ sactorTime = EM.empty
, strajTime = EM.empty
, strajPushedBy = EM.empty
, steamGear = EM.empty
, steamGearCur = EM.empty
, stcounter = EM.empty
, sfactionAn = EM.empty
, sactorAn = EM.empty
, sgenerationAn = EM.fromDistinctAscList
$ zip [minBound..maxBound] (repeat EM.empty)
, sactorStasis = ES.empty
, sdiscoKindRev = emptyDiscoveryKindRev
, suniqueSet = ES.empty
, sitemRev = HM.empty
, sflavour = emptyFlavourMap
, sacounter = toEnum 0
, sicounter = toEnum 0
, snumSpawned = EM.empty
, sbandSpawned = IM.fromList [(1, 0), (2, 0), (3, 0)]
, sundo = ()
, sclientStates = EM.empty
, smetaBackup = EM.empty
, sperFid = EM.empty
, sperValidFid = EM.empty
, sperCacheFid = EM.empty
, sfovLucidLid = EM.empty
, sfovClearLid = EM.empty
, sfovLitLid = EM.empty
, sarenas = ES.empty
, svalidArenas = False
, srandom = SM.mkSMGen 42
, srngs = RNGs { dungeonRandomGenerator = Nothing
, startingRandomGenerator = Nothing }
, sbreakLoop = False
, sbreakASAP = False
, swriteSave = False
, soptions = defServerOptions
, soptionsNxt = defServerOptions
}
updateActorTime :: FactionId -> LevelId -> ActorId -> Time -> ActorTime
-> ActorTime
updateActorTime !fid !lid !aid !time =
EM.adjust (EM.adjust (EM.insert aid time) lid) fid
lookupActorTime :: FactionId -> LevelId -> ActorId -> ActorTime
-> Maybe Time
lookupActorTime !fid !lid !aid !atime = do
m1 <- EM.lookup fid atime
m2 <- EM.lookup lid m1
EM.lookup aid m2
ageActor :: FactionId -> LevelId -> ActorId -> Delta Time -> ActorTime
-> ActorTime
ageActor !fid !lid !aid !delta =
EM.adjust (EM.adjust (EM.adjust (`timeShift` delta) aid) lid) fid
instance Binary StateServer where
put StateServer{..} = do
put sactorTime
put strajTime
put strajPushedBy
put steamGear
put steamGearCur
put stcounter
put sfactionAn
put sactorAn
put sgenerationAn
put sactorStasis
put sdiscoKindRev
put suniqueSet
put sitemRev
put sflavour
put sacounter
put sicounter
put snumSpawned
put sbandSpawned
put sclientStates
put smetaBackup
put (show srandom)
put srngs
put soptions
get = do
sactorTime <- get
strajTime <- get
strajPushedBy <- get
steamGear <- get
steamGearCur <- get
stcounter <- get
sfactionAn <- get
sactorAn <- get
sgenerationAn <- get
sactorStasis <- get
sdiscoKindRev <- get
suniqueSet <- get
sitemRev <- get
sflavour <- get
sacounter <- get
sicounter <- get
snumSpawned <- get
sbandSpawned <- get
sclientStates <- get
smetaBackup <- get
g <- get
srngs <- get
soptions <- get
let srandom = read g
sundo = ()
sperFid = EM.empty
sperValidFid = EM.empty
sperCacheFid = EM.empty
sfovLucidLid = EM.empty
sfovClearLid = EM.empty
sfovLitLid = EM.empty
sarenas = ES.empty
svalidArenas = False
sbreakLoop = False
sbreakASAP = False
swriteSave = False
soptionsNxt = defServerOptions
return $! StateServer{..}
|