mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-06 03:25:36 +00:00
dep/rcheevos: Sync with branch
This commit is contained in:
parent
4ec372cd4e
commit
481fba357c
@ -224,6 +224,39 @@ typedef struct rc_client_user_game_summary_t {
|
||||
*/
|
||||
RC_EXPORT void RC_CCONV rc_client_get_user_game_summary(const rc_client_t* client, rc_client_user_game_summary_t* summary);
|
||||
|
||||
typedef struct rc_client_all_user_progress_entry_t {
|
||||
uint32_t game_id;
|
||||
uint32_t num_achievements;
|
||||
uint32_t num_unlocked_achievements;
|
||||
uint32_t num_unlocked_achievements_hardcore;
|
||||
} rc_client_all_user_progress_entry_t;
|
||||
|
||||
typedef struct rc_client_all_user_progress_t {
|
||||
rc_client_all_user_progress_entry_t* entries;
|
||||
uint32_t num_entries;
|
||||
} rc_client_all_user_progress_t;
|
||||
|
||||
/**
|
||||
* Callback that is fired when an all progress query completes. list may be null if the query failed.
|
||||
*/
|
||||
typedef void(RC_CCONV* rc_client_fetch_all_user_progress_callback_t)(int result, const char* error_message,
|
||||
rc_client_all_user_progress_t* list,
|
||||
rc_client_t* client, void* callback_userdata);
|
||||
|
||||
/**
|
||||
* Starts an asynchronous request for all progress for the given console.
|
||||
* This query returns the total number of achievements for all games tracked by this console, as well as
|
||||
* the user's achievement unlock count for both softcore and hardcore modes.
|
||||
*/
|
||||
RC_EXPORT rc_client_async_handle_t* RC_CCONV
|
||||
rc_client_begin_fetch_all_user_progress(rc_client_t* client, uint32_t console_id,
|
||||
rc_client_fetch_all_user_progress_callback_t callback, void* callback_userdata);
|
||||
|
||||
/**
|
||||
* Destroys a previously-allocated result from the rc_client_begin_fetch_all_progress_list() callback.
|
||||
*/
|
||||
RC_EXPORT void RC_CCONV rc_client_destroy_all_user_progress(rc_client_all_user_progress_t* list);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Game |
|
||||
\*****************************************************************************/
|
||||
@ -326,19 +359,13 @@ RC_EXPORT const rc_client_subset_t* RC_CCONV rc_client_get_subset_info(rc_client
|
||||
| Fetch Game Hashes |
|
||||
\*****************************************************************************/
|
||||
|
||||
typedef struct rc_client_hash_library_entry_t
|
||||
{
|
||||
/* The hash for the game */
|
||||
const char* hash;
|
||||
/* The unique identifier of the game */
|
||||
typedef struct rc_client_hash_library_entry_t {
|
||||
char hash[33];
|
||||
uint32_t game_id;
|
||||
} rc_client_hash_library_entry_t;
|
||||
|
||||
typedef struct rc_client_hash_library_t
|
||||
{
|
||||
/* An array of entries, one per game */
|
||||
typedef struct rc_client_hash_library_t {
|
||||
rc_client_hash_library_entry_t* entries;
|
||||
/* The number of items in the entries array */
|
||||
uint32_t num_entries;
|
||||
} rc_client_hash_library_t;
|
||||
|
||||
@ -362,51 +389,6 @@ RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_fetch_hash_library(
|
||||
*/
|
||||
RC_EXPORT void RC_CCONV rc_client_destroy_hash_library(rc_client_hash_library_t* list);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Game Info |
|
||||
\*****************************************************************************/
|
||||
|
||||
typedef struct rc_client_all_user_progress_entry_t
|
||||
{
|
||||
/* The unique identifier of the game */
|
||||
uint32_t game_id;
|
||||
/* The total number of achievements for this game */
|
||||
uint32_t num_achievements;
|
||||
/* The total number of unlocked achievements for this game in softcore mode */
|
||||
uint32_t num_unlocked_achievements;
|
||||
/* The total number of unlocked achievements for this game in hardcore mode */
|
||||
uint32_t num_unlocked_achievements_hardcore;
|
||||
} rc_client_all_user_progress_entry_t;
|
||||
|
||||
typedef struct rc_client_all_user_progress_t
|
||||
{
|
||||
/* An array of entries, one per game */
|
||||
rc_client_all_user_progress_entry_t* entries;
|
||||
/* The number of items in the entries array */
|
||||
uint32_t num_entries;
|
||||
} rc_client_all_user_progress_t;
|
||||
|
||||
/**
|
||||
* Callback that is fired when an all progress query completes. list may be null if the query failed.
|
||||
*/
|
||||
typedef void(RC_CCONV* rc_client_fetch_all_user_progress_callback_t)(int result, const char* error_message,
|
||||
rc_client_all_user_progress_t* list,
|
||||
rc_client_t* client, void* callback_userdata);
|
||||
|
||||
/**
|
||||
* Starts an asynchronous request for all progress for the given console.
|
||||
* This query returns the total number of achievements for all games tracked by this console, as well as
|
||||
* the user's achievement unlock count for both softcore and hardcore modes.
|
||||
*/
|
||||
RC_EXPORT rc_client_async_handle_t* RC_CCONV
|
||||
rc_client_begin_fetch_all_user_progress(rc_client_t* client, uint32_t console_id,
|
||||
rc_client_fetch_all_user_progress_callback_t callback, void* callback_userdata);
|
||||
|
||||
/**
|
||||
* Destroys a previously-allocated result from the rc_client_begin_fetch_all_progress_list() callback.
|
||||
*/
|
||||
RC_EXPORT void RC_CCONV rc_client_destroy_all_user_progress(rc_client_all_user_progress_t* list);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Achievements |
|
||||
\*****************************************************************************/
|
||||
|
@ -511,6 +511,7 @@ int rc_api_init_fetch_hash_library_request_hosted(rc_api_request_t* request,
|
||||
/* note: unauthenticated request */
|
||||
rc_url_builder_init(&builder, &request->buffer, 48);
|
||||
rc_url_builder_append_str_param(&builder, "r", "hashlibrary");
|
||||
if (api_params->console_id != 0)
|
||||
rc_url_builder_append_unum_param(&builder, "c", api_params->console_id);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
|
@ -978,6 +978,126 @@ void rc_client_get_user_game_summary(const rc_client_t* client, rc_client_user_g
|
||||
rc_mutex_unlock((rc_mutex_t*)&client->state.mutex); /* remove const cast for mutex access */
|
||||
}
|
||||
|
||||
typedef struct rc_client_fetch_all_user_progress_callback_data_t {
|
||||
rc_client_t* client;
|
||||
rc_client_fetch_all_user_progress_callback_t callback;
|
||||
void* callback_userdata;
|
||||
uint32_t console_id;
|
||||
rc_client_async_handle_t async_handle;
|
||||
} rc_client_fetch_all_user_progress_callback_data_t;
|
||||
|
||||
static void rc_client_fetch_all_user_progress_callback(const rc_api_server_response_t* server_response,
|
||||
void* callback_data)
|
||||
{
|
||||
rc_client_fetch_all_user_progress_callback_data_t* ap_callback_data =
|
||||
(rc_client_fetch_all_user_progress_callback_data_t*)callback_data;
|
||||
rc_client_t* client = ap_callback_data->client;
|
||||
rc_api_fetch_all_user_progress_response_t ap_response;
|
||||
const char* error_message;
|
||||
int result;
|
||||
|
||||
result = rc_client_end_async(client, &ap_callback_data->async_handle);
|
||||
if (result) {
|
||||
if (result != RC_CLIENT_ASYNC_DESTROYED)
|
||||
RC_CLIENT_LOG_VERBOSE(client, "Fetch all progress aborted");
|
||||
|
||||
free(ap_callback_data);
|
||||
return;
|
||||
}
|
||||
|
||||
result = rc_api_process_fetch_all_user_progress_server_response(&ap_response, server_response);
|
||||
error_message = rc_client_server_error_message(&result, server_response->http_status_code, &ap_response.response);
|
||||
if (error_message) {
|
||||
RC_CLIENT_LOG_ERR_FORMATTED(client, "Fetch all progress for console %u failed: %s", ap_callback_data->console_id,
|
||||
error_message);
|
||||
ap_callback_data->callback(result, error_message, NULL, client, ap_callback_data->callback_userdata);
|
||||
} else {
|
||||
rc_client_all_user_progress_t* list;
|
||||
const size_t list_size = sizeof(*list) + sizeof(rc_client_all_user_progress_entry_t) * ap_response.num_entries;
|
||||
|
||||
list = (rc_client_all_user_progress_t*)malloc(list_size);
|
||||
if (!list) {
|
||||
ap_callback_data->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client,
|
||||
ap_callback_data->callback_userdata);
|
||||
} else {
|
||||
rc_client_all_user_progress_entry_t* entry = list->entries =
|
||||
(rc_client_all_user_progress_entry_t*)((uint8_t*)list + sizeof(*list));
|
||||
const rc_api_all_user_progress_entry_t* hlentry = ap_response.entries;
|
||||
const rc_api_all_user_progress_entry_t* stop = hlentry + ap_response.num_entries;
|
||||
|
||||
for (; hlentry < stop; ++hlentry, ++entry)
|
||||
{
|
||||
entry->game_id = hlentry->game_id;
|
||||
entry->num_achievements = hlentry->num_achievements;
|
||||
entry->num_unlocked_achievements = hlentry->num_unlocked_achievements;
|
||||
entry->num_unlocked_achievements_hardcore = hlentry->num_unlocked_achievements_hardcore;
|
||||
}
|
||||
|
||||
list->num_entries = ap_response.num_entries;
|
||||
|
||||
ap_callback_data->callback(RC_OK, NULL, list, client, ap_callback_data->callback_userdata);
|
||||
}
|
||||
}
|
||||
|
||||
rc_api_destroy_fetch_all_user_progress_response(&ap_response);
|
||||
free(ap_callback_data);
|
||||
}
|
||||
|
||||
rc_client_async_handle_t* rc_client_begin_fetch_all_user_progress(rc_client_t* client, uint32_t console_id,
|
||||
rc_client_fetch_all_user_progress_callback_t callback,
|
||||
void* callback_userdata)
|
||||
{
|
||||
rc_api_fetch_all_user_progress_request_t api_params;
|
||||
rc_client_fetch_all_user_progress_callback_data_t* callback_data;
|
||||
rc_client_async_handle_t* async_handle;
|
||||
rc_api_request_t request;
|
||||
int result;
|
||||
const char* error_message;
|
||||
|
||||
if (!client) {
|
||||
callback(RC_INVALID_STATE, "client is required", NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
} else if (client->state.user != RC_CLIENT_USER_STATE_LOGGED_IN) {
|
||||
callback(RC_INVALID_STATE, "client must be logged in", NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
api_params.username = client->user.username;
|
||||
api_params.api_token = client->user.token;
|
||||
api_params.console_id = console_id;
|
||||
|
||||
result = rc_api_init_fetch_all_user_progress_request_hosted(&request, &api_params, &client->state.host);
|
||||
|
||||
if (result != RC_OK) {
|
||||
error_message = rc_error_str(result);
|
||||
callback(result, error_message, NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
callback_data = (rc_client_fetch_all_user_progress_callback_data_t*)calloc(1, sizeof(*callback_data));
|
||||
if (!callback_data) {
|
||||
callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
callback_data->client = client;
|
||||
callback_data->callback = callback;
|
||||
callback_data->callback_userdata = callback_userdata;
|
||||
callback_data->console_id = console_id;
|
||||
|
||||
async_handle = &callback_data->async_handle;
|
||||
rc_client_begin_async(client, async_handle);
|
||||
client->callbacks.server_call(&request, rc_client_fetch_all_user_progress_callback, callback_data, client);
|
||||
rc_api_destroy_request(&request);
|
||||
|
||||
return rc_client_async_handle_valid(client, async_handle) ? async_handle : NULL;
|
||||
}
|
||||
|
||||
void rc_client_destroy_all_user_progress(rc_client_all_user_progress_t* list)
|
||||
{
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* ===== Game ===== */
|
||||
|
||||
static void rc_client_free_game(rc_client_game_info_t* game)
|
||||
@ -3253,10 +3373,9 @@ const rc_client_subset_t* rc_client_get_subset_info(rc_client_t* client, uint32_
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ===== Game Info ===== */
|
||||
/* ===== Fetch Game Hashes ===== */
|
||||
|
||||
typedef struct rc_client_fetch_hash_library_callback_data_t
|
||||
{
|
||||
typedef struct rc_client_fetch_hash_library_callback_data_t {
|
||||
rc_client_t* client;
|
||||
rc_client_fetch_hash_library_callback_t callback;
|
||||
void* callback_userdata;
|
||||
@ -3274,8 +3393,7 @@ static void rc_client_fetch_hash_library_callback(const rc_api_server_response_t
|
||||
int result;
|
||||
|
||||
result = rc_client_end_async(client, &hashlib_callback_data->async_handle);
|
||||
if (result)
|
||||
{
|
||||
if (result) {
|
||||
if (result != RC_CLIENT_ASYNC_DESTROYED)
|
||||
RC_CLIENT_LOG_VERBOSE(client, "Fetch hash library aborted");
|
||||
|
||||
@ -3286,43 +3404,26 @@ static void rc_client_fetch_hash_library_callback(const rc_api_server_response_t
|
||||
result = rc_api_process_fetch_hash_library_server_response(&hashlib_response, server_response);
|
||||
error_message =
|
||||
rc_client_server_error_message(&result, server_response->http_status_code, &hashlib_response.response);
|
||||
if (error_message)
|
||||
{
|
||||
if (error_message) {
|
||||
RC_CLIENT_LOG_ERR_FORMATTED(client, "Fetch hash library for console %u failed: %s",
|
||||
hashlib_callback_data->console_id, error_message);
|
||||
hashlib_callback_data->callback(result, error_message, NULL, client, hashlib_callback_data->callback_userdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
rc_client_hash_library_t* list;
|
||||
const size_t list_size = sizeof(*list) + sizeof(rc_client_hash_library_entry_t) * hashlib_response.num_entries;
|
||||
size_t needed_size = list_size;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < hashlib_response.num_entries; i++)
|
||||
needed_size += strlen(hashlib_response.entries[i].hash) + 1;
|
||||
|
||||
list = (rc_client_hash_library_t*)malloc(needed_size);
|
||||
if (!list)
|
||||
{
|
||||
list = (rc_client_hash_library_t*)malloc(list_size);
|
||||
if (!list) {
|
||||
hashlib_callback_data->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client,
|
||||
hashlib_callback_data->callback_userdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
rc_client_hash_library_entry_t* entry = list->entries =
|
||||
(rc_client_hash_library_entry_t*)((uint8_t*)list + sizeof(*list));
|
||||
char* hash = (char*)((uint8_t*)list + list_size);
|
||||
const rc_api_hash_library_entry_t* hlentry = hashlib_response.entries;
|
||||
const rc_api_hash_library_entry_t* stop = hlentry + hashlib_response.num_entries;
|
||||
|
||||
for (; hlentry < stop; ++hlentry, ++entry)
|
||||
{
|
||||
const size_t len = strlen(hlentry->hash) + 1;
|
||||
entry->hash = hash;
|
||||
for (; hlentry < stop; ++hlentry, ++entry) {
|
||||
snprintf(entry->hash, sizeof(entry->hash), "%s", hlentry->hash);
|
||||
entry->game_id = hlentry->game_id;
|
||||
memcpy(hash, hlentry->hash, len);
|
||||
hash += len;
|
||||
}
|
||||
|
||||
list->num_entries = hashlib_response.num_entries;
|
||||
@ -3346,8 +3447,7 @@ rc_client_async_handle_t* rc_client_begin_fetch_hash_library(rc_client_t* client
|
||||
int result;
|
||||
const char* error_message;
|
||||
|
||||
if (!client)
|
||||
{
|
||||
if (!client) {
|
||||
callback(RC_INVALID_STATE, "client is required", NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
@ -3355,16 +3455,14 @@ rc_client_async_handle_t* rc_client_begin_fetch_hash_library(rc_client_t* client
|
||||
api_params.console_id = console_id;
|
||||
result = rc_api_init_fetch_hash_library_request_hosted(&request, &api_params, &client->state.host);
|
||||
|
||||
if (result != RC_OK)
|
||||
{
|
||||
if (result != RC_OK) {
|
||||
error_message = rc_error_str(result);
|
||||
callback(result, error_message, NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
callback_data = (rc_client_fetch_hash_library_callback_data_t*)calloc(1, sizeof(*callback_data));
|
||||
if (!callback_data)
|
||||
{
|
||||
if (!callback_data) {
|
||||
callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
@ -3387,141 +3485,6 @@ void rc_client_destroy_hash_library(rc_client_hash_library_t* list)
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* ===== Fetch Game Hashes ===== */
|
||||
|
||||
typedef struct rc_client_fetch_all_user_progress_callback_data_t
|
||||
{
|
||||
rc_client_t* client;
|
||||
rc_client_fetch_all_user_progress_callback_t callback;
|
||||
void* callback_userdata;
|
||||
uint32_t console_id;
|
||||
rc_client_async_handle_t async_handle;
|
||||
} rc_client_fetch_all_user_progress_callback_data_t;
|
||||
|
||||
static void rc_client_fetch_all_user_progress_callback(const rc_api_server_response_t* server_response,
|
||||
void* callback_data)
|
||||
{
|
||||
rc_client_fetch_all_user_progress_callback_data_t* ap_callback_data =
|
||||
(rc_client_fetch_all_user_progress_callback_data_t*)callback_data;
|
||||
rc_client_t* client = ap_callback_data->client;
|
||||
rc_api_fetch_all_user_progress_response_t ap_response;
|
||||
const char* error_message;
|
||||
int result;
|
||||
|
||||
result = rc_client_end_async(client, &ap_callback_data->async_handle);
|
||||
if (result)
|
||||
{
|
||||
if (result != RC_CLIENT_ASYNC_DESTROYED)
|
||||
RC_CLIENT_LOG_VERBOSE(client, "Fetch all progress aborted");
|
||||
|
||||
free(ap_callback_data);
|
||||
return;
|
||||
}
|
||||
|
||||
result = rc_api_process_fetch_all_user_progress_server_response(&ap_response, server_response);
|
||||
error_message = rc_client_server_error_message(&result, server_response->http_status_code, &ap_response.response);
|
||||
if (error_message)
|
||||
{
|
||||
RC_CLIENT_LOG_ERR_FORMATTED(client, "Fetch all progress for console %u failed: %s", ap_callback_data->console_id,
|
||||
error_message);
|
||||
ap_callback_data->callback(result, error_message, NULL, client, ap_callback_data->callback_userdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc_client_all_user_progress_t* list;
|
||||
const size_t list_size = sizeof(*list) + sizeof(rc_client_all_user_progress_entry_t) * ap_response.num_entries;
|
||||
|
||||
list = (rc_client_all_user_progress_t*)malloc(list_size);
|
||||
if (!list)
|
||||
{
|
||||
ap_callback_data->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client,
|
||||
ap_callback_data->callback_userdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc_client_all_user_progress_entry_t* entry = list->entries =
|
||||
(rc_client_all_user_progress_entry_t*)((uint8_t*)list + sizeof(*list));
|
||||
const rc_api_all_user_progress_entry_t* hlentry = ap_response.entries;
|
||||
const rc_api_all_user_progress_entry_t* stop = hlentry + ap_response.num_entries;
|
||||
|
||||
for (; hlentry < stop; ++hlentry, ++entry)
|
||||
{
|
||||
entry->game_id = hlentry->game_id;
|
||||
entry->num_achievements = hlentry->num_achievements;
|
||||
entry->num_unlocked_achievements = hlentry->num_unlocked_achievements;
|
||||
entry->num_unlocked_achievements_hardcore = hlentry->num_unlocked_achievements_hardcore;
|
||||
}
|
||||
|
||||
list->num_entries = ap_response.num_entries;
|
||||
|
||||
ap_callback_data->callback(RC_OK, NULL, list, client, ap_callback_data->callback_userdata);
|
||||
}
|
||||
}
|
||||
|
||||
rc_api_destroy_fetch_all_user_progress_response(&ap_response);
|
||||
free(ap_callback_data);
|
||||
}
|
||||
|
||||
rc_client_async_handle_t* rc_client_begin_fetch_all_user_progress(rc_client_t* client, uint32_t console_id,
|
||||
rc_client_fetch_all_user_progress_callback_t callback,
|
||||
void* callback_userdata)
|
||||
{
|
||||
rc_api_fetch_all_user_progress_request_t api_params;
|
||||
rc_client_fetch_all_user_progress_callback_data_t* callback_data;
|
||||
rc_client_async_handle_t* async_handle;
|
||||
rc_api_request_t request;
|
||||
int result;
|
||||
const char* error_message;
|
||||
|
||||
if (!client)
|
||||
{
|
||||
callback(RC_INVALID_STATE, "client is required", NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
else if (client->state.user != RC_CLIENT_USER_STATE_LOGGED_IN)
|
||||
{
|
||||
callback(RC_INVALID_STATE, "client must be logged in", NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
api_params.username = client->user.username;
|
||||
api_params.api_token = client->user.token;
|
||||
api_params.console_id = console_id;
|
||||
|
||||
result = rc_api_init_fetch_all_user_progress_request_hosted(&request, &api_params, &client->state.host);
|
||||
|
||||
if (result != RC_OK)
|
||||
{
|
||||
error_message = rc_error_str(result);
|
||||
callback(result, error_message, NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
callback_data = (rc_client_fetch_all_user_progress_callback_data_t*)calloc(1, sizeof(*callback_data));
|
||||
if (!callback_data)
|
||||
{
|
||||
callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
callback_data->client = client;
|
||||
callback_data->callback = callback;
|
||||
callback_data->callback_userdata = callback_userdata;
|
||||
callback_data->console_id = console_id;
|
||||
|
||||
async_handle = &callback_data->async_handle;
|
||||
rc_client_begin_async(client, async_handle);
|
||||
client->callbacks.server_call(&request, rc_client_fetch_all_user_progress_callback, callback_data, client);
|
||||
rc_api_destroy_request(&request);
|
||||
|
||||
return rc_client_async_handle_valid(client, async_handle) ? async_handle : NULL;
|
||||
}
|
||||
|
||||
void rc_client_destroy_all_user_progress(rc_client_all_user_progress_t* list)
|
||||
{
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* ===== Achievements ===== */
|
||||
|
||||
static void rc_client_update_achievement_display_information(rc_client_t* client, rc_client_achievement_info_t* achievement, time_t recent_unlock_time)
|
||||
|
Loading…
x
Reference in New Issue
Block a user