@@ -293,48 +293,50 @@ void ClientNetworkGameSocketHandler::Cli
_sync_frame = 0;
} else if (_sync_frame < _frame_counter) {
Debug(net, 1, "Missed frame for sync-test: {} / {}", _sync_frame, _frame_counter);
}
return true;
/** Our client's connection. */
ClientNetworkGameSocketHandler * ClientNetworkGameSocketHandler::my_client = nullptr;
/** Last frame we performed an ack. */
static uint32 last_ack_frame;
/** One bit of 'entropy' used to generate a salt for the company passwords. */
static uint32 _password_game_seed;
/** The other bit of 'entropy' used to generate a salt for the company passwords. */
static std::string _password_server_id;
/** Maximum number of companies of the currently joined server. */
static uint8 _network_server_max_companies;
/** The current name of the server you are on. */
std::string _network_server_name;
/** Information about the game to join to. */
NetworkJoinInfo _network_join;
/** Make sure the server ID length is the same as a md5 hash. */
static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1);
/***********
* Sending functions
* DEF_CLIENT_SEND_COMMAND has no parameters
************/
/**
* Query the server for server information.
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendInformationQuery(bool request_company_info)
{
my_client->status = STATUS_GAME_INFO;
my_client->SendPacket(new Packet(PACKET_CLIENT_GAME_INFO));
if (request_company_info) {
my_client->status = STATUS_COMPANY_INFO;
_network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO;
SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
@@ -1166,48 +1168,49 @@ NetworkRecvStatus ClientNetworkGameSocke
/* definitely an invalid client id, debug message and do nothing. */
Debug(net, 1, "Received invalid client index = 0");
return NETWORK_RECV_STATUS_MALFORMED_PACKET;
const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id);
/* Just make sure we do not try to use a client_index that does not exist */
if (ci == nullptr) return NETWORK_RECV_STATUS_OKAY;
/* if not valid player, force spectator, else check player exists */
if (!Company::IsValidID(company_id)) company_id = COMPANY_SPECTATOR;
if (client_id == _network_own_client_id) {
SetLocalCompany(company_id);
return NETWORK_RECV_STATUS_OKAY;
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CONFIG_UPDATE(Packet *p)
if (this->status < STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
_network_server_max_companies = p->Recv_uint8();
_network_server_name = p->Recv_string(NETWORK_NAME_LENGTH);
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_COMPANY_UPDATE(Packet *p)
_network_company_passworded = p->Recv_uint16();
SetWindowClassesDirty(WC_COMPANY);
* Check the connection's state, i.e. is the connection still up?
void ClientNetworkGameSocketHandler::CheckConnection()
/* Only once we're authorized we can expect a steady stream of packets. */
if (this->status < STATUS_AUTHORIZED) 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;
@@ -1580,56 +1580,56 @@ NetworkCompanyInfo *GetLobbyCompanyInfo(
* Get the game information for the lobby.
* @return the game info struct to write the (downloaded) data to.
NetworkGameList *GetLobbyGameInfo()
NetworkLobbyWindow *lobby = dynamic_cast<NetworkLobbyWindow *>(FindWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY));
return lobby != nullptr ? lobby->server : nullptr;
/* The window below gives information about the connected clients
* and also makes able to kick them (if server) and stuff like that. */
extern void DrawCompanyIcon(CompanyID cid, int x, int y);
static const NWidgetPart _nested_client_list_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_CL_SERVER_SELECTOR),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER, STR_NULL), SetPadding(4, 4, 0, 4), SetPIP(0, 2, 0),
NWidget(NWID_HORIZONTAL), SetPIP(0, 3, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_NAME, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(10, 0),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_SERVER_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_SERVER_NAME_EDIT_TOOLTIP),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY, STR_NULL),
NWidget(NWID_SPACER), SetMinimalSize(10, 0), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_CL_SERVER_VISIBILITY), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_VISIBILITY_TOOLTIP),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE, STR_NULL),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_INVITE_CODE), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_SERVER_INVITE_CODE_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE, STR_NULL),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_SERVER_CONNECTION_TYPE), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_STRING, STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_NETWORK_CLIENT_LIST_PLAYER, STR_NULL), SetPadding(4, 4, 4, 4), SetPIP(0, 2, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalTextLines(1, 0), SetDataTip(STR_NETWORK_CLIENT_LIST_PLAYER_NAME, STR_NULL),
NWidget(WWT_TEXT, COLOUR_GREY, WID_CL_CLIENT_NAME), SetFill(1, 0), SetMinimalTextLines(1, 0), SetResize(1, 0), SetDataTip(STR_BLACK_RAW_STRING, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_TOOLTIP), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CL_CLIENT_NAME_EDIT), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_NETWORK_CLIENT_LIST_PLAYER_NAME_EDIT_TOOLTIP),
@@ -1996,83 +1996,84 @@ private:
return nullptr;
public:
NetworkClientListWindow(WindowDesc *desc, WindowNumber window_number) :
Window(desc),
hover_index(-1),
player_self_index(-1),
player_host_index(-1)
this->CreateNestedTree();
this->vscroll = this->GetScrollbar(WID_CL_SCROLLBAR);
this->OnInvalidateData();
this->FinishInitNested(window_number);
void OnInvalidateData(int data = 0, bool gui_scope = true) override
this->RebuildList();
/* Currently server information is not sync'd to clients, so we cannot show it on clients. */
this->GetWidget<NWidgetStacked>(WID_CL_SERVER_SELECTOR)->SetDisplayedPlane(_network_server ? 0 : SZSP_HORIZONTAL);
this->SetWidgetDisabledState(WID_CL_SERVER_NAME_EDIT, !_network_server);
void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
switch (widget) {
case WID_CL_SERVER_VISIBILITY:
*size = maxdim(maxdim(GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_LOCAL), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_PUBLIC)), GetStringBoundingBox(STR_NETWORK_SERVER_VISIBILITY_INVITE_ONLY));
size->width += padding.width;
size->height += padding.height;
break;
case WID_CL_MATRIX: {
uint height = std::max({GetSpriteSize(SPR_COMPANY_ICON).height, GetSpriteSize(SPR_JOIN).height, GetSpriteSize(SPR_ADMIN).height, GetSpriteSize(SPR_CHAT).height});
height += ScaleGUITrad(WD_FRAMERECT_TOP) + ScaleGUITrad(WD_FRAMERECT_BOTTOM);
this->line_height = std::max(height, (uint)FONT_HEIGHT_NORMAL) + ScaleGUITrad(WD_MATRIX_TOP + WD_MATRIX_BOTTOM);
resize->width = 1;
resize->height = this->line_height;
fill->height = this->line_height;
size->height = std::max(size->height, 5 * this->line_height);
void OnResize() override
this->vscroll->SetCapacityFromWidget(this, WID_CL_MATRIX);
void SetStringParameters(int widget) const override
case WID_CL_SERVER_NAME:
SetDParamStr(0, _settings_client.network.server_name);
SetDParamStr(0, _network_server ? _settings_client.network.server_name : _network_server_name);
SetDParam(0, STR_NETWORK_SERVER_VISIBILITY_LOCAL + _settings_client.network.server_game_type);
case WID_CL_SERVER_INVITE_CODE: {
static std::string empty = {};
SetDParamStr(0, _network_server_connection_type == CONNECTION_TYPE_UNKNOWN ? empty : _network_server_invite_code);
case WID_CL_SERVER_CONNECTION_TYPE:
SetDParam(0, STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN + _network_server_connection_type);
case WID_CL_CLIENT_NAME:
SetDParamStr(0, _settings_client.network.client_name);
case WID_CL_CLIENT_COMPANY_COUNT:
SetDParam(0, NetworkClientInfo::GetNumItems());
SetDParam(1, Company::GetNumItems());
@@ -65,48 +65,51 @@ enum NetworkJoinStatus {
extern uint32 _frame_counter_server; // The frame_counter of the server, if in network-mode
extern uint32 _frame_counter_max; // To where we may go with our clients
extern uint32 _frame_counter;
extern uint32 _last_sync_frame; // Used in the server to store the last time a sync packet was sent to clients.
/* networking settings */
extern NetworkAddressList _broadcast_list;
extern uint32 _sync_seed_1;
#ifdef NETWORK_SEND_DOUBLE_SEED
extern uint32 _sync_seed_2;
#endif
extern uint32 _sync_frame;
extern bool _network_first_time;
/* Vars needed for the join-GUI */
extern NetworkJoinStatus _network_join_status;
extern uint8 _network_join_waiting;
extern uint32 _network_join_bytes;
extern uint32 _network_join_bytes_total;
extern ConnectionType _network_server_connection_type;
extern std::string _network_server_invite_code;
/* Variable available for clients. */
extern std::string _network_server_name;
extern uint8 _network_reconnect;
extern CompanyMask _network_company_passworded;
void NetworkQueryServer(const std::string &connection_string);
void NetworkQueryLobbyServer(const std::string &connection_string);
void GetBindAddresses(NetworkAddressList *addresses, uint16 port);
struct NetworkGameList *NetworkAddServer(const std::string &connection_string, bool manually = true, bool never_expire = false);
void NetworkRebuildHostList();
void UpdateNetworkGameWindow();
/* From network_command.cpp */
* Everything we need to know about a command to be able to execute it.
struct CommandPacket : CommandContainer {
/** Make sure the pointer is nullptr. */
CommandPacket() : next(nullptr), company(INVALID_COMPANY), frame(0), my_cmd(false) {}
CommandPacket *next; ///< the next command packet (if in queue)
CompanyID company; ///< company that is executing the command
uint32 frame; ///< the frame in which this packet is executed
bool my_cmd; ///< did the command originate from "me"
};
@@ -799,48 +799,49 @@ NetworkRecvStatus ServerNetworkGameSocke
Packet *p = new Packet(PACKET_SERVER_MOVE);
p->Send_uint32(client_id);
p->Send_uint8(company_id);
this->SendPacket(p);
/** Send an update about the company password states. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyUpdate()
Packet *p = new Packet(PACKET_SERVER_COMPANY_UPDATE);
p->Send_uint16(_network_company_passworded);
/** Send an update about the max company/spectator counts. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendConfigUpdate()
Packet *p = new Packet(PACKET_SERVER_CONFIG_UPDATE);
p->Send_uint8(_settings_client.network.max_companies);
p->Send_string(_settings_client.network.server_name);
* Receiving functions
* DEF_SERVER_RECEIVE_COMMAND has parameter: NetworkClientSocket *cs, Packet *p
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GAME_INFO(Packet *p)
return this->SendGameInfo();
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_INFO(Packet *p)
return this->SendCompanyInfo();
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_NEWGRFS_CHECKED(Packet *p)
if (this->status != STATUS_NEWGRFS_CHECK) {
/* Illegal call, return error and ignore the packet */
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
Status change: