I'm using Network
and Gloss
for a game server in Haskell. It works fine, except that the client has to close for the server to receive the data it sent. I bet it's a case of laziness...
Minimalist server:
import Network
import System.IO
main = do
sock <- listenOn (PortNumber (fromIntegral 12345))
loop sock
loop sock = do
(hIn, _, _) <- accept sock
str <- hGetContents hIn
print str
loop sock
Minimalist client:
import Network
import System.IO
import Graphics.Gloss.Interface.IO.Game
main = playIO
(InWindow "Test Multi" (500, 500) (500, 500))
white
60
Nothing
draw
(\_ x -> return x)
advance
draw Nothing = return blank
draw (Just x) = return (Text (show x))
advance _ Nothing = do
hOut <- connectTo "000.000.0.0" (PortNumber (fromIntegral 12345))
hSetBuffering hOut NoBuffering
hPutStr hOut "Hello!"
return (Just hOut)
advance _ x = return x
I start the server, wait 10 seconds, then start the client, wait 15 seconds, see that nothing happens on the server, closes the client, see "Hello!" suddenly appear on the server.
I would like "Hello!" to appear while the client is running, in an advance
call, otherwise I can't make a multiplayer game (sob)!
However, if I change the client's code to
main = loop Nothing
loop x = do
x' <- advance 0 x
getLine
the sever immediatly shows "Hello!" while the client is waiting for my input.
I tried, as suggested in another question, to use bang patterns and hClose
:
-- ...
!str <- hGetContents hIn
hClose hIn
-- ...
which makes the output appear immediatly, without the client closing. That's great. But, I plan to use bytestrings because the data I send to the server is serialized, so I import qualified Data.ByteString as B
and change hGetContents
to B.hGetContents
, which makes the problem re-appear.
The problem was indeed a case of laziness. hGetContents
reads lazily all the contents of the Handle
, so it finishes only when it's closed, when the client aborts the connection. Instead, I used hGetLine
that returns the content each time it encounters a \n
, which I use as a end-of-message
tag.
Making a game in Haskell is a pioneering process. Despite the fact that there's a page on the wiki and a full subreddit dedicated to the purpose of making a game in this beautiful language, not many people have actually succeeded making anything close to what current game developers can already achieve.
Yeah, Haskell is much faster than any other popular language: you can develop complex applications 2–5 times faster and with smaller team! :D.
I might be completely wrong, but isn't the problem hGetContents? Surely that should wait till the entire contents sent through your socket have arrived before the next line (print...) starts. hGetContents is designed to give you all the contents sent until the socket is closed. Something like hGetLine could terminate straight away and you can leave the socket open to send more data through later. Your client could then use a hPutStrLn instead of hPutStr.
It defaults to line-buffered output, which is why hPutStr
(which doesn't provide a line ending) doesn't output anything until you flush the buffer. There are two ways you can solve this:
a) Call hFlush stdout
manually any time you want to flush the output.
b) Use hSetBuffering
to set the buffering to NoBuffering
All of those functions are found in the System.IO
module.
Edit: Never mind, I just saw where you did that in the client. I retract my answer with apologies.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With