mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 13:47:58 +03:00
Improve the update checker so it can be opt-out (#696)
* Add a cvar to enable/disable update checking * Refactor update checker This splits the thread part from the main part to clarify the code * Move and update the configuration documentation, for a more general approach
This commit is contained in:
parent
5b81f6a977
commit
456b660b2a
6 changed files with 216 additions and 127 deletions
|
@ -16,7 +16,7 @@ The main goal of OpenMoHAA is to ensure the future and continuity of **Medal of
|
|||
|
||||
- [Downloading and installing OpenMoHAA](docs/getting_started_installation.md)
|
||||
- [Running OpenMoHAA and using expansion assets](docs/getting_started_running.md)
|
||||
- [Dedicated server configuration](docs/configuration/server.md)
|
||||
- [Game configuration](docs/configuration.md)
|
||||
|
||||
If you encounter any issues, please refer to the [FAQ](docs/faq.md) for possible solutions.
|
||||
|
||||
|
|
|
@ -127,7 +127,8 @@ cvar_t *precache;
|
|||
cvar_t *com_target_game;
|
||||
cvar_t *com_target_version;
|
||||
cvar_t *com_target_demo;
|
||||
cvar_t *com_updateCheckInterval;
|
||||
cvar_t *com_updatecheck_enabled;
|
||||
cvar_t *com_updatecheck_interval;
|
||||
|
||||
int protocol_version_demo;
|
||||
int protocol_version_full;
|
||||
|
@ -1919,8 +1920,9 @@ void Com_Init( char *commandLine ) {
|
|||
#ifdef LEGACY_PROTOCOL
|
||||
com_legacyprotocol = Cvar_Get("com_legacyprotocol", va("%i", PROTOCOL_LEGACY_VERSION), CVAR_INIT);
|
||||
|
||||
com_updateCheckInterval = Cvar_Get("com_updateCheckInterval", "15", 0);
|
||||
Cvar_CheckRange(com_updateCheckInterval, 5, 240, qtrue);
|
||||
com_updatecheck_enabled = Cvar_Get("com_updatecheck_enabled", "1", CVAR_ARCHIVE);
|
||||
com_updatecheck_interval = Cvar_Get("com_updatecheck_interval", "15", 0);
|
||||
Cvar_CheckRange(com_updatecheck_interval, 5, 240, qtrue);
|
||||
|
||||
// Keep for compatibility with old mods / mods that haven't updated yet.
|
||||
if(com_legacyprotocol->integer > 0)
|
||||
|
|
|
@ -1060,7 +1060,8 @@ extern cvar_t* con_autochat;
|
|||
extern cvar_t* com_target_version;
|
||||
extern cvar_t* com_target_game;
|
||||
extern cvar_t* com_target_demo;
|
||||
extern cvar_t* com_updateCheckInterval;
|
||||
extern cvar_t* com_updatecheck_enabled;
|
||||
extern cvar_t* com_updatecheck_interval;
|
||||
|
||||
extern int protocol_version_demo;
|
||||
extern int protocol_version_full;
|
||||
|
|
|
@ -54,53 +54,29 @@ UpdateChecker::UpdateChecker()
|
|||
{
|
||||
lastMajor = lastMinor = lastPatch = 0;
|
||||
versionChecked = false;
|
||||
handle = NULL;
|
||||
thread = NULL;
|
||||
requestThreadIsActive = qfalse;
|
||||
}
|
||||
|
||||
UpdateChecker::~UpdateChecker()
|
||||
{
|
||||
if (handle) {
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
UpdateChecker::~UpdateChecker() {}
|
||||
|
||||
void UpdateChecker::Init()
|
||||
{
|
||||
#ifdef HAS_LIBCURL
|
||||
CURLcode result;
|
||||
|
||||
assert(!handle);
|
||||
assert(!thread);
|
||||
|
||||
handle = curl_easy_init();
|
||||
if (!handle) {
|
||||
Com_DPrintf("Failed to create curl client\n");
|
||||
return;
|
||||
}
|
||||
|
||||
result = curl_easy_setopt(handle, CURLOPT_URL, "https://api.github.com/repos/openmoh/openmohaa/releases/latest");
|
||||
|
||||
if (result != CURLE_OK) {
|
||||
Com_DPrintf("Failed to set curl URL: %s\n", curl_easy_strerror(result));
|
||||
curl_easy_cleanup(handle);
|
||||
handle = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
curl_easy_setopt(handle, CURLOPT_USERAGENT, "curl");
|
||||
#endif
|
||||
|
||||
CheckInitClientThread();
|
||||
}
|
||||
|
||||
void UpdateChecker::CheckInitClientThread()
|
||||
{
|
||||
if (!requestThreadIsActive && CanHaveRequestThread()) {
|
||||
requestThreadIsActive = qtrue;
|
||||
if (!thread) {
|
||||
if (CanHaveRequestThread()) {
|
||||
thread = new UpdateCheckerThread();
|
||||
thread->Init();
|
||||
}
|
||||
} else {
|
||||
if (!CanHaveRequestThread()) {
|
||||
Com_DPrintf("Shutting down the update checker thread\n");
|
||||
|
||||
thread = new std::thread(&UpdateChecker::RequestThread, this);
|
||||
delete thread;
|
||||
thread = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,6 +87,11 @@ bool UpdateChecker::CanHaveRequestThread() const
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!com_updatecheck_enabled->integer) {
|
||||
// Update checking has been disabled
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef HAS_LIBCURL
|
||||
return true;
|
||||
#else
|
||||
|
@ -118,21 +99,28 @@ bool UpdateChecker::CanHaveRequestThread() const
|
|||
#endif
|
||||
}
|
||||
|
||||
void UpdateChecker::SetLatestVersion(int major, int minor, int patch)
|
||||
{
|
||||
lastMajor = major;
|
||||
lastMinor = minor;
|
||||
lastPatch = patch;
|
||||
|
||||
versionChecked = true;
|
||||
}
|
||||
|
||||
void UpdateChecker::Process()
|
||||
{
|
||||
// Initialize the client thread when necessary
|
||||
CheckInitClientThread();
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> currentTime = std::chrono::steady_clock::now();
|
||||
if (currentTime
|
||||
< lastMessageTime + std::chrono::milliseconds(Q_max(1, com_updateCheckInterval->integer) * 60 * 1000)) {
|
||||
if (currentTime < nextMessageTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckNewVersion()) {
|
||||
return;
|
||||
}
|
||||
lastMessageTime = currentTime;
|
||||
|
||||
Com_Printf(
|
||||
"New release v%d.%d.%d published *\\(^ o ^)/*. Your current version is v%s. See www.openmohaa.org\n",
|
||||
|
@ -141,37 +129,16 @@ void UpdateChecker::Process()
|
|||
lastPatch,
|
||||
PRODUCT_VERSION_NUMBER_STRING
|
||||
);
|
||||
|
||||
nextMessageTime = currentTime + std::chrono::milliseconds(Q_max(1, com_updatecheck_interval->integer) * 60 * 1000);
|
||||
}
|
||||
|
||||
void UpdateChecker::Shutdown()
|
||||
{
|
||||
ShutdownClient();
|
||||
ShutdownThread();
|
||||
}
|
||||
|
||||
void UpdateChecker::ShutdownClient()
|
||||
{
|
||||
#ifdef HAS_LIBCURL
|
||||
std::lock_guard<std::shared_mutex> l(clientMutex);
|
||||
|
||||
if (!handle) {
|
||||
return;
|
||||
if (thread) {
|
||||
delete thread;
|
||||
thread = NULL;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(handle);
|
||||
handle = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void UpdateChecker::ShutdownThread()
|
||||
{
|
||||
if (!thread) {
|
||||
return;
|
||||
}
|
||||
|
||||
thread->join();
|
||||
delete thread;
|
||||
thread = NULL;
|
||||
}
|
||||
|
||||
bool UpdateChecker::CheckNewVersion() const
|
||||
|
@ -212,7 +179,91 @@ bool UpdateChecker::CheckNewVersion(int& major, int& minor, int& patch) const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool UpdateChecker::ParseVersionNumber(const char *value, int& major, int& minor, int& patch) const
|
||||
UpdateCheckerThread::UpdateCheckerThread()
|
||||
{
|
||||
handle = NULL;
|
||||
osThread = NULL;
|
||||
requestThreadIsActive = qfalse;
|
||||
shouldBeActive = qfalse;
|
||||
}
|
||||
|
||||
UpdateCheckerThread::~UpdateCheckerThread()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void UpdateCheckerThread::Init()
|
||||
{
|
||||
shouldBeActive = qtrue;
|
||||
requestThreadIsActive = qtrue;
|
||||
|
||||
osThread = new std::thread(&UpdateCheckerThread::RequestThread, this);
|
||||
}
|
||||
|
||||
void UpdateCheckerThread::Shutdown()
|
||||
{
|
||||
if (!shouldBeActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
shouldBeActive = qfalse;
|
||||
|
||||
if (osThread) {
|
||||
// Notify and shutdown the thread
|
||||
clientWake.notify_all();
|
||||
osThread->join();
|
||||
|
||||
delete osThread;
|
||||
osThread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool UpdateCheckerThread::IsRoutineActive() const
|
||||
{
|
||||
return requestThreadIsActive;
|
||||
}
|
||||
|
||||
void UpdateCheckerThread::InitClient()
|
||||
{
|
||||
#ifdef HAS_LIBCURL
|
||||
CURLcode result;
|
||||
|
||||
assert(!handle);
|
||||
|
||||
handle = curl_easy_init();
|
||||
if (!handle) {
|
||||
Com_DPrintf("Failed to create curl client\n");
|
||||
return;
|
||||
}
|
||||
|
||||
result = curl_easy_setopt(handle, CURLOPT_URL, "https://api.github.com/repos/openmoh/openmohaa/releases/latest");
|
||||
|
||||
if (result != CURLE_OK) {
|
||||
Com_DPrintf("Failed to set curl URL: %s\n", curl_easy_strerror(result));
|
||||
curl_easy_cleanup(handle);
|
||||
handle = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
curl_easy_setopt(handle, CURLOPT_USERAGENT, "curl");
|
||||
#else
|
||||
Com_DPrintf("Project was compiled without libcurl, will not check for updates\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void UpdateCheckerThread::ShutdownClient()
|
||||
{
|
||||
#ifdef HAS_LIBCURL
|
||||
if (!handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(handle);
|
||||
handle = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UpdateCheckerThread::ParseVersionNumber(const char *value, int& major, int& minor, int& patch) const
|
||||
{
|
||||
const char *p = value;
|
||||
const char *pn = value;
|
||||
|
@ -268,12 +319,11 @@ size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp)
|
|||
return size * nmemb;
|
||||
}
|
||||
|
||||
void UpdateChecker::DoRequest()
|
||||
void UpdateCheckerThread::DoRequest()
|
||||
{
|
||||
#ifdef HAS_LIBCURL
|
||||
std::lock_guard<std::shared_mutex> l(clientMutex);
|
||||
CURLcode result;
|
||||
std::string responseString;
|
||||
CURLcode result;
|
||||
std::string responseString;
|
||||
|
||||
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &WriteCallback);
|
||||
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &responseString);
|
||||
|
@ -297,31 +347,30 @@ void UpdateChecker::DoRequest()
|
|||
int major, minor, patch;
|
||||
ParseVersionNumber(tagName.c_str(), major, minor, patch);
|
||||
|
||||
lastMajor = major;
|
||||
lastMinor = minor;
|
||||
lastPatch = patch;
|
||||
|
||||
versionChecked = true;
|
||||
updateChecker.SetLatestVersion(major, minor, patch);
|
||||
} catch (std::out_of_range&) {}
|
||||
#endif
|
||||
}
|
||||
|
||||
void UpdateChecker::RequestThread()
|
||||
void UpdateCheckerThread::RequestThread()
|
||||
{
|
||||
std::chrono::time_point<std::chrono::steady_clock> currentTime = std::chrono::steady_clock::now();
|
||||
std::chrono::time_point<std::chrono::steady_clock> lastCheckTime;
|
||||
// Initialize the curl client
|
||||
InitClient();
|
||||
|
||||
while (handle && CanHaveRequestThread()) {
|
||||
currentTime = std::chrono::steady_clock::now();
|
||||
if (currentTime
|
||||
>= lastCheckTime + std::chrono::milliseconds(Q_max(1, com_updateCheckInterval->integer) * 60 * 1000)) {
|
||||
lastCheckTime = currentTime;
|
||||
|
||||
DoRequest();
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
while (handle && shouldBeActive) {
|
||||
DoRequest();
|
||||
RequestThreadSleep();
|
||||
}
|
||||
|
||||
ShutdownClient();
|
||||
|
||||
requestThreadIsActive = qfalse;
|
||||
}
|
||||
|
||||
void UpdateCheckerThread::RequestThreadSleep()
|
||||
{
|
||||
const std::chrono::seconds interval = std::chrono::seconds(Q_max(1, com_updatecheck_interval->integer) * 60);
|
||||
|
||||
std::unique_lock<std::mutex> l(clientWakeMutex);
|
||||
clientWake.wait_for(l, interval);
|
||||
}
|
||||
|
|
|
@ -41,6 +41,37 @@ extern "C" {
|
|||
# include <thread>
|
||||
# include <shared_mutex>
|
||||
# include <chrono>
|
||||
# include <condition_variable>
|
||||
|
||||
class UpdateCheckerThread
|
||||
{
|
||||
public:
|
||||
UpdateCheckerThread();
|
||||
~UpdateCheckerThread();
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
bool IsRoutineActive() const;
|
||||
|
||||
private:
|
||||
void ShutdownThread();
|
||||
|
||||
void InitClient();
|
||||
void ShutdownClient();
|
||||
|
||||
bool ParseVersionNumber(const char *value, int& major, int& minor, int& patch) const;
|
||||
void RequestThread();
|
||||
void RequestThreadSleep();
|
||||
void DoRequest();
|
||||
|
||||
private:
|
||||
void *handle;
|
||||
std::mutex clientWakeMutex;
|
||||
std::condition_variable clientWake;
|
||||
std::thread *osThread;
|
||||
qboolean requestThreadIsActive;
|
||||
qboolean shouldBeActive;
|
||||
};
|
||||
|
||||
class UpdateChecker
|
||||
{
|
||||
|
@ -54,15 +85,11 @@ public:
|
|||
|
||||
bool CheckNewVersion() const;
|
||||
bool CheckNewVersion(int& major, int& minor, int& patch) const;
|
||||
void SetLatestVersion(int major, int minor, int patch);
|
||||
|
||||
private:
|
||||
void ShutdownClient();
|
||||
void ShutdownThread();
|
||||
void RequestThread();
|
||||
void CheckInitClientThread();
|
||||
bool CanHaveRequestThread() const;
|
||||
void DoRequest();
|
||||
bool ParseVersionNumber(const char *value, int& major, int& minor, int& patch) const;
|
||||
|
||||
private:
|
||||
//
|
||||
|
@ -73,15 +100,9 @@ private:
|
|||
int lastPatch;
|
||||
bool versionChecked;
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> lastMessageTime;
|
||||
std::chrono::time_point<std::chrono::steady_clock> nextMessageTime;
|
||||
|
||||
//
|
||||
// Thread-related variables
|
||||
//
|
||||
void *handle;
|
||||
std::shared_mutex clientMutex;
|
||||
std::thread *thread;
|
||||
qboolean requestThreadIsActive;
|
||||
UpdateCheckerThread *thread;
|
||||
};
|
||||
|
||||
extern UpdateChecker updateChecker;
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
# Server configuration and commands
|
||||
# Configuration and commands
|
||||
|
||||
## General configuration
|
||||
|
||||
This documentation currently only lists new changes that were introduced in OpenMoHAA.
|
||||
|
||||
If you want to use containers, see [Building a server container in Docker](../../docker/server/README.md) to build an image for your dedicated server.
|
||||
|
||||
## Home directory
|
||||
### Home directory
|
||||
|
||||
In original MOH:AA, the game installation directory is used to store mods and data, it's not the case in OpenMoHAA as it uses the home directory by default to write data in here, and the home directory can be used to store mods. This behavior can be changed:
|
||||
|
||||
|
@ -18,19 +20,44 @@ The variable defaults to the following value depending on the OS:
|
|||
- `~/.openmohaa` on Linux
|
||||
- `~/Library/Application Support/openmohaa` on macOS
|
||||
|
||||
## Networking
|
||||
### Networking
|
||||
|
||||
### Configure the network components
|
||||
#### Configure the network components
|
||||
|
||||
The network settings can be adjusted to use either IPv4, IPv6, or both. By default, IPv6 is disabled on dedicated servers. The following commands adjust network settings:
|
||||
|
||||
- `set net_enabled 0`: This networking.
|
||||
- `set net_enabled 1`: This enables IPv4 only (the default setting for dedicated servers).
|
||||
- `set net_enabled 2`: This enables IPv6 only.
|
||||
- `set net_enabled 3`: This enables both IPv4 and IPv6 (the default setting when running the standalone game).
|
||||
|
||||
*Note: The master server (using the GameSpy protocol) does not support IPv6. If IPv4 is disabled, the server won't appear in the online server list for internet games, even if IPv6 is enabled.*
|
||||
|
||||
### Optimization / Antichams
|
||||
### Flood protection differences with MOH: Spearhead
|
||||
|
||||
Flood protection is turned on by default in all games (`sv_floodProtection 1`).
|
||||
|
||||
- In MOH: Allied Assault and OpenMoHAA, flood protection checks all commands.
|
||||
- In MOH: Spearhead 2.0 and later, flood protection only checks for text messages.
|
||||
|
||||
While flood protection prevents spam, it can sometimes be annoying in certain situations like reloading and checking scores within a short period of time. If needed, it can be disabled with `set sv_floodProtection 0`.
|
||||
|
||||
For more details on preventing message spamming, check out the [Chat](#chat) section below.
|
||||
|
||||
### Updates
|
||||
|
||||
The game periodically retrieves the latest version number from the GitHub project page at fixed intervals to check for available updates. This process runs in the background and does not interfere with the main thread, meaning it will not cause hangs or stability issues. Updates are not applied automatically, they must be downloaded and installed manually.
|
||||
|
||||
Update checking is enabled by default, but can be disabled under any of the following conditions:
|
||||
- `net_enabled` is set to 0, disables networking, as mentioned above
|
||||
- `com_updatechecker_enabled` is set to 0
|
||||
- The project is compiled without libcurl support
|
||||
|
||||
## Server configuration
|
||||
|
||||
### Networking
|
||||
|
||||
#### Optimization / Antichams
|
||||
|
||||
A new variable, `sv_netoptimize`, enables a feature that optimizes network bandwidth by not sending players information about others they can't see. For each client, the server optimizes by only transmitting data about players within their view. Clients will not receive information about players they can't see. This feature also helps protect against cheaters:
|
||||
|
||||
|
@ -40,7 +67,7 @@ A new variable, `sv_netoptimize`, enables a feature that optimizes network bandw
|
|||
|
||||
This option exists since **Medal of Honor: Allied Assault Breakthrough** 2.30, however it was improved in OpenMoHAA: sounds like footsteps will be sent so players don't get confused.
|
||||
|
||||
### Managing bans
|
||||
#### Managing bans
|
||||
|
||||
A new feature was introduced to ban IP addresses, thanks to the [ioquake3](https://ioquake3.org/) project. Bans are saved by default in `serverbans.dat` but it can be modified with the `sv_banFile` variable. Here are commands to manage bans:
|
||||
|
||||
|
@ -66,20 +93,9 @@ Examples:
|
|||
|
||||
To calculate IP subnets, search for `IP subnet calculator` on Internet.
|
||||
|
||||
## Flood protection differences with MOH: Spearhead
|
||||
### Game
|
||||
|
||||
Flood protection is turned on by default in all games (`sv_floodProtection 1`).
|
||||
|
||||
- In MOH: Allied Assault and OpenMoHAA, flood protection checks all commands.
|
||||
- In MOH: Spearhead 2.0 and later, flood protection only checks for text messages.
|
||||
|
||||
While flood protection prevents spam, it can sometimes be annoying in certain situations like reloading and checking scores within a short period of time. If needed, it can be disabled with `set sv_floodProtection 0`.
|
||||
|
||||
For more details on preventing message spamming, check out the [Chat](#chat) section below.
|
||||
|
||||
## Game
|
||||
|
||||
### Chat
|
||||
#### Chat
|
||||
|
||||
Chat messages will be logged in the console and in the logfile without requiring to set the `developer` variable.
|
||||
|
||||
|
@ -92,7 +108,7 @@ The in-game chat can be tweaked:
|
|||
|
||||
Temporarily disabling text messages can be useful in situations where tensions arise in the chat. Otherwise, it's best to keep them enabled under normal circumstances.
|
||||
|
||||
### Balancing teams
|
||||
#### Balancing teams
|
||||
|
||||
This setting prevents clients from joining a team if that team already has more players than the others. By default, it's turned off, but it can be switched on with the command `set g_teambalance 1`.
|
||||
|
||||
|
@ -100,7 +116,7 @@ This feature is passive: it only checks the team sizes when someone tries to joi
|
|||
|
||||
*Note: This check doesn't apply in server scripts; it only works when clients join teams directly.*
|
||||
|
||||
### Bots
|
||||
#### Bots
|
||||
|
||||
OpenMoHAA introduced multiplayer bots which can be used for entertainment or for testing purposes. Bots are players controlled by the computer, they appear in the scoreboard with their ping set to **bot**.
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue