Ticket #1950: pausing_3_wip.patch
File pausing_3_wip.patch, 20.4 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/gui/common/functions_global_object.js
function updateCounters() 147 147 function displayGamestateNotifications() 148 148 { 149 149 let messages = []; 150 150 let maxTextWidth = 0; 151 151 152 // TODO: Players who paused the game should be added here153 154 152 // Add network warnings 155 153 if (Engine.ConfigDB_GetValue("user", "overlay.netwarnings") == "true") 156 154 { 157 155 let netwarnings = getNetworkWarnings(); 158 156 messages = messages.concat(netwarnings.messages); -
binaries/data/mods/public/gui/credits/texts/programming.json
54 54 {"nick": "Deiz"}, 55 55 {"nick": "Dietger", "name": "Dietger van Antwerpen"}, 56 56 {"nick": "dumbo"}, 57 57 {"nick": "dvangennip", "name": "Doménique"}, 58 58 {"nick": "Echelon9", "name": "Rhys Kidd"}, 59 {"nick": "echotangoecho"}, 59 60 {"nick": "eihrul", "name": "Lee Salzman"}, 60 61 {"nick": "elexis", "name": "Alexander Heinsius"}, 61 62 {"nick": "EmjeR", "name": "Matthijs de Rijk"}, 62 63 {"nick": "EMontana"}, 63 64 {"nick": "ericb"}, -
binaries/data/mods/public/gui/session/menu.js
function openStrucTree() 668 668 "callback": "resumeGame", 669 669 }); 670 670 } 671 671 672 672 /** 673 * Pause the game in single player mode. 673 * Pause or resume the game. 674 * 675 * @param avoidMultiplayerPause - Pause if a message box is opened in a singleplayer game, but don't pause a multiplayer match for that. 674 676 */ 675 function pauseGame( )677 function pauseGame(pause = true, avoidMultiplayerPause = true) 676 678 { 677 if (g_IsNetworked )679 if (g_IsNetworked && avoidMultiplayerPause) 678 680 return; 679 681 680 Engine.GetGUIObjectByName("pauseButtonText").caption = translate("Resume");681 Engine. GetGUIObjectByName("pauseOverlay").hidden = false;682 Engine.SetPaused(true);682 g_Paused = pause; 683 Engine.SetPaused(pause, true); 684 updatePauseOverlay(); 683 685 } 684 686 685 687 function resumeGame() 686 688 { 687 Engine.GetGUIObjectByName("pauseButtonText").caption = translate("Pause"); 688 Engine.GetGUIObjectByName("pauseOverlay").hidden = true; 689 Engine.SetPaused(false); 689 resumeGame(); 690 690 } 691 691 692 /** 693 * Called when the current player explicitly wants to pause. 694 */ 692 695 function togglePause() 693 696 { 694 697 if (!Engine.GetGUIObjectByName("pauseButton").enabled) 695 698 return; 696 699 697 700 closeOpenDialogs(); 698 701 699 let pauseOverlay = Engine.GetGUIObjectByName("pauseOverlay"); 702 g_Paused = !g_Paused; 703 704 if (g_IsNetworked) 705 setClientPauseState(Engine.GetPlayerGUID(), g_Paused); 706 707 pauseGame(g_Paused, false); 708 } 709 710 function setClientPauseState(guid, paused) 711 { 712 if (paused) 713 g_PausingClients.push(guid); 714 else 715 g_PausingClients.splice(g_PausingClients.indexOf(guid), 1); 700 716 701 Engine.SetPaused(pauseOverlay.hidden); 702 Engine.GetGUIObjectByName("pauseButtonText").caption = pauseOverlay.hidden ? translate("Resume") : translate("Pause"); 717 updatePauseOverlay(); 718 Engine.SetPaused(paused, false); 719 } 720 721 function updatePauseOverlay() 722 { 723 Engine.GetGUIObjectByName("pauseButtonText").caption = g_Paused ? translate("Pause") : translate("Resume"); 724 Engine.GetGUIObjectByName("multiplayerPauseText").caption = translate("Game paused by") + " " + 725 g_PausingClients.map(guid => colorizePlayernameByGUID(guid)).join(translate(", ")); 703 726 704 pauseOverlay.hidden = !pauseOverlay.hidden; 727 Engine.GetGUIObjectByName("multiplayerPauseOverlay").hidden = !g_PausingClients.length; 728 Engine.GetGUIObjectByName("multiplayerResumeMessage").hidden = !g_Paused; 729 Engine.GetGUIObjectByName("pauseOverlay").hidden = !g_Paused; 705 730 } 706 731 707 732 function openManual() 708 733 { 709 734 closeOpenDialogs(); -
binaries/data/mods/public/gui/session/messages.js
var g_ChatTimers = []; 29 29 */ 30 30 var g_NetMessageTypes = { 31 31 "netstatus": msg => handleNetStatusMessage(msg), 32 32 "netwarn": msg => addNetworkWarning(msg), 33 33 "players": msg => handlePlayerAssignmentsMessage(msg), 34 "paused": msg => setClientPauseState(message.guid, message.pause), 34 35 "rejoined": msg => addChatMessage({ "type": "rejoined", "guid": msg.guid }), 35 36 "kicked": msg => addChatMessage({ "type": "system", "text": sprintf(translate("%(username)s has been kicked"), { "username": msg.username }) }), 36 37 "banned": msg => addChatMessage({ "type": "system", "text": sprintf(translate("%(username)s has been banned"), { "username": msg.username }) }), 37 38 "chat": msg => addChatMessage({ "type": "message", "guid": msg.guid, "text": msg.text }), 38 39 "aichat": msg => addChatMessage({ "type": "message", "guid": msg.guid, "text": msg.text, "translate": true }), … … function handlePlayerAssignmentsMessage( 459 460 for (let guid in g_PlayerAssignments) 460 461 { 461 462 if (message.hosts[guid]) 462 463 continue; 463 464 465 setClientPauseState(guid, false); 466 464 467 addChatMessage({ "type": "disconnect", "guid": guid }); 465 468 466 469 for (let id in g_Players) 467 470 if (g_Players[id].guid == guid) 468 471 g_Players[id].offline = true; -
binaries/data/mods/public/gui/session/session.js
var g_IsObserver = false; 36 36 * True if the current user has rejoined (or joined the game after it started). 37 37 */ 38 38 var g_HasRejoined = false; 39 39 40 40 /** 41 * True if the current player has paused the game. 42 */ 43 var g_Paused = false; 44 45 /** 46 * The list of GUIDs of players who have currently paused the game, if the game is networked. 47 */ 48 var g_PausingClients = []; 49 50 /** 41 51 * The playerID selected in the change perspective tool. 42 52 */ 43 53 var g_ViewedPlayer = Engine.GetPlayerID(); 44 54 45 55 /** -
binaries/data/mods/public/gui/session/session.xml
190 190 </object> 191 191 <action on="Press">togglePause();</action> 192 192 </object> 193 193 194 194 <!-- ================================ ================================ --> 195 <!-- Multiplayer Pause Overlay --> 196 <!-- ================================ ================================ --> 197 <object type="button" 198 name="multiplayerPauseOverlay" 199 size="0 0 100% 100%" 200 tooltip_style="sessionToolTip" 201 hidden="true" 202 z="0" 203 > 204 <object size="0 0 100% 100%" type="image" sprite="devCommandsBackground" ghost="true" z="0"/> 205 <object name="multiplayerPauseText" size="50%-256 50%-60 50%+256 50%+60" type="text" style="PauseText" ghost="true" z="0"/> 206 <object name="multiplayerResumeMessage" size="50%-128 50%+60 50%+128 50%+70" type="text" style="PauseMessageText" ghost="true" z="0" hidden="true"> 207 <translatableAttribute id="caption">Click to Resume Game</translatableAttribute> 208 </object> 209 <action on="Press">togglePause();</action> 210 </object> 211 212 <!-- ================================ ================================ --> 195 213 <!-- Notification Area --> 196 214 <!-- ================================ ================================ --> 197 215 <object name="notificationPanel" type="image" size="50%-300 60 50%+300 120" ghost="true"> 198 216 <object name="notificationText" size="0 0 100% 100%" type="text" style="notificationPanel" ghost="true"/> 199 217 </object> -
source/gui/scripting/ScriptFunctions.cpp
bool IsPaused(ScriptInterface::CxPrivate 770 770 771 771 return g_Game->m_Paused; 772 772 } 773 773 774 774 // Pause/unpause the game 775 void SetPaused(ScriptInterface::CxPrivate* pCxPrivate, bool pause )775 void SetPaused(ScriptInterface::CxPrivate* pCxPrivate, bool pause, bool sendMessage) 776 776 { 777 777 if (!g_Game) 778 778 { 779 779 JS_ReportError(pCxPrivate->pScriptInterface->GetContext(), "Game is not started"); 780 780 return; … … void SetPaused(ScriptInterface::CxPrivat 782 782 g_Game->m_Paused = pause; 783 783 #if CONFIG2_AUDIO 784 784 if (g_SoundManager) 785 785 g_SoundManager->Pause(pause); 786 786 #endif 787 788 if (g_NetClient && sendMessage) 789 g_NetClient->SendPausedMessage(pause); 787 790 } 788 791 789 792 // Return the global frames-per-second value. 790 793 // params: 791 794 // returns: FPS [int] … … void GuiScriptingInit(ScriptInterface& s 1085 1088 scriptInterface.RegisterFunction<bool, std::string, &HotkeyIsPressed_>("HotkeyIsPressed"); 1086 1089 scriptInterface.RegisterFunction<void, std::wstring, &DisplayErrorDialog>("DisplayErrorDialog"); 1087 1090 scriptInterface.RegisterFunction<JS::Value, &GetProfilerState>("GetProfilerState"); 1088 1091 scriptInterface.RegisterFunction<void, &ExitProgram>("Exit"); 1089 1092 scriptInterface.RegisterFunction<bool, &IsPaused>("IsPaused"); 1090 scriptInterface.RegisterFunction<void, bool, &SetPaused>("SetPaused");1093 scriptInterface.RegisterFunction<void, bool, bool, &SetPaused>("SetPaused"); 1091 1094 scriptInterface.RegisterFunction<int, &GetFps>("GetFPS"); 1092 1095 scriptInterface.RegisterFunction<std::wstring, int, &GetBuildTimestamp>("GetBuildTimestamp"); 1093 1096 scriptInterface.RegisterFunction<JS::Value, std::wstring, &ReadJSONFile>("ReadJSONFile"); 1094 1097 scriptInterface.RegisterFunction<void, std::wstring, JS::HandleValue, &WriteJSONFile>("WriteJSONFile"); 1095 1098 scriptInterface.RegisterFunction<bool, std::string, &TemplateExists>("TemplateExists"); -
source/network/NetClient.cpp
CNetClient::CNetClient(CGame* game, bool 120 120 121 121 AddTransition(NCS_INGAME, (uint)NMT_REJOINED, NCS_INGAME, (void*)&OnRejoined, context); 122 122 AddTransition(NCS_INGAME, (uint)NMT_KICKED, NCS_INGAME, (void*)&OnKicked, context); 123 123 AddTransition(NCS_INGAME, (uint)NMT_CLIENT_TIMEOUT, NCS_INGAME, (void*)&OnClientTimeout, context); 124 124 AddTransition(NCS_INGAME, (uint)NMT_CLIENT_PERFORMANCE, NCS_INGAME, (void*)&OnClientPerformance, context); 125 AddTransition(NCS_INGAME, (uint)NMT_CLIENT_PAUSED, NCS_INGAME, (void*)&OnClientPaused, context); 125 126 AddTransition(NCS_INGAME, (uint)NMT_CHAT, NCS_INGAME, (void*)&OnChat, context); 126 127 AddTransition(NCS_INGAME, (uint)NMT_GAME_SETUP, NCS_INGAME, (void*)&OnGameSetup, context); 127 128 AddTransition(NCS_INGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_INGAME, (void*)&OnPlayerAssignment, context); 128 129 AddTransition(NCS_INGAME, (uint)NMT_SIMULATION_COMMAND, NCS_INGAME, (void*)&OnInGame, context); 129 130 AddTransition(NCS_INGAME, (uint)NMT_SYNC_ERROR, NCS_INGAME, (void*)&OnInGame, context); … … void CNetClient::SendRejoinedMessage() 339 340 { 340 341 CRejoinedMessage rejoinedMessage; 341 342 SendMessage(&rejoinedMessage); 342 343 } 343 344 345 void CNetClient::SendPausedMessage(bool pause) 346 { 347 CClientPausedMessage pausedMessage; 348 pausedMessage.m_Pause = pause; 349 SendMessage(&pausedMessage); 350 } 351 344 352 bool CNetClient::HandleMessage(CNetMessage* message) 345 353 { 346 354 // Handle non-FSM messages first 347 355 348 356 Status status = m_Session->GetFileTransferer().HandleMessageReceive(message); … … bool CNetClient::OnClientPerformance(voi 725 733 } 726 734 727 735 return true; 728 736 } 729 737 738 bool CNetClient::OnClientPaused(void *context, CFsmEvent *event) 739 { 740 ENSURE(event->GetType() == (uint)NMT_CLIENT_PAUSED); 741 742 CNetClient* client = (CNetClient*)context; 743 JSContext* cx = client->GetScriptInterface().GetContext(); 744 CClientPausedMessage* message = (CClientPausedMessage*)event->GetParamRef(); 745 746 JS::RootedValue msg(cx); 747 client->GetScriptInterface().Eval("({ 'type':'paused' })", &msg); 748 client->GetScriptInterface().SetProperty(msg, "pause", message->m_Pause != 0); 749 client->GetScriptInterface().SetProperty(msg, "guid", message->m_GUID); 750 client->PushGuiMessage(msg); 751 752 return true; 753 } 754 730 755 bool CNetClient::OnLoadedGame(void* context, CFsmEvent* event) 731 756 { 732 757 ENSURE(event->GetType() == (uint)NMT_LOADED_GAME); 733 758 734 759 CNetClient* client = (CNetClient*)context; -
source/network/NetClient.h
public: 195 195 * Call when the client has rejoined a running match and finished 196 196 * the loading screen. 197 197 */ 198 198 void SendRejoinedMessage(); 199 199 200 /** 201 * Call when the client has paused or unpaused the game. 202 */ 203 void SendPausedMessage(bool pause); 204 200 205 private: 201 206 // Net message / FSM transition handlers 202 207 static bool OnConnect(void* context, CFsmEvent* event); 203 208 static bool OnHandshake(void* context, CFsmEvent* event); 204 209 static bool OnHandshakeResponse(void* context, CFsmEvent* event); … … private: 213 218 static bool OnJoinSyncEndCommandBatch(void* context, CFsmEvent* event); 214 219 static bool OnRejoined(void* context, CFsmEvent* event); 215 220 static bool OnKicked(void* context, CFsmEvent* event); 216 221 static bool OnClientTimeout(void* context, CFsmEvent* event); 217 222 static bool OnClientPerformance(void* context, CFsmEvent* event); 223 static bool OnClientPaused(void* context, CFsmEvent* event); 218 224 static bool OnLoadedGame(void* context, CFsmEvent* event); 219 225 220 226 /** 221 227 * Take ownership of a session object, and use it for all network communication. 222 228 */ -
source/network/NetMessage.cpp
CNetMessage* CNetMessageFactory::CreateM 145 145 146 146 case NMT_CLIENT_PERFORMANCE: 147 147 pNewMessage = new CClientPerformanceMessage; 148 148 break; 149 149 150 case NMT_CLIENT_PAUSED: 151 pNewMessage = new CClientPausedMessage; 152 break; 153 150 154 case NMT_LOADED_GAME: 151 155 pNewMessage = new CLoadedGameMessage; 152 156 break; 153 157 154 158 case NMT_SERVER_HANDSHAKE: -
source/network/NetMessages.h
26 26 #include "ps/CStr.h" 27 27 #include "scriptinterface/ScriptVal.h" 28 28 29 29 #define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?' 30 30 #define PS_PROTOCOL_MAGIC_RESPONSE 0x50630121 // 'P', 'c', 0x01, '!' 31 #define PS_PROTOCOL_VERSION 0x0101001 2// Arbitrary protocol31 #define PS_PROTOCOL_VERSION 0x01010013 // Arbitrary protocol 32 32 #define PS_DEFAULT_PORT 0x5073 // 'P', 's' 33 33 34 34 // Defines the list of message types. The order of the list must not change. 35 35 // The message types having a negative value are used internally and not sent 36 36 // over the network. The message types used for network communication have … … enum NetMessageType 60 60 NMT_REJOINED, 61 61 NMT_KICKED, 62 62 63 63 NMT_CLIENT_TIMEOUT, 64 64 NMT_CLIENT_PERFORMANCE, 65 NMT_CLIENT_PAUSED, 65 66 66 67 NMT_LOADED_GAME, 67 68 NMT_GAME_START, 68 69 NMT_END_COMMAND_BATCH, 69 70 NMT_SYNC_CHECK, // OOS-detection hash checking … … START_NMT_CLASS_(ClientPerformance, NMT_ 181 182 NMT_FIELD(CStr8, m_GUID) 182 183 NMT_FIELD_INT(m_MeanRTT, u32, 4) 183 184 NMT_END_ARRAY() 184 185 END_NMT_CLASS() 185 186 187 START_NMT_CLASS_(ClientPaused, NMT_CLIENT_PAUSED) 188 NMT_FIELD(CStr, m_GUID) 189 NMT_FIELD_INT(m_Pause, u8, 1) 190 END_NMT_CLASS() 191 186 192 START_NMT_CLASS_(LoadedGame, NMT_LOADED_GAME) 187 193 NMT_FIELD_INT(m_CurrentTurn, u32, 4) 188 194 END_NMT_CLASS() 189 195 190 196 START_NMT_CLASS_(GameStart, NMT_GAME_START) -
source/network/NetServer.cpp
void CNetServerWorker::SetupSession(CNet 653 653 654 654 session->AddTransition(NSS_JOIN_SYNCING, (uint)NMT_CONNECTION_LOST, NSS_UNCONNECTED, (void*)&OnDisconnect, context); 655 655 session->AddTransition(NSS_JOIN_SYNCING, (uint)NMT_LOADED_GAME, NSS_INGAME, (void*)&OnJoinSyncingLoadedGame, context); 656 656 657 657 session->AddTransition(NSS_INGAME, (uint)NMT_REJOINED, NSS_INGAME, (void*)&OnRejoined, context); 658 session->AddTransition(NSS_INGAME, (uint)NMT_CLIENT_PAUSED, NSS_INGAME, (void*)&OnClientPaused, context); 658 659 session->AddTransition(NSS_INGAME, (uint)NMT_CONNECTION_LOST, NSS_UNCONNECTED, (void*)&OnDisconnect, context); 659 660 session->AddTransition(NSS_INGAME, (uint)NMT_CHAT, NSS_INGAME, (void*)&OnChat, context); 660 661 session->AddTransition(NSS_INGAME, (uint)NMT_SIMULATION_COMMAND, NSS_INGAME, (void*)&OnInGame, context); 661 662 session->AddTransition(NSS_INGAME, (uint)NMT_SYNC_CHECK, NSS_INGAME, (void*)&OnInGame, context); 662 663 session->AddTransition(NSS_INGAME, (uint)NMT_END_COMMAND_BATCH, NSS_INGAME, (void*)&OnInGame, context); … … void CNetServerWorker::OnUserJoin(CNetSe 696 697 session->SendMessage(&assignMessage); 697 698 } 698 699 699 700 void CNetServerWorker::OnUserLeave(CNetServerSession* session) 700 701 { 702 std::vector<CStr>::iterator pausing = std::find(m_PausingPlayers.begin(), m_PausingPlayers.end(), session->GetGUID()); 703 if (pausing != m_PausingPlayers.end()) 704 m_PausingPlayers.erase(pausing); 705 701 706 RemovePlayer(session->GetGUID()); 702 707 703 708 if (m_ServerTurnManager && session->GetCurrState() != NSS_JOIN_SYNCING) 704 709 m_ServerTurnManager->UninitialiseClient(session->GetHostID()); // TODO: only for non-observers 705 710 … … bool CNetServerWorker::OnJoinSyncingLoad 1167 1172 // Tell the client that everything has finished loading and it should start now 1168 1173 CLoadedGameMessage loaded; 1169 1174 loaded.m_CurrentTurn = readyTurn; 1170 1175 session->SendMessage(&loaded); 1171 1176 1177 // Send all pausing players to the client. 1178 for (const CStr& guid : server.m_PausingPlayers) 1179 { 1180 CClientPausedMessage pausedMessage; 1181 pausedMessage.m_GUID = guid; 1182 pausedMessage.m_Pause = true; 1183 session->SendMessage(&pausedMessage); 1184 } 1185 1172 1186 return true; 1173 1187 } 1174 1188 1175 1189 bool CNetServerWorker::OnRejoined(void* context, CFsmEvent* event) 1176 1190 { … … bool CNetServerWorker::OnDisconnect(void 1199 1213 server.OnUserLeave(session); 1200 1214 1201 1215 return true; 1202 1216 } 1203 1217 1218 bool CNetServerWorker::OnClientPaused(void *context, CFsmEvent *event) 1219 { 1220 ENSURE(event->GetType() == (uint)NMT_CLIENT_PAUSED); 1221 1222 CNetServerSession* session = (CNetServerSession*)context; 1223 CNetServerWorker& server = session->GetServer(); 1224 1225 CClientPausedMessage* message = (CClientPausedMessage*)event->GetParamRef(); 1226 1227 message->m_GUID = session->GetGUID(); 1228 1229 // Update the list of pausing players. 1230 std::vector<CStr>::iterator player = std::find(server.m_PausingPlayers.begin(), server.m_PausingPlayers.end(), session->GetGUID()); 1231 1232 if (message->m_Pause) 1233 { 1234 if (player != server.m_PausingPlayers.end()) 1235 return true; 1236 1237 server.m_PausingPlayers.push_back(session->GetGUID()); 1238 } 1239 else 1240 { 1241 if (player == server.m_PausingPlayers.end()) 1242 return true; 1243 1244 server.m_PausingPlayers.erase(player); 1245 } 1246 1247 // Send messages to clients that are in game, and are not the client who paused. 1248 for (CNetServerSession* session : server.m_Sessions) 1249 { 1250 if (session->GetCurrState() == NSS_INGAME && message->m_GUID != session->GetGUID()) 1251 session->SendMessage(message); 1252 } 1253 1254 return true; 1255 } 1256 1204 1257 void CNetServerWorker::CheckGameLoadStatus(CNetServerSession* changedSession) 1205 1258 { 1206 1259 for (const CNetServerSession* session : m_Sessions) 1207 1260 { 1208 1261 if (session != changedSession && session->GetCurrState() != NSS_INGAME) -
source/network/NetServer.h
private: 272 272 static bool OnReady(void* context, CFsmEvent* event); 273 273 static bool OnLoadedGame(void* context, CFsmEvent* event); 274 274 static bool OnJoinSyncingLoadedGame(void* context, CFsmEvent* event); 275 275 static bool OnRejoined(void* context, CFsmEvent* event); 276 276 static bool OnDisconnect(void* context, CFsmEvent* event); 277 static bool OnClientPaused(void* context, CFsmEvent* event); 277 278 278 279 void CheckGameLoadStatus(CNetServerSession* changedSession); 279 280 280 281 void ConstructPlayerAssignmentMessage(CPlayerAssignmentMessage& message); 281 282 … … private: 314 315 CStrW m_WelcomeMessage; 315 316 316 317 std::vector<u32> m_BannedIPs; 317 318 std::vector<CStrW> m_BannedPlayers; 318 319 320 /** 321 * Holds the GUIDs of all currently paused players. 322 */ 323 std::vector<CStr> m_PausingPlayers; 324 319 325 u32 m_NextHostID; 320 326 321 327 CNetServerTurnManager* m_ServerTurnManager; 322 328 323 329 CStr m_HostGUID;