# HG changeset patch # User unc0rr # Date 1216633540 0 # Node ID 596b1dcdc1df81a1591664f084f6e31c3b1b9024 # Parent 5be338fa4e2c8f13228aba8939683ce58d97e971 - Modify network protocol to use new delimiter - Much improve dedicated server usability diff -r 5be338fa4e2c -r 596b1dcdc1df QTfrontend/netconnectedclient.cpp --- a/QTfrontend/netconnectedclient.cpp Tue Jul 15 16:40:50 2008 +0000 +++ b/QTfrontend/netconnectedclient.cpp Mon Jul 21 09:45:40 2008 +0000 @@ -47,19 +47,22 @@ void HWConnectedClient::ClientRead() { try { - while (m_client->canReadLine()) { - ParseLine(m_client->readLine().trimmed()); - } + while (m_client->canReadLine()) { + QString s = QString::fromUtf8(m_client->readLine().trimmed()); + if (s.size() == 0) { + ParseCmd(cmdbuf); + cmdbuf.clear(); + } else + cmdbuf << s; + } } catch(ShouldDisconnectException& e) { m_client->close(); } } -void HWConnectedClient::ParseLine(const QByteArray & line) +void HWConnectedClient::ParseCmd(const QStringList & lst) { - QString msg = QString::fromUtf8 (line.data(), line.size()); - QStringList lst = msg.split(delimeter); -//qDebug() << "Parsing: " << lst; +qDebug() << "Server: Parsing:" << lst; if(!lst.size()) { qWarning("Net server: Bad message"); @@ -101,7 +104,7 @@ if(client_nick=="") { - qWarning(QString("Net server: Message from unnamed client: '%1'").arg(msg).toAscii().data()); + qWarning() << "Net server: Message from unnamed client:" << lst; return; } @@ -117,7 +120,7 @@ if(lst[0]=="HHNUM") { if (lst.size()<4) { - qWarning((QString("Net server: Bad 'HHNUM' message: ")+msg+" size="+QString("%1").arg(lst.size())).toAscii().data()); + qWarning() << "Net server: Bad 'HHNUM' message:" << lst; return; } if(!m_hwserver->isChiefClient(this)) @@ -131,15 +134,16 @@ m_hwserver->hhnum+=newTeamHHNum-oldTeamHHNum; qDebug() << "HHNUM hhnum = " << m_hwserver->hhnum; // create CONFIG_PARAM to save HHNUM at server from lst - lst=QStringList("CONFIG_PARAM") << confstr << lst[3]; - m_hwserver->sendOthers(this, lst.join(QString(delimeter))); - m_hwserver->m_gameCfg[lst[1]]=lst.mid(2); + QStringList tmp = lst; + tmp=QStringList("CONFIG_PARAM") << confstr << lst[3]; + m_hwserver->sendOthers(this, tmp.join(QString(delimeter))); + m_hwserver->m_gameCfg[tmp[1]]=tmp.mid(2); return; } if(lst[0]=="CONFIG_PARAM") { if (lst.size()<3) { - qWarning((QString("Net server: Bad 'CONFIG_PARAM' message: ")+msg).toAscii().data()); + qWarning() << "Net server: Bad 'CONFIG_PARAM' message:" << lst; return; } @@ -156,11 +160,12 @@ qWarning("Net server: Bad 'ADDTEAM' message"); return; } - lst.pop_front(); + QStringList tmp = lst; + tmp.pop_front(); // add team ID static unsigned int netTeamID=0; - lst.insert(1, QString::number(++netTeamID)); + tmp.insert(1, QString::number(++netTeamID)); // hedgehogs num count int maxAdd=18-m_hwserver->hhnum; @@ -173,21 +178,21 @@ m_hwserver->hhnum+=toAdd; qDebug() << "to add = " << toAdd << "m_hwserver->hhnum = " << m_hwserver->hhnum; // hedgehogs num config - QString hhnumCfg=QString("CONFIG_PARAM%1HHNUM+%2+%3%1%4").arg(delimeter).arg(lst[0])\ + QString hhnumCfg=QString("CONFIG_PARAM%1HHNUM+%2+%3%1%4").arg(delimeter).arg(tmp[0])\ .arg(netTeamID)\ .arg(toAdd); // creating color config for new team - QString colorCfg=QString("CONFIG_PARAM%1TEAM_COLOR+%2+%3%1%4").arg(delimeter).arg(lst[0])\ + QString colorCfg=QString("CONFIG_PARAM%1TEAM_COLOR+%2+%3%1%4").arg(delimeter).arg(tmp[0])\ .arg(netTeamID)\ - .arg(lst.takeAt(2)); + .arg(tmp.takeAt(2)); m_hwserver->m_gameCfg[colorCfg.split(delimeter)[1]]=colorCfg.split(delimeter).mid(2); m_hwserver->m_gameCfg[hhnumCfg.split(delimeter)[1]]=hhnumCfg.split(delimeter).mid(2); - m_teamsCfg.push_back(lst); + m_teamsCfg.push_back(tmp); - m_hwserver->sendOthers(this, QString("ADDTEAM:")+delimeter+lst.join(QString(delimeter))); - RawSendNet(QString("TEAM_ACCEPTED%1%2%1%3").arg(delimeter).arg(lst[0]).arg(lst[1])); + m_hwserver->sendOthers(this, QString("ADDTEAM:")+delimeter+tmp.join(QString(delimeter))); + RawSendNet(QString("TEAM_ACCEPTED%1%2%1%3").arg(delimeter).arg(tmp[0]).arg(tmp[1])); m_hwserver->sendAll(colorCfg); m_hwserver->sendAll(hhnumCfg); return; @@ -219,7 +224,7 @@ return; } - m_hwserver->sendOthers(this, msg); + m_hwserver->sendOthers(this, lst.join(QString(delimeter))); } unsigned int HWConnectedClient::removeTeam(const QString& tname) @@ -251,7 +256,7 @@ void HWConnectedClient::RawSendNet(const QByteArray & buf) { m_client->write(buf); - m_client->write("\n", 1); + m_client->write("\n\n", 2); } QString HWConnectedClient::getClientNick() const diff -r 5be338fa4e2c -r 596b1dcdc1df QTfrontend/netconnectedclient.h --- a/QTfrontend/netconnectedclient.h Tue Jul 15 16:40:50 2008 +0000 +++ b/QTfrontend/netconnectedclient.h Mon Jul 21 09:45:40 2008 +0000 @@ -22,6 +22,7 @@ #include #include #include +#include class HWNetServer; class QTcpSocket; @@ -49,7 +50,7 @@ class ShouldDisconnectException {}; QString client_nick; - void ParseLine(const QByteArray & line); + void ParseCmd(const QStringList & lst); unsigned int removeTeam(const QString& tname); // returns netID HWNetServer* m_hwserver; @@ -58,7 +59,7 @@ void RawSendNet(const QString & buf); void RawSendNet(const QByteArray & buf); - //QByteArray readbuffer; + QStringList cmdbuf; signals: void HWClientDisconnected(HWConnectedClient* client); diff -r 5be338fa4e2c -r 596b1dcdc1df QTfrontend/newnetclient.cpp --- a/QTfrontend/newnetclient.cpp Tue Jul 15 16:40:50 2008 +0000 +++ b/QTfrontend/newnetclient.cpp Mon Jul 21 09:45:40 2008 +0000 @@ -26,7 +26,7 @@ #include "gamecfgwidget.h" #include "teamselect.h" -char delimeter=0x17; +char delimeter='\n'; HWNewNet::HWNewNet(GameUIConfig * config, GameCFGWidget* pGameCFGWidget, TeamSelWidget* pTeamSelWidget) : config(config), @@ -54,9 +54,14 @@ NetSocket.disconnectFromHost(); } -void HWNewNet::JoinGame(const QString & game) +void HWNewNet::CreateRoom(const QString & room) { - RawSendNet(QString("JOIN%1%2").arg(delimeter).arg(game)); + RawSendNet(QString("CREATE%1%2").arg(delimeter).arg(room)); +} + +void HWNewNet::JoinRoom(const QString & room) +{ + RawSendNet(QString("JOIN%1%2").arg(delimeter).arg(room)); } void HWNewNet::AddTeam(const HWTeam & team) @@ -87,7 +92,6 @@ { QString msg = QString(buf.toBase64()); - //NetBuffer += buf; RawSendNet(QString("GAMEMSG:%1%2").arg(delimeter).arg(msg)); } @@ -100,19 +104,27 @@ { qDebug() << "Client: " << buf; NetSocket.write(buf); - NetSocket.write("\n", 1); + NetSocket.write("\n\n", 2); } void HWNewNet::ClientRead() { - while (NetSocket.canReadLine()) { - ParseLine(NetSocket.readLine().trimmed()); - } + while (NetSocket.canReadLine()) { + QString s = QString::fromUtf8(NetSocket.readLine().trimmed()); + + if (s.size() == 0) { + ParseCmd(cmdbuf); + cmdbuf.clear(); + } else + cmdbuf << s; + } } void HWNewNet::OnConnect() { RawSendNet(QString("NICK%1%2").arg(delimeter).arg(mynick)); + RawSendNet(QString("PROTO%1%2").arg(delimeter).arg(*cProtoVer)); + RawSendNet(QString("CREATE%1%2").arg(delimeter).arg("myroom")); } void HWNewNet::OnDisconnect() @@ -141,13 +153,16 @@ } } -void HWNewNet::ParseLine(const QByteArray & line) +void HWNewNet::ParseCmd(const QStringList & lst) { -qDebug() << "Server: " << line; - QString msg = QString::fromUtf8 (line.data(), line.size()); +qDebug() << "Server: " << lst; - QStringList lst = msg.split(delimeter); -//qDebug() << "Parsing: " << lst; + if(!lst.size()) + { + qWarning("Net client: Bad message"); + return; + } + if (lst[0] == "ERRONEUSNICKNAME") { QMessageBox::information(0, 0, "Your net nickname is in use or cannot be used"); return; @@ -161,13 +176,14 @@ } if (lst[0] == "CHAT_STRING") { - lst.pop_front(); - if(lst.size() < 2) + if(lst.size() < 3) { qWarning("Net: Empty CHAT_STRING message"); return; } - emit chatStringFromNet(lst); + QStringList tmp = lst; + tmp.removeFirst(); + emit chatStringFromNet(tmp); return; } @@ -177,8 +193,9 @@ qWarning("Net: Too short ADDTEAM message"); return; } - lst.pop_front(); - emit AddNetTeam(lst); + QStringList tmp = lst; + tmp.removeFirst(); + emit AddNetTeam(tmp); return; } @@ -230,18 +247,19 @@ } if (lst[0] == "CONFIGURED") { - lst.pop_front(); - if(lst.size() < 6) + QStringList tmp = lst; + tmp.removeFirst(); + if(tmp.size() < 6) { qWarning("Net: Bad CONFIGURED message"); return; } - emit seedChanged(lst[0]); - emit mapChanged(lst[1]); - emit themeChanged(lst[2]); - emit initHealthChanged(lst[3].toUInt()); - emit turnTimeChanged(lst[4].toUInt()); - emit fortsModeChanged(lst[5].toInt() != 0); + emit seedChanged(tmp[0]); + emit mapChanged(tmp[1]); + emit themeChanged(tmp[2]); + emit initHealthChanged(tmp[3].toUInt()); + emit turnTimeChanged(tmp[4].toUInt()); + emit fortsModeChanged(tmp[5].toInt() != 0); return; } @@ -310,7 +328,7 @@ emit hhnumChanged(tmptm); return; } - qWarning(QString("Net: Unknown 'CONFIG_PARAM' message: '%1'").arg(msg).toAscii().data()); + qWarning() << "Net: Unknown 'CONFIG_PARAM' message:" << lst; return; } @@ -328,7 +346,7 @@ return; } - qWarning(QString("Net: Unknown message: '%1'").arg(msg).toAscii().data()); + qWarning() << "Net: Unknown message:" << lst; } diff -r 5be338fa4e2c -r 596b1dcdc1df QTfrontend/newnetclient.h --- a/QTfrontend/newnetclient.h Tue Jul 15 16:40:50 2008 +0000 +++ b/QTfrontend/newnetclient.h Mon Jul 21 09:45:40 2008 +0000 @@ -41,7 +41,8 @@ HWNewNet(GameUIConfig * config, GameCFGWidget* pGameCFGWidget, TeamSelWidget* pTeamSelWidget); void Connect(const QString & hostName, quint16 port, const QString & nick); void Disconnect(); - void JoinGame(const QString & game); + void JoinRoom(const QString & room); + void CreateRoom(const QString & room); void StartGame(); private: @@ -79,9 +80,11 @@ emit FromNet(enginemsg); } + QStringList cmdbuf; + void RawSendNet(const QString & buf); void RawSendNet(const QByteArray & buf); - void ParseLine(const QByteArray & line); + void ParseCmd(const QStringList & lst); signals: void AskForRunGame(); diff -r 5be338fa4e2c -r 596b1dcdc1df netserver/HWProto.hs --- a/netserver/HWProto.hs Tue Jul 15 16:40:50 2008 +0000 +++ b/netserver/HWProto.hs Mon Jul 21 09:45:40 2008 +0000 @@ -6,94 +6,85 @@ import Miscutils import Maybe (fromMaybe, fromJust) --- 'noInfo' clients state command handlers -handleCmd_noInfo :: Handle -> [ClientInfo] -> [RoomInfo] -> [String] -> ([ClientInfo], [RoomInfo], [Handle], [String]) +-- Main state-independent cmd handler +handleCmd :: CmdHandler +handleCmd client _ rooms ("QUIT":xs) = + if null (room client) then + (noChangeClients, noChangeRooms, clientOnly, ["QUIT"]) + else if isMaster client then + (noChangeClients, removeRoom (room client), sameRoom, ["ROOMABANDONED"]) -- core disconnects clients on ROOMABANDONED command + else + (noChangeClients, noChangeRooms, sameRoom, ["QUIT", nick client]) -handleCmd_noInfo clhandle clients rooms ("NICK":newNick:[]) = +-- check state and call state-dependent commmand handlers +handleCmd client clients rooms cmd = + if null (nick client) || protocol client == 0 then + handleCmd_noInfo client clients rooms cmd + else if null (room client) then + handleCmd_noRoom client clients rooms cmd + else + handleCmd_inRoom client clients rooms cmd + +-- 'no info' state - need to get protocol number and nickname +handleCmd_noInfo :: CmdHandler +handleCmd_noInfo client clients _ ["NICK", newNick] = if not . null $ nick client then - (clients, rooms, [clhandle], ["ERROR", "The nick already chosen"]) + (noChangeClients, noChangeRooms, clientOnly, ["ERROR", "The nick already chosen"]) else if haveSameNick then - (clients, rooms, [clhandle], ["WARNING", "Choose another nick"]) + (noChangeClients, noChangeRooms, clientOnly, ["WARNING", "Choose another nick"]) else - (modifyClient clhandle clients (\cl -> cl{nick = newNick}), rooms, [clhandle], ["NICK", newNick]) + (modifyClient client{nick = newNick}, noChangeRooms, clientOnly, ["NICK", newNick]) where haveSameNick = not . null $ filter (\cl -> newNick == nick cl) clients - client = clientByHandle clhandle clients -handleCmd_noInfo clhandle clients rooms ("PROTO":protoNum:[]) = +handleCmd_noInfo client _ _ ["PROTO", protoNum] = if protocol client > 0 then - (clients, rooms, [clhandle], ["ERROR", "Protocol number already known"]) + (noChangeClients, noChangeRooms, clientOnly, ["ERROR", "Protocol number already known"]) else if parsedProto == 0 then - (clients, rooms, [clhandle], ["ERROR", "Bad input"]) + (noChangeClients, noChangeRooms, clientOnly, ["ERROR", "Bad input"]) else - (modifyClient clhandle clients (\cl -> cl{protocol = parsedProto}), rooms, [], []) + (modifyClient client{protocol = parsedProto}, noChangeRooms, clientOnly, ["PROTO", show parsedProto]) where parsedProto = fromMaybe 0 (maybeRead protoNum :: Maybe Word16) - client = clientByHandle clhandle clients -handleCmd_noInfo clhandle clients rooms _ = (clients, rooms, [clhandle], ["ERROR", "Bad command or incorrect parameter"]) - +handleCmd_noInfo _ _ _ _ = (noChangeClients, noChangeRooms, clientOnly, badCmd) -- 'noRoom' clients state command handlers -handleCmd_noRoom :: Handle -> [ClientInfo] -> [RoomInfo] -> [String] -> ([ClientInfo], [RoomInfo], [Handle], [String]) - -handleCmd_noRoom clhandle clients rooms ("LIST":[]) = - (clients, rooms, [clhandle], ["ROOMS"] ++ map (\r -> name r) rooms) +handleCmd_noRoom :: CmdHandler +handleCmd_noRoom client _ rooms ["LIST"] = + (noChangeClients, noChangeRooms, clientOnly, ["ROOMS"] ++ map name rooms) -handleCmd_noRoom clhandle clients rooms ("CREATE":newRoom:roomPassword:[]) = +handleCmd_noRoom client _ rooms ["CREATE", newRoom, roomPassword] = if haveSameRoom then - (clients, rooms, [clhandle], ["WARNING", "There's already a room with that name"]) + (noChangeClients, noChangeRooms, clientOnly, ["WARNING", "There's already a room with that name"]) else - (modifyClient clhandle clients (\cl -> cl{room = newRoom, isMaster = True}), (RoomInfo newRoom roomPassword):rooms, [clhandle], ["JOINS", nick client]) + (modifyClient client{room = newRoom, isMaster = True}, addRoom (RoomInfo newRoom roomPassword), clientOnly, ["JOINED", nick client]) where haveSameRoom = not . null $ filter (\room -> newRoom == name room) rooms - client = clientByHandle clhandle clients -handleCmd_noRoom clhandle clients rooms ("CREATE":newRoom:[]) = - handleCmd_noRoom clhandle clients rooms ["CREATE", newRoom, ""] - -handleCmd_noRoom clhandle clients rooms ("JOIN":roomName:roomPassword:[]) = +handleCmd_noRoom client clients rooms ["CREATE", newRoom] = + handleCmd_noRoom client clients rooms ["CREATE", newRoom, ""] + +handleCmd_noRoom client _ rooms ["JOIN", roomName, roomPassword] = if noSuchRoom then - (clients, rooms, [clhandle], ["WARNING", "There's no room with that name"]) + (noChangeClients, noChangeRooms, clientOnly, ["WARNING", "There's no room with that name"]) else if roomPassword /= password (roomByName roomName rooms) then - (clients, rooms, [clhandle], ["WARNING", "Wrong password"]) + (noChangeClients, noChangeRooms, clientOnly, ["WARNING", "Wrong password"]) else - (modifyClient clhandle clients (\cl -> cl{room = roomName}), rooms, clhandle : (fromRoomHandles roomName clients), ["JOINS", nick client]) + (modifyClient client{room = roomName}, noChangeRooms, fromRoom roomName, ["JOINED", nick client]) where noSuchRoom = null $ filter (\room -> roomName == name room) rooms - client = clientByHandle clhandle clients -handleCmd_noRoom clhandle clients rooms ("JOIN":roomName:[]) = - handleCmd_noRoom clhandle clients rooms ["JOIN", roomName, ""] +handleCmd_noRoom client clients rooms ["JOIN", roomName] = + handleCmd_noRoom client clients rooms ["JOIN", roomName, ""] -handleCmd_noRoom clhandle clients rooms _ = (clients, rooms, [clhandle], ["ERROR", "Bad command or incorrect parameter"]) +handleCmd_noRoom _ _ _ _ = (noChangeClients, noChangeRooms, clientOnly, badCmd) -- 'inRoom' clients state command handlers -handleCmd_inRoom :: Handle -> [ClientInfo] -> [RoomInfo] -> [String] -> ([ClientInfo], [RoomInfo], [Handle], [String]) +handleCmd_inRoom :: CmdHandler -handleCmd_inRoom clhandle clients rooms _ = (clients, rooms, [clhandle], ["ERROR", "Bad command or incorrect parameter"]) - --- state-independent command handlers -handleCmd :: Handle -> [ClientInfo] -> [RoomInfo] -> [String] -> ([ClientInfo], [RoomInfo], [Handle], [String]) +handleCmd_inRoom client _ _ ["CHAT_STRING", _, msg] = (noChangeClients, noChangeRooms, othersInRoom, ["CHAT_STRING", nick client, msg]) -handleCmd clhandle clients rooms ("QUIT":xs) = - if null (room client) then - (clients, rooms, [clhandle], ["QUIT"]) - else if isMaster client then - (clients, filter (\rm -> room client /= name rm) rooms, roomMates, ["ROOMABANDONED"]) -- core disconnects clients on ROOMABANDONED command - else - (clients, rooms, roomMates, ["QUIT", nick client]) - where - client = clientByHandle clhandle clients - roomMates = fromRoomHandles (room client) clients +handleCmd_inRoom client clients rooms ["CONFIG_PARAM", paramName, value] = (noChangeClients, noChangeRooms, othersInRoom, ["CONFIG_PARAM", paramName, value]) --- check state and call state-dependent commmand handlers -handleCmd clhandle clients rooms cmd = - if null (nick client) || protocol client == 0 then - handleCmd_noInfo clhandle clients rooms cmd - else if null (room client) then - handleCmd_noRoom clhandle clients rooms cmd - else - handleCmd_inRoom clhandle clients rooms cmd - where - client = clientByHandle clhandle clients +handleCmd_inRoom _ _ _ _ = (noChangeClients, noChangeRooms, clientOnly, badCmd) diff -r 5be338fa4e2c -r 596b1dcdc1df netserver/Miscutils.hs --- a/netserver/Miscutils.hs Tue Jul 15 16:40:50 2008 +0000 +++ b/netserver/Miscutils.hs Mon Jul 21 09:45:40 2008 +0000 @@ -9,9 +9,9 @@ data ClientInfo = - ClientInfo + ClientInfo { - chan :: TChan String, + chan :: TChan [String], handle :: Handle, nick :: String, protocol :: Word16, @@ -19,6 +19,9 @@ isMaster :: Bool } +instance Eq ClientInfo where + a1 == a2 = handle a1 == handle a2 + data RoomInfo = RoomInfo { @@ -26,24 +29,17 @@ password :: String } -clientByHandle :: Handle -> [ClientInfo] -> ClientInfo -clientByHandle clhandle clients = fromJust $ find (\ci -> handle ci == clhandle) clients +type ClientsTransform = [ClientInfo] -> [ClientInfo] +type RoomsTransform = [RoomInfo] -> [RoomInfo] +type HandlesSelector = ClientInfo -> [ClientInfo] -> [RoomInfo] -> [Handle] +type CmdHandler = ClientInfo -> [ClientInfo] -> [RoomInfo] -> [String] -> (ClientsTransform, RoomsTransform, HandlesSelector, [String]) + roomByName :: String -> [RoomInfo] -> RoomInfo roomByName roomName rooms = fromJust $ find (\room -> roomName == name room) rooms -fromRoomHandles :: String -> [ClientInfo] -> [Handle] -fromRoomHandles roomName clients = map (\ci -> handle ci) $ filter (\ci -> room ci == roomName) clients - -modifyClient :: Handle -> [ClientInfo] -> (ClientInfo -> ClientInfo) -> [ClientInfo] -modifyClient clhandle (cl:cls) func = - if handle cl == clhandle then - (func cl) : cls - else - cl : (modifyClient clhandle cls func) - -tselect :: [ClientInfo] -> STM (String, Handle) -tselect = foldl orElse retry . map (\ci -> (flip (,) $ handle ci) `fmap` readTChan (chan ci)) +tselect :: [ClientInfo] -> STM ([String], ClientInfo) +tselect = foldl orElse retry . map (\ci -> (flip (,) ci) `fmap` readTChan (chan ci)) maybeRead :: Read a => String -> Maybe a maybeRead s = case reads s of @@ -56,3 +52,37 @@ deleteFirstsBy2t :: (a -> b -> Bool) -> [a] -> [b] -> [a] deleteFirstsBy2t eq = foldl (flip (deleteBy2t eq)) + +sameRoom :: HandlesSelector +sameRoom client clients rooms = map handle $ filter (\ci -> room ci == room client) clients + +othersInRoom :: HandlesSelector +othersInRoom client clients rooms = map handle $ filter (client /=) $ filter (\ci -> room ci == room client) clients + +fromRoom :: String -> HandlesSelector +fromRoom roomName _ clients _ = map handle $ filter (\ci -> room ci == roomName) clients + +clientOnly :: HandlesSelector +clientOnly client _ _ = [handle client] + +noChangeClients :: ClientsTransform +noChangeClients a = a + +modifyClient :: ClientInfo -> ClientsTransform +modifyClient client (cl:cls) = + if cl == client then + client : cls + else + cl : (modifyClient client cls) + +noChangeRooms :: RoomsTransform +noChangeRooms a = a + +addRoom :: RoomInfo -> RoomsTransform +addRoom room rooms = room:rooms + +removeRoom :: String -> RoomsTransform +removeRoom roomname rooms = filter (\rm -> roomname /= name rm) rooms + +badCmd :: [String] +badCmd = ["ERROR", "Bad command, state or incorrect parameter"] diff -r 5be338fa4e2c -r 596b1dcdc1df netserver/newhwserv.hs --- a/netserver/newhwserv.hs Tue Jul 15 16:40:50 2008 +0000 +++ b/netserver/newhwserv.hs Mon Jul 21 09:45:40 2008 +0000 @@ -17,20 +17,23 @@ cChan <- atomically newTChan forkIO $ clientLoop cHandle cChan atomically $ writeTChan acceptChan (ClientInfo cChan cHandle "" 0 "" False) - hPutStrLn cHandle "CONNECTED" + hPutStrLn cHandle "CONNECTED\n" acceptLoop servSock acceptChan -listenLoop :: Handle -> TChan String -> IO () -listenLoop handle chan = do +listenLoop :: Handle -> [String] -> TChan [String] -> IO () +listenLoop handle buf chan = do str <- hGetLine handle - atomically $ writeTChan chan str - listenLoop handle chan + if str == "" then do + atomically $ writeTChan chan buf + listenLoop handle [] chan + else + listenLoop handle (buf ++ [str]) chan -clientLoop :: Handle -> TChan String -> IO () +clientLoop :: Handle -> TChan [String] -> IO () clientLoop handle chan = - listenLoop handle chan + listenLoop handle [] chan `catch` (const $ clientOff >> return ()) - where clientOff = atomically $ writeTChan chan "QUIT" + where clientOff = atomically $ writeTChan chan ["QUIT"] mainLoop :: Socket -> TChan ClientInfo -> [ClientInfo] -> [RoomInfo] -> IO () mainLoop servSock acceptChan clients rooms = do @@ -38,17 +41,24 @@ case r of Left ci -> do mainLoop servSock acceptChan (ci:clients) rooms - Right (line, clhandle) -> do - let (mclients, mrooms, recipients, strs) = handleCmd clhandle clients rooms $ words line + Right (cmd, client) -> do + print ("> " ++ show cmd) + let (clientsFunc, roomsFunc, handlesFunc, answer) = handleCmd client clients rooms $ cmd + print ("< " ++ show answer) + let mclients = clientsFunc clients + let mrooms = roomsFunc rooms + let recipients = handlesFunc client clients rooms + clHandles' <- forM recipients $ \ch -> do - forM_ strs (\str -> hPutStrLn ch str) + forM_ answer (\str -> hPutStrLn ch str) + hPutStrLn ch "" hFlush ch - if (not $ null strs) && (head strs == "ROOMABANDONED") then hClose ch >> return [ch] else return [] + if (not $ null answer) && (head answer == "ROOMABANDONED") then hClose ch >> return [ch] else return [] `catch` const (hClose ch >> return [ch]) - clHandle' <- if (not $ null strs) && (head strs == "QUIT") then hClose clhandle >> return [clhandle] else return [] + clHandle' <- if (not $ null answer) && (head answer == "QUIT") then hClose (handle client) >> return [handle client] else return [] mainLoop servSock acceptChan (remove (remove mclients (concat clHandles')) clHandle') mrooms where