feat(Deps/libsidecar): Libsidecar (clustering) ABI versions check and portable shared-library loading (#26890)
This commit is contained in:
parent
c1793976c8
commit
23baad639b
12 changed files with 243 additions and 10 deletions
6
deps/PackageList.txt
vendored
6
deps/PackageList.txt
vendored
|
|
@ -86,6 +86,6 @@ fkYAML (A C++ header-only YAML library)
|
|||
https://github.com/fktn-k/fkYAML
|
||||
Version: 721edb3e1a817e527fd9e1e18a3bea300822522e
|
||||
|
||||
libsidecar (enables interaction with other components when the worldserver is running in cluster mode. It requires building a shared library first and putting it in the corresponding folder.)
|
||||
https://github.com/walkline/ToCloud9/tree/master/game-server/libsidecar
|
||||
Version: master
|
||||
libsidecar (enables interaction with other components when the worldserver is running in cluster mode. Place the shared library from the release assets in deps/libsidecar for builds. UNIX: BUILD_RPATH finds deps/libsidecar; install copies to ${prefix}/lib; runtime also searches next to worldserver and ../lib. Windows: no rpath - build copies the DLL next to worldserver; install places it next to worldserver. Headers under deps/libsidecar/include - including tc9_version.h - must come from the same libsidecar build/release as the linked binary. worldserver checks ABI early when Cluster.Enabled.)
|
||||
https://github.com/walkline/ToCloud9/releases/tag/libsidecar-v1.0.0
|
||||
Version: 1.0.0 (TC9_VERSION in deps/libsidecar/include/tc9_version.h)
|
||||
|
|
|
|||
38
deps/libsidecar/CMakeLists.txt
vendored
38
deps/libsidecar/CMakeLists.txt
vendored
|
|
@ -37,30 +37,54 @@ else()
|
|||
set(LIBSIDECAR_EXTENSION "so")
|
||||
endif()
|
||||
|
||||
# Visible to worldserver CMake (rpath on UNIX; POST_BUILD copy on Windows).
|
||||
set(LIBSIDECAR_SHARED_FILE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/libsidecar.${LIBSIDECAR_EXTENSION}"
|
||||
CACHE INTERNAL "Path to the real libsidecar shared library file")
|
||||
|
||||
if (NOT EXISTS "${LIBSIDECAR_SHARED_FILE}")
|
||||
message(WARNING
|
||||
"USE_REAL_LIBSIDECAR is ON but ${LIBSIDECAR_SHARED_FILE} was not found. "
|
||||
"Place the matching shared library (and headers) from the same libsidecar "
|
||||
"build/release before linking or installing.")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(libsidecar
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libsidecar.${LIBSIDECAR_EXTENSION}
|
||||
${LIBSIDECAR_SHARED_FILE}
|
||||
IMPORTED_IMPLIB
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${LIBSIDECAR_IMPORT_LIB}
|
||||
INTERFACE_INCLUDE_DIRECTORIES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
# worldserver installs to CMAKE_INSTALL_PREFIX root; DLL must sit next to it.
|
||||
# Build-tree runs: apps/CMakeLists.txt copies this DLL next to worldserver
|
||||
# (Windows has no rpath; the loader only searches the exe directory / PATH).
|
||||
install(FILES "${LIBSIDECAR_SHARED_FILE}"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}")
|
||||
else()
|
||||
set_target_properties(libsidecar
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libsidecar.${LIBSIDECAR_EXTENSION}
|
||||
${LIBSIDECAR_SHARED_FILE}
|
||||
INTERFACE_INCLUDE_DIRECTORIES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
IMPORTED_NO_SONAME
|
||||
true)
|
||||
|
||||
set_target_properties(libsidecar PROPERTIES
|
||||
IMPORTED_LOCATION_NOCONFIG
|
||||
"${CMAKE_INSTALL_RPATH}/libsidecar.${LIBSIDECAR_EXTENSION}"
|
||||
IMPORTED_SONAME_NOCONFIG
|
||||
"libsidecar.${LIBSIDECAR_EXTENSION}")
|
||||
# Source-tree dir for BUILD_RPATH only (dev runs from the build tree).
|
||||
# Do not put this path on INSTALL_RPATH — that would bake the build
|
||||
# machine's source tree into installed binaries.
|
||||
# Do not use INTERFACE_LINK_OPTIONS -rpath: game, scripts, and
|
||||
# worldserver all link libsidecar and would get duplicate -rpath warnings.
|
||||
set(LIBSIDECAR_RUNTIME_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL
|
||||
"Directory containing the real libsidecar shared library (build tree)")
|
||||
|
||||
# Install next to the rest of the prefix libraries (worldserver → bin/,
|
||||
# so runtime rpath uses $ORIGIN/../lib and CMAKE_INSTALL_RPATH = prefix/lib).
|
||||
install(FILES "${LIBSIDECAR_SHARED_FILE}" DESTINATION lib)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
|
|
|||
10
deps/libsidecar/include/libsidecar.h
vendored
10
deps/libsidecar/include/libsidecar.h
vendored
|
|
@ -19,6 +19,9 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/* Compile-time version macros (must match the libsidecar you link) */
|
||||
#include "tc9_version.h"
|
||||
|
||||
/* Include all API headers */
|
||||
#include "battleground-api.h"
|
||||
#include "events-group.h"
|
||||
|
|
@ -33,6 +36,13 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ABI / package version of the *linked* library (not the caller's headers).
|
||||
* Pass TC9_VERSION_MAJOR / TC9_VERSION_MINOR from the headers you compiled
|
||||
* against into TC9CheckAbiCompatible. Returns 0 if compatible. */
|
||||
TC9_API void TC9GetVersion(int* major, int* minor, int* patch);
|
||||
TC9_API const char* TC9GetVersionString(void);
|
||||
TC9_API int TC9CheckAbiCompatible(int required_major, int required_minor);
|
||||
|
||||
/* Main library functions */
|
||||
TC9_API void TC9InitLib(uint16_t port, uint32_t realmID, uint8_t isCrossRealm, char* availableMaps, uint32_t** assignedMaps, int* assignedMapsSize);
|
||||
TC9_API void TC9GracefulShutdown();
|
||||
|
|
|
|||
20
deps/libsidecar/include/tc9_version.h
vendored
Normal file
20
deps/libsidecar/include/tc9_version.h
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef TC9_VERSION_H
|
||||
#define TC9_VERSION_H
|
||||
|
||||
/* Vendored snapshot of libsidecar-cpp generated version header.
|
||||
* Keep in sync with the libsidecar binary in this deps folder.
|
||||
* Upstream source of truth: project(libsidecar VERSION ...) in
|
||||
* game-server/libsidecar-cpp/CMakeLists.txt (generates this file).
|
||||
*/
|
||||
|
||||
#define TC9_VERSION_MAJOR 1
|
||||
#define TC9_VERSION_MINOR 0
|
||||
#define TC9_VERSION_PATCH 0
|
||||
|
||||
#define TC9_VERSION_STRING "1.0.0"
|
||||
|
||||
/* major*10000 + minor*100 + patch */
|
||||
#define TC9_VERSION_NUMBER \
|
||||
(TC9_VERSION_MAJOR * 10000 + TC9_VERSION_MINOR * 100 + TC9_VERSION_PATCH)
|
||||
|
||||
#endif /* TC9_VERSION_H */
|
||||
26
deps/libsidecar/stub/libsidecar.c
vendored
26
deps/libsidecar/stub/libsidecar.c
vendored
|
|
@ -7,6 +7,32 @@ void panicWithTC9Unavailable(const char* message) {
|
|||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Version APIs work without a real libsidecar so callers can always query
|
||||
// the header version this tree was built against.
|
||||
void TC9GetVersion(int* major, int* minor, int* patch)
|
||||
{
|
||||
if (major)
|
||||
*major = TC9_VERSION_MAJOR;
|
||||
if (minor)
|
||||
*minor = TC9_VERSION_MINOR;
|
||||
if (patch)
|
||||
*patch = TC9_VERSION_PATCH;
|
||||
}
|
||||
|
||||
const char* TC9GetVersionString(void)
|
||||
{
|
||||
return TC9_VERSION_STRING;
|
||||
}
|
||||
|
||||
int TC9CheckAbiCompatible(int required_major, int required_minor)
|
||||
{
|
||||
if (TC9_VERSION_MAJOR != required_major)
|
||||
return 1;
|
||||
if (TC9_VERSION_MINOR < required_minor)
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TC9SetBattlegroundStartHandler sets handler for starting battleground.
|
||||
//
|
||||
extern void TC9SetBattlegroundStartHandler(BattlegroundStartHandler h) { panicWithTC9Unavailable("TC9SetBattlegroundStartHandler"); }
|
||||
|
|
|
|||
8
deps/libsidecar/stub/libsidecar.h
vendored
8
deps/libsidecar/stub/libsidecar.h
vendored
|
|
@ -4,6 +4,9 @@
|
|||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Compile-time version macros (vendored with real headers under include/) */
|
||||
#include "tc9_version.h"
|
||||
|
||||
/* Include all API headers */
|
||||
#include "battleground-api.h"
|
||||
#include "events-group.h"
|
||||
|
|
@ -18,6 +21,11 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Version APIs (implemented by stub without panic; real lib reports its build) */
|
||||
void TC9GetVersion(int* major, int* minor, int* patch);
|
||||
const char* TC9GetVersionString(void);
|
||||
int TC9CheckAbiCompatible(int required_major, int required_minor);
|
||||
|
||||
/* Main library functions */
|
||||
void TC9InitLib(uint16_t port, uint32_t realmID, uint8_t isCrossRealm, char* availableMaps, uint32_t** assignedMaps, int* assignedMapsSize);
|
||||
void TC9GracefulShutdown();
|
||||
|
|
|
|||
21
deps/libsidecar/stub/tc9_version.h
vendored
Normal file
21
deps/libsidecar/stub/tc9_version.h
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef TC9_VERSION_H
|
||||
#define TC9_VERSION_H
|
||||
|
||||
/* Vendored snapshot of libsidecar-cpp generated version header.
|
||||
* Keep in sync with include/tc9_version.h and the linked libsidecar binary.
|
||||
* Built only when USE_REAL_LIBSIDECAR is OFF (static stub, not the real .so/.dll).
|
||||
*/
|
||||
|
||||
#define TC9_LIBSIDECAR_IS_STUB 1
|
||||
|
||||
#define TC9_VERSION_MAJOR 1
|
||||
#define TC9_VERSION_MINOR 0
|
||||
#define TC9_VERSION_PATCH 0
|
||||
|
||||
#define TC9_VERSION_STRING "1.0.0"
|
||||
|
||||
/* major*10000 + minor*100 + patch */
|
||||
#define TC9_VERSION_NUMBER \
|
||||
(TC9_VERSION_MAJOR * 10000 + TC9_VERSION_MINOR * 100 + TC9_VERSION_PATCH)
|
||||
|
||||
#endif /* TC9_VERSION_H */
|
||||
|
|
@ -123,6 +123,16 @@ endif()
|
|||
|
||||
if ( USE_REAL_LIBSIDECAR )
|
||||
message("* Use stub for libsidecar : No")
|
||||
# Required ABI only applies when linking the real shared library (include/).
|
||||
if (EXISTS "${CMAKE_SOURCE_DIR}/deps/libsidecar/include/tc9_version.h")
|
||||
file(STRINGS "${CMAKE_SOURCE_DIR}/deps/libsidecar/include/tc9_version.h" _tc9_ver_line
|
||||
REGEX "^#define TC9_VERSION_STRING ")
|
||||
if (_tc9_ver_line)
|
||||
string(REGEX REPLACE "^#define TC9_VERSION_STRING \"([^\"]+)\".*" "\\1" TC9_VENDORED_VERSION "${_tc9_ver_line}")
|
||||
message("* libsidecar required ABI : ${TC9_VENDORED_VERSION}")
|
||||
endif()
|
||||
unset(_tc9_ver_line)
|
||||
endif()
|
||||
else()
|
||||
message("* Use stub for libsidecar : Yes")
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -148,6 +148,45 @@ foreach(APPLICATION_NAME ${APPLICATIONS_BUILD_LIST})
|
|||
|
||||
set_target_properties(${APP_PROJECT_NAME} PROPERTIES LINK_FLAGS "${${APP_PROJECT_NAME}_LINK_FLAGS}")
|
||||
|
||||
# Real libsidecar runtime location (not via imported-target INTERFACE -rpath:
|
||||
# game, scripts, and worldserver all link libsidecar and would get duplicates).
|
||||
#
|
||||
# UNIX/macOS — rpath search order:
|
||||
# BUILD: deps/libsidecar → next to binary → ../lib
|
||||
# INSTALL: target inherits CMAKE_INSTALL_RPATH (${prefix}/lib) first, then
|
||||
# APPEND next to binary and ../lib (no source-tree path)
|
||||
#
|
||||
# Windows — no rpath; loader only searches the exe directory (then PATH).
|
||||
# BUILD: POST_BUILD copy DLL next to worldserver
|
||||
# INSTALL: install(FILES) places DLL next to worldserver (see deps/libsidecar)
|
||||
if (USE_REAL_LIBSIDECAR)
|
||||
if (WIN32 AND LIBSIDECAR_SHARED_FILE)
|
||||
add_custom_command(TARGET ${APP_PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${LIBSIDECAR_SHARED_FILE}"
|
||||
"$<TARGET_FILE_DIR:${APP_PROJECT_NAME}>"
|
||||
COMMENT "Copy libsidecar.dll next to worldserver (Windows build tree)")
|
||||
elseif (LIBSIDECAR_RUNTIME_DIR)
|
||||
if (APPLE)
|
||||
set_property(TARGET ${APP_PROJECT_NAME} APPEND PROPERTY BUILD_RPATH
|
||||
"${LIBSIDECAR_RUNTIME_DIR}"
|
||||
"@loader_path"
|
||||
"@loader_path/../lib")
|
||||
set_property(TARGET ${APP_PROJECT_NAME} APPEND PROPERTY INSTALL_RPATH
|
||||
"@loader_path"
|
||||
"@loader_path/../lib")
|
||||
elseif (UNIX)
|
||||
set_property(TARGET ${APP_PROJECT_NAME} APPEND PROPERTY BUILD_RPATH
|
||||
"${LIBSIDECAR_RUNTIME_DIR}"
|
||||
"$ORIGIN"
|
||||
"$ORIGIN/../lib")
|
||||
set_property(TARGET ${APP_PROJECT_NAME} APPEND PROPERTY INSTALL_RPATH
|
||||
"$ORIGIN"
|
||||
"$ORIGIN/../lib")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Add all dynamic projects as dependency to the worldserver
|
||||
if (WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES)
|
||||
add_dependencies(${APP_PROJECT_NAME} ${WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES})
|
||||
|
|
|
|||
|
|
@ -205,6 +205,11 @@ int main(int argc, char** argv)
|
|||
LOG_INFO("server.worldserver", "> Using Boost version: {}.{}.{}", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
|
||||
});
|
||||
|
||||
// Cluster.Enabled is known from config here. Fail before DB/network if the
|
||||
// loaded libsidecar is the stub or does not match the headers we built with.
|
||||
if (!sToCloud9Sidecar->CheckLibsidecarAbi())
|
||||
return 1;
|
||||
|
||||
OpenSSLCrypto::threadsSetup();
|
||||
|
||||
std::shared_ptr<void> opensslHandle(nullptr, [](void*) { OpenSSLCrypto::threadsCleanup(); });
|
||||
|
|
|
|||
|
|
@ -44,6 +44,71 @@ ToCloud9Sidecar::ToCloud9Sidecar() : _clusterModeEnabled(false), _isCrossrealm(f
|
|||
{
|
||||
}
|
||||
|
||||
bool ToCloud9Sidecar::CheckLibsidecarAbi()
|
||||
{
|
||||
if (!sConfigMgr->GetOption<bool>("Cluster.Enabled", false))
|
||||
return true;
|
||||
|
||||
int libMajor = 0;
|
||||
int libMinor = 0;
|
||||
int libPatch = 0;
|
||||
TC9GetVersion(&libMajor, &libMinor, &libPatch);
|
||||
char const* libVersionStr = TC9GetVersionString();
|
||||
|
||||
#if defined(TC9_LIBSIDECAR_IS_STUB)
|
||||
char const* libKind = "stub";
|
||||
#else
|
||||
char const* libKind = "shared library";
|
||||
#endif
|
||||
|
||||
#if defined(TC9_LIBSIDECAR_IS_STUB)
|
||||
// Stub always "matches" its own macros; fail here instead of after the realm
|
||||
// is online when TC9InitLib panics.
|
||||
// SetSynchronous first: this runs before the io_context pool starts, so async
|
||||
// LOG_* would never print on early return. Success path leaves async alone.
|
||||
sLog->SetSynchronous();
|
||||
LOG_INFO("server", "libsidecar ({}) runtime {}.{}.{} ({}) - headers {}.{}.{} ({})",
|
||||
libKind,
|
||||
libMajor, libMinor, libPatch,
|
||||
libVersionStr ? libVersionStr : "?",
|
||||
TC9_VERSION_MAJOR, TC9_VERSION_MINOR, TC9_VERSION_PATCH,
|
||||
TC9_VERSION_STRING);
|
||||
LOG_ERROR("server",
|
||||
"Cluster.Enabled requires the real libsidecar shared library. "
|
||||
"This worldserver was built with the stub (default). "
|
||||
"Rebuild with -DUSE_REAL_LIBSIDECAR=ON and place matching headers + library "
|
||||
"from the same libsidecar release under deps/libsidecar.");
|
||||
return false;
|
||||
#else
|
||||
if (TC9CheckAbiCompatible(TC9_VERSION_MAJOR, TC9_VERSION_MINOR) != 0)
|
||||
{
|
||||
sLog->SetSynchronous();
|
||||
LOG_INFO("server", "libsidecar ({}) runtime {}.{}.{} ({}) - headers {}.{}.{} ({})",
|
||||
libKind,
|
||||
libMajor, libMinor, libPatch,
|
||||
libVersionStr ? libVersionStr : "?",
|
||||
TC9_VERSION_MAJOR, TC9_VERSION_MINOR, TC9_VERSION_PATCH,
|
||||
TC9_VERSION_STRING);
|
||||
LOG_ERROR("server",
|
||||
"libsidecar ABI mismatch: worldserver was built for {}.{}.{} (headers {}), "
|
||||
"but loaded library is {}.{}.{} ({}). "
|
||||
"Copy matching headers + library from the same libsidecar build/release.",
|
||||
TC9_VERSION_MAJOR, TC9_VERSION_MINOR, TC9_VERSION_PATCH, TC9_VERSION_STRING,
|
||||
libMajor, libMinor, libPatch,
|
||||
libVersionStr ? libVersionStr : "?");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("server", "libsidecar ({}) runtime {}.{}.{} ({}) - headers {}.{}.{} ({})",
|
||||
libKind,
|
||||
libMajor, libMinor, libPatch,
|
||||
libVersionStr ? libVersionStr : "?",
|
||||
TC9_VERSION_MAJOR, TC9_VERSION_MINOR, TC9_VERSION_PATCH,
|
||||
TC9_VERSION_STRING);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ToCloud9Sidecar::Init(uint16 port, int realmId)
|
||||
{
|
||||
_clusterModeEnabled = sConfigMgr->GetOption<bool>("Cluster.Enabled", false);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,11 @@ private:
|
|||
public:
|
||||
static ToCloud9Sidecar* instance();
|
||||
|
||||
/// When Cluster.Enabled, validate libsidecar (real library required; ABI match).
|
||||
/// Call after config and logging are ready, before DB/network startup.
|
||||
/// @return false if worldserver should exit with code 1.
|
||||
bool CheckLibsidecarAbi();
|
||||
|
||||
void Init(uint16 port, int realmId);
|
||||
void Deinit();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue