Update to 1.11.0-RC1
This commit is contained in:
@@ -25,9 +25,10 @@
|
||||
* @param s The socket to connect with.
|
||||
*/
|
||||
NetworkGameSocketHandler::NetworkGameSocketHandler(SOCKET s) : info(nullptr), client_id(INVALID_CLIENT_ID),
|
||||
last_frame(_frame_counter), last_frame_server(_frame_counter), last_packet(_realtime_tick)
|
||||
last_frame(_frame_counter), last_frame_server(_frame_counter)
|
||||
{
|
||||
this->sock = s;
|
||||
this->last_packet = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +64,7 @@ NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet *p)
|
||||
{
|
||||
PacketGameType type = (PacketGameType)p->Recv_uint8();
|
||||
|
||||
this->last_packet = _realtime_tick;
|
||||
this->last_packet = std::chrono::steady_clock::now();
|
||||
|
||||
switch (this->HasClientQuit() ? PACKET_END : type) {
|
||||
case PACKET_SERVER_FULL: return this->Receive_SERVER_FULL(p);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "tcp.h"
|
||||
#include "../network_type.h"
|
||||
#include "../../core/pool_type.hpp"
|
||||
#include <chrono>
|
||||
|
||||
/**
|
||||
* Enum with all types of TCP packets.
|
||||
@@ -518,7 +519,7 @@ public:
|
||||
uint32 last_frame; ///< Last frame we have executed
|
||||
uint32 last_frame_server; ///< Last frame the server has executed
|
||||
CommandQueue incoming_queue; ///< The command-queue awaiting handling
|
||||
uint last_packet; ///< Time we received the last frame.
|
||||
std::chrono::steady_clock::time_point last_packet; ///< Time we received the last frame.
|
||||
|
||||
NetworkRecvStatus CloseConnection(bool error = true) override;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ NetworkAdminSocketPool _networkadminsocket_pool("NetworkAdminSocket");
|
||||
INSTANTIATE_POOL_METHODS(NetworkAdminSocket)
|
||||
|
||||
/** The timeout for authorisation of the client. */
|
||||
static const int ADMIN_AUTHORISATION_TIMEOUT = 10000;
|
||||
static const std::chrono::seconds ADMIN_AUTHORISATION_TIMEOUT(10);
|
||||
|
||||
|
||||
/** Frequencies, which may be registered for a certain update type. */
|
||||
@@ -64,7 +64,7 @@ ServerNetworkAdminSocketHandler::ServerNetworkAdminSocketHandler(SOCKET s) : Net
|
||||
{
|
||||
_network_admins_connected++;
|
||||
this->status = ADMIN_STATUS_INACTIVE;
|
||||
this->realtime_connect = _realtime_tick;
|
||||
this->connect_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,8 +95,8 @@ ServerNetworkAdminSocketHandler::~ServerNetworkAdminSocketHandler()
|
||||
/* static */ void ServerNetworkAdminSocketHandler::Send()
|
||||
{
|
||||
for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::Iterate()) {
|
||||
if (as->status == ADMIN_STATUS_INACTIVE && as->realtime_connect + ADMIN_AUTHORISATION_TIMEOUT < _realtime_tick) {
|
||||
DEBUG(net, 1, "[admin] Admin did not send its authorisation within %d seconds", ADMIN_AUTHORISATION_TIMEOUT / 1000);
|
||||
if (as->status == ADMIN_STATUS_INACTIVE && std::chrono::steady_clock::now() > as->connect_time + ADMIN_AUTHORISATION_TIMEOUT) {
|
||||
DEBUG(net, 1, "[admin] Admin did not send its authorisation within %d seconds", (uint32)std::chrono::duration_cast<std::chrono::seconds>(ADMIN_AUTHORISATION_TIMEOUT).count());
|
||||
as->CloseConnection(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ protected:
|
||||
NetworkRecvStatus SendPong(uint32 d1);
|
||||
public:
|
||||
AdminUpdateFrequency update_frequency[ADMIN_UPDATE_END]; ///< Admin requested update intervals.
|
||||
uint32 realtime_connect; ///< Time of connection.
|
||||
std::chrono::steady_clock::time_point connect_time; ///< Time of connection.
|
||||
NetworkAddress address; ///< Address of the admin.
|
||||
|
||||
ServerNetworkAdminSocketHandler(SOCKET s);
|
||||
|
||||
@@ -40,7 +40,7 @@ static const uint NETWORK_CHAT_LINE_SPACING = 3;
|
||||
struct ChatMessage {
|
||||
char message[DRAW_STRING_BUFFER]; ///< The action message.
|
||||
TextColour colour; ///< The colour of the message.
|
||||
uint32 remove_time; ///< The time to remove the message.
|
||||
std::chrono::steady_clock::time_point remove_time; ///< The time to remove the message.
|
||||
};
|
||||
|
||||
/* used for chat window */
|
||||
@@ -97,7 +97,7 @@ void CDECL NetworkAddChatMessage(TextColour colour, uint duration, const char *m
|
||||
ChatMessage *cmsg = &_chatmsg_list[msg_count++];
|
||||
strecpy(cmsg->message, buf, lastof(cmsg->message));
|
||||
cmsg->colour = (colour & TC_IS_PALETTE_COLOUR) ? colour : TC_WHITE;
|
||||
cmsg->remove_time = _realtime_tick + duration * 1000;
|
||||
cmsg->remove_time = std::chrono::steady_clock::now() + std::chrono::seconds(duration);
|
||||
|
||||
_chatmessage_dirty = true;
|
||||
}
|
||||
@@ -180,7 +180,7 @@ void NetworkChatMessageLoop()
|
||||
if (cmsg->message[0] == '\0') continue;
|
||||
|
||||
/* Message has expired, remove from the list */
|
||||
if (cmsg->remove_time < _realtime_tick) {
|
||||
if (std::chrono::steady_clock::now() > cmsg->remove_time) {
|
||||
/* Move the remaining messages over the current message */
|
||||
if (i != MAX_CHAT_MESSAGES - 1) memmove(cmsg, cmsg + 1, sizeof(*cmsg) * (MAX_CHAT_MESSAGES - i - 1));
|
||||
|
||||
|
||||
@@ -877,7 +877,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet
|
||||
bool load_success = SafeLoad({}, SLO_LOAD, DFT_GAME_FILE, GM_NORMAL, NO_DIRECTORY, lf);
|
||||
|
||||
/* Long savegame loads shouldn't affect the lag calculation! */
|
||||
this->last_packet = _realtime_tick;
|
||||
this->last_packet = std::chrono::steady_clock::now();
|
||||
|
||||
if (!load_success) {
|
||||
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
|
||||
@@ -1178,27 +1178,23 @@ void ClientNetworkGameSocketHandler::CheckConnection()
|
||||
/* Only once we're authorized we can expect a steady stream of packets. */
|
||||
if (this->status < STATUS_AUTHORIZED) return;
|
||||
|
||||
/* It might... sometimes occur that the realtime ticker overflows. */
|
||||
if (_realtime_tick < this->last_packet) this->last_packet = _realtime_tick;
|
||||
|
||||
/* Lag is in milliseconds; 5 seconds are roughly twice the
|
||||
* server's "you're slow" threshold (1 game day). */
|
||||
uint lag = (_realtime_tick - this->last_packet) / 1000;
|
||||
if (lag < 5) return;
|
||||
/* 5 seconds are roughly twice the server's "you're slow" threshold (1 game day). */
|
||||
std::chrono::steady_clock::duration lag = std::chrono::steady_clock::now() - this->last_packet;
|
||||
if (lag < std::chrono::seconds(5)) return;
|
||||
|
||||
/* 20 seconds are (way) more than 4 game days after which
|
||||
* the server will forcefully disconnect you. */
|
||||
if (lag > 20) {
|
||||
if (lag > std::chrono::seconds(20)) {
|
||||
this->NetworkGameSocketHandler::CloseConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prevent showing the lag message every tick; just update it when needed. */
|
||||
static uint last_lag = 0;
|
||||
if (last_lag == lag) return;
|
||||
static std::chrono::steady_clock::duration last_lag = {};
|
||||
if (std::chrono::duration_cast<std::chrono::seconds>(last_lag) == std::chrono::duration_cast<std::chrono::seconds>(lag)) return;
|
||||
|
||||
last_lag = lag;
|
||||
SetDParam(0, lag);
|
||||
SetDParam(0, std::chrono::duration_cast<std::chrono::seconds>(lag).count());
|
||||
ShowErrorMessage(STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION, STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION, WL_INFO);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ static CommandCallback * const _callback_table[] = {
|
||||
/* 0x01 */ CcBuildPrimaryVehicle,
|
||||
/* 0x02 */ CcBuildAirport,
|
||||
/* 0x03 */ CcBuildBridge,
|
||||
/* 0x04 */ CcPlaySound_SPLAT_WATER,
|
||||
/* 0x04 */ CcPlaySound_CONSTRUCTION_WATER,
|
||||
/* 0x05 */ CcBuildDocks,
|
||||
/* 0x06 */ CcFoundTown,
|
||||
/* 0x07 */ CcBuildRoadTunnel,
|
||||
@@ -33,8 +33,8 @@ static CommandCallback * const _callback_table[] = {
|
||||
/* 0x0B */ CcRailDepot,
|
||||
/* 0x0C */ CcPlaceSign,
|
||||
/* 0x0D */ CcPlaySound_EXPLOSION,
|
||||
/* 0x0E */ CcPlaySound_SPLAT_OTHER,
|
||||
/* 0x0F */ CcPlaySound_SPLAT_RAIL,
|
||||
/* 0x0E */ CcPlaySound_CONSTRUCTION_OTHER,
|
||||
/* 0x0F */ CcPlaySound_CONSTRUCTION_RAIL,
|
||||
/* 0x10 */ CcStation,
|
||||
/* 0x11 */ CcTerraform,
|
||||
/* 0x12 */ CcAI,
|
||||
@@ -298,7 +298,7 @@ const char *NetworkGameSocketHandler::ReceiveCommand(Packet *p, CommandPacket *c
|
||||
cp->company = (CompanyID)p->Recv_uint8();
|
||||
cp->cmd = p->Recv_uint32();
|
||||
if (!IsValidCommand(cp->cmd)) return "invalid command";
|
||||
if (GetCommandFlags(cp->cmd) & CMD_OFFLINE) return "offline only command";
|
||||
if (GetCommandFlags(cp->cmd) & CMD_OFFLINE) return "single-player only command";
|
||||
if ((cp->cmd & CMD_FLAGS_MASK) != 0) return "invalid command flag";
|
||||
|
||||
cp->p1 = p->Recv_uint32();
|
||||
|
||||
@@ -711,9 +711,9 @@ ClientNetworkContentSocketHandler::ClientNetworkContentSocketHandler() :
|
||||
http_response_index(-2),
|
||||
curFile(nullptr),
|
||||
curInfo(nullptr),
|
||||
isConnecting(false),
|
||||
lastActivity(_realtime_tick)
|
||||
isConnecting(false)
|
||||
{
|
||||
this->lastActivity = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
/** Clear up the mess ;) */
|
||||
@@ -743,7 +743,7 @@ public:
|
||||
void OnConnect(SOCKET s) override
|
||||
{
|
||||
assert(_network_content_client.sock == INVALID_SOCKET);
|
||||
_network_content_client.lastActivity = _realtime_tick;
|
||||
_network_content_client.lastActivity = std::chrono::steady_clock::now();
|
||||
_network_content_client.isConnecting = false;
|
||||
_network_content_client.sock = s;
|
||||
_network_content_client.Reopen();
|
||||
@@ -780,7 +780,7 @@ void ClientNetworkContentSocketHandler::SendReceive()
|
||||
{
|
||||
if (this->sock == INVALID_SOCKET || this->isConnecting) return;
|
||||
|
||||
if (this->lastActivity + IDLE_TIMEOUT < _realtime_tick) {
|
||||
if (std::chrono::steady_clock::now() > this->lastActivity + IDLE_TIMEOUT) {
|
||||
this->Close();
|
||||
return;
|
||||
}
|
||||
@@ -788,7 +788,7 @@ void ClientNetworkContentSocketHandler::SendReceive()
|
||||
if (this->CanSendReceive()) {
|
||||
if (this->ReceivePackets()) {
|
||||
/* Only update activity once a packet is received, instead of every time we try it. */
|
||||
this->lastActivity = _realtime_tick;
|
||||
this->lastActivity = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ protected:
|
||||
FILE *curFile; ///< Currently downloaded file
|
||||
ContentInfo *curInfo; ///< Information about the currently downloaded file
|
||||
bool isConnecting; ///< Whether we're connecting
|
||||
uint32 lastActivity; ///< The last time there was network activity
|
||||
std::chrono::steady_clock::time_point lastActivity; ///< The last time there was network activity
|
||||
|
||||
friend class NetworkContentConnecter;
|
||||
|
||||
@@ -100,7 +100,7 @@ protected:
|
||||
void DownloadSelectedContentFallback(const ContentIDList &content);
|
||||
public:
|
||||
/** The idle timeout; when to close the connection because it's idle. */
|
||||
static const int IDLE_TIMEOUT = 60 * 1000;
|
||||
static constexpr std::chrono::seconds IDLE_TIMEOUT = std::chrono::seconds(60);
|
||||
|
||||
ClientNetworkContentSocketHandler();
|
||||
~ClientNetworkContentSocketHandler();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "../ai/ai.hpp"
|
||||
#include "../game/game.hpp"
|
||||
#include "../base_media_base.h"
|
||||
#include "../openttd.h"
|
||||
#include "../sortlist_type.h"
|
||||
#include "../stringfilter_type.h"
|
||||
#include "../querystring_gui.h"
|
||||
@@ -236,7 +237,7 @@ public:
|
||||
break;
|
||||
|
||||
case CONTENT_TYPE_NEWGRF:
|
||||
ScanNewGRFFiles(nullptr);
|
||||
RequestNewGRFScan();
|
||||
break;
|
||||
|
||||
case CONTENT_TYPE_SCENARIO:
|
||||
@@ -325,7 +326,7 @@ class NetworkContentListWindow : public Window, ContentCallback {
|
||||
char url[1024];
|
||||
const char *last = lastof(url);
|
||||
|
||||
char *pos = strecpy(url, "http://grfsearch.openttd.org/?", last);
|
||||
char *pos = strecpy(url, "https://grfsearch.openttd.org/?", last);
|
||||
|
||||
if (this->auto_select) {
|
||||
pos = strecpy(pos, "do=searchgrfid&q=", last);
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "../genworld.h"
|
||||
#include "../map_type.h"
|
||||
#include "../guitimer_func.h"
|
||||
#include "../zoom_func.h"
|
||||
|
||||
#include "../widgets/network_widget.h"
|
||||
|
||||
@@ -330,10 +331,9 @@ protected:
|
||||
if (r == 0) r = b->info.compatible - a->info.compatible;
|
||||
/* Passworded servers should be below unpassworded servers */
|
||||
if (r == 0) r = a->info.use_password - b->info.use_password;
|
||||
/* Finally sort on the number of clients of the server */
|
||||
if (r == 0) return NGameClientSorter(a, b);
|
||||
|
||||
return r < 0;
|
||||
/* Finally sort on the number of clients of the server in reverse order. */
|
||||
return (r != 0) ? r < 0 : NGameClientSorter(b, a);
|
||||
}
|
||||
|
||||
/** Sort the server list */
|
||||
@@ -381,6 +381,7 @@ protected:
|
||||
/* offsets to vertically centre text and icons */
|
||||
int text_y_offset = (this->resize.step_height - FONT_HEIGHT_NORMAL) / 2 + 1;
|
||||
int icon_y_offset = (this->resize.step_height - GetSpriteSize(SPR_BLOT).height) / 2;
|
||||
int lock_y_offset = (this->resize.step_height - GetSpriteSize(SPR_LOCK).height) / 2;
|
||||
|
||||
DrawString(nwi_name->pos_x + WD_FRAMERECT_LEFT, nwi_name->pos_x + nwi_name->current_x - WD_FRAMERECT_RIGHT, y + text_y_offset, cur_item->info.server_name, TC_BLACK);
|
||||
|
||||
@@ -425,13 +426,13 @@ protected:
|
||||
}
|
||||
|
||||
/* draw a lock if the server is password protected */
|
||||
if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, nwi_info->pos_x + this->lock_offset, y + icon_y_offset - 1);
|
||||
if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, nwi_info->pos_x + this->lock_offset, y + lock_y_offset);
|
||||
|
||||
/* draw red or green icon, depending on compatibility with server */
|
||||
DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), nwi_info->pos_x + this->blot_offset, y + icon_y_offset);
|
||||
DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), nwi_info->pos_x + this->blot_offset, y + icon_y_offset + 1);
|
||||
|
||||
/* draw flag according to server language */
|
||||
DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, nwi_info->pos_x + this->flag_offset, y + icon_y_offset);
|
||||
DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, nwi_info->pos_x + this->flag_offset, y + (this->resize.step_height - GetSpriteSize(SPR_FLAGS_BASE + cur_item->info.server_lang).height) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1360,7 +1361,7 @@ struct NetworkLobbyWindow : public Window {
|
||||
break;
|
||||
|
||||
case WID_NL_MATRIX:
|
||||
resize->height = WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
|
||||
resize->height = WD_MATRIX_TOP + std::max<uint>(std::max(GetSpriteSize(SPR_LOCK).height, GetSpriteSize(SPR_PROFIT_LOT).height), FONT_HEIGHT_NORMAL) + WD_MATRIX_BOTTOM;
|
||||
size->height = 10 * resize->height;
|
||||
break;
|
||||
|
||||
@@ -1414,31 +1415,32 @@ struct NetworkLobbyWindow : public Window {
|
||||
bool rtl = _current_text_dir == TD_RTL;
|
||||
uint left = r.left + WD_FRAMERECT_LEFT;
|
||||
uint right = r.right - WD_FRAMERECT_RIGHT;
|
||||
uint text_offset = (this->resize.step_height - WD_MATRIX_TOP - WD_MATRIX_BOTTOM - FONT_HEIGHT_NORMAL) / 2 + WD_MATRIX_TOP;
|
||||
|
||||
Dimension lock_size = GetSpriteSize(SPR_LOCK);
|
||||
int lock_width = lock_size.width;
|
||||
int lock_y_offset = (this->resize.step_height - WD_MATRIX_TOP - WD_MATRIX_BOTTOM - lock_size.height) / 2;
|
||||
int lock_y_offset = (this->resize.step_height - WD_MATRIX_TOP - WD_MATRIX_BOTTOM - lock_size.height) / 2 + WD_MATRIX_TOP;
|
||||
|
||||
Dimension profit_size = GetSpriteSize(SPR_PROFIT_LOT);
|
||||
int profit_width = lock_size.width;
|
||||
int profit_y_offset = (this->resize.step_height - WD_MATRIX_TOP - WD_MATRIX_BOTTOM - profit_size.height) / 2;
|
||||
int profit_y_offset = (this->resize.step_height - WD_MATRIX_TOP - WD_MATRIX_BOTTOM - profit_size.height) / 2 + WD_MATRIX_TOP;
|
||||
|
||||
uint text_left = left + (rtl ? lock_width + profit_width + 4 : 0);
|
||||
uint text_right = right - (rtl ? 0 : lock_width + profit_width + 4);
|
||||
uint profit_left = rtl ? left : right - profit_width;
|
||||
uint lock_left = rtl ? left + profit_width + 2 : right - profit_width - lock_width - 2;
|
||||
|
||||
int y = r.top + WD_MATRIX_TOP;
|
||||
int y = r.top;
|
||||
/* Draw company list */
|
||||
int pos = this->vscroll->GetPosition();
|
||||
while (pos < this->server->info.companies_on) {
|
||||
byte company = NetworkLobbyFindCompanyIndex(pos);
|
||||
bool income = false;
|
||||
if (this->company == company) {
|
||||
GfxFillRect(r.left + 1, y - 2, r.right - 1, y + FONT_HEIGHT_NORMAL, PC_GREY); // show highlighted item with a different colour
|
||||
GfxFillRect(r.left + WD_BEVEL_LEFT, y + 1, r.right - WD_BEVEL_RIGHT, y + this->resize.step_height - 2, PC_GREY); // show highlighted item with a different colour
|
||||
}
|
||||
|
||||
DrawString(text_left, text_right, y, this->company_info[company].company_name, TC_BLACK);
|
||||
DrawString(text_left, text_right, y + text_offset, this->company_info[company].company_name, TC_BLACK);
|
||||
if (this->company_info[company].use_password != 0) DrawSprite(SPR_LOCK, PAL_NONE, lock_left, y + lock_y_offset);
|
||||
|
||||
/* If the company's income was positive puts a green dot else a red dot */
|
||||
|
||||
@@ -265,7 +265,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
|
||||
*/
|
||||
if (this->sock == INVALID_SOCKET) return status;
|
||||
|
||||
if (status != NETWORK_RECV_STATUS_CONN_LOST && !this->HasClientQuit() && this->status >= STATUS_AUTHORIZED) {
|
||||
if (status != NETWORK_RECV_STATUS_CONN_LOST && status != NETWORK_RECV_STATUS_SERVER_ERROR && !this->HasClientQuit() && this->status >= STATUS_AUTHORIZED) {
|
||||
/* We did not receive a leave message from this client... */
|
||||
char client_name[NETWORK_CLIENT_NAME_LENGTH];
|
||||
|
||||
@@ -281,6 +281,16 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
|
||||
}
|
||||
}
|
||||
|
||||
/* If we were transfering a map to this client, stop the savegame creation
|
||||
* process and queue the next client to receive the map. */
|
||||
if (this->status == STATUS_MAP) {
|
||||
/* Ensure the saving of the game is stopped too. */
|
||||
this->savegame->Destroy();
|
||||
this->savegame = nullptr;
|
||||
|
||||
this->CheckNextClientToSendMap(this);
|
||||
}
|
||||
|
||||
NetworkAdminClientError(this->client_id, NETWORK_ERROR_CONNECTION_LOST);
|
||||
DEBUG(net, 1, "Closed client connection %d", this->client_id);
|
||||
|
||||
@@ -550,7 +560,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendWelcome()
|
||||
/** Tell the client that its put in a waiting queue. */
|
||||
NetworkRecvStatus ServerNetworkGameSocketHandler::SendWait()
|
||||
{
|
||||
int waiting = 0;
|
||||
int waiting = 1; // current player getting the map counts as 1
|
||||
Packet *p;
|
||||
|
||||
/* Count how many clients are waiting in the queue, in front of you! */
|
||||
@@ -565,6 +575,33 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendWait()
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
void ServerNetworkGameSocketHandler::CheckNextClientToSendMap(NetworkClientSocket *ignore_cs)
|
||||
{
|
||||
/* Find the best candidate for joining, i.e. the first joiner. */
|
||||
NetworkClientSocket *best = nullptr;
|
||||
for (NetworkClientSocket *new_cs : NetworkClientSocket::Iterate()) {
|
||||
if (ignore_cs == new_cs) continue;
|
||||
|
||||
if (new_cs->status == STATUS_MAP_WAIT) {
|
||||
if (best == nullptr || best->GetInfo()->join_date > new_cs->GetInfo()->join_date || (best->GetInfo()->join_date == new_cs->GetInfo()->join_date && best->client_id > new_cs->client_id)) {
|
||||
best = new_cs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Is there someone else to join? */
|
||||
if (best != nullptr) {
|
||||
/* Let the first start joining. */
|
||||
best->status = STATUS_AUTHORIZED;
|
||||
best->SendMap();
|
||||
|
||||
/* And update the rest. */
|
||||
for (NetworkClientSocket *new_cs : NetworkClientSocket::Iterate()) {
|
||||
if (new_cs->status == STATUS_MAP_WAIT) new_cs->SendWait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** This sends the map to the client */
|
||||
NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap()
|
||||
{
|
||||
@@ -620,27 +657,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap()
|
||||
* to send it is ready (maybe that happens like never ;)) */
|
||||
this->status = STATUS_DONE_MAP;
|
||||
|
||||
/* Find the best candidate for joining, i.e. the first joiner. */
|
||||
NetworkClientSocket *best = nullptr;
|
||||
for (NetworkClientSocket *new_cs : NetworkClientSocket::Iterate()) {
|
||||
if (new_cs->status == STATUS_MAP_WAIT) {
|
||||
if (best == nullptr || best->GetInfo()->join_date > new_cs->GetInfo()->join_date || (best->GetInfo()->join_date == new_cs->GetInfo()->join_date && best->client_id > new_cs->client_id)) {
|
||||
best = new_cs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Is there someone else to join? */
|
||||
if (best != nullptr) {
|
||||
/* Let the first start joining. */
|
||||
best->status = STATUS_AUTHORIZED;
|
||||
best->SendMap();
|
||||
|
||||
/* And update the rest. */
|
||||
for (NetworkClientSocket *new_cs : NetworkClientSocket::Iterate()) {
|
||||
if (new_cs->status == STATUS_MAP_WAIT) new_cs->SendWait();
|
||||
}
|
||||
}
|
||||
this->CheckNextClientToSendMap();
|
||||
}
|
||||
|
||||
switch (this->SendPackets()) {
|
||||
@@ -1100,12 +1117,12 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet
|
||||
|
||||
|
||||
if ((GetCommandFlags(cp.cmd) & CMD_SERVER) && ci->client_id != CLIENT_ID_SERVER) {
|
||||
IConsolePrintF(CC_ERROR, "WARNING: server only command from: client %d (IP: %s), kicking...", ci->client_id, this->GetClientIP());
|
||||
IConsolePrintF(CC_ERROR, "WARNING: server only command %u from client %u (IP: %s), kicking...", cp.cmd & CMD_ID_MASK, ci->client_id, this->GetClientIP());
|
||||
return this->SendError(NETWORK_ERROR_KICKED);
|
||||
}
|
||||
|
||||
if ((GetCommandFlags(cp.cmd) & CMD_SPECTATOR) == 0 && !Company::IsValidID(cp.company) && ci->client_id != CLIENT_ID_SERVER) {
|
||||
IConsolePrintF(CC_ERROR, "WARNING: spectator issuing command from client %d (IP: %s), kicking...", ci->client_id, this->GetClientIP());
|
||||
IConsolePrintF(CC_ERROR, "WARNING: spectator (client: %u, IP: %s) issued non-spectator command %u, kicking...", ci->client_id, this->GetClientIP(), cp.cmd & CMD_ID_MASK);
|
||||
return this->SendError(NETWORK_ERROR_KICKED);
|
||||
}
|
||||
|
||||
@@ -1816,7 +1833,7 @@ void NetworkServer_Tick(bool send_frame)
|
||||
case NetworkClientSocket::STATUS_ACTIVE:
|
||||
if (lag > _settings_client.network.max_lag_time) {
|
||||
/* Client did still not report in within the specified limit. */
|
||||
IConsolePrintF(CC_ERROR, cs->last_packet + lag * MILLISECONDS_PER_TICK > _realtime_tick ?
|
||||
IConsolePrintF(CC_ERROR, cs->last_packet + std::chrono::milliseconds(lag * MILLISECONDS_PER_TICK) > std::chrono::steady_clock::now() ?
|
||||
/* A packet was received in the last three game days, so the client is likely lagging behind. */
|
||||
"Client #%d is dropped because the client's game state is more than %d ticks behind" :
|
||||
/* No packet was received in the last three game days; sounds like a lost connection. */
|
||||
@@ -1827,11 +1844,11 @@ void NetworkServer_Tick(bool send_frame)
|
||||
}
|
||||
|
||||
/* Report once per time we detect the lag, and only when we
|
||||
* received a packet in the last 2000 milliseconds. If we
|
||||
* received a packet in the last 2 seconds. If we
|
||||
* did not receive a packet, then the client is not just
|
||||
* slow, but the connection is likely severed. Mentioning
|
||||
* frame_freq is not useful in this case. */
|
||||
if (lag > (uint)DAY_TICKS && cs->lag_test == 0 && cs->last_packet + 2000 > _realtime_tick) {
|
||||
if (lag > (uint)DAY_TICKS && cs->lag_test == 0 && cs->last_packet + std::chrono::seconds(2) > std::chrono::steady_clock::now()) {
|
||||
IConsolePrintF(CC_WARNING, "[%d] Client #%d is slow, try increasing [network.]frame_freq to a higher value!", _frame_counter, cs->client_id);
|
||||
cs->lag_test = 1;
|
||||
}
|
||||
@@ -1856,6 +1873,21 @@ void NetworkServer_Tick(bool send_frame)
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkClientSocket::STATUS_MAP_WAIT:
|
||||
/* Send every two seconds a packet to the client, to make sure
|
||||
* he knows the server is still there; just someone else is
|
||||
* still receiving the map. */
|
||||
if (std::chrono::steady_clock::now() > cs->last_packet + std::chrono::seconds(2)) {
|
||||
cs->SendWait();
|
||||
/* We need to reset the timer, as otherwise we will be
|
||||
* spamming the client. Strictly speaking this variable
|
||||
* tracks when we last received a packet from the client,
|
||||
* but as he is waiting, he will not send us any till we
|
||||
* start sending him data. */
|
||||
cs->last_packet = std::chrono::steady_clock::now();
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkClientSocket::STATUS_MAP:
|
||||
/* Downloading the map... this is the amount of time since starting the saving. */
|
||||
if (lag > _settings_client.network.max_download_time) {
|
||||
@@ -1885,11 +1917,6 @@ void NetworkServer_Tick(bool send_frame)
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkClientSocket::STATUS_MAP_WAIT:
|
||||
/* This is an internal state where we do not wait
|
||||
* on the client to move to a different state. */
|
||||
break;
|
||||
|
||||
case NetworkClientSocket::STATUS_END:
|
||||
/* Bad server/code. */
|
||||
NOT_REACHED();
|
||||
|
||||
@@ -43,7 +43,6 @@ protected:
|
||||
NetworkRecvStatus SendCompanyInfo();
|
||||
NetworkRecvStatus SendNewGRFCheck();
|
||||
NetworkRecvStatus SendWelcome();
|
||||
NetworkRecvStatus SendWait();
|
||||
NetworkRecvStatus SendNeedGamePassword();
|
||||
NetworkRecvStatus SendNeedCompanyPassword();
|
||||
|
||||
@@ -80,6 +79,9 @@ public:
|
||||
NetworkRecvStatus CloseConnection(NetworkRecvStatus status) override;
|
||||
void GetClientName(char *client_name, const char *last) const;
|
||||
|
||||
void CheckNextClientToSendMap(NetworkClientSocket *ignore_cs = nullptr);
|
||||
|
||||
NetworkRecvStatus SendWait();
|
||||
NetworkRecvStatus SendMap();
|
||||
NetworkRecvStatus SendErrorQuit(ClientID client_id, NetworkErrorCode errorno);
|
||||
NetworkRecvStatus SendQuit(ClientID client_id);
|
||||
|
||||
@@ -39,8 +39,8 @@ static std::mutex _network_udp_mutex;
|
||||
/** Session key to register ourselves to the master server */
|
||||
static uint64 _session_key = 0;
|
||||
|
||||
static const uint32 ADVERTISE_NORMAL_INTERVAL = 15 * 60 * 1000; ///< interval between advertising in ms (15 minutes)
|
||||
static const uint32 ADVERTISE_RETRY_INTERVAL = 10 * 1000; ///< re-advertise when no response after this many ms (10 seconds)
|
||||
static const std::chrono::minutes ADVERTISE_NORMAL_INTERVAL(15); ///< interval between advertising.
|
||||
static const std::chrono::seconds ADVERTISE_RETRY_INTERVAL(10); ///< re-advertise when no response after this amount of time.
|
||||
static const uint32 ADVERTISE_RETRY_TIMES = 3; ///< give up re-advertising after this much failed retries
|
||||
|
||||
NetworkUDPSocketHandler *_udp_client_socket = nullptr; ///< udp client socket
|
||||
@@ -570,37 +570,29 @@ static void NetworkUDPAdvertiseThread()
|
||||
*/
|
||||
void NetworkUDPAdvertise()
|
||||
{
|
||||
static uint32 _last_advertisement = 0; ///< The time of the last advertisement (used to check for wrapping of time)
|
||||
static uint32 _next_advertisement = 0; ///< The next time we should perform a normal advertisement.
|
||||
static uint32 _next_retry = 0; ///< The next time we should perform a retry of an advertisement.
|
||||
static std::chrono::steady_clock::time_point _last_advertisement = {}; ///< The last time we performed an advertisement.
|
||||
|
||||
/* Check if we should send an advertise */
|
||||
if (!_networking || !_network_server || !_network_udp_server || !_settings_client.network.server_advertise) return;
|
||||
|
||||
if (_network_need_advertise || _realtime_tick < _last_advertisement) {
|
||||
/* Forced advertisement, or a wrapping of time in which case we determine the advertisement/retry times again. */
|
||||
if (_network_need_advertise) {
|
||||
/* Forced advertisement. */
|
||||
_network_need_advertise = false;
|
||||
_network_advertise_retries = ADVERTISE_RETRY_TIMES;
|
||||
} else {
|
||||
/* Only send once every ADVERTISE_NORMAL_INTERVAL ticks */
|
||||
if (_network_advertise_retries == 0) {
|
||||
if (_realtime_tick <= _next_advertisement) return;
|
||||
if (std::chrono::steady_clock::now() <= _last_advertisement + ADVERTISE_NORMAL_INTERVAL) return;
|
||||
|
||||
_network_advertise_retries = ADVERTISE_RETRY_TIMES;
|
||||
} else {
|
||||
/* An actual retry. */
|
||||
if (_realtime_tick <= _next_retry) return;
|
||||
if (std::chrono::steady_clock::now() <= _last_advertisement + ADVERTISE_RETRY_INTERVAL) return;
|
||||
}
|
||||
}
|
||||
|
||||
_network_advertise_retries--;
|
||||
_last_advertisement = _realtime_tick;
|
||||
_next_advertisement = _realtime_tick + ADVERTISE_NORMAL_INTERVAL;
|
||||
_next_retry = _realtime_tick + ADVERTISE_RETRY_INTERVAL;
|
||||
|
||||
/* Make sure we do not have an overflow when checking these; when time wraps, we simply force an advertisement. */
|
||||
if (_next_advertisement < _last_advertisement) _next_advertisement = UINT32_MAX;
|
||||
if (_next_retry < _last_advertisement) _next_retry = UINT32_MAX;
|
||||
_last_advertisement = std::chrono::steady_clock::now();
|
||||
|
||||
if (!StartNewThread(nullptr, "ottd:udp-advert", &NetworkUDPAdvertiseThread)) {
|
||||
NetworkUDPAdvertiseThread();
|
||||
|
||||
Reference in New Issue
Block a user