Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Added

- **Remote configs listener in the C API** — New `gameAnalytics_configureRemoteConfigsListener()` with `GARemoteConfigsListener` callback, making remote configs notifications usable from managed runtimes (e.g. Unity P/Invoke). Single listener; re-registering replaces it, `NULL` unregisters.

## 5.2.0

### Added
Expand Down
3 changes: 3 additions & 0 deletions CMakeIncludes/exported_symbols_apple.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Symbols exported from the GameAnalytics shared library on Apple platforms.
# Used via -Wl,-exported_symbols_list. Glob patterns are supported by ld64.
_gameAnalytics_*
23 changes: 21 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,11 @@ elseif(APPLE)

create_source_groups(MACOS_SOURCES)

elseif(LINUX)
elseif(UNIX AND NOT APPLE)
# Linux
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGUID_STDLIB -std=c++17")

if (CMAKE_CXX_COMPILER MATCHES "clang")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
message(STATUS "Detected Clang compiler: ${CMAKE_CXX_COMPILER}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
Expand All @@ -213,6 +214,24 @@ endif()

add_library(GameAnalytics ${LIB_TYPE} ${CPP_SOURCES})
target_link_libraries(GameAnalytics PRIVATE ${LIBS} PUBLIC ${PUBLIC_LIBS})

# Hide symbols by default (GCC/Clang -fvisibility=hidden); GA_API in GameAnalyticsExtern.h
# still marks the public C API as exported. No-op on MSVC (dllexport/dllimport controls that).
set_target_properties(GameAnalytics PROPERTIES
C_VISIBILITY_PRESET hidden
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
)

if(${GA_SHARED_LIB})
if(APPLE)
target_link_options(GameAnalytics PRIVATE
"-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/CMakeIncludes/exported_symbols_apple.txt")
elseif(UNIX)
target_link_options(GameAnalytics PRIVATE "-Wl,--exclude-libs,ALL")
endif()
endif()

message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}")
message(STATUS "CMAKE_EXE_LINKER_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}")
message(STATUS "CMAKE_SHARED_LINKER_FLAGS: ${CMAKE_SHARED_LINKER_FLAGS}")
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,37 @@ void myLogHandler(const char* message, GALoggerMessageType type)
gameAnalytics_configureCustomLogHandler(myLogHandler);
```

### Remote configs listener
To be notified whenever remote configs are populated:

**C++ API:**
``` c++
struct RemoteConfigs: public gameanalytics::IRemoteConfigsListener
{
void onRemoteConfigsUpdated(std::string const& configs) override
{
// configs have been updated
}
};

auto listener = std::make_shared<RemoteConfigs>();
gameanalytics::GameAnalytics::addRemoteConfigsListener(listener);
```

**C API (shared lib):**
```c
void myRemoteConfigsListener(const char* configs)
{
// configs have been updated
}

