Index: binaries/data/mods/public/gui/session/messages.js =================================================================== --- binaries/data/mods/public/gui/session/messages.js +++ binaries/data/mods/public/gui/session/messages.js @@ -45,6 +45,9 @@ "paused": msg => { setClientPauseState(msg.guid, msg.pause); }, + "clients-waiting-for": msg => { + handleClientsWaitingForMessage(msg.guids); + }, "rejoined": msg => { addChatMessage({ "type": "rejoined", @@ -134,7 +137,7 @@ sprintf(translate("Reason: %(reason)s."), { "reason": getDisconnectReason(msg.reason, true) }), - "waiting_for_players": msg => translate("Waiting for other players to connect..."), + "waiting_for_players": msg => translate("Waiting for players to connect:"), "join_syncing": msg => translate("Synchronising gameplay with other players..."), "active": msg => "" }; @@ -583,11 +586,14 @@ g_IsNetworkedActive = message.status == "active"; - let label = Engine.GetGUIObjectByName("netStatus"); + let netStatus = Engine.GetGUIObjectByName("netStatus"); let statusMessage = g_StatusMessageTypes[message.status](message); - label.caption = statusMessage; - label.hidden = !statusMessage; + netStatus.caption = statusMessage; + netStatus.hidden = !statusMessage; + let netStatusText = Engine.GetGUIObjectByName("netStatusText"); + netStatusText.hidden = message.status != "waiting_for_players"; + if (message.status == "disconnected") { // Hide the pause overlay, and pause animations. @@ -599,6 +605,12 @@ } } +function handleClientsWaitingForMessage(guids) +{ + let netStatusText = Engine.GetGUIObjectByName("netStatusText"); + netStatusText.caption = guids.map(guid => colorizePlayernameByGUID(guid)).join(translate(", ")); +} + function handlePlayerAssignmentsMessage(message) { for (let guid in g_PlayerAssignments) Index: binaries/data/mods/public/gui/session/session.xml =================================================================== --- binaries/data/mods/public/gui/session/session.xml +++ binaries/data/mods/public/gui/session/session.xml @@ -159,6 +159,7 @@ leaveGame(); + Index: source/network/NetClient.h =================================================================== --- source/network/NetClient.h +++ source/network/NetClient.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -233,6 +233,7 @@ static bool OnKicked(void* context, CFsmEvent* event); static bool OnClientTimeout(void* context, CFsmEvent* event); static bool OnClientPerformance(void* context, CFsmEvent* event); + static bool OnClientsWaitingFor(void* context, CFsmEvent* event); static bool OnClientPaused(void* context, CFsmEvent* event); static bool OnLoadedGame(void* context, CFsmEvent* event); Index: source/network/NetClient.cpp =================================================================== --- source/network/NetClient.cpp +++ source/network/NetClient.cpp @@ -119,6 +119,7 @@ AddTransition(NCS_LOADING, (uint)NMT_KICKED, NCS_LOADING, (void*)&OnKicked, context); AddTransition(NCS_LOADING, (uint)NMT_CLIENT_TIMEOUT, NCS_LOADING, (void*)&OnClientTimeout, context); AddTransition(NCS_LOADING, (uint)NMT_CLIENT_PERFORMANCE, NCS_LOADING, (void*)&OnClientPerformance, context); + AddTransition(NCS_LOADING, (uint)NMT_CLIENTS_WAITING_FOR, NCS_LOADING, (void*)&OnClientsWaitingFor, context); AddTransition(NCS_LOADING, (uint)NMT_LOADED_GAME, NCS_INGAME, (void*)&OnLoadedGame, context); AddTransition(NCS_INGAME, (uint)NMT_REJOINED, NCS_INGAME, (void*)&OnRejoined, context); @@ -125,6 +126,7 @@ AddTransition(NCS_INGAME, (uint)NMT_KICKED, NCS_INGAME, (void*)&OnKicked, context); AddTransition(NCS_INGAME, (uint)NMT_CLIENT_TIMEOUT, NCS_INGAME, (void*)&OnClientTimeout, context); AddTransition(NCS_INGAME, (uint)NMT_CLIENT_PERFORMANCE, NCS_INGAME, (void*)&OnClientPerformance, context); + AddTransition(NCS_INGAME, (uint)NMT_CLIENTS_WAITING_FOR, NCS_INGAME, (void*)&OnClientsWaitingFor, context); AddTransition(NCS_INGAME, (uint)NMT_CLIENT_PAUSED, NCS_INGAME, (void*)&OnClientPaused, context); AddTransition(NCS_INGAME, (uint)NMT_CHAT, NCS_INGAME, (void*)&OnChat, context); AddTransition(NCS_INGAME, (uint)NMT_GAME_SETUP, NCS_INGAME, (void*)&OnGameSetup, context); @@ -778,6 +780,27 @@ return true; } +bool CNetClient::OnClientsWaitingFor(void *context, CFsmEvent *event) +{ + ENSURE(event->GetType() == (uint)NMT_CLIENTS_WAITING_FOR); + + CClientsWaitingForMessage* message = (CClientsWaitingForMessage*)event->GetParamRef(); + + std::vector guids; + for (size_t i = 0; i < message->m_Clients.size(); ++i) + guids.push_back(message->m_Clients[i].m_GUID); + + CNetClient* client = (CNetClient*)context; + JSContext* cx = client->GetScriptInterface().GetContext(); + JSAutoRequest rq(cx); + + JS::RootedValue msg(cx); + client->GetScriptInterface().Eval("({ 'type':'clients-waiting-for' })", &msg); + client->GetScriptInterface().SetProperty(msg, "guids", guids); + client->PushGuiMessage(msg); + return true; +} + bool CNetClient::OnClientPaused(void *context, CFsmEvent *event) { ENSURE(event->GetType() == (uint)NMT_CLIENT_PAUSED); Index: source/network/NetMessage.cpp =================================================================== --- source/network/NetMessage.cpp +++ source/network/NetMessage.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2016 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -147,6 +147,10 @@ pNewMessage = new CClientPerformanceMessage; break; + case NMT_CLIENTS_WAITING_FOR: + pNewMessage = new CClientsWaitingForMessage; + break; + case NMT_CLIENT_PAUSED: pNewMessage = new CClientPausedMessage; break; Index: source/network/NetMessages.h =================================================================== --- source/network/NetMessages.h +++ source/network/NetMessages.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016 Wildfire Games. +/* Copyright (C) 2017 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -28,7 +28,7 @@ #define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?' #define PS_PROTOCOL_MAGIC_RESPONSE 0x50630121 // 'P', 'c', 0x01, '!' -#define PS_PROTOCOL_VERSION 0x01010014 // Arbitrary protocol +#define PS_PROTOCOL_VERSION 0x01010015 // Arbitrary protocol #define PS_DEFAULT_PORT 0x5073 // 'P', 's' // Defines the list of message types. The order of the list must not change. @@ -67,6 +67,7 @@ NMT_CLIENT_TIMEOUT, NMT_CLIENT_PERFORMANCE, + NMT_CLIENTS_WAITING_FOR, NMT_CLIENT_PAUSED, NMT_LOADED_GAME, @@ -193,6 +194,12 @@ NMT_END_ARRAY() END_NMT_CLASS() +START_NMT_CLASS_(ClientsWaitingFor, NMT_CLIENTS_WAITING_FOR) + NMT_START_ARRAY(m_Clients) + NMT_FIELD(CStr, m_GUID) + NMT_END_ARRAY() +END_NMT_CLASS() + START_NMT_CLASS_(ClientPaused, NMT_CLIENT_PAUSED) NMT_FIELD(CStr, m_GUID) NMT_FIELD_INT(m_Pause, u8, 1) Index: source/network/NetServer.cpp =================================================================== --- source/network/NetServer.cpp +++ source/network/NetServer.cpp @@ -1166,13 +1166,24 @@ { ENSURE(event->GetType() == (uint)NMT_LOADED_GAME); - CNetServerSession* session = (CNetServerSession*)context; - CNetServerWorker& server = session->GetServer(); + CNetServerSession* loadedSession = (CNetServerSession*)context; + CNetServerWorker& server = loadedSession->GetServer(); + CClientsWaitingForMessage message; + for (CNetServerSession* session : server.m_Sessions) + if (session->GetCurrState() != NSS_INGAME && loadedSession->GetGUID() != session->GetGUID()) + { + CClientsWaitingForMessage::S_m_Clients client; + client.m_GUID = session->GetGUID(); + message.m_Clients.push_back(client); + } + // Send to the loaded player + loadedSession->SendMessage(&message); + server.Broadcast(&message, { NSS_INGAME }); // We're in the loading state, so wait until every player has loaded before // starting the game ENSURE(server.m_State == SERVER_STATE_LOADING); - server.CheckGameLoadStatus(session); + server.CheckGameLoadStatus(loadedSession); return true; }