Fix #11: Queue outgoing commands and don't send more than 2 per frame

This commit is contained in:
dP
2021-10-29 02:54:25 +03:00
parent 4df77324fc
commit 3bef49da29
9 changed files with 161 additions and 41 deletions

View File

@@ -26,6 +26,8 @@ add_files(
cm_cargo_table_gui.cpp cm_cargo_table_gui.cpp
cm_client_list_gui.hpp cm_client_list_gui.hpp
cm_client_list_gui.cpp cm_client_list_gui.cpp
cm_commands.hpp
cm_commands.cpp
cm_commands_gui.hpp cm_commands_gui.hpp
cm_commands_gui.cpp cm_commands_gui.cpp
cm_highlight.hpp cm_highlight.hpp

View File

@@ -2,6 +2,7 @@
#include "cm_blueprint.hpp" #include "cm_blueprint.hpp"
#include "cm_commands.hpp"
#include "cm_highlight.hpp" #include "cm_highlight.hpp"
#include "../command_func.h" #include "../command_func.h"
@@ -37,31 +38,6 @@ bool operator!=(const TileIndexDiffC &a, const TileIndexDiffC &b) {
return a.x != b.x || a.y != b.y; return a.x != b.x || a.y != b.y;
} }
typedef std::tuple<TileIndex, uint32, uint32, uint32> CommandTuple;
std::map<CommandTuple, std::vector<CommandCallback>> _command_callbacks;
void AddCommandCallback(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback callback) {
_command_callbacks[std::make_tuple(tile, p1, p2, cmd)].push_back(callback);
}
void DoCommandWithCallback(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback callback) {
AddCommandCallback(tile, p1, p2, cmd & CMD_ID_MASK, callback);
DoCommandP(tile, p1, p2, cmd);
}
void DoCommandWithCallback(const CommandContainer &cc, CommandCallback callback) {
DoCommandWithCallback(cc.tile, cc.p1, cc.p2, cc.cmd, callback);
}
void CommandExecuted(bool res, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd) {
CommandTuple ct {tile, p1, p2, cmd & CMD_ID_MASK};
auto p = _command_callbacks.find(ct);
if (p == _command_callbacks.end()) return;
for (auto &cb : p->second)
cb(res);
_command_callbacks.erase(p);
}
template<typename Func> template<typename Func>
void IterateStation(TileIndex start_tile, Axis axis, byte numtracks, byte plat_len, Func visitor) { void IterateStation(TileIndex start_tile, Axis axis, byte numtracks, byte plat_len, Func visitor) {
auto plat_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); auto plat_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
@@ -641,8 +617,7 @@ void BuildBlueprint(sp<Blueprint> &blueprint, TileIndex start) {
return true; return true;
}; };
if (last_rail.cmd != CMD_END) { // there can't be any signals if there are no rails if (last_rail.cmd != CMD_END) { // there can't be any signals if there are no rails
if (_networking) AddCommandCallback(last_rail.tile, last_rail.p1, last_rail.p2, last_rail.cmd, signal_callback); AddCommandCallback(last_rail.tile, last_rail.p1, last_rail.p2, last_rail.cmd, "", signal_callback);
else signal_callback(true);
} }
} }

View File

