module MOO.Builtins.Network ( builtins ) where
import Control.Applicative ((<$>))
import Control.Concurrent.STM (readTVar)
import Control.Monad (unless, (<=<))
import Control.Monad.State (gets)
import Data.Time (UTCTime, diffUTCTime)
import qualified Data.Map as M
import MOO.Builtins.Common
import MOO.Network
import MOO.Connection
import MOO.Object (systemObject)
import MOO.Task
import MOO.Types
import qualified MOO.String as Str
builtins :: [Builtin]
builtins = [
bf_connected_players
, bf_connected_seconds
, bf_idle_seconds
, bf_notify
, bf_buffered_output_length
, bf_read
, bf_force_input
, bf_flush_input
, bf_output_delimiters
, bf_boot_player
, bf_connection_name
, bf_set_connection_option
, bf_connection_options
, bf_connection_option
, bf_open_network_connection
, bf_listen
, bf_unlisten
, bf_listeners
]
bf_connected_players = Builtin "connected_players" 0 (Just 1)
[TAny] TLst $ \optional -> do
let [include_all] = booleanDefaults optional [False]
objects <- withConnections $ return . M.keys
return $ objectList $ if include_all then objects else filter (>= 0) objects
secondsSince :: UTCTime -> MOO Value
secondsSince utcTime = Int . floor . (`diffUTCTime` utcTime) <$> gets startTime
bf_connected_seconds = Builtin "connected_seconds" 1 (Just 1)
[TObj] TInt $ \[Obj player] ->
withConnection player $ maybe (raise E_INVARG) secondsSince <=<
liftSTM . readTVar . connectionConnectedTime
bf_idle_seconds = Builtin "idle_seconds" 1 (Just 1)
[TObj] TInt $ \[Obj player] ->
withConnection player $ secondsSince <=<
liftSTM . readTVar . connectionActivityTime
bf_notify = Builtin "notify" 2 (Just 3)
[TObj, TStr, TAny] TAny $ \(Obj conn : Str string : optional) -> do
let [no_flush] = booleanDefaults optional [False]
checkPermission conn
truthValue <$> notify' no_flush conn string
bf_buffered_output_length = Builtin "buffered_output_length" 0 (Just 1)
[TObj] TInt $ \optional -> do
let (conn : _) = maybeDefaults optional
len <- case conn of
Just (Obj oid) -> do
checkPermission oid
withConnection oid $ liftSTM . bufferedOutputLength . Just
Nothing -> liftSTM $ bufferedOutputLength Nothing
return (Int $ fromIntegral len)
bf_read = Builtin "read" 0 (Just 2) [TObj, TAny] TAny $ \optional -> do
(conn, optional) <- case optional of
Obj conn : optional -> checkPermission conn >> return (conn, optional)
[] -> checkWizard >> getPlayer >>= \player -> return (player, [])
let [non_blocking] = booleanDefaults optional [False]
readFromConnection conn non_blocking
bf_force_input = Builtin "force_input" 2 (Just 3) [TObj, TStr, TAny]
TAny $ \(Obj conn : Str line : optional) -> do
let [at_front] = booleanDefaults optional [False]
checkPermission conn
forceInput at_front conn line
return zero
bf_flush_input = Builtin "flush_input" 1 (Just 2)
[TObj, TAny] TAny $ \(Obj conn : optional) -> do
let [show_messages] = booleanDefaults optional [False]
checkPermission conn
withMaybeConnection conn $ maybe (return ()) $
liftSTM . flushInput show_messages
return zero
bf_output_delimiters = Builtin "output_delimiters" 1 (Just 1)
[TObj] TLst $ \[Obj player] -> do
checkPermission player
(prefix, suffix) <- withConnection player $
liftSTM . readTVar . connectionOutputDelimiters
return $ stringList [Str.fromText prefix, Str.fromText suffix]
bf_boot_player = Builtin "boot_player" 1 (Just 1) [TObj] TAny $ \[Obj player] ->
checkPermission player >> bootPlayer player >> return zero
bf_connection_name = Builtin "connection_name" 1 (Just 1)
[TObj] TStr $ \[Obj player] -> do
checkPermission player
Str . Str.fromString <$> withConnection player (liftSTM . connectionName)
bf_set_connection_option = Builtin "set_connection_option" 3 (Just 3)
[TObj, TStr, TAny]
TAny $ \[Obj conn, Str option, value] -> do
checkPermission conn
setConnectionOption conn (toId option) value
return zero
bf_connection_options = Builtin "connection_options" 1 (Just 1)
[TObj] TLst $ \[Obj conn] -> do
checkPermission conn
fromListBy pair . M.toList <$> getConnectionOptions conn
where pair (k, v) = fromList [Str $ fromId k, v]
bf_connection_option = Builtin "connection_option" 2 (Just 2)
[TObj, TStr] TAny $ \[Obj conn, Str name] -> do
checkPermission conn
getConnectionOptions conn >>=
maybe (raise E_INVARG) return . M.lookup (toId name)
bf_open_network_connection = Builtin "open_network_connection" 2 (Just 3)
[TStr, TInt, TObj]
TObj $ \(Str host : Int port : optional) -> do
let [Obj listener] = defaults optional [Obj systemObject]
checkWizard
world <- getWorld
unless (outboundNetwork world) $ raise E_PERM
notyet "open_network_connection"
bf_listen = Builtin "listen" 2 (Just 3)
[TObj, TAny, TAny] TAny $ \(Obj object : point : optional) -> do
let [print_messages] = booleanDefaults optional [False]
checkWizard
checkValid object
point <- value2point point
point2value <$> listen object point print_messages
bf_unlisten = Builtin "unlisten" 1 (Just 1) [TAny] TAny $ \[canon] -> do
checkWizard
unlisten =<< value2point canon
return zero
bf_listeners = Builtin "listeners" 0 (Just 0) [] TLst $ \[] ->
fromListBy formatListener . M.elems . listeners <$> getWorld
where formatListener Listener { listenerObject = object
, listenerPoint = point
, listenerPrintMessages = printMessages } =
fromList [Obj object, point2value point, truthValue printMessages]