diff --git a/src/rc_client.c b/src/rc_client.c index 4f016405..6544dec7 100644 --- a/src/rc_client.c +++ b/src/rc_client.c @@ -1302,6 +1302,8 @@ static void rc_client_activate_leaderboards(rc_client_game_info_t* game, rc_clie { rc_client_leaderboard_info_t* leaderboard; rc_client_leaderboard_info_t* stop; + const uint8_t leaderboards_allowed = + client->state.hardcore || client->state.allow_leaderboards_in_softcore; uint32_t active_count = 0; rc_client_subset_info_t* subset = game->subsets; @@ -1318,7 +1320,7 @@ static void rc_client_activate_leaderboards(rc_client_game_info_t* game, rc_clie continue; case RC_CLIENT_LEADERBOARD_STATE_INACTIVE: - if (client->state.hardcore) { + if (leaderboards_allowed) { rc_reset_lboard(leaderboard->lboard); leaderboard->public_.state = RC_CLIENT_LEADERBOARD_STATE_ACTIVE; ++active_count; @@ -1326,7 +1328,7 @@ static void rc_client_activate_leaderboards(rc_client_game_info_t* game, rc_clie break; default: - if (client->state.hardcore) + if (leaderboards_allowed) ++active_count; else leaderboard->public_.state = RC_CLIENT_LEADERBOARD_STATE_INACTIVE; @@ -4008,6 +4010,11 @@ static void rc_client_submit_leaderboard_entry(rc_client_t* client, rc_client_le { rc_client_submit_leaderboard_entry_callback_data_t* callback_data; + if (!client->state.hardcore) { + RC_CLIENT_LOG_INFO_FORMATTED(client, "Leaderboard %u entry submission not allowed in softcore", leaderboard->public_.id); + return; + } + if (client->callbacks.can_submit_leaderboard_entry && !client->callbacks.can_submit_leaderboard_entry(leaderboard->public_.id, client)) { RC_CLIENT_LOG_INFO_FORMATTED(client, "Leaderboard %u entry submission blocked by client", leaderboard->public_.id); @@ -4946,7 +4953,7 @@ void rc_client_do_frame(rc_client_t* client) if (client->game->pending_events & RC_CLIENT_GAME_PENDING_EVENT_PROGRESS_TRACKER) rc_client_do_frame_update_progress_tracker(client, client->game); - if (client->state.hardcore) { + if (client->state.hardcore || client->state.allow_leaderboards_in_softcore) { for (subset = client->game->subsets; subset; subset = subset->next) { if (subset->active) rc_client_do_frame_process_leaderboards(client, subset); @@ -5412,7 +5419,9 @@ static void rc_client_disable_hardcore(rc_client_t* client) if (client->game) { rc_client_toggle_hardcore_achievements(client->game, client, RC_CLIENT_ACHIEVEMENT_UNLOCKED_SOFTCORE); - rc_client_deactivate_leaderboards(client->game, client); + + if (!client->state.allow_leaderboards_in_softcore) + rc_client_deactivate_leaderboards(client->game, client); } } diff --git a/src/rc_client_internal.h b/src/rc_client_internal.h index 509c5675..5e570915 100644 --- a/src/rc_client_internal.h +++ b/src/rc_client_internal.h @@ -325,6 +325,7 @@ typedef struct rc_client_state_t { uint8_t log_level; uint8_t user; uint8_t disconnect; + uint8_t allow_leaderboards_in_softcore; struct rc_client_load_state_t* load; struct rc_client_async_handle_t* async_handles[4]; diff --git a/test/test_rc_client.c b/test/test_rc_client.c index 81ed4a12..9f0fdabb 100644 --- a/test/test_rc_client.c +++ b/test/test_rc_client.c @@ -6885,6 +6885,7 @@ static void test_do_frame_leaderboard_submit_blocked(void) ASSERT_NUM_EQUALS(event->leaderboard_tracker->id, 1); ASSERT_STR_EQUALS(event->leaderboard_tracker->display, "000017"); + /* but make sure the server wasn't called */ assert_api_not_called(api_call); event_count = 0; @@ -6900,6 +6901,90 @@ static void test_do_frame_leaderboard_submit_blocked(void) ASSERT_PTR_NOT_NULL(find_event(RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED, 44)); ASSERT_PTR_NOT_NULL(find_event(RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD, 44)); + /* make sure the server was called */ + assert_api_called(api_call); + + rc_client_destroy(g_client); +} + +static void test_do_frame_leaderboard_submit_softcore(void) +{ + rc_client_leaderboard_info_t* leaderboard; + rc_client_event_t* event; + uint8_t memory[64]; + const char* api_call = "r=submitlbentry&u=Username&t=ApiToken&i=44&s=17&m=0123456789ABCDEF&v=a27fa205f7f30c8d13d74806ea5425b6"; + memset(memory, 0, sizeof(memory)); + + g_client = mock_client_game_loaded(patchdata_exhaustive, no_unlocks); + rc_client_set_hardcore_enabled(g_client, 0); + + ASSERT_PTR_NOT_NULL(g_client->game); + mock_memory(memory, sizeof(memory)); + + mock_api_response(api_call, + "{\"Success\":true,\"Response\":{\"Score\":17,\"BestScore\":23," + "\"TopEntries\":[{\"User\":\"Player1\",\"Score\":44,\"Rank\":1},{\"User\":\"Username\",\"Score\":23,\"Rank\":2}]," + "\"RankInfo\":{\"Rank\":2,\"NumEntries\":\"2\"}}}"); + + event_count = 0; + rc_client_do_frame(g_client); + ASSERT_NUM_EQUALS(event_count, 0); + + /* start the leaderboard - will be ignored in softcore */ + memory[0x0B] = 1; + memory[0x0E] = 17; + rc_client_do_frame(g_client); + ASSERT_NUM_EQUALS(event_count, 0); + + /* allow leaderboards to be processed in softcore. have to manually activate as it wasn't activated on game load */ + g_client->state.allow_leaderboards_in_softcore = 1; + leaderboard = (rc_client_leaderboard_info_t*)rc_client_get_leaderboard_info(g_client, 44); + leaderboard->public_.state = RC_CLIENT_LEADERBOARD_BUCKET_ACTIVE; + leaderboard->lboard->state = RC_LBOARD_STATE_ACTIVE; + + rc_client_do_frame(g_client); + ASSERT_NUM_EQUALS(event_count, 2); + ASSERT_PTR_NOT_NULL(find_event(RC_CLIENT_EVENT_LEADERBOARD_STARTED, 44)); + ASSERT_PTR_NOT_NULL(find_event(RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW, 1)); + + event_count = 0; + rc_client_do_frame(g_client); + ASSERT_NUM_EQUALS(event_count, 0); + + /* submit the leaderboard */ + memory[0x0B] = 0; + memory[0x0D] = 1; + rc_client_do_frame(g_client); + ASSERT_NUM_EQUALS(event_count, 2); + + event = find_event(RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED, 44); + ASSERT_PTR_NOT_NULL(event); + ASSERT_NUM_EQUALS(event->leaderboard->state, RC_CLIENT_LEADERBOARD_STATE_ACTIVE); + ASSERT_STR_EQUALS(event->leaderboard->tracker_value, "000017"); + ASSERT_PTR_EQUALS(event->leaderboard, rc_client_get_leaderboard_info(g_client, 44)); + + event = find_event(RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE, 1); + ASSERT_PTR_NOT_NULL(event); + ASSERT_NUM_EQUALS(event->leaderboard_tracker->id, 1); + ASSERT_STR_EQUALS(event->leaderboard_tracker->display, "000017"); + + /* but make sure the server wasn't called */ + assert_api_not_called(api_call); + + event_count = 0; + rc_client_do_frame(g_client); + ASSERT_NUM_EQUALS(event_count, 0); + + g_client->state.hardcore = 1; + + /* restart the leaderboard - will immediately submit */ + memory[0x0B] = 1; + rc_client_do_frame(g_client); + ASSERT_NUM_EQUALS(event_count, 2); + ASSERT_PTR_NOT_NULL(find_event(RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED, 44)); + ASSERT_PTR_NOT_NULL(find_event(RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD, 44)); + + /* make sure the server was called */ assert_api_called(api_call); rc_client_destroy(g_client); @@ -8729,6 +8814,7 @@ void test_client(void) { TEST(test_do_frame_leaderboard_submit_immediate); TEST(test_do_frame_leaderboard_submit_hidden); TEST(test_do_frame_leaderboard_submit_blocked); + TEST(test_do_frame_leaderboard_submit_softcore); TEST(test_do_frame_leaderboard_tracker_sharing); TEST(test_do_frame_leaderboard_tracker_sharing_hits); TEST(test_do_frame_leaderboard_submit_automatic_retry);