Fix #11: Queue outgoing commands and don't send more than 2 per frame
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
121
src/citymania/cm_commands.cpp
Normal file
121
src/citymania/cm_commands.cpp
Normal 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
|
||||||
22
src/citymania/cm_commands.hpp
Normal file
22
src/citymania/cm_commands.hpp
Normal 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
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user