mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-09-21 20:06:40 +00:00
Unique network transmission handling of flares
This patch addresses issues concerning a1796ed71f:
Allow for a more elegant implementation of observer flares.
And still display flares even if the sender is lagging behind:
Split off flares from simulation commands.
Remove the new, problematic 'observer commands' entirely.
Provide an engine function 'SendNetworkFlare' to the JS interface.
-> which sets off the (pretty ordinary) transmission process.
Add a new type of net messages exclusively for flares
-> contains the flare's position and its sender's GUID.
This commit is contained in:
@@ -1271,14 +1271,9 @@ function triggerFlareAction(position)
|
||||
|
||||
g_LastFlareTime = now;
|
||||
|
||||
const guid = Engine.GetPlayerGUID();
|
||||
displayFlare(position, guid);
|
||||
Engine.PlayUISound(g_FlareSound, false);
|
||||
Engine.PostNetworkCommand({
|
||||
"type": "map-flare",
|
||||
"position": position,
|
||||
"guid": guid
|
||||
});
|
||||
renderAndPlayFlare(position, Engine.GetPlayerGUID());
|
||||
if (Engine.HasNetClient())
|
||||
Engine.SendNetworkFlare(position);
|
||||
}
|
||||
|
||||
function handleUnitAction(position, action)
|
||||
|
||||
@@ -96,6 +96,9 @@ var g_NetMessageTypes = {
|
||||
"text": msg.text
|
||||
});
|
||||
},
|
||||
"flare": msg => {
|
||||
handleFlare(msg);
|
||||
},
|
||||
"gamesetup": msg => {}, // Needed for autostart
|
||||
"start": msg => {}
|
||||
};
|
||||
@@ -287,29 +290,6 @@ var g_NotificationsTypes =
|
||||
}
|
||||
|
||||
global.music.setLocked(notification.lock);
|
||||
},
|
||||
"map-flare": function(notification, player)
|
||||
{
|
||||
const shouldSeeFlare = g_IsObserver || g_Players[player]?.isMutualAlly[Engine.GetPlayerID()];
|
||||
|
||||
// Don't display for the player that did the flare because they will see it immediately.
|
||||
if (!shouldSeeFlare || notification.guid == Engine.GetPlayerGUID())
|
||||
return;
|
||||
|
||||
let now = Date.now();
|
||||
if (g_FlareRateLimitLastTimes.length)
|
||||
{
|
||||
g_FlareRateLimitLastTimes = g_FlareRateLimitLastTimes.filter(t => now - t < g_FlareRateLimitScope * 1000);
|
||||
if (g_FlareRateLimitLastTimes.length >= g_FlareRateLimitMaximumFlares)
|
||||
{
|
||||
warn("Received too many flares. Dropping a flare request by '" + g_Players[player].name + "'.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
g_FlareRateLimitLastTimes.push(now);
|
||||
|
||||
displayFlare(notification.position, notification.guid);
|
||||
Engine.PlayUISound(g_FlareSound, false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -524,6 +504,30 @@ function clearChatMessages()
|
||||
g_Chat.ChatOverlay.clearChatMessages();
|
||||
}
|
||||
|
||||
function handleFlare(data)
|
||||
{
|
||||
const playerID = g_PlayerAssignments[data.guid].player;
|
||||
const shouldSeeFlare = g_IsObserver || g_Players[playerID]?.isMutualAlly[Engine.GetPlayerID()];
|
||||
|
||||
// Don't display for the player that sent the flare because they will see it immediately.
|
||||
if (!shouldSeeFlare || data.guid == Engine.GetPlayerGUID())
|
||||
return;
|
||||
|
||||
let now = Date.now();
|
||||
if (g_FlareRateLimitLastTimes.length)
|
||||
{
|
||||
g_FlareRateLimitLastTimes = g_FlareRateLimitLastTimes.filter(t => now - t < g_FlareRateLimitScope * 1000);
|
||||
if (g_FlareRateLimitLastTimes.length >= g_FlareRateLimitMaximumFlares)
|
||||
{
|
||||
warn("Received too many flares. Dropping a flare request by '" + g_PlayerAssignments[data.guid].name + "'.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
g_FlareRateLimitLastTimes.push(now);
|
||||
|
||||
renderAndPlayFlare(data.position, data.guid);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used for AIs, whose names don't exist in g_PlayerAssignments.
|
||||
*/
|
||||
|
||||
@@ -1937,7 +1937,7 @@ function DrawTargetMarker(position)
|
||||
});
|
||||
}
|
||||
|
||||
function displayFlare(position, playerGUID)
|
||||
function renderAndPlayFlare(position, playerGUID)
|
||||
{
|
||||
const playerID = g_PlayerAssignments[playerGUID].player;
|
||||
Engine.GuiInterfaceCall("AddTargetMarker", {
|
||||
@@ -1948,6 +1948,7 @@ function displayFlare(position, playerGUID)
|
||||
"owner": playerID != -1 ? playerID : 0
|
||||
});
|
||||
g_MiniMapPanel.flare(position, playerID);
|
||||
Engine.PlayUISound(g_FlareSound, false);
|
||||
addChatMessage({
|
||||
"type": "flare",
|
||||
"guid": playerGUID,
|
||||
|
||||
@@ -4,13 +4,6 @@ var g_DebugCommands = false;
|
||||
|
||||
function ProcessCommand(player, cmd)
|
||||
{
|
||||
if (player == -1)
|
||||
{
|
||||
if (g_ObserverCommands[cmd.type])
|
||||
g_ObserverCommands[cmd.type](player, cmd, {});
|
||||
return;
|
||||
}
|
||||
|
||||
let cmpPlayer = QueryPlayerIDInterface(player);
|
||||
if (!cmpPlayer)
|
||||
return;
|
||||
@@ -882,17 +875,6 @@ var g_Commands = {
|
||||
}
|
||||
},
|
||||
|
||||
"map-flare": function(player, cmd, data)
|
||||
{
|
||||
let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGuiInterface.PushNotification({
|
||||
"type": "map-flare",
|
||||
"players": [player],
|
||||
"guid": cmd.guid,
|
||||
"position": cmd.position
|
||||
});
|
||||
},
|
||||
|
||||
"autoqueue-on": function(player, cmd, data)
|
||||
{
|
||||
for (let ent of data.entities)
|
||||
@@ -915,10 +897,6 @@ var g_Commands = {
|
||||
|
||||
};
|
||||
|
||||
var g_ObserverCommands = {
|
||||
"map-flare": g_Commands["map-flare"]
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends a GUI notification about unit(s) that failed to ungarrison.
|
||||
*/
|
||||
|
||||
@@ -116,6 +116,7 @@ CNetClient::CNetClient(CGame* game) :
|
||||
AddTransition(NCS_INGAME, (uint)NMT_GAME_SETUP, NCS_INGAME, &OnGameSetup, this);
|
||||
AddTransition(NCS_INGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_INGAME, &OnPlayerAssignment, this);
|
||||
AddTransition(NCS_INGAME, (uint)NMT_SIMULATION_COMMAND, NCS_INGAME, &OnInGame, this);
|
||||
AddTransition(NCS_INGAME, (uint)NMT_FLARE, NCS_INGAME, &OnFlare, this);
|
||||
AddTransition(NCS_INGAME, (uint)NMT_SYNC_ERROR, NCS_INGAME, &OnInGame, this);
|
||||
AddTransition(NCS_INGAME, (uint)NMT_END_COMMAND_BATCH, NCS_INGAME, &OnInGame, this);
|
||||
|
||||
@@ -489,6 +490,15 @@ void CNetClient::SendStartGameMessage(const CStr& initAttribs)
|
||||
SendMessage(&gameStart);
|
||||
}
|
||||
|
||||
void CNetClient::SendFlareMessage(const CStr& positionX, const CStr& positionY, const CStr& positionZ)
|
||||
{
|
||||
CFlareMessage flare;
|
||||
flare.m_PositionX = positionX;
|
||||
flare.m_PositionY = positionY;
|
||||
flare.m_PositionZ = positionZ;
|
||||
SendMessage(&flare);
|
||||
}
|
||||
|
||||
void CNetClient::SendRejoinedMessage()
|
||||
{
|
||||
CRejoinedMessage rejoinedMessage;
|
||||
@@ -963,3 +973,28 @@ bool CNetClient::OnInGame(CNetClient* client, CFsmEvent* event)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNetClient::OnFlare(CNetClient* client, CFsmEvent* event)
|
||||
{
|
||||
ENSURE(event->GetType() == static_cast<uint>(NMT_FLARE));
|
||||
|
||||
CFlareMessage* message = static_cast<CFlareMessage*>(event->GetParamRef());
|
||||
|
||||
const ScriptInterface& scriptInterface = client->m_Game->GetSimulation2()->GetScriptInterface();
|
||||
ScriptRequest rq(scriptInterface);
|
||||
JS::RootedValue position(rq.cx);
|
||||
Script::CreateObject(
|
||||
rq, &position,
|
||||
// The coordinates are transmitted as strings (because because direct (de)serialisation of floating point numbers is not supported).
|
||||
"x", message->m_PositionX.ToDouble(),
|
||||
"y", message->m_PositionY.ToDouble(),
|
||||
"z", message->m_PositionZ.ToDouble()
|
||||
);
|
||||
|
||||
client->PushGuiMessage(
|
||||
"type", "flare",
|
||||
"guid", message->m_GUID,
|
||||
"position", position);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#ifndef NETCLIENT_H
|
||||
#define NETCLIENT_H
|
||||
|
||||
|
||||
#include "network/FSM.h"
|
||||
#include "network/NetFileTransfer.h"
|
||||
#include "network/NetHost.h"
|
||||
@@ -236,6 +237,11 @@ public:
|
||||
|
||||
void SendStartGameMessage(const CStr& initAttribs);
|
||||
|
||||
/**
|
||||
* Call when the client (player or observer) has sent a flare.
|
||||
*/
|
||||
void SendFlareMessage(const CStr& positionX, const CStr& positionY, const CStr& positionZ);
|
||||
|
||||
/**
|
||||
* Call when the client has rejoined a running match and finished
|
||||
* the loading screen.
|
||||
@@ -279,6 +285,7 @@ private:
|
||||
static bool OnGameStart(CNetClient* client, CFsmEvent* event);
|
||||
static bool OnJoinSyncStart(CNetClient* client, CFsmEvent* event);
|
||||
static bool OnJoinSyncEndCommandBatch(CNetClient* client, CFsmEvent* event);
|
||||
static bool OnFlare(CNetClient* client, CFsmEvent* event);
|
||||
static bool OnRejoined(CNetClient* client, CFsmEvent* event);
|
||||
static bool OnKicked(CNetClient* client, CFsmEvent* event);
|
||||
static bool OnClientTimeout(CNetClient* client, CFsmEvent* event);
|
||||
|
||||
@@ -207,6 +207,10 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
|
||||
pNewMessage = new CSimulationMessage(scriptInterface);
|
||||
break;
|
||||
|
||||
case NMT_FLARE:
|
||||
pNewMessage = new CFlareMessage;
|
||||
break;
|
||||
|
||||
case NMT_CLEAR_ALL_READY:
|
||||
pNewMessage = new CClearAllReadyMessage;
|
||||
break;
|
||||
|
||||
@@ -80,7 +80,8 @@ enum NetMessageType
|
||||
NMT_SYNC_CHECK, // OOS-detection hash checking
|
||||
NMT_SYNC_ERROR, // OOS-detection error
|
||||
|
||||
NMT_SIMULATION_COMMAND
|
||||
NMT_SIMULATION_COMMAND,
|
||||
NMT_FLARE
|
||||
};
|
||||
|
||||
// Authentication result codes
|
||||
@@ -177,6 +178,13 @@ START_NMT_CLASS_(JoinSyncStart, NMT_JOIN_SYNC_START)
|
||||
NMT_FIELD(CStr, m_InitAttributes)
|
||||
END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(Flare, NMT_FLARE)
|
||||
NMT_FIELD(CStr, m_GUID) // ignored when client->server, valid when server->client
|
||||
NMT_FIELD(CStr, m_PositionX)
|
||||
NMT_FIELD(CStr, m_PositionY)
|
||||
NMT_FIELD(CStr, m_PositionZ)
|
||||
END_NMT_CLASS()
|
||||
|
||||
START_NMT_CLASS_(Rejoined, NMT_REJOINED)
|
||||
NMT_FIELD(CStr, m_GUID)
|
||||
END_NMT_CLASS()
|
||||
|
||||
@@ -673,6 +673,7 @@ void CNetServerWorker::SetupSession(CNetServerSession* session)
|
||||
session->AddTransition(NSS_INGAME, (uint)NMT_CONNECTION_LOST, NSS_UNCONNECTED, &OnDisconnect, session);
|
||||
session->AddTransition(NSS_INGAME, (uint)NMT_CHAT, NSS_INGAME, &OnChat, session);
|
||||
session->AddTransition(NSS_INGAME, (uint)NMT_SIMULATION_COMMAND, NSS_INGAME, &OnSimulationCommand, session);
|
||||
session->AddTransition(NSS_INGAME, (uint)NMT_FLARE, NSS_INGAME, &OnFlare, session);
|
||||
session->AddTransition(NSS_INGAME, (uint)NMT_SYNC_CHECK, NSS_INGAME, &OnSyncCheck, session);
|
||||
session->AddTransition(NSS_INGAME, (uint)NMT_END_COMMAND_BATCH, NSS_INGAME, &OnEndCommandBatch, session);
|
||||
|
||||
@@ -1194,6 +1195,18 @@ bool CNetServerWorker::OnSimulationCommand(CNetServerSession* session, CFsmEvent
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNetServerWorker::OnFlare(CNetServerSession* session, CFsmEvent* event)
|
||||
{
|
||||
ENSURE(event->GetType() == (uint)NMT_FLARE);
|
||||
|
||||
CNetServerWorker& server = session->GetServer();
|
||||
CFlareMessage* message = (CFlareMessage*)event->GetParamRef();
|
||||
message->m_GUID = session->GetGUID();
|
||||
server.Broadcast(message, { NSS_INGAME });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNetServerWorker::OnSyncCheck(CNetServerSession* session, CFsmEvent* event)
|
||||
{
|
||||
ENSURE(event->GetType() == (uint)NMT_SYNC_CHECK);
|
||||
|
||||
@@ -297,6 +297,7 @@ private:
|
||||
static bool OnClientHandshake(CNetServerSession* session, CFsmEvent* event);
|
||||
static bool OnAuthenticate(CNetServerSession* session, CFsmEvent* event);
|
||||
static bool OnSimulationCommand(CNetServerSession* session, CFsmEvent* event);
|
||||
static bool OnFlare(CNetServerSession* session, CFsmEvent* event);
|
||||
static bool OnSyncCheck(CNetServerSession* session, CFsmEvent* event);
|
||||
static bool OnEndCommandBatch(CNetServerSession* session, CFsmEvent* event);
|
||||
static bool OnChat(CNetServerSession* session, CFsmEvent* event);
|
||||
|
||||
@@ -274,6 +274,29 @@ void SetTurnLength(int length)
|
||||
LOGERROR("Only network host can change turn length");
|
||||
}
|
||||
|
||||
void SendNetworkFlare(JS::HandleValue position)
|
||||
{
|
||||
ENSURE(g_NetClient);
|
||||
|
||||
ScriptRequest rq(g_NetClient->GetScriptInterface());
|
||||
ENSURE(position.isObject());
|
||||
JS::RootedObject positionObj(rq.cx, &position.toObject());
|
||||
JS::RootedValue positionX(rq.cx);
|
||||
JS::RootedValue positionY(rq.cx);
|
||||
JS::RootedValue positionZ(rq.cx);
|
||||
ENSURE(JS_GetProperty(rq.cx, positionObj, "x", &positionX));
|
||||
ENSURE(JS_GetProperty(rq.cx, positionObj, "y", &positionY));
|
||||
ENSURE(JS_GetProperty(rq.cx, positionObj, "z", &positionZ));
|
||||
|
||||
// (TODO?): Converting the doubles into strings here is a workaround because direct (de)serialisation of floating point numbers is not supported.
|
||||
// It causes somewhat awkward message handling, but the resulting efficiency losses are negligible.
|
||||
g_NetClient->SendFlareMessage(
|
||||
CStr::FromDouble(positionX.toNumber()),
|
||||
CStr::FromDouble(positionY.toNumber()),
|
||||
CStr::FromDouble(positionZ.toNumber())
|
||||
);
|
||||
}
|
||||
|
||||
void RegisterScriptFunctions(const ScriptRequest& rq)
|
||||
{
|
||||
ScriptFunction::Register<&GetDefaultPort>(rq, "GetDefaultPort");
|
||||
@@ -294,5 +317,6 @@ void RegisterScriptFunctions(const ScriptRequest& rq)
|
||||
ScriptFunction::Register<&ClearAllPlayerReady>(rq, "ClearAllPlayerReady");
|
||||
ScriptFunction::Register<&StartNetworkGame>(rq, "StartNetworkGame");
|
||||
ScriptFunction::Register<&SetTurnLength>(rq, "SetTurnLength");
|
||||
ScriptFunction::Register<&SendNetworkFlare>(rq, "SendNetworkFlare");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user