module Main where import Control.Exception import Control.Monad.IO.Class import Data.Proxy import Game import Game.Core import Game.GoreAndAsh import Game.GoreAndAsh.Actor import Game.GoreAndAsh.Network import Game.GoreAndAsh.Sync import Network.BSD (getHostByName, hostAddress) import Network.Socket (SockAddr(..)) import System.Exit import Data.IORef import System.Environment import Text.Read import FPS simulationFPS :: Int simulationFPS = 60 parseArgs :: IO (String, Int) parseArgs = do args <- getArgs case args of [h, p] -> case readMaybe p of Nothing -> fail "Failed to parse port" Just pint -> return (h, pint) _ -> fail "Misuse of arguments: gore-and-ash-server HOST PORT" main :: IO () main = withModule (Proxy :: Proxy AppMonad) $ do gs <- newGameState $ runActor' mainWire gsRef <- newIORef gs (host, port) <- liftIO parseArgs fps <- makeFPSBounder simulationFPS firstStep fps host port gs gsRef `onCtrlC` exitHandler gsRef where -- | What to do on emergency exit exitHandler gsRef = do gs <- readIORef gsRef cleanupGameState gs exitSuccess -- | Resolve given hostname and port getAddr s p = do he <- getHostByName s return $ SockAddrInet p $ hostAddress he -- | Initialization step firstStep fps host port gs gsRef = do (_, gs') <- stepGame gs $ do networkSetDetailedLoggingM False syncSetLoggingM True syncSetRoleM SyncMaster addr <- liftIO $ getAddr host (fromIntegral port) networkBind (Just addr) 100 2 0 0 writeIORef gsRef gs' gameLoop fps gs' gsRef -- | Normal game loop gameLoop fps gs gsRef = do waitFPSBound fps (_, gs') <- stepGame gs (return ()) writeIORef gsRef gs' gameLoop fps gs' gsRef -- | Executes given handler on Ctrl-C pressing onCtrlC :: IO a -> IO () -> IO a p `onCtrlC` q = catchJust isUserInterrupt p (const $ q >> p `onCtrlC` q) where isUserInterrupt :: AsyncException -> Maybe () isUserInterrupt UserInterrupt = Just () isUserInterrupt _ = Nothing