@@ -11,24 +11,35 @@
#include <stdarg.h>
#include "console_func.h"
#include "debug.h"
#include "string_func.h"
#include "fileio_func.h"
#include "settings_type.h"
#include <mutex>
#if defined(_WIN32)
#include "os/windows/win32.h"
#endif
#include "walltime_func.h"
#include "network/network_admin.h"
SOCKET _debug_socket = INVALID_SOCKET;
#include "safeguards.h"
/** Element in the queue of debug messages that have to be passed to either NetworkAdminConsole or IConsolePrint.*/
struct QueuedDebugItem {
std::string level; ///< The used debug level.
std::string message; ///< The actual formatted message.
};
std::atomic<bool> _debug_remote_console; ///< Whether we need to send data to either NetworkAdminConsole or IConsolePrint.
std::mutex _debug_remote_console_mutex; ///< Mutex to guard the queue of debug messages for either NetworkAdminConsole or IConsolePrint.
std::vector<QueuedDebugItem> _debug_remote_console_queue; ///< Queue for debug messages to be passed to NetworkAdminConsole or IConsolePrint.
std::vector<QueuedDebugItem> _debug_remote_console_queue_spare; ///< Spare queue to swap with _debug_remote_console_queue.
int _debug_driver_level;
int _debug_grf_level;
int _debug_map_level;
int _debug_misc_level;
int _debug_net_level;
int _debug_sprite_level;
@@ -104,12 +115,17 @@ char *DumpDebugFacilityNames(char *buf,
* @param message The message to output.
*/
void DebugPrint(const char *level, const std::string &message)
{
if (_debug_socket != INVALID_SOCKET) {
std::string msg = fmt::format("{}dbg: [{}] {}\n", GetLogPrefix(), level, message);
/* Prevent sending a message concurrently, as that might cause interleaved messages. */
static std::mutex _debug_socket_mutex;
std::lock_guard<std::mutex> lock(_debug_socket_mutex);
/* Sending out an error when this fails would be nice, however... the error
* would have to be send over this failing socket which won't work. */
send(_debug_socket, msg.c_str(), (int)msg.size(), 0);
return;
}
if (strcmp(level, "desync") == 0) {
@@ -127,14 +143,17 @@ void DebugPrint(const char *level, const
fflush(f);
} else {
fputs(msg.c_str(), stderr);
NetworkAdminConsole(level, message);
if (_settings_client.gui.developer >= 2) IConsolePrint(CC_DEBUG, "dbg: [{}] {}", level, message);
if (_debug_remote_console.load()) {
/* Only add to the queue when there is at least one consumer of the data. */
std::lock_guard<std::mutex> lock(_debug_remote_console_mutex);
_debug_remote_console_queue.push_back({ level, message });
/**
* Set debugging levels by parsing the text in \a s.
* For setting individual levels a string like \c "net=3,grf=6" should be used.
@@ -226,6 +245,50 @@ const char *GetLogPrefix()
*_log_prefix = '\0';
return _log_prefix;
* Send the queued Debug messages to either NetworkAdminConsole or IConsolePrint from the
* GameLoop thread to prevent concurrent accesses to both the NetworkAdmin's packet queue
* as well as IConsolePrint's buffers.
*
* This is to be called from the GameLoop thread.
void DebugSendRemoteMessages()
if (!_debug_remote_console.load()) return;
std::swap(_debug_remote_console_queue, _debug_remote_console_queue_spare);
for (auto &item : _debug_remote_console_queue_spare) {
NetworkAdminConsole(item.level, item.message);
if (_settings_client.gui.developer >= 2) IConsolePrint(CC_DEBUG, "dbg: [{}] {}", item.level, item.message);
_debug_remote_console_queue_spare.clear();
* Reconsider whether we need to send debug messages to either NetworkAdminConsole
* or IConsolePrint. The former is when they have enabled console handling whereas
* the latter depends on the gui.developer setting's value.
void DebugReconsiderSendRemoteMessages()
bool enable = _settings_client.gui.developer >= 2;
for (ServerNetworkAdminSocketHandler *as : ServerNetworkAdminSocketHandler::IterateActive()) {
if (as->update_frequency[ADMIN_UPDATE_CONSOLE] & ADMIN_FREQUENCY_AUTOMATIC) {
enable = true;
break;
_debug_remote_console.store(enable);
@@ -119,7 +119,10 @@ const char *GetDebugString();
void ShowInfo(const char *str);
void CDECL ShowInfoF(const char *str, ...) WARN_FORMAT(1, 2);
const char *GetLogPrefix();
void DebugSendRemoteMessages();
void DebugReconsiderSendRemoteMessages();
#endif /* DEBUG_H */
@@ -73,12 +73,17 @@ ServerNetworkAdminSocketHandler::ServerN
ServerNetworkAdminSocketHandler::~ServerNetworkAdminSocketHandler()
_network_admins_connected--;
Debug(net, 3, "[admin] '{}' ({}) has disconnected", this->admin_name, this->admin_version);
if (_redirect_console_to_admin == this->index) _redirect_console_to_admin = INVALID_ADMIN_ID;
if (this->update_frequency[ADMIN_UPDATE_CONSOLE] & ADMIN_FREQUENCY_AUTOMATIC) {
this->update_frequency[ADMIN_UPDATE_CONSOLE] = (AdminUpdateFrequency)0;
DebugReconsiderSendRemoteMessages();
* Whether a connection is allowed or not at this moment.
* @return Whether the connection is allowed.
@@ -685,12 +690,14 @@ NetworkRecvStatus ServerNetworkAdminSock
Debug(net, 1, "[admin] Not supported update frequency {} ({}) from '{}' ({})", type, freq, this->admin_name, this->admin_version);
return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET);
this->update_frequency[type] = freq;
if (type == ADMIN_UPDATE_CONSOLE) DebugReconsiderSendRemoteMessages();
return NETWORK_RECV_STATUS_OKAY;
NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_POLL(Packet *p)
if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
@@ -1450,12 +1450,14 @@ void GameLoop()
IncreaseSpriteLRU();
/* Check for UDP stuff */
if (_network_available) NetworkBackgroundLoop();
DebugSendRemoteMessages();
if (_networking && !HasModalProgress()) {
/* Multiplayer */
NetworkGameLoop();
if (_network_reconnect > 0 && --_network_reconnect == 0) {
/* This means that we want to reconnect to the last host
@@ -1239,12 +1239,13 @@ void LoadFromConfig(bool startup)
PrepareOldDiffCustom();
IniLoadSettings(generic_ini, _old_gameopt_settings, "gameopt", &_settings_newgame, false);
HandleOldDiffCustom(false);
ValidateSettings();
/* Display scheduled errors */
extern void ScheduleErrorMessage(ErrorList &datas);
ScheduleErrorMessage(_settings_error_list);
if (FindWindowById(WC_ERRMSG, 0) == nullptr) ShowFirstError();
@@ -750,12 +750,13 @@ var = gui.developer
type = SLE_UINT8
flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC
def = 1
min = 0
max = 2
cat = SC_EXPERT
post_cb = [](auto) { DebugReconsiderSendRemoteMessages(); }
[SDTC_BOOL]
var = gui.newgrf_developer_tools
def = false
post_cb = InvalidateNewGRFChangeWindows
Status change: