--- 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)
--- 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);
--- 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);
--- 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
--- 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 <nickname>, map <name>"]]
+ return [AnswerClients [sendChan cl] ["CHAT", "[server]", "Available callvote commands: kick <nickname>, map <name>, 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
--- 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"]
--- 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
--- 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);
--- 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);
--- 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
--- 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}