# HG changeset patch # User alfadur # Date 1408654627 -14400 # Node ID 5012e1f9e89314c50667329f1e57c1ff65a9f1eb # Parent ce3ccc45d7909fe8255d68464f7d3746c7c0d172 - Support for pausing multiplayer games - Some code cleanup diff -r ce3ccc45d790 -r 5012e1f9e893 QTfrontend/game.cpp --- a/QTfrontend/game.cpp Fri Aug 22 00:37:26 2014 +0400 +++ b/QTfrontend/game.cpp Fri Aug 22 00:57:07 2014 +0400 @@ -312,6 +312,13 @@ config->Form->ui.pageOptions->windowHeightEdit->setValue(wh[1].toInt()); break; } + case '~': + { + int size = msg.size(); + QString msgbody = QString::fromUtf8(msg.mid(2).left(size - 4)); + emit SendConsoleCommand(msgbody); + break; + } default: { if (gameType == gtNet && !netSuspend) diff -r ce3ccc45d790 -r 5012e1f9e893 QTfrontend/game.h --- a/QTfrontend/game.h Fri Aug 22 00:37:26 2014 +0400 +++ b/QTfrontend/game.h Fri Aug 22 00:57:07 2014 +0400 @@ -99,6 +99,7 @@ void HaveRecord(RecordType type, const QByteArray & record); void ErrorMessage(const QString &); void CampStateChanged(int); + void SendConsoleCommand(const QString & command); public slots: void FromNet(const QByteArray & msg); diff -r ce3ccc45d790 -r 5012e1f9e893 QTfrontend/hwform.cpp --- a/QTfrontend/hwform.cpp Fri Aug 22 00:37:26 2014 +0400 +++ b/QTfrontend/hwform.cpp Fri Aug 22 00:57:07 2014 +0400 @@ -1753,6 +1753,7 @@ connect(game, SIGNAL(SendNet(const QByteArray &)), hwnet, SLOT(SendNet(const QByteArray &))); connect(game, SIGNAL(SendChat(const QString &)), hwnet, SLOT(chatLineToNet(const QString &))); + connect(game, SIGNAL(SendConsoleCommand(const QString&)), hwnet, SLOT(consoleCommand(const QString&))); connect(game, SIGNAL(SendTeamMessage(const QString &)), hwnet, SLOT(SendTeamMessage(const QString &))); connect(hwnet, SIGNAL(chatStringFromNet(const QString &)), game, SLOT(FromNetChat(const QString &)), Qt::QueuedConnection); diff -r ce3ccc45d790 -r 5012e1f9e893 gameServer/CoreTypes.hs --- a/gameServer/CoreTypes.hs Fri Aug 22 00:37:26 2014 +0400 +++ b/gameServer/CoreTypes.hs Fri Aug 22 00:57:07 2014 +0400 @@ -171,7 +171,8 @@ teamsInGameNumber :: Int, allPlayersHaveRegisteredAccounts :: !Bool, giMapParams :: Map.Map B.ByteString B.ByteString, - giParams :: Map.Map B.ByteString [B.ByteString] + giParams :: Map.Map B.ByteString [B.ByteString], + isPaused :: Bool } deriving (Show, Read) newGameInfo :: [TeamInfo] @@ -179,6 +180,7 @@ -> Bool -> Map.Map ByteString ByteString -> Map.Map ByteString [ByteString] + -> Bool -> GameInfo newGameInfo = GameInfo @@ -298,6 +300,7 @@ data VoteType = VoteKick B.ByteString | VoteMap B.ByteString + | VotePause newVoting :: VoteType -> Voting diff -r ce3ccc45d790 -r 5012e1f9e893 gameServer/HWProtoInRoomState.hs --- a/gameServer/HWProtoInRoomState.hs Fri Aug 22 00:37:26 2014 +0400 +++ b/gameServer/HWProtoInRoomState.hs Fri Aug 22 00:57:07 2014 +0400 @@ -30,7 +30,7 @@ return [ ModifyRoom (\r -> r{ - gameInfo = Just $ newGameInfo (teams rm) (length $ teams rm) allPlayersRegistered (mapParams rm) (params rm) + gameInfo = Just $ newGameInfo (teams rm) (length $ teams rm) allPlayersRegistered (mapParams rm) (params rm) False } ) , AnswerClients chans ["RUN_GAME"] @@ -374,7 +374,7 @@ handleCmd_inRoom ["CALLVOTE"] = do cl <- thisClient - return [AnswerClients [sendChan cl] ["CHAT", "[server]", "Available callvote commands: kick , map "]] + return [AnswerClients [sendChan cl] ["CHAT", "[server]", "Available callvote commands: kick , map , pause"]] handleCmd_inRoom ["CALLVOTE", "KICK"] = do cl <- thisClient @@ -412,6 +412,14 @@ else return [AnswerClients [sendChan cl] ["CHAT", "[server]", "callvote map: no such map"]] +handleCmd_inRoom ["CALLVOTE", "PAUSE"] = do + cl <- thisClient + rm <- thisRoom + + if isJust $ gameInfo rm then + startVote VotePause + else + return [AnswerClients [sendChan cl] ["CHAT", "[server]", "callvote pause: no game in progress"]] handleCmd_inRoom ["VOTE", m] = do cl <- thisClient diff -r ce3ccc45d790 -r 5012e1f9e893 gameServer/Votes.hs --- a/gameServer/Votes.hs Fri Aug 22 00:37:26 2014 +0400 +++ b/gameServer/Votes.hs Fri Aug 22 00:57:07 2014 +0400 @@ -12,6 +12,7 @@ import Utils import CoreTypes import HandlerUtils +import EngineInteraction voted :: Bool -> Reader (ClientIndex, IRnC) [Action] @@ -20,23 +21,27 @@ rm <- thisRoom uid <- liftM clUID thisClient - if isNothing $ voting rm then - return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "There's no voting going on"]] - else if uid `L.notElem` entitledToVote (fromJust $ voting rm) then - return [] - else if uid `L.elem` map fst (votes . fromJust $ voting rm) then - return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "You already have voted"]] - else - actOnVoting . fromJust . liftM (\v -> v{votes = (uid, vote):votes v}) $ voting rm + case voting rm of + Nothing -> + return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "There's no voting going on"]] + Just voting -> + if uid `L.notElem` entitledToVote voting then + return [] + else if uid `L.elem` map fst (votes voting) then + return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "You already have voted"]] + else + actOnVoting $ voting{votes = (uid, vote):votes voting} + where actOnVoting :: Voting -> Reader (ClientIndex, IRnC) [Action] actOnVoting vt = do let (pro, contra) = L.partition snd $ votes vt - let v = (length $ entitledToVote vt) `div` 2 + 1 + let totalV = length $ entitledToVote vt + let successV = totalV `div` 2 + 1 - if length contra >= v then + if length contra > totalV - successV then closeVoting - else if length pro >= v then do + else if length pro >= successV then do a <- act $ voteType vt c <- closeVoting return $ c ++ a @@ -79,6 +84,13 @@ where replaceChans chans (AnswerClients _ msg) = AnswerClients chans msg replaceChans _ a = a + act (VotePause) = do + rm <- thisRoom + chans <- roomClientsChans + let modifyGameInfo f room = room{gameInfo = fmap f $ gameInfo room} + return [ModifyRoom (modifyGameInfo $ \g -> g{isPaused = not $ isPaused g}), + AnswerClients chans ["CHAT", "[server]", "Pause toggled"], + AnswerClients chans ["EM", toEngineMsg "I"]] startVote :: VoteType -> Reader (ClientIndex, IRnC) [Action] @@ -123,3 +135,4 @@ voteInfo :: VoteType -> B.ByteString voteInfo (VoteKick n) = B.concat [loc "kick", " ", n] voteInfo (VoteMap n) = B.concat [loc "map", " ", n] +voteInfo (VotePause) = B.concat [loc "pause"] diff -r ce3ccc45d790 -r 5012e1f9e893 hedgewars/uChat.pas --- a/hedgewars/uChat.pas Fri Aug 22 00:37:26 2014 +0400 +++ b/hedgewars/uChat.pas Fri Aug 22 00:57:07 2014 +0400 @@ -262,6 +262,12 @@ ParseCommand('/hogsay '+s, true) end; +procedure SendConsoleCommand(s: shortstring); +begin + Delete(s, 1, 1); + SendIPC('~' + s) +end; + procedure AcceptChatString(s: shortstring); var i: TWave; j: TChatCmd; @@ -383,7 +389,10 @@ ParseCommand(ChatCommandz[j].ProcedureCallChatCmd, true); exit end; - end + end; + + if (gameType = gmtNet) then + SendConsoleCommand(s) end else begin diff -r ce3ccc45d790 -r 5012e1f9e893 hedgewars/uCommandHandlers.pas --- a/hedgewars/uCommandHandlers.pas Fri Aug 22 00:37:26 2014 +0400 +++ b/hedgewars/uCommandHandlers.pas Fri Aug 22 00:57:07 2014 +0400 @@ -26,7 +26,7 @@ procedure freeModule; implementation -uses uCommands, uTypes, uVariables, uIO, uDebug, uConsts, uScript, uUtils, SDLh, uRandom, uCaptions +uses uCommands, uTypes, uVariables, uIO, uDebug, uConsts, uScript, uUtils, SDLh, uWorld, uRandom, uCaptions {$IFDEF USE_VIDEO_RECORDING}, uVideoRec {$ENDIF}; var prevGState: TGameState = gsConfirm; @@ -52,14 +52,12 @@ begin prevGState:= GameState; GameState:= gsConfirm; - SDL_ShowCursor(1) end else - if GameState = gsConfirm then - begin - GameState:= prevGState; - SDL_ShowCursor(ord(isPaused)) - end + if GameState = gsConfirm then + GameState:= prevGState; + + updateCursorVisibility; end; procedure chForceQuit(var s: shortstring); @@ -176,7 +174,7 @@ procedure chLeft_p(var s: shortstring); begin s:= s; // avoid compiler hint -if CheckNoTeamOrHH or isPaused then +if CheckNoTeamOrHH then exit; if not isExternalSource then SendIPC(_S'L'); @@ -201,7 +199,7 @@ procedure chRight_p(var s: shortstring); begin s:= s; // avoid compiler hint -if CheckNoTeamOrHH or isPaused then +if CheckNoTeamOrHH then exit; if not isExternalSource then SendIPC(_S'R'); @@ -226,7 +224,7 @@ procedure chUp_p(var s: shortstring); begin s:= s; // avoid compiler hint -if CheckNoTeamOrHH or isPaused then +if CheckNoTeamOrHH then exit; if not isExternalSource then SendIPC(_S'U'); @@ -251,7 +249,7 @@ procedure chDown_p(var s: shortstring); begin s:= s; // avoid compiler hint -if CheckNoTeamOrHH or isPaused then +if CheckNoTeamOrHH then exit; if not isExternalSource then SendIPC(_S'D'); @@ -276,7 +274,7 @@ procedure chPrecise_p(var s: shortstring); begin s:= s; // avoid compiler hint -if CheckNoTeamOrHH or isPaused then +if CheckNoTeamOrHH then exit; if not isExternalSource then SendIPC(_S'Z'); @@ -301,7 +299,7 @@ procedure chLJump(var s: shortstring); begin s:= s; // avoid compiler hint -if CheckNoTeamOrHH or isPaused then +if CheckNoTeamOrHH then exit; if not isExternalSource then SendIPC(_S'j'); @@ -314,7 +312,7 @@ procedure chHJump(var s: shortstring); begin s:= s; // avoid compiler hint -if CheckNoTeamOrHH or isPaused then +if CheckNoTeamOrHH then exit; if not isExternalSource then SendIPC(_S'J'); @@ -327,7 +325,7 @@ procedure chAttack_p(var s: shortstring); begin s:= s; // avoid compiler hint -if CheckNoTeamOrHH or isPaused then +if CheckNoTeamOrHH then exit; bShowFinger:= false; with CurrentHedgehog^.Gear^ do @@ -362,7 +360,7 @@ procedure chSwitch(var s: shortstring); begin s:= s; // avoid compiler hint -if CheckNoTeamOrHH or isPaused then +if CheckNoTeamOrHH then exit; if not isExternalSource then SendIPC(_S'S'); @@ -576,7 +574,7 @@ procedure chFindhh(var s: shortstring); begin s:= s; // avoid compiler hint -if CheckNoTeamOrHH or isPaused then +if CheckNoTeamOrHH then exit; if autoCameraOn then @@ -597,8 +595,7 @@ procedure chPause(var s: shortstring); begin -s:= s; // avoid compiler hint -if gameType <> gmtNet then +if (gameType <> gmtNet) or (s = 'server') then isPaused:= not isPaused else if (CurrentTeam^.ExtDriven) or (CurrentHedgehog^.BotLevel > 0) then @@ -606,10 +603,7 @@ else isAFK:= false; // for real ninjas -if isPaused or isAFK then - SDL_ShowCursor(1) - else - SDL_ShowCursor(ord(GameState = gsConfirm)) +updateCursorVisibility; end; procedure chRotateMask(var s: shortstring); diff -r ce3ccc45d790 -r 5012e1f9e893 hedgewars/uIO.pas --- a/hedgewars/uIO.pas Fri Aug 22 00:37:26 2014 +0400 +++ b/hedgewars/uIO.pas Fri Aug 22 00:57:07 2014 +0400 @@ -118,9 +118,22 @@ WriteLnToConsole(msgOK) end; +procedure ParseChatCommand(command: shortstring; message: shortstring; + messageStartIndex: Byte); +var + text: shortstring; +begin + text:= copy(message, messageStartIndex, + Length(message) - messageStartIndex + 1); + ParseCommand(command + text, true); + WriteLnToConsole(text) +end; + procedure ParseIPCCommand(s: shortstring); var loTicks: Word; + isProcessed: boolean; begin +isProcessed := true; case s[1] of '!': begin AddFileLog('Ping? Pong!'); isPonged:= true; end; @@ -140,12 +153,26 @@ 'V': begin if s[2] = '.' then ParseCommand('campvar ' + copy(s, 3, length(s) - 2), true); - end + end; + 'I': ParseCommand('pause server', true); + 's': if gameType = gmtNet then + ParseChatCommand('chatmsg ', s, 2) + else + isProcessed:= false; + 'b': if gameType = gmtNet then + ParseChatCommand('chatmsg ' + #4, s, 2) + else + isProcessed:= false; else - loTicks:= SDLNet_Read16(@s[byte(s[0]) - 1]); - AddCmd(loTicks, s); - AddFileLog('[IPC in] ' + sanitizeCharForLog(s[1]) + ' ticks ' + IntToStr(lastcmd^.loTime)); - end + isProcessed:= false; + end; + + if (not isProcessed) then + begin + loTicks:= SDLNet_Read16(@s[byte(s[0]) - 1]); + AddCmd(loTicks, s); + AddFileLog('[IPC in] ' + sanitizeCharForLog(s[1]) + ' ticks ' + IntToStr(lastcmd^.loTime)); + end end; procedure IPCCheckSock; @@ -357,16 +384,8 @@ s:= copy(headcmd^.str, 2, Pred(headcmd^.len)); ParseCommand('gencmd ' + s, true); end; - 's': begin - s:= copy(headcmd^.str, 2, Pred(headcmd^.len)); - ParseCommand('chatmsg ' + s, true); - WriteLnToConsole(s) - end; - 'b': begin - s:= copy(headcmd^.str, 2, Pred(headcmd^.len)); - ParseCommand('chatmsg ' + #4 + s, true); - WriteLnToConsole(s) - end; + 's': ParseChatCommand('chatmsg ', headcmd^.str, 2); + 'b': ParseChatCommand('chatmsg ' + #4, headcmd^.str, 2); 'F': ParseCommand('teamgone u' + copy(headcmd^.str, 2, Pred(headcmd^.len)), true); 'G': ParseCommand('teamback u' + copy(headcmd^.str, 2, Pred(headcmd^.len)), true); 'f': ParseCommand('teamgone s' + copy(headcmd^.str, 2, Pred(headcmd^.len)), true); diff -r ce3ccc45d790 -r 5012e1f9e893 hedgewars/uInputHandler.pas --- a/hedgewars/uInputHandler.pas Fri Aug 22 00:37:26 2014 +0400 +++ b/hedgewars/uInputHandler.pas Fri Aug 22 00:57:07 2014 +0400 @@ -171,6 +171,8 @@ if (code > 3) and KeyDown and (not ((CurrentBinds[code] = 'put')) or (CurrentBinds[code] = 'ammomenu') or (CurrentBinds[code] = '+cur_u') or (CurrentBinds[code] = '+cur_d') or (CurrentBinds[code] = '+cur_l') or (CurrentBinds[code] = '+cur_r')) and (CurrentTeam <> nil) and (not CurrentTeam^.ExtDriven) then bShowAmmoMenu:= false; if KeyDown then begin + Trusted:= Trusted and (not isPaused); //releasing keys during pause should be allowed on the other hand + if CurrentBinds[code] = 'switch' then LocalMessage:= LocalMessage or gmSwitch else if CurrentBinds[code] = '+precise' then diff -r ce3ccc45d790 -r 5012e1f9e893 hedgewars/uWorld.pas --- a/hedgewars/uWorld.pas Fri Aug 22 00:37:26 2014 +0400 +++ b/hedgewars/uWorld.pas Fri Aug 22 00:57:07 2014 +0400 @@ -40,6 +40,7 @@ procedure animateWidget(widget: POnScreenWidget; fade, showWidget: boolean); procedure MoveCamera; procedure onFocusStateChanged; +procedure updateCursorVisibility; implementation uses @@ -2054,6 +2055,14 @@ end; end; +procedure updateCursorVisibility; +begin + if isPaused or isAFK then + SDL_ShowCursor(1) + else + SDL_ShowCursor(ord(GameState = gsConfirm)) +end; + procedure SetUtilityWidgetState(ammoType: TAmmoType); begin {$IFDEF USE_TOUCH_INTERFACE}