gameAnalytics_configureRemoteConfigsListener(myRemoteConfigsListener);
```

The C API supports a single listener; registering again replaces the previous one and passing `NULL` unregisters. Register it before `gameAnalytics_initialize` to not miss the first update.

> **Note:** The listener is invoked on the SDK's internal thread and the `configs` string is only valid during the call — copy it if needed (do not pass it to `gameAnalytics_freeString`).

### Custom HTTP client

By default, the SDK uses cURL for HTTP requests. If you need to use a different HTTP library (e.g. on consoles or custom platforms), you can provide your own implementation by subclassing `GAHttpClient`:
Expand Down
4 changes: 2 additions & 2 deletions sample/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release")

set(CMAKE_CXX_STANDARD 17)

if(LINUX)
if(UNIX AND NOT APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")

if(CLANG)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()

Expand Down
2 changes: 1 addition & 1 deletion sample_shared/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release")

set(CMAKE_CXX_STANDARD 17)

if(LINUX)
if(UNIX AND NOT APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")

if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
Expand Down
8 changes: 8 additions & 0 deletions sample_shared/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ void testCrash()
*i = 10;
}

void onRemoteConfigsUpdated(const char* configs)
{
std::cout << "remote configs = " << (configs ? configs : "") << '\n';
}

int main(int argc, char** argv)
{
std::cout << "start\n";
Expand Down Expand Up @@ -44,6 +49,9 @@ int main(int argc, char** argv)

gameAnalytics_setCustomDimension01("test");

// register before initialize or the first update is missed
gameAnalytics_configureRemoteConfigsListener(onRemoteConfigsUpdated);

using namespace std::chrono_literals;

gameAnalytics_initialize(GAME_KEY, SECRET_KEY);
Expand Down
4 changes: 4 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ def main():

triplet = f'{arch}-{platform}'

# match CMAKE_MSVC_RUNTIME_LIBRARY: /MD shared, /MT static
if args.platform.startswith('win'):
triplet = f'{arch}-windows-static-md' if args.shared else f'{arch}-windows-static'

if args.platform == 'osx':
osx_arch = arch if arch == 'arm64' else 'x86_64' # no official universal triplet for osx vcpkg
cmake_command += f' -DVCPKG_HOST_TRIPLET={triplet}'
Expand Down
3 changes: 2 additions & 1 deletion source/gameanalytics/GAState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,8 @@ namespace gameanalytics
}
catch (json::exception& e)
{
logging::GALogger::e(e.what());
// discard unparseable cached config
logging::GALogger::w("Discarding incompatible cached sdk config: %s", e.what());
store::GAStore::setState("sdk_config_cached", "");
}
}
Expand Down
45 changes: 45 additions & 0 deletions source/gameanalytics/GameAnalyticsExtern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "GAUtilities.h"
#include <cstring>
#include <cstdlib>
#include <memory>

gameanalytics::StringVector makeStringVector(const char** arr, int size)
{
Expand Down Expand Up @@ -315,6 +316,50 @@ GA_API const char* gameAnalytics_getRemoteConfigsValueAsJson(const char* key)
return gameAnalytics_allocString(returnValue);
}

namespace
{
class ExternRemoteConfigsListener : public gameanalytics::IRemoteConfigsListener
{
public:

explicit ExternRemoteConfigsListener(GARemoteConfigsListener listener):
_listener(listener)
{
}

void onRemoteConfigsUpdated(std::string const& remoteConfigs) override
{
if (_listener)
{
_listener(remoteConfigs.c_str());
}
}

private:

GARemoteConfigsListener _listener;
};

std::shared_ptr<gameanalytics::IRemoteConfigsListener> g_externRemoteConfigsListener;
}

GA_API void gameAnalytics_configureRemoteConfigsListener(GARemoteConfigsListener listener)
{
if (g_externRemoteConfigsListener)
{
gameanalytics::GameAnalytics::removeRemoteConfigsListener(g_externRemoteConfigsListener);
g_externRemoteConfigsListener.reset();
}

if (!listener)
{
return;
}

g_externRemoteConfigsListener = std::make_shared<ExternRemoteConfigsListener>(listener);
gameanalytics::GameAnalytics::addRemoteConfigsListener(g_externRemoteConfigsListener);
}

GA_API const char* gameAnalytics_getABTestingId()
{
std::string returnValue = gameanalytics::GameAnalytics::getABTestingId();
Expand Down
8 changes: 8 additions & 0 deletions source/gameanalytics/GameAnalyticsExtern.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ enum GALoggerMessageType
typedef float(*GAFpsTracker)(void);
typedef void(*GALogHandler)(const char* message, GALoggerMessageType messageType);

// called when remote configs are populated (on the SDK's internal thread)
// the string is only valid during the call - copy it if needed, do not free it
typedef void(*GARemoteConfigsListener)(const char* remoteConfigs);

GA_API void gameAnalytics_freeString(const char* ptr);

GA_API void gameAnalytics_configureAvailableCustomDimensions01(const char **customDimensions, int size);
Expand Down Expand Up @@ -138,6 +142,10 @@ GA_API const char* gameAnalytics_getExternalUserId();
GA_API GAStatus gameAnalytics_isRemoteConfigsReady();
GA_API const char* gameAnalytics_getRemoteConfigsContentAsString();

// single listener; registering again replaces it, NULL unregisters
// register before initialize to not miss the first update
GA_API void gameAnalytics_configureRemoteConfigsListener(GARemoteConfigsListener listener);

GA_API const char* gameAnalytics_getABTestingId();
GA_API const char* gameAnalytics_getABTestingVariantId();

Expand Down
16 changes: 9 additions & 7 deletions source/gameanalytics/Platform/GALinux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,24 +78,26 @@ std::string gameanalytics::GAPlatformLinux::getOSVersion()
struct utsname info;
uname(&info);

std::string version;
int const strSize = strlen(info.release);
std::string version = info.release;
size_t const strSize = version.size();

int dotCount = 0;
for (size_t i = 0; i < strSize; ++i)
{
if (info.release[i] == '.')
{
char const c = version[i];

if (c == '.')
{
++dotCount;
if (dotCount == 3)
{
version = std::string(info.release, info.release + i);
version.resize(i);
break;
}
}
else if (!isdigit(info.release[i]))
else if (!isdigit(static_cast<unsigned char>(c)))
{
version = std::string(info.release, info.release + i);
version.resize(i);
break;
}
}
Expand Down
Loading