delay subsequent unlock attempts on failure

This commit is contained in:
Jamiras 2020-02-21 20:00:20 -07:00
parent 4f50b2c698
commit e00449c410
3 changed files with 162 additions and 93 deletions

View File

@ -78,9 +78,6 @@
/* Define this macro to prevent cheevos from being deactivated. */
#undef CHEEVOS_DONT_DEACTIVATE
/* Define this macro to log URLs (will log the user token). */
#undef CHEEVOS_LOG_URLS
/* Define this macro to dump all cheevos' addresses. */
#undef CHEEVOS_DUMP_ADDRS
@ -95,6 +92,9 @@
* that name. */
#undef CHEEVOS_SAVE_JSON
/* Define this macro to log URLs. */
#undef CHEEVOS_LOG_URLS
/* Define this macro to have the password and token logged. THIS WILL DISCLOSE
* THE USER'S PASSWORD, TAKE CARE! */
#undef CHEEVOS_LOG_PASSWORD
@ -130,6 +130,22 @@ typedef struct
retro_time_t last_update;
} rcheevos_richpresence_t;
enum rcheevos_async_io_type
{
CHEEVOS_ASYNC_RICHPRESENCE,
CHEEVOS_ASYNC_AWARD_ACHIEVEMENT
};
typedef struct rcheevos_async_io_request
{
int id;
int value;
int attempt_count;
char type;
char hardcore;
char user_agent[256];
} rcheevos_async_io_request;
typedef struct
{
retro_task_t* task;
@ -355,6 +371,46 @@ static void rcheevos_log_url(const char* format, const char* url)
#endif
}
static retro_time_t rcheevos_async_send_rich_presence(rcheevos_async_io_request* request);
static void rcheevos_async_award_achievement(rcheevos_async_io_request* request);
static void rcheevos_async_task_handler(retro_task_t* task)
{
rcheevos_async_io_request* request = (rcheevos_async_io_request*)task->user_data;
switch (request->type)
{
case CHEEVOS_ASYNC_RICHPRESENCE:
if (request->id == (int)rcheevos_locals.patchdata.game_id)
{
/* update the task to fire again in two minutes */
task->when = rcheevos_async_send_rich_presence(request);
}
else
{
/* game changed; stop the recurring task - a new one will be scheduled for the next game */
task_set_finished(task, 1);
free(request);
}
break;
case CHEEVOS_ASYNC_AWARD_ACHIEVEMENT:
rcheevos_async_award_achievement(request);
task_set_finished(task, 1);
break;
}
}
static void rcheevos_async_schedule(rcheevos_async_io_request* request, retro_time_t delay)
{
retro_task_t* task = task_init();
task->when = cpu_features_get_time_usec() + delay;
task->handler = rcheevos_async_task_handler;
task->user_data = request;
task->progress = -1;
task_queue_push(task);
}
static const char* rcheevos_rc_error(int ret)
{
switch (ret)
@ -587,7 +643,6 @@ static int rcheevos_parse(const char* json)
{
char *buffer = (char*)malloc(buffer_size);
rcheevos_locals.richpresence.richpresence = rc_parse_richpresence(buffer, rcheevos_locals.patchdata.richpresence_script, NULL, 0);
rcheevos_locals.richpresence.last_update = cpu_features_get_time_usec();
}
rcheevos_locals.richpresence.evaluation[0] = '\0';
@ -599,6 +654,14 @@ static int rcheevos_parse(const char* json)
"Playing %s", rcheevos_locals.patchdata.title);
}
/* schedule the first rich presence call in 30 seconds */
{
rcheevos_async_io_request* request = (rcheevos_async_io_request*)calloc(1, sizeof(rcheevos_async_io_request));
request->id = rcheevos_locals.patchdata.game_id;
request->type = CHEEVOS_ASYNC_RICHPRESENCE;
rcheevos_async_schedule(request, CHEEVOS_PING_FREQUENCY / 4);
}
return 0;
error:
@ -614,70 +677,43 @@ error:
Test all the achievements (call once per frame).
*****************************************************************************/
static void rcheevos_award_task_softcore(retro_task_t *task, void* task_data, void* user_data,
const char* error)
static void rcheevos_award_task_callback(retro_task_t* task, void* task_data, void* user_data, const char* error)
{
int ret;
char buffer[256], user_agent[256];
settings_t *settings = config_get_ptr();
const rcheevos_cheevo_t* cheevo = (const rcheevos_cheevo_t*)user_data;
buffer[0] = 0;
rcheevos_async_io_request* request = (rcheevos_async_io_request*)user_data;
if (!error)
{
CHEEVOS_LOG(RCHEEVOS_TAG "Awarded achievement %u\n", cheevo->info->id);
return;
CHEEVOS_LOG(RCHEEVOS_TAG "Awarded achievement %u\n", request->id);
free(request);
}
if (*error)
CHEEVOS_ERR(RCHEEVOS_TAG "Error awarding achievement %u: %s\n", cheevo->info->id, error);
/* Try again. */
ret = rc_url_award_cheevo(buffer, sizeof(buffer), settings->arrays.cheevos_username, rcheevos_locals.token, cheevo->info->id, 0);
if (ret != 0)
else
{
CHEEVOS_ERR(RCHEEVOS_TAG "Buffer to small to create URL");
return;
/* double the wait between each attempt until we hit a maximum delay of two minutes
* 250ms -> 500ms -> 1s -> 2s -> 4s -> 8s -> 16s -> 32s -> 64s -> 120s -> 120s... */
retro_time_t retry_delay = (request->attempt_count > 8) ? (120 * 1000 * 1000) : ((250 * 1000) << request->attempt_count);
request->attempt_count++;
rcheevos_async_schedule(request, retry_delay);
CHEEVOS_ERR(RCHEEVOS_TAG "Error awarding achievement %u: %s\n", request->id, error);
}
rcheevos_get_user_agent(user_agent);
rcheevos_log_url(RCHEEVOS_TAG "rc_url_award_cheevo: %s\n", buffer);
task_push_http_transfer_with_user_agent(buffer, true, NULL, user_agent, rcheevos_award_task_softcore, user_data);
}
static void rcheevos_award_task_hardcore(retro_task_t *task, void* task_data, void* user_data,
const char* error)
static void rcheevos_async_award_achievement(rcheevos_async_io_request* request)
{
int ret;
char buffer[256], user_agent[256];
const rcheevos_cheevo_t* cheevo = (const rcheevos_cheevo_t*)user_data;
settings_t *settings = config_get_ptr();
buffer[0] = 0;
if (!error)
{
CHEEVOS_LOG(RCHEEVOS_TAG "Awarded achievement %u\n", cheevo->info->id);
return;
}
if (*error)
CHEEVOS_ERR(RCHEEVOS_TAG "Error awarding achievement %u: %s\n", cheevo->info->id, error);
/* Try again. */
ret = rc_url_award_cheevo(buffer, sizeof(buffer), settings->arrays.cheevos_username, rcheevos_locals.token, cheevo->info->id, 1);
char buffer[256];
settings_t *settings = config_get_ptr();
int ret = rc_url_award_cheevo(buffer, sizeof(buffer), settings->arrays.cheevos_username, rcheevos_locals.token, request->id, request->hardcore);
if (ret != 0)
{
CHEEVOS_ERR(RCHEEVOS_TAG "Buffer to small to create URL\n");
CHEEVOS_ERR(RCHEEVOS_TAG "Buffer too small to create URL\n");
free(request);
return;
}
rcheevos_get_user_agent(user_agent);
rcheevos_log_url(RCHEEVOS_TAG "rc_url_award_cheevo: %s\n", buffer);
task_push_http_transfer_with_user_agent(buffer, true, NULL, user_agent, rcheevos_award_task_hardcore, user_data);
task_push_http_transfer_with_user_agent(buffer, true, NULL, request->user_agent, rcheevos_award_task_callback, request);
}
static void rcheevos_award(rcheevos_cheevo_t* cheevo, int mode)
@ -711,10 +747,14 @@ static void rcheevos_award(rcheevos_cheevo_t* cheevo, int mode)
}
/* Start the award task. */
if ((mode & RCHEEVOS_ACTIVE_HARDCORE) != 0)
rcheevos_award_task_hardcore(NULL, NULL, cheevo, "");
else
rcheevos_award_task_softcore(NULL, NULL, cheevo, "");
{
rcheevos_async_io_request* request = (rcheevos_async_io_request*)calloc(1, sizeof(rcheevos_async_io_request));
request->type = CHEEVOS_ASYNC_AWARD_ACHIEVEMENT;
request->id = cheevo->info->id;
request->hardcore = ((mode & RCHEEVOS_ACTIVE_HARDCORE) != 0) ? 1 : 0;
rcheevos_get_user_agent(request->user_agent);
rcheevos_async_award_achievement(request);
}
/* Take a screenshot of the achievement. */
if (settings && settings->bools.cheevos_auto_screenshot)
@ -991,42 +1031,21 @@ const char* rcheevos_get_richpresence(void)
return rcheevos_locals.richpresence.evaluation;
}
static void rcheevos_test_richpresence(void)
static retro_time_t rcheevos_async_send_rich_presence(rcheevos_async_io_request* request)
{
retro_time_t now = cpu_features_get_time_usec();
settings_t *settings = config_get_ptr();
settings_t *settings = config_get_ptr();
const char *cheevos_username = settings->arrays.cheevos_username;
bool cheevos_richpresence_enable = settings->bools.cheevos_richpresence_enable;
#ifdef HAVE_DISCORD
bool discord_enable = settings->bools.discord_enable;
#endif
if (cheevos_richpresence_enable)
if (cheevos_richpresence_enable && rcheevos_locals.richpresence.richpresence)
{
/* Update rich presence every two minutes */
if (now < rcheevos_locals.richpresence.last_update
+ CHEEVOS_PING_FREQUENCY)
return;
if (rcheevos_locals.richpresence.richpresence)
{
rc_evaluate_richpresence(rcheevos_locals.richpresence.richpresence,
rcheevos_locals.richpresence.evaluation,
sizeof(rcheevos_locals.richpresence.evaluation), rcheevos_peek, NULL, NULL);
}
}
else
{
/* Send ping every four minutes */
if (now < rcheevos_locals.richpresence.last_update
+ CHEEVOS_PING_FREQUENCY * 2)
return;
rc_evaluate_richpresence(rcheevos_locals.richpresence.richpresence,
rcheevos_locals.richpresence.evaluation,
sizeof(rcheevos_locals.richpresence.evaluation), rcheevos_peek, NULL, NULL);
}
rcheevos_locals.richpresence.last_update = now;
{
char user_agent[256], url[256], post_data[1024];
char url[256], post_data[1024];
snprintf(url, sizeof(url),
"http://retroachievements.org/dorequest.php?r=ping&u=%s&t=%s",
@ -1040,16 +1059,23 @@ static void rcheevos_test_richpresence(void)
CHEEVOS_FREE(tmp);
#ifdef HAVE_DISCORD
if (discord_enable)
if (settings->bools.discord_enable)
discord_update(DISCORD_PRESENCE_RETROACHIEVEMENTS, false);
#endif
}
else
snprintf(post_data, sizeof(post_data), "g=%u", rcheevos_locals.patchdata.game_id);
rcheevos_get_user_agent(user_agent);
task_push_http_post_transfer_with_user_agent(url, post_data, true, "POST", user_agent, NULL, NULL);
rcheevos_get_user_agent(request->user_agent);
task_push_http_post_transfer_with_user_agent(url, post_data, true, "POST", request->user_agent, NULL, NULL);
}
/* Update rich presence every two minutes */
if (settings->bools.cheevos_richpresence_enable)
return cpu_features_get_time_usec() + CHEEVOS_PING_FREQUENCY;
/* Send ping every four minutes */
return cpu_features_get_time_usec() + CHEEVOS_PING_FREQUENCY * 2;
}
void rcheevos_reset_game(void)
@ -1334,8 +1360,6 @@ void rcheevos_test(void)
settings->bools.cheevos_leaderboards_enable &&
!rcheevos_hardcore_paused)
rcheevos_test_leaderboards();
rcheevos_test_richpresence();
}
}
@ -1486,7 +1510,7 @@ static int rcheevos_prepare_hash_psx(rcheevos_coro_t* coro)
char buffer[2048];
char exe_name_buffer[64];
size_t exe_name_size;
char* exe_name = NULL;
const char* exe_name = NULL;
char* scan = NULL;
int success = 0;
size_t to_read = 0;
@ -1535,7 +1559,7 @@ static int rcheevos_prepare_hash_psx(rcheevos_coro_t* coro)
if (exe_name)
{
scan = exe_name;
scan = (char*)exe_name;
while (!isspace(*scan) && *scan != ';')
++scan;
*scan = '\0';

View File

@ -30,6 +30,8 @@
#include <retro_common.h>
#include <retro_common_api.h>
#include <libretro.h>
RETRO_BEGIN_DECLS
enum task_type
@ -125,6 +127,9 @@ struct retro_task
/* don't touch this. */
retro_task_t *next;
/* when the task should run (0 for as soon as possible) */
retro_time_t when;
};
typedef struct task_finder_data

View File

@ -26,6 +26,8 @@
#include <queues/task_queue.h>
#include <features/features_cpu.h>
#ifdef HAVE_THREADS
#include <rthreads/rthreads.h>
#define SLOCK_LOCK(x) slock_lock(x)
@ -112,7 +114,20 @@ static void task_queue_put(task_queue_t *queue, retro_task_t *task)
task->next = NULL;
if (queue->front)
{
if (queue->back->when > task->when)
{
retro_task_t** prev = &queue->front;
while (*prev && (*prev)->when <= task->when)
prev = &((*prev)->next);
task->next = *prev;
*prev = task;
return;
}
queue->back->next = task;
}
else
queue->front = task;
@ -181,9 +196,13 @@ static void retro_task_regular_gather(void)
for (task = queue; task; task = next)
{
next = task->next;
task->handler(task);
task_queue_push_progress(task);
if (!task->when || task->when < cpu_features_get_time_usec())
{
task->handler(task);
task_queue_push_progress(task);
}
if (task->finished)
task_queue_put(&tasks_finished, task);
@ -304,9 +323,10 @@ static void task_queue_remove(task_queue_t *queue, retro_task_t *task)
{
slock_lock(queue_lock);
queue->front = task->next;
if (queue->back == task) /* if only element, also update back */
queue->back = NULL;
slock_unlock(queue_lock);
task->next = NULL;
return;
}
@ -320,6 +340,14 @@ static void task_queue_remove(task_queue_t *queue, retro_task_t *task)
{
t->next = task->next;
task->next = NULL;
if (queue->back == task)
{
slock_lock(queue_lock);
if (queue->back == task)
queue->back = t;
slock_unlock(queue_lock);
}
break;
}
@ -453,6 +481,18 @@ static void threaded_worker(void *userdata)
continue;
}
if (task->when)
{
retro_time_t now = cpu_features_get_time_usec();
retro_time_t delay = task->when - now - 500; /* allow half a millisecond for context switching */
if (delay > 0)
{
scond_wait_timeout(worker_cond, running_lock, delay);
slock_unlock(running_lock);
continue;
}
}
slock_unlock(running_lock);
task->handler(task);