Skip to content

Commit

Permalink
feat: Reward codes (#1308)
Browse files Browse the repository at this point in the history
* feat: reward codes
this is for giving rewards across characters as the did in live.
Tested that the default config works
Tested that all claim codes work
Tested that saving and loading claim codes work
Tested that mail sends correctly

* newlines

* include array

* delete cascade

* newline

* address feedback
  • Loading branch information
aronwk-aaron authored Nov 22, 2023
1 parent 9c5388c commit df83f0d
Show file tree
Hide file tree
Showing 20 changed files with 216 additions and 8 deletions.
2 changes: 2 additions & 0 deletions dAuthServer/AuthServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ int main(int argc, char** argv) {
uint32_t framesSinceMasterDisconnect = 0;
uint32_t framesSinceLastSQLPing = 0;

AuthPackets::LoadClaimCodes();

while (!Game::shouldShutdown) {
//Check if we're still connected to master:
if (!Game::server->GetIsConnectedToMaster()) {
Expand Down
2 changes: 2 additions & 0 deletions dDatabase/CDClientDatabase/CDClientManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "CDPropertyTemplateTable.h"
#include "CDFeatureGatingTable.h"
#include "CDRailActivatorComponent.h"
#include "CDRewardCodesTable.h"

// Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory.
// A vanilla CDClient takes about 46MB of memory + the regular world data.
Expand Down Expand Up @@ -82,6 +83,7 @@ CDClientManager::CDClientManager() {
CDRailActivatorComponentTable::Instance().LoadValuesFromDatabase();
CDRarityTableTable::Instance().LoadValuesFromDatabase();
CDRebuildComponentTable::Instance().LoadValuesFromDatabase();
CDRewardCodesTable::Instance().LoadValuesFromDatabase();
CDRewardsTable::Instance().LoadValuesFromDatabase();
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
Expand Down
47 changes: 47 additions & 0 deletions dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "CDRewardCodesTable.h"

void CDRewardCodesTable::LoadValuesFromDatabase() {

// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RewardCodes");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);

tableSize.nextRow();
}

tableSize.finalize();

// Reserve the size
this->entries.reserve(size);

// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RewardCodes");
while (!tableData.eof()) {
CDRewardCode entry;
entry.id = tableData.getIntField("id", -1);
entry.code = tableData.getStringField("code", "");
entry.attachmentLOT = tableData.getIntField("attachmentLOT", -1);
UNUSED_COLUMN(entry.locStatus = tableData.getIntField("locStatus", -1));
UNUSED_COLUMN(entry.gate_version = tableData.getStringField("gate_version", ""));

this->entries.push_back(entry);
tableData.nextRow();
}
}

LOT CDRewardCodesTable::GetAttachmentLOT(uint32_t rewardCodeId) const {
for (auto const &entry : this->entries){
if (rewardCodeId == entry.id) return entry.attachmentLOT;
}
return LOT_NULL;
}

uint32_t CDRewardCodesTable::GetCodeID(std::string code) const {
for (auto const &entry : this->entries){
if (code == entry.code) return entry.id;
}
return -1;
}

25 changes: 25 additions & 0 deletions dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

// Custom Classes
#include "CDTable.h"


struct CDRewardCode {
uint32_t id;
std::string code;
LOT attachmentLOT;
UNUSED(uint32_t locStatus);
UNUSED(std::string gate_version);
};


class CDRewardCodesTable : public CDTable<CDRewardCodesTable> {
private:
std::vector<CDRewardCode> entries;

public:
void LoadValuesFromDatabase();
const std::vector<CDRewardCode>& GetEntries() const;
LOT GetAttachmentLOT(uint32_t rewardCodeId) const;
uint32_t GetCodeID(std::string code) const;
};
1 change: 1 addition & 0 deletions dDatabase/CDClientDatabase/CDClientTables/CDTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <string>
#include <vector>
#include <map>
#include <cstdint>

// CPPLinq
#ifdef _WIN32
Expand Down
1 change: 1 addition & 0 deletions dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp"
"CDRailActivatorComponent.cpp"
"CDRarityTableTable.cpp"
"CDRebuildComponentTable.cpp"
"CDRewardCodesTable.cpp"
"CDRewardsTable.cpp"
"CDScriptComponentTable.cpp"
"CDSkillBehaviorTable.cpp"
Expand Down
3 changes: 2 additions & 1 deletion dDatabase/GameDatabase/GameDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "ICharInfo.h"
#include "IAccounts.h"
#include "IActivityLog.h"
#include "IAccountsRewardCodes.h"

namespace sql {
class Statement;
Expand All @@ -38,7 +39,7 @@ class GameDatabase :
public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports,
public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo,
public IAccounts, public IActivityLog {
public IAccounts, public IActivityLog, public IAccountsRewardCodes {
public:
virtual ~GameDatabase() = default;
// TODO: These should be made private.
Expand Down
13 changes: 13 additions & 0 deletions dDatabase/GameDatabase/ITables/IAccountsRewardCodes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef __IACCOUNTSREWARDCODES__H__
#define __IACCOUNTSREWARDCODES__H__

#include <cstdint>
#include <vector>

class IAccountsRewardCodes {
public:
virtual void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) = 0;
virtual std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) = 0;
};

#endif //!__IACCOUNTSREWARDCODES__H__
2 changes: 2 additions & 0 deletions dDatabase/GameDatabase/MySQL/MySQLDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ class MySQLDatabase : public GameDatabase {
std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override;
std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override;
std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override;
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
private:

// Generic query functions that can be used for any query.
Expand Down
17 changes: 17 additions & 0 deletions dDatabase/GameDatabase/MySQL/Tables/AccountsRewardCodes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "MySQLDatabase.h"

void MySQLDatabase::InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) {
ExecuteInsert("INSERT IGNORE INTO accounts_rewardcodes (account_id, rewardcode) VALUES (?, ?);", account_id, reward_code);
}

std::vector<uint32_t> MySQLDatabase::GetRewardCodesByAccountID(const uint32_t account_id) {
auto result = ExecuteSelect("SELECT rewardcode FROM accounts_rewardcodes WHERE account_id = ?;", account_id);

std::vector<uint32_t> toReturn;
toReturn.reserve(result->rowsCount());
while (result->next()) {
toReturn.push_back(result->getUInt("rewardcode"));
}

return toReturn;
}
1 change: 1 addition & 0 deletions dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
"Accounts.cpp"
"AccountsRewardCodes.cpp"
"ActivityLog.cpp"
"BugReports.cpp"
"CharInfo.cpp"
Expand Down
56 changes: 52 additions & 4 deletions dGame/dComponents/CharacterComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
#include "Amf3.h"
#include "eGameMasterLevel.h"
#include "eGameActivity.h"
#include "User.h"
#include "Database.h"
#include "CDRewardCodesTable.h"
#include "Mail.h"
#include <ctime>

CharacterComponent::CharacterComponent(Entity* parent, Character* character) : Component(parent) {
Expand Down Expand Up @@ -74,10 +78,14 @@ CharacterComponent::~CharacterComponent() {
void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {

if (bIsInitialUpdate) {
outBitStream->Write0();
outBitStream->Write0();
outBitStream->Write0();
outBitStream->Write0();
outBitStream->Write(m_ClaimCodes[0] != 0);
if (m_ClaimCodes[0] != 0) outBitStream->Write(m_ClaimCodes[0]);
outBitStream->Write(m_ClaimCodes[1] != 0);
if (m_ClaimCodes[1] != 0) outBitStream->Write(m_ClaimCodes[1]);
outBitStream->Write(m_ClaimCodes[2] != 0);
if (m_ClaimCodes[2] != 0) outBitStream->Write(m_ClaimCodes[2]);
outBitStream->Write(m_ClaimCodes[3] != 0);
if (m_ClaimCodes[3] != 0) outBitStream->Write(m_ClaimCodes[3]);

outBitStream->Write(m_Character->GetHairColor());
outBitStream->Write(m_Character->GetHairStyle());
Expand Down Expand Up @@ -186,6 +194,13 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
SetReputation(0);
}

character->QueryUnsigned64Attribute("co", &m_ClaimCodes[0]);
character->QueryUnsigned64Attribute("co1", &m_ClaimCodes[1]);
character->QueryUnsigned64Attribute("co2", &m_ClaimCodes[2]);
character->QueryUnsigned64Attribute("co3", &m_ClaimCodes[3]);

AwardClaimCodes();

character->QueryInt64Attribute("ls", &m_Uscore);

// Load the statistics
Expand Down Expand Up @@ -308,6 +323,11 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
return;
}

if (m_ClaimCodes[0] != 0) character->SetAttribute("co", m_ClaimCodes[0]);
if (m_ClaimCodes[1] != 0) character->SetAttribute("co1", m_ClaimCodes[1]);
if (m_ClaimCodes[2] != 0) character->SetAttribute("co2", m_ClaimCodes[2]);
if (m_ClaimCodes[3] != 0) character->SetAttribute("co3", m_ClaimCodes[3]);

character->SetAttribute("ls", m_Uscore);
// Custom attribute to keep track of reputation.
character->SetAttribute("rpt", GetReputation());
Expand Down Expand Up @@ -738,3 +758,31 @@ void CharacterComponent::UpdateClientMinimap(bool showFaction, std::string ventu
arrayToSend.Insert(ventureVisionType, showFaction);
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent ? m_Parent->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS, "SetFactionVisibility", arrayToSend);
}

void CharacterComponent::AwardClaimCodes() {
if (!m_Parent) return;
auto* user = m_Parent->GetParentUser();
if (!user) return;

auto rewardCodes = Database::Get()->GetRewardCodesByAccountID(user->GetAccountID());
if (rewardCodes.empty()) return;

auto* cdrewardCodes = CDClientManager::Instance().GetTable<CDRewardCodesTable>();
for (auto const rewardCode: rewardCodes){
LOG_DEBUG("Processing RewardCode %i", rewardCode);
const uint32_t rewardCodeIndex = rewardCode >> 6;
const uint32_t bitIndex = rewardCode % 64;
if (GeneralUtils::CheckBit(m_ClaimCodes[rewardCodeIndex], bitIndex)) continue;
m_ClaimCodes[rewardCodeIndex] = GeneralUtils::SetBit(m_ClaimCodes[rewardCodeIndex], bitIndex);

// Don't send it on this one since it's default and the mail doesn't make sense
if (rewardCode == 30) continue;

auto attachmentLOT = cdrewardCodes->GetAttachmentLOT(rewardCode);
std::ostringstream subject;
subject << "%[RewardCodes_" << rewardCode << "_subjectText]";
std::ostringstream body;
body << "%[RewardCodes_" << rewardCode << "_bodyText]";
Mail::SendMail(LWOOBJID_EMPTY, "%[MAIL_SYSTEM_NOTIFICATION]", m_Parent, subject.str(), body.str(), attachmentLOT, 1);
}
}
5 changes: 5 additions & 0 deletions dGame/dComponents/CharacterComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "CDMissionsTable.h"
#include "tinyxml2.h"
#include "eReplicaComponentType.h"
#include <array>

enum class eGameActivity : uint32_t;

Expand Down Expand Up @@ -566,6 +567,10 @@ class CharacterComponent : public Component {
LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY;

LWOOBJID m_CurrentInteracting = LWOOBJID_EMPTY;

std::array<uint64_t, 4> m_ClaimCodes{};

void AwardClaimCodes();
};

#endif // CHARACTERCOMPONENT_H
8 changes: 8 additions & 0 deletions dGame/dUtilities/SlashCommandHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
#include "eChatInternalMessageType.h"
#include "eMasterMessageType.h"

#include "CDRewardCodesTable.h"
#include "CDObjectsTable.h"
#include "CDZoneTableTable.h"
#include "ePlayerFlag.h"
Expand Down Expand Up @@ -1911,6 +1912,13 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
}
}

if (chatCommand == "setrewardcode" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() == 1) {
auto* cdrewardCodes = CDClientManager::Instance().GetTable<CDRewardCodesTable>();

auto id = cdrewardCodes->GetCodeID(args[0]);
if (id != -1) Database::Get()->InsertRewardCode(user->GetAccountID(), id);
}

if (chatCommand == "inspect" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 1) {
Entity* closest = nullptr;

Expand Down
20 changes: 20 additions & 0 deletions dNet/AuthPackets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@
#include "eMasterMessageType.h"
#include "eGameMasterLevel.h"

namespace {
std::vector<uint32_t> claimCodes;
}

void AuthPackets::LoadClaimCodes() {
if(!claimCodes.empty()) return;
auto rcstring = Game::config->GetValue("rewardcodes");
auto codestrings = GeneralUtils::SplitString(rcstring, ',');
for(auto const &codestring: codestrings){
uint32_t code = -1;
if(GeneralUtils::TryParse(codestring, code) && code != -1){
claimCodes.push_back(code);
}
}
}

void AuthPackets::HandleHandshake(dServer* server, Packet* packet) {
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
Expand Down Expand Up @@ -129,6 +145,10 @@ void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) {
AuthPackets::SendLoginResponse(server, system, eLoginResponse::SUCCESS, "", zoneIP, zonePort, username);
});
}

for(auto const code: claimCodes){
Database::Get()->InsertRewardCode(accountInfo->id, code);
}
}

void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username) {
Expand Down
2 changes: 2 additions & 0 deletions dNet/AuthPackets.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ namespace AuthPackets {

void HandleLoginRequest(dServer* server, Packet* packet);
void SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username);
void LoadClaimCodes();

}

#endif // AUTHPACKETS_H
5 changes: 2 additions & 3 deletions dWorldServer/WorldServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1033,9 +1033,8 @@ void HandlePacket(Packet* packet) {
Game::entityManager->ConstructAllEntities(packet->systemAddress);

auto* characterComponent = player->GetComponent<CharacterComponent>();
if (characterComponent) {
player->GetComponent<CharacterComponent>()->RocketUnEquip(player);
}
if (!characterComponent) return;
characterComponent->RocketUnEquip(player);

// Do charxml fixes here
auto* levelComponent = player->GetComponent<LevelProgressionComponent>();
Expand Down
1 change: 1 addition & 0 deletions docs/Commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ These commands are primarily for development and testing. The usage of many of t
|setfaction|`/setfaction <faction id>`|Clears the users current factions and sets it|8|
|addfaction|`/addfaction <faction id>`|Add the faction to the users list of factions|8|
|getfactions|`/getfactions`|Shows the player's factions|8|
|setrewardcode|`/setrewardcode <code>`|Sets the rewardcode for the account you are logged into if it's a valid rewardcode, See cdclient table `RewardCodes`|8|
## Detailed `/inspect` Usage

`/inspect <component> (-m <waypoint> | -a <animation> | -s | -p | -f (faction) | -t)`
Expand Down
5 changes: 5 additions & 0 deletions migrations/dlu/14_reward_codes.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS accounts_rewardcodes (
account_id INT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
rewardcode INT NOT NULL,
PRIMARY KEY (account_id, rewardcode)
);
8 changes: 8 additions & 0 deletions resources/authconfig.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@ port=1001
# 0 or 1, should ignore playkeys
# If 1 everyone with an account will be able to login, regardless of if they have a key or not
dont_use_keys=0

# list of rewardcodes to set on the accounts by default
# ex: 30,1,0,3
# See RewardCodes in the CDclient for what codes exist
# Default 4,30
# 4 allows LEGOClub access
# 30 makes the client not consume bricks when in bbb mode
rewardcodes=4,30

0 comments on commit df83f0d

Please sign in to comment.