@@ -7,9 +7,6 @@
namespace citymania { namespace citymania {
typedef std::function<bool(bool)> CommandCallback;
void AddCommandCallback(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback callback);
void BlueprintCopyArea(TileIndex start, TileIndex end); void BlueprintCopyArea(TileIndex start, TileIndex end);
void ResetActiveBlueprint(); void ResetActiveBlueprint();
void SetBlueprintHighlight(const TileInfo *ti, TileHighlight &th); void SetBlueprintHighlight(const TileInfo *ti, TileHighlight &th);
@@ -21,6 +18,6 @@ void RotateActiveBlueprint();
void CommandExecuted(bool res, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd); void CommandExecuted(bool res, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd);
} //- namespace citymania } // namespace citymania
#endif #endif

View File

@@ -0,0 +1,121 @@
#include "../stdafx.h"
#include "cm_commands.hpp"
#include "../command_func.h"
#include "../network/network.h"
#include "../network/network_client.h"
#include <queue>
namespace citymania {
const uint32 MAX_CALLBACK_LIFETIME = 30; // it should be executed within few frames so 30 more than enough
std::map<size_t, std::pair<uint32, std::vector<CommandCallback>>> _command_callbacks;
std::queue<std::pair<size_t, uint32>> _command_sent;
uint GetCurrentQueueDelay();
template <typename T, typename... Rest>
void hash_combine(std::size_t& seed, const T& v, const Rest&... rest)
{
seed ^= std::hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
(hash_combine(seed, rest), ...);
}
size_t GetCommandHash(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, const std::string &text) {
size_t res = 0;
hash_combine(res, tile, p1, p2, cmd, text);
return res;
}
void AddCommandCallback(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, const std::string &text, CommandCallback callback) {
if (!_networking) {
callback(true);
return;
}
auto hash = GetCommandHash(tile, p1, p2, cmd & CMD_ID_MASK, text);
auto sent_frame = _frame_counter + GetCurrentQueueDelay();
_command_callbacks[hash].first = sent_frame;
_command_callbacks[hash].second.push_back(callback);
_command_sent.push(std::make_pair(hash, sent_frame));
}
bool DoCommandWithCallback(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, ::CommandCallback *callback, const std::string &text, CommandCallback cm_callback) {
if (_networking) {
AddCommandCallback(tile, p1, p2, cmd, text, cm_callback);
DoCommandP(tile, p1, p2, cmd, callback, text);
return true;
}
auto res = DoCommandP(tile, p1, p2, cmd, callback, text);
cm_callback(res);
return res;
}
bool DoCommandWithCallback(const CommandContainer &cc, CommandCallback callback) {
return DoCommandWithCallback(cc.tile, cc.p1, cc.p2, cc.cmd, cc.callback, cc.text, callback);
}
void HandleCommandExecution(bool res, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, const std::string &text) {
auto hash = GetCommandHash(tile, p1, p2, cmd & CMD_ID_MASK, text);
auto p = _command_callbacks.find(hash);
if (p == _command_callbacks.end()) return;
for (auto &cb : p->second.second)
cb(res);
_command_callbacks.erase(p);
}
void ClearOldCallbacks() {
while(!_command_sent.empty() && _command_sent.front().second + MAX_CALLBACK_LIFETIME < _frame_counter) {
auto hash = _command_sent.front().first;
_command_sent.pop();
auto p = _command_callbacks.find(hash);
if (p != _command_callbacks.end() && p->first + MAX_CALLBACK_LIFETIME < _frame_counter) {
_command_callbacks.erase(p);
}
}
}
std::queue<CommandPacket> _outgoing_queue;
uint _commands_this_frame;
void InitCommandQueue() {
_commands_this_frame = 0;
std::queue<CommandPacket>().swap(_outgoing_queue); // clear queue
_command_callbacks.clear();
}
bool CanSendCommand() {
return _commands_this_frame < 2;
}
uint GetCurrentQueueDelay() {
return _outgoing_queue.size() / 2;
}
void FlushCommandQueue() {
while (!_outgoing_queue.empty() && CanSendCommand()) {
MyClient::SendCommand(&_outgoing_queue.front());
_outgoing_queue.pop();
_commands_this_frame++;
}
}
void HandleNextClientFrame() {
_commands_this_frame = 0;
FlushCommandQueue();
ClearOldCallbacks();
}
void SendClientCommand(const CommandPacket *cp) {
if (_outgoing_queue.empty() && CanSendCommand()) {
MyClient::SendCommand(cp);
_commands_this_frame++;
return;
}
_outgoing_queue.push(*cp);
}
} // namespace citymania

View File

@@ -0,0 +1,22 @@
#ifndef CM_COMMANDS_HPP
#define CM_COMMANDS_HPP
#include "../command_type.h"
#include "../tile_type.h"
#include "../network/network_internal.h"
namespace citymania {
typedef std::function<bool(bool)> CommandCallback;
void AddCommandCallback(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, const std::string &text, CommandCallback callback);
bool DoCommandWithCallback(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, ::CommandCallback *callback, const std::string &text, CommandCallback cm_callback);
bool DoCommandWithCallback(const CommandContainer &cc, CommandCallback callback);
void HandleCommandExecution(bool res, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, const std::string &text);
void InitCommandQueue();
void HandleNextClientFrame();
void SendClientCommand(const CommandPacket *cp);
} // namespace citymania
#endif

View File

@@ -30,7 +30,7 @@
#include "table/strings.h" #include "table/strings.h"
#include "citymania/cm_blueprint.hpp" #include "citymania/cm_commands.hpp"
#include "citymania/cm_hotkeys.hpp" #include "citymania/cm_hotkeys.hpp"
#include "citymania/cm_watch_gui.hpp" #include "citymania/cm_watch_gui.hpp"
@@ -617,7 +617,7 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallbac
callback(res, tile, p1, p2, cmd); callback(res, tile, p1, p2, cmd);
} }
if (!estimate_only && !only_sending) if (!estimate_only && !only_sending)
citymania::CommandExecuted(res.Succeeded(), tile, p1, p2, cmd); citymania::HandleCommandExecution(res.Succeeded(), tile, p1, p2, cmd, text);
return res.Succeeded(); return res.Succeeded();
} }

View File

@@ -34,8 +34,9 @@
#include "../town.h" #include "../town.h"
#include "network_func.h" #include "network_func.h"
#include "../citymania/cm_newgrf_revisions.hpp"
#include "../citymania/cm_client_list_gui.hpp" #include "../citymania/cm_client_list_gui.hpp"
#include "../citymania/cm_commands.hpp"
#include "../citymania/cm_newgrf_revisions.hpp"
#include "../safeguards.h" #include "../safeguards.h"
@@ -276,6 +277,8 @@ void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
extern void StateGameLoop(); extern void StateGameLoop();
StateGameLoop(); StateGameLoop();
citymania::HandleNextClientFrame();
/* Check if we are in sync! */ /* Check if we are in sync! */
if (_sync_frame != 0) { if (_sync_frame != 0) {
if (_sync_frame == _frame_counter) { if (_sync_frame == _frame_counter) {
@@ -776,6 +779,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_BEGIN(Packe
this->savegame = new PacketReader(); this->savegame = new PacketReader();
_frame_counter = _frame_counter_server = _frame_counter_max = p->Recv_uint32(); _frame_counter = _frame_counter_server = _frame_counter_max = p->Recv_uint32();
citymania::InitCommandQueue();
_network_join_bytes = 0; _network_join_bytes = 0;
_network_join_bytes_total = 0; _network_join_bytes_total = 0;

View File

@@ -15,6 +15,8 @@
#include "../company_func.h" #include "../company_func.h"
#include "../settings_type.h" #include "../settings_type.h"
#include "../citymania/cm_commands.hpp"
#include "../safeguards.h" #include "../safeguards.h"
/** Table with all the callbacks we'll use for conversion*/ /** Table with all the callbacks we'll use for conversion*/
@@ -163,7 +165,8 @@ void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, Comman
c.frame = 0; // The client can't tell which frame, so just make it 0 c.frame = 0; // The client can't tell which frame, so just make it 0
/* Clients send their command to the server and forget all about the packet */ /* Clients send their command to the server and forget all about the packet */
MyClient::SendCommand(&c); // MyClient::SendCommand(&c);
citymania::SendClientCommand(&c);
} }
/** /**

View File

@@ -42,8 +42,8 @@
#include "widgets/rail_widget.h" #include "widgets/rail_widget.h"
#include "network/network.h"
#include "citymania/cm_blueprint.hpp" #include "citymania/cm_blueprint.hpp"
#include "citymania/cm_commands.hpp"
#include "citymania/cm_hotkeys.hpp" #include "citymania/cm_hotkeys.hpp"
#include "citymania/cm_highlight.hpp" #include "citymania/cm_highlight.hpp"
#include "citymania/cm_station_gui.hpp" #include "citymania/cm_station_gui.hpp"
@@ -408,16 +408,12 @@ static bool DoAutodirTerraform(bool diagonal, TileIndex start_tile, TileIndex en
int diag_flag = (int)diagonal; int diag_flag = (int)diagonal;
DoCommandP(e1, s1, ((h1 < h2 ? LM_RAISE : LM_LEVEL) << 1) | diag_flag, CMD_LEVEL_LAND, CcTerraform); DoCommandP(e1, s1, ((h1 < h2 ? LM_RAISE : LM_LEVEL) << 1) | diag_flag, CMD_LEVEL_LAND, CcTerraform);
uint32 p2 = ((h2 < h1 ? LM_RAISE : LM_LEVEL) << 1) | diag_flag; uint32 p2 = ((h2 < h1 ? LM_RAISE : LM_LEVEL) << 1) | diag_flag;
DoCommandP(e2, s2, p2, CMD_LEVEL_LAND, CcTerraform);
auto rail_callback = [rail_cmd, start_tile, end_tile, track](bool res) -> bool { auto rail_callback = [rail_cmd, start_tile, end_tile, track](bool res) -> bool {
if (!citymania::_estimate_mod && end_tile != INVALID_TILE) if (!citymania::_estimate_mod && end_tile != INVALID_TILE)
StoreRailPlacementEndpoints(start_tile, end_tile, track, true); StoreRailPlacementEndpoints(start_tile, end_tile, track, true);
return DoCommandP(&rail_cmd); return DoCommandP(&rail_cmd);
} ; } ;
if (_networking) { return citymania::DoCommandWithCallback(e2, s2, p2, CMD_LEVEL_LAND, CcTerraform, "", rail_callback);
citymania::AddCommandCallback(e2, s2, p2, CMD_LEVEL_LAND, rail_callback);
return true;
} else return rail_callback(true);
} }
static bool HandleAutodirTerraform(TileIndex start_tile, TileIndex end_tile, Track track, CommandContainer &rail_cmd) { static bool HandleAutodirTerraform(TileIndex start_tile, TileIndex end_tile, Track track, CommandContainer &rail_cmd) {