feat: Add Automatic Gamedata Updates (#925)

This commit is contained in:
Michael Wilson
2025-07-10 10:23:48 +12:00
committed by GitHub
parent c746c4e2e7
commit 8ab61b00e8
12 changed files with 12946 additions and 2 deletions

View File

@@ -5,6 +5,9 @@ project(counterstrikesharp C CXX ASM)
include("makefiles/shared.cmake")
# Find OpenSSL for httplib SSL support
find_package(OpenSSL REQUIRED)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
add_subdirectory(libraries/spdlog)
@@ -43,6 +46,8 @@ set(SOURCE_FILES
src/core/coreconfig.cpp
src/core/gameconfig.h
src/core/gameconfig.cpp
src/core/gameconfig_updater.h
src/core/gameconfig_updater.cpp
src/core/log.h
src/core/log.cpp
src/scripting/script_engine.h

View File

@@ -6,5 +6,7 @@
"PluginAutoLoadEnabled": true,
"ServerLanguage": "en",
"UnlockConCommands": true,
"UnlockConVars": true
"UnlockConVars": true,
"AutoUpdateEnabled": true,
"AutoUpdateURL": "https://gamedata.cssharp.dev"
}

View File

@@ -47,3 +47,10 @@ When enabled, will remove the `FCVAR_HIDDEN`,`FCVAR_DEVELOPMENTONLY`, `FCVAR_MIS
## UnlockConVars
When enabled, will remove the `FCVAR_HIDDEN`,`FCVAR_DEVELOPMENTONLY`, `FCVAR_MISSING0`, `FCVAR_MISSING1`, `FCVAR_MISSING2`, `FCVAR_MISSING3` flags from all console variables.
## AutoUpdateEnabled
When enabled, CS# will check for any updates to the gamedata.json file and automatically update it if a new version is available.
## AutoUpdateURL
The URL to use for the auto-update feature. This URL should point to a JSON file that contains the latest version of the gamedata.json file.

12818
libraries/httplib/httplib.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -29,4 +29,6 @@ set(
distorm
funchook-static
dynohook
OpenSSL::SSL
OpenSSL::Crypto
)

View File

@@ -19,4 +19,6 @@ set(COUNTER_STRIKE_SHARP_LINK_LIBRARIES
distorm
funchook-static
dynohook
OpenSSL::SSL
OpenSSL::Crypto
)

View File

@@ -61,6 +61,12 @@ namespace CounterStrikeSharp.API.Core
[JsonPropertyName("UnlockConVars")]
public bool UnlockConVars { get; set; } = true;
[JsonPropertyName("AutoUpdateEnabled")]
public bool AutoUpdateEnabled { get; set; } = true;
[JsonPropertyName("AutoUpdateURL")]
public string AutoUpdateURL { get; set; } = "https://gamedata.cssharp.dev";
}
/// <summary>
@@ -157,7 +163,7 @@ namespace CounterStrikeSharp.API.Core
{
var data = JsonSerializer.Deserialize<CoreConfigData>(File.ReadAllText(_coreConfigPath),
new JsonSerializerOptions() { ReadCommentHandling = JsonCommentHandling.Skip });
if (data != null)
{
_coreConfig = data;

View File

@@ -60,6 +60,8 @@ bool CCoreConfig::Init(char* conf_error, int conf_error_size)
ServerLanguage = m_json.value("ServerLanguage", ServerLanguage);
UnlockConCommands = m_json.value("UnlockConCommands", UnlockConCommands);
UnlockConVars = m_json.value("UnlockConVars", UnlockConVars);
AutoUpdateEnabled = m_json.value("AutoUpdateEnabled", AutoUpdateEnabled);
AutoUpdateURL = m_json.value("AutoUpdateURL", AutoUpdateURL);
}
catch (const std::exception& ex)
{

View File

@@ -35,6 +35,8 @@ class CCoreConfig
std::string ServerLanguage = "en";
bool UnlockConCommands = true;
bool UnlockConVars = true;
bool AutoUpdateEnabled = true;
std::string AutoUpdateURL = std::string("https://gamedata.cssharp.dev");
using json = nlohmann::json;
CCoreConfig(const std::string& path);

View File

@@ -0,0 +1,82 @@
#include "core/gameconfig_updater.h"
#include "core/utils.h"
#include "core/coreconfig.h"
#include "core/log.h"
#include "core/globals.h"
#include <string>
#include <fstream>
#include <filesystem>
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib/httplib.h"
namespace counterstrikesharp::update {
/// @brief
/// Attempts to update the game configuration by checking the ETag of the remote gamedata.json file.
/// If the ETag has changed, it downloads the new gamedata.json and updates the local
/// etag file with the new ETag.
/// @return
bool TryUpdateGameConfig()
{
auto gamedata_path = std::string(utils::GamedataDirectory() + "/gamedata.json");
auto etag_path = std::string(utils::GamedataDirectory() + "/gamedata.etag");
httplib::Client client(globals::coreConfig->AutoUpdateURL);
client.set_follow_location(true);
client.set_read_timeout(10, 0);
auto headRes = client.Head("/");
if (!headRes || headRes->status != 200)
{
return false;
}
auto etag = headRes->get_header_value("ETag");
std::string localETag = "";
if (std::filesystem::exists(etag_path))
{
std::ifstream etag_file(etag_path);
if (etag_file.is_open())
{
std::getline(etag_file, localETag);
etag_file.close();
}
}
if (etag == localETag)
{
CSSHARP_CORE_INFO("Gamedata is up to date, ETag: {}", etag);
return true;
}
auto res = client.Get("/");
if (res && res->status == 200)
{
auto etag = res->get_header_value("ETag");
std::ofstream gamedata_file(gamedata_path);
if (gamedata_file.is_open())
{
gamedata_file << res->body;
gamedata_file.close();
CSSHARP_CORE_INFO("Gamedata file written to: {} with ETag {}", gamedata_path, res->get_header_value("ETag"));
}
std::ofstream etag_file_out(etag_path);
if (etag_file_out.is_open())
{
etag_file_out << etag;
etag_file_out.close();
}
}
else
{
CSSHARP_CORE_ERROR("Failed to connect to the auto update server at: {}", globals::coreConfig->AutoUpdateURL);
return false;
}
return true;
}
} // namespace counterstrikesharp::update

View File

@@ -0,0 +1,5 @@
#pragma once
namespace counterstrikesharp::update {
bool TryUpdateGameConfig();
} // namespace counterstrikesharp::update

View File

@@ -19,6 +19,7 @@
#include "core/coreconfig.h"
#include "core/game_system.h"
#include "core/gameconfig.h"
#include "core/gameconfig_updater.h"
#include "core/global_listener.h"
#include "core/log.h"
#include "core/managers/entity_manager.h"
@@ -117,6 +118,16 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
CSSHARP_CORE_INFO("CoreConfig loaded.");
if (globals::coreConfig->AutoUpdateEnabled)
{
CSSHARP_CORE_INFO("AutoUpdate enabled, checking for gamedata updates...");
if (!update::TryUpdateGameConfig())
{
CSSHARP_CORE_ERROR("Failed to update game config.");
}
}
auto gamedata_path = std::string(utils::GamedataDirectory() + "/gamedata.json");
globals::gameConfig = new CGameConfig(gamedata_path);
char conf_error[255] = "";