diff --git a/dll/dll/steam_client.h b/dll/dll/steam_client.h index 1d9422c5..f3954034 100644 --- a/dll/dll/steam_client.h +++ b/dll/dll/steam_client.h @@ -64,6 +64,35 @@ enum Steam_Pipe { SERVER }; +class Steam_Client; +class Client_Background_Thread +{ +private: + // don't run immediately, give the game some time to initialize + constexpr const static auto initial_delay = std::chrono::seconds(2); + // max allowed time in which RunCallbacks() might not be called + constexpr const static auto max_stall_ms = std::chrono::milliseconds(300); + + std::thread background_keepalive{}; + std::mutex kill_background_thread_mutex{}; + std::condition_variable kill_background_thread_cv{}; + bool kill_background_thread{}; + + Steam_Client *client_instance{}; + + void thread_proc(); + +public: + Client_Background_Thread(); + ~Client_Background_Thread(); + + // spawn the thread if necessary, never call this inside the ctor of Steam_Client + // since the thread will attempt to get the global client pointer during initialization (which will still be under construction) + void start(Steam_Client *client_instance); + // kill the thread if necessary + void kill(); +}; + class Steam_Client : public ISteamClient007, public ISteamClient008, @@ -144,14 +173,14 @@ public: Steam_Masterserver_Updater *steam_masterserver_updater{}; Steam_AppTicket *steam_app_ticket{}; + Client_Background_Thread *background_thread{}; + Steam_Overlay* steam_overlay{}; bool steamclient_server_inited = false; bool gameserver_has_ipv6_functions{}; - std::thread background_keepalive{}; - unsigned steam_pipe_counter = 1; std::map steam_pipes{}; diff --git a/dll/steam_client.cpp b/dll/steam_client.cpp index 6c73fd16..69e70afd 100644 --- a/dll/steam_client.cpp +++ b/dll/steam_client.cpp @@ -19,60 +19,13 @@ #include "dll/settings_parser.h" -static std::mutex kill_background_thread_mutex{}; -static std::condition_variable kill_background_thread_cv{}; -static bool kill_background_thread{}; -static void background_thread(Steam_Client *client) -{ - // max allowed time in which RunCallbacks() might not be called - constexpr const static auto max_stall_ms = std::chrono::milliseconds(300); - - // wait 1 sec - { - std::unique_lock lck(kill_background_thread_mutex); - if (kill_background_thread || kill_background_thread_cv.wait_for(lck, std::chrono::seconds(1)) != std::cv_status::timeout) { - if (kill_background_thread) { - PRINT_DEBUG("early exit"); - return; - } - } - } - - PRINT_DEBUG("starting"); - - while (1) { - { - std::unique_lock lck(kill_background_thread_mutex); - if (kill_background_thread || kill_background_thread_cv.wait_for(lck, max_stall_ms) != std::cv_status::timeout) { - if (kill_background_thread) { - PRINT_DEBUG("exit"); - return; - } - } - } - - auto now_ms = (unsigned long long)std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - - // if our time exceeds last run time of callbacks and it wasn't processing already - if (!client->cb_run_active && (now_ms >= (client->last_cb_run + max_stall_ms.count()))) { - global_mutex.lock(); - PRINT_DEBUG("run @@@@@@@@@@@@@@@@@@@@@@@@@@@"); - client->last_cb_run = now_ms; // update the time counter just to avoid overlap - client->network->Run(); // networking must run first since it receives messages used by each run_callback() - client->run_every_runcb->run(); // call each run_callback() - global_mutex.unlock(); - } - } -} - - - Steam_Client::Steam_Client() { PRINT_DEBUG("start ----------"); uint32 appid = create_localstorage_settings(&settings_client, &settings_server, &local_storage); local_storage->update_save_filenames(Local_Storage::remote_storage_folder); + background_thread = new Client_Background_Thread(); network = new Networking(settings_server->get_local_steam_id(), appid, settings_server->get_port(), &(settings_server->custom_broadcasts), settings_server->disable_networking); run_every_runcb = new RunEveryRunCB(); @@ -333,17 +286,14 @@ HSteamUser Steam_Client::ConnectToGlobalUser( HSteamPipe hSteamPipe ) userLogIn(); - // games like appid 1740720 and 2379780 do not call SteamAPI_RunCallbacks() or SteamAPI_ManualDispatch_RunFrame() or Steam_BGetCallback() // hence all run_callbacks() will never run, which might break the assumption that these callbacks are always run // also networking callbacks won't run // hence we spawn the background thread here which trigger all run_callbacks() and run networking callbacks - if (!background_keepalive.joinable()) { - background_keepalive = std::thread(background_thread, this); - PRINT_DEBUG("spawned background thread *********"); - } + background_thread->start(this); steam_overlay->SetupOverlay(); + steam_pipes[hSteamPipe] = Steam_Pipe::CLIENT; return CLIENT_HSTEAMUSER; } @@ -427,21 +377,8 @@ bool Steam_Client::BShutdownIfAllPipesClosed() PRINT_DEBUG_ENTRY(); if (steam_pipes.size()) return false; // not all pipes are released via BReleaseSteamPipe() yet - bool joinable = background_keepalive.joinable(); - if (joinable) { - { - std::lock_guard lk(kill_background_thread_mutex); - kill_background_thread = true; - } - kill_background_thread_cv.notify_one(); - } - + background_thread->kill(); steam_controller->Shutdown(); - - - if (joinable) { - background_keepalive.join(); - } steam_overlay->UnSetupOverlay(); PRINT_DEBUG("all pipes closed"); diff --git a/dll/steam_client_background_thread.cpp b/dll/steam_client_background_thread.cpp new file mode 100644 index 00000000..d5887565 --- /dev/null +++ b/dll/steam_client_background_thread.cpp @@ -0,0 +1,90 @@ +/* Copyright (C) 2019 Mr Goldberg + This file is part of the Goldberg Emulator + + The Goldberg Emulator is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + The Goldberg Emulator is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the Goldberg Emulator; if not, see + . */ + +#include "dll/steam_client.h" +#include "dll/dll.h" + +void Client_Background_Thread::thread_proc() +{ + // wait for some time + { + + std::unique_lock lck(kill_background_thread_mutex); + if (kill_background_thread_cv.wait_for(lck, initial_delay, [&] { return kill_background_thread; })) { + PRINT_DEBUG("early exit"); + return; + } + } + + PRINT_DEBUG("starting"); + + while (1) { + { + std::unique_lock lck(kill_background_thread_mutex); + if (kill_background_thread_cv.wait_for(lck, max_stall_ms, [&] { return kill_background_thread; })) { + PRINT_DEBUG("exit"); + return; + } + } + + auto now_ms = (unsigned long long)std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + + // if our time exceeds last run time of callbacks and it wasn't processing already + const auto runcallbacks_timeout_ms = client_instance->get_last_runcallbacks_time() + max_stall_ms.count(); + if (!client_instance->runcallbacks_active() && (now_ms >= runcallbacks_timeout_ms)) { + std::lock_guard lock(global_mutex); + PRINT_DEBUG("run @@@@@@@@@@@@@@@@@@@@@@@@@@@"); + client_instance->set_last_runcallbacks_time(now_ms); // update the time counter just to avoid overlap + client_instance->network->Run(); // networking must run first since it receives messages used by each run_callback() + client_instance->run_every_runcb->run(); // call each run_callback() + } + } +} + + +Client_Background_Thread::Client_Background_Thread() +{ + +} + +Client_Background_Thread::~Client_Background_Thread() +{ + kill(); +} + +void Client_Background_Thread::start(Steam_Client *client_instance) +{ + if (background_keepalive.joinable()) return; // alrady spawned + + this->client_instance = client_instance; + background_keepalive = std::thread([this] { thread_proc(); }); + PRINT_DEBUG("spawned background thread *********"); +} + +void Client_Background_Thread::kill() +{ + if (!background_keepalive.joinable()) return; // already killed + + { + std::lock_guard lk(kill_background_thread_mutex); + kill_background_thread = true; + } + kill_background_thread_cv.notify_one(); + PRINT_DEBUG("joining worker thread"); + background_keepalive.join(); + PRINT_DEBUG("worker thread killed"); +}