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
49 changes: 31 additions & 18 deletions src/data/models/AchievementModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "data\models\LocalBadgesModel.hh"
#include "data\models\TriggerValidation.hh"

#include "services\IClock.hh"
#include "services\ServiceLocator.hh"

#include <rcheevos\src\rcheevos\rc_internal.h>
Expand Down Expand Up @@ -405,6 +404,27 @@ void AchievementModel::SyncTriggerToRuntime()
ParseTrigger();
}

static void UpdateRuntimeLeaderboard(rc_client_game_info_t* pGame, rc_client_achievement_info_t* pAchievementInfo, rc_trigger_t* trigger) noexcept
{
const auto* pOldTrigger = pAchievementInfo->trigger;
pAchievementInfo->trigger = trigger;

// update the runtime memory reference too
auto* pRuntimeTrigger = pGame->runtime.triggers;
const auto* pRuntimeTriggerStop = pRuntimeTrigger + pGame->runtime.trigger_count;
for (; pRuntimeTrigger < pRuntimeTriggerStop; ++pRuntimeTrigger)
{
if (pRuntimeTrigger->trigger == pOldTrigger &&
pRuntimeTrigger->id == pAchievementInfo->public_.id)
{
pRuntimeTrigger->trigger = pAchievementInfo->trigger;
pRuntimeTrigger->serialized_size = 0;
memcpy(pRuntimeTrigger->md5, pAchievementInfo->md5, sizeof(pAchievementInfo->md5));
break;
}
}
}

void AchievementModel::ParseTrigger() const
{
const auto& sTrigger = GetTrigger();
Expand Down Expand Up @@ -464,29 +484,22 @@ void AchievementModel::ParseTrigger() const
rc_parse_trigger_internal(&trigger->trigger, &sMemaddr, &preparse.parse);
trigger->trigger.has_memrefs = 1;

const auto* pOldTrigger = m_pAchievementInfo->trigger;
m_pAchievementInfo->trigger = &trigger->trigger;

// update the runtime memory reference too
auto* pRuntimeTrigger = pGame->runtime.triggers;
const auto* pRuntimeTriggerStop = pRuntimeTrigger + pGame->runtime.trigger_count;
for (; pRuntimeTrigger < pRuntimeTriggerStop; ++pRuntimeTrigger)
{
if (pRuntimeTrigger->trigger == pOldTrigger &&
pRuntimeTrigger->id == m_pAchievementInfo->public_.id)
{
pRuntimeTrigger->trigger = m_pAchievementInfo->trigger;
pRuntimeTrigger->serialized_size = 0;
memcpy(pRuntimeTrigger->md5, md5, sizeof(md5));
break;
}
}
UpdateRuntimeLeaderboard(pGame, m_pAchievementInfo, &trigger->trigger);

m_pTriggerBuffer = std::move(trigger_buffer);

SyncStateToRuntime();
}
}
else
{
// parse error - disable achievement
m_pAchievementInfo->public_.state = RC_CLIENT_ACHIEVEMENT_STATE_DISABLED;
UpdateRuntimeLeaderboard(pGame, m_pAchievementInfo, nullptr);

// release allocated memory
m_pTriggerBuffer.reset();
}

rc_mutex_unlock(&pClient->state.mutex);

Expand Down
51 changes: 30 additions & 21 deletions src/data/models/LeaderboardModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@

#include "context\IRcClient.hh"

#include "data\context\GameContext.hh"

#include "data\models\TriggerValidation.hh"

#include "services\AchievementRuntime.hh"
#include "services\ServiceLocator.hh"

#include <rcheevos\src\rc_client_internal.h>
Expand Down Expand Up @@ -331,6 +328,27 @@ void LeaderboardModel::SyncDefinitionToRuntime()
ParseDefinition();
}

static void UpdateRuntimeLeaderboard(rc_client_game_info_t* pGame, rc_client_leaderboard_info_t* pLeaderboardInfo, rc_lboard_t* lboard) noexcept
{
const auto* pOldLboard = pLeaderboardInfo->lboard;
pLeaderboardInfo->lboard = lboard;

// update the runtime memory reference too
auto* pRuntimeLboard = pGame->runtime.lboards;
const auto* pRuntimeLboardStop = pRuntimeLboard + pGame->runtime.lboard_count;
for (; pRuntimeLboard < pRuntimeLboardStop; ++pRuntimeLboard)
{
if (pRuntimeLboard->lboard == pOldLboard &&
pRuntimeLboard->id == pLeaderboardInfo->public_.id)
{
pRuntimeLboard->lboard = lboard;
pRuntimeLboard->serialized_size = 0;
memcpy(pRuntimeLboard->md5, pLeaderboardInfo->md5, sizeof(pLeaderboardInfo->md5));
break;
}
}
}

void LeaderboardModel::ParseDefinition() const
{
const auto& sMemAddr = GetDefinition();
Expand Down Expand Up @@ -388,23 +406,7 @@ void LeaderboardModel::ParseDefinition() const
rc_parse_lboard_internal(&lboard->lboard, sMemAddr.c_str(), &preparse.parse);
lboard->lboard.has_memrefs = 1;

const auto* pOldLboard = m_pLeaderboardInfo->lboard;
m_pLeaderboardInfo->lboard = &lboard->lboard;

// update the runtime memory reference too
auto* pRuntimeLboard = pGame->runtime.lboards;
const auto* pRuntimeLboardStop = pRuntimeLboard + pGame->runtime.lboard_count;
for (; pRuntimeLboard < pRuntimeLboardStop; ++pRuntimeLboard)
{
if (pRuntimeLboard->lboard == pOldLboard &&
pRuntimeLboard->id == m_pLeaderboardInfo->public_.id)
{
pRuntimeLboard->lboard = m_pLeaderboardInfo->lboard;
pRuntimeLboard->serialized_size = 0;
memcpy(pRuntimeLboard->md5, md5, sizeof(md5));
break;
}
}
UpdateRuntimeLeaderboard(pGame, m_pLeaderboardInfo, &lboard->lboard);

m_pLeaderboardBuffer = std::move(lboard_buffer);

Expand All @@ -417,8 +419,15 @@ void LeaderboardModel::ParseDefinition() const
}
else
{
// parse error - discard old tracker
// parse error - disable leaderboard
m_pLeaderboardInfo->public_.state = RC_CLIENT_LEADERBOARD_STATE_DISABLED;
UpdateRuntimeLeaderboard(pGame, m_pLeaderboardInfo, nullptr);

// discard old tracker
ReleaseLeaderboardTracker(m_pLeaderboardInfo);

// release allocated memory
m_pLeaderboardBuffer.reset();
}

rc_mutex_unlock(&pClient->state.mutex);
Expand Down
4 changes: 1 addition & 3 deletions src/ui/viewmodels/AssetEditorViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -683,9 +683,7 @@ void AssetEditorViewModel::HandleStateChanged(ra::data::models::AssetState nOldS

static void AppendTrigger(std::string& sDefinition, const std::string& sTrigger)
{
if (sTrigger.empty())
sDefinition += "0=1";
else
if (!sTrigger.empty())
sDefinition += sTrigger;
}

Expand Down
51 changes: 36 additions & 15 deletions tests/ui/viewmodels/AssetEditorViewModel_Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,22 +768,14 @@ TEST_CLASS(AssetEditorViewModel_Tests)
Assert::IsTrue(editor.IsLeaderboard());
Assert::IsTrue(editor.IsTrigger());
Assert::IsTrue(editor.HasMeasured());
Assert::IsFalse(editor.HasAssetValidationError());
Assert::IsTrue(editor.HasAssetValidationWarning());
Assert::AreEqual(std::wstring(L"No Start condition"), editor.GetAssetValidationWarning());
Assert::IsTrue(editor.HasAssetValidationError());
Assert::AreEqual(std::wstring(L"Missing start condition"), editor.GetAssetValidationError());
Assert::AreEqual(std::wstring(L"Groups:"), editor.GetGroupsHeaderLabel());

ra::ui::viewmodels::mocks::MockWindowManager mockWindowManager; // to copy address from memory viewer
editor.Trigger().NewCondition();
Assert::IsFalse(editor.HasAssetValidationError());
Assert::IsTrue(editor.HasAssetValidationWarning());
Assert::AreEqual(std::wstring(L"No Start condition"), editor.GetAssetValidationWarning());

// warning is only updated when record is saved to prevent spam when constructing a record
leaderboard.Validate();
Assert::IsFalse(editor.HasAssetValidationError());
Assert::IsTrue(editor.HasAssetValidationWarning());
Assert::AreEqual(std::wstring(L"No Cancel condition"), editor.GetAssetValidationWarning());
Assert::IsTrue(editor.HasAssetValidationError());
Assert::AreEqual(std::wstring(L"Missing cancel condition"), editor.GetAssetValidationError());
}

TEST_METHOD(TestActivateNewLeaderboard)
Expand All @@ -798,9 +790,8 @@ TEST_CLASS(AssetEditorViewModel_Tests)

editor.LoadAsset(leaderboard);

Assert::IsFalse(editor.HasAssetValidationError());
Assert::IsTrue(editor.HasAssetValidationWarning());
Assert::AreEqual(std::wstring(L"No Start condition"), editor.GetAssetValidationWarning());
Assert::IsTrue(editor.HasAssetValidationError());
Assert::AreEqual(std::wstring(L"Missing start condition"), editor.GetAssetValidationError());

// attempt to start incomplete leaderboard should fail
bool bDialogSeen = false;
Expand Down Expand Up @@ -2462,6 +2453,36 @@ TEST_CLASS(AssetEditorViewModel_Tests)
Assert::AreEqual(2, editor.Trigger().GetSelectedGroupIndex());
Assert::AreEqual(nDefaultColor, editor.Trigger().Conditions().GetItemAt(0)->GetRowColor().ARGB);
}

TEST_METHOD(TestClearOutLeaderboardSubmit)
{
AssetEditorViewModelHarness editor;
editor.mockDesktop.ExpectWindow<ra::ui::viewmodels::MessageBoxViewModel>(
[](ra::ui::viewmodels::MessageBoxViewModel&)
{
return DialogResult::Yes;
});

LeaderboardModel leaderboard;
leaderboard.SetDefinition("STA:0x1234=1::CAN:0x1234=2::SUB:0x1234=3::VAL=0x1235");
leaderboard.SetValueFormat(ra::data::ValueFormat::Score);
leaderboard.CreateServerCheckpoint();
leaderboard.CreateLocalCheckpoint();

editor.LoadAsset(&leaderboard);
editor.SetSelectedLeaderboardPart(AssetEditorViewModel::LeaderboardPart::Submit);

Assert::AreEqual({ 1 }, editor.Trigger().Conditions().Count());
editor.Trigger().SelectRange(0, 0, true);
editor.Trigger().RemoveSelectedConditions();
Assert::AreEqual({ 0 }, editor.Trigger().Conditions().Count());

editor.SetSelectedLeaderboardPart(AssetEditorViewModel::LeaderboardPart::Cancel);
Assert::AreEqual({ 1 }, editor.Trigger().Conditions().Count());

editor.SetSelectedLeaderboardPart(AssetEditorViewModel::LeaderboardPart::Submit);
Assert::AreEqual({ 0 }, editor.Trigger().Conditions().Count());
}
};

} // namespace tests
Expand Down
Loading