{- License: This code is placed in the Public Domain. Author: Douglas Burke (dburke.gw@gmail.com) Usage: ./hmcpi Connects to MineCraft-PI and allows you to enter commands (it can be thought of as telnet specialized to connect to just MineCraft). At present there is no attempt to provide an improved interface, such as help messages, auto-completion of commands, ignoring blank or comment lines. These could be added, but at least the current implementation does let you see exactly what is sent to and from MineCraft-PI. One useful change would be for control-D - or some other terminal character or string - to exit the program. At the moment the only way to exit is to kill the program (e.g. with control-C). -} module Main where import qualified Control.Exception as CE import Control.Concurrent (forkIO) import Control.Monad (when) import Control.Proxy import Network (PortID (..), connectTo) import System.Environment (getArgs, getProgName) import System.Exit (exitFailure) import System.IO ( BufferMode(LineBuffering), Handle , hClose, hIsTerminalDevice , hPutStrLn, hSetBuffering , stderr, stdin, stdout ) -- | Link together the two handles, so that content -- moves from the first to the second. -- -- It might be nice to clean out user input - e.g. skip blank -- or empty lines - as well as provide support (e.g. help), but -- for now just pass through everything. This at least means -- that we can see what the server is actually doing, and -- means we can use the same code for connecting user input -- to the server as well as displaying the user output. -- pipeTo :: Handle -> Handle -> IO () pipeTo inHdl outHdl = runProxy $ hGetLineS inHdl >-> hPutStrLnD outHdl {- How best to handle a user exit? Need to signal to the main program that we have finished pipeToWithExit :: Handle -> Handle -> IO () pipeToWithExit inHdl outHdl = runProxy $ hGetLineS inHdl >-> takeWhileD ('\EOT' `notElem`) >-> hPutStrLnD outHdl -} pipeToWithExit :: Handle -> Handle -> IO () pipeToWithExit = pipeTo -- | Only display the message if @stdin@ is connected to -- a terminal device (i.e. the input is not being piped -- from a file). -- logMsg :: String -> IO () logMsg msg = hIsTerminalDevice stdin >>= \flag -> when flag (putStrLn msg) mcpi :: IO () mcpi = let errorHandler :: CE.IOException -> IO () errorHandler _ = do hPutStrLn stderr "ERROR: Unable to connect MineCraft. Is it running?" exitFailure in CE.bracket (connectTo "" (PortNumber 4711)) hClose (\server -> do logMsg "*** Connected to MineCraft" mapM_ (`hSetBuffering` LineBuffering) [stdin, stdout, server] _ <- forkIO $ stdin `pipeToWithExit` server server `pipeTo` stdout ) `CE.catch` errorHandler usage :: IO () usage = do progName <- getProgName hPutStrLn stderr $ "Usage: " ++ progName exitFailure main :: IO () main = do args <- getArgs if null args then mcpi else usage