mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
feat: optional property rent
Off unless property_rent_enabled is on. Each property world's rent comes from its PropertyTemplate row (minimumPrice every rentDuration x durationType; Block Yard is free), unless the new Property Rent dashboard page sets another price or period (property_rent_manage). Rent is taken from the owner's coins shortly after their character loads, with a mail receipt; unpaid rent is mailed and, after property_rent_grace_days, makes the property private until it is paid, like live. The property management component refuses public or best friends privacy while rent is overdue and a property world that loads overdue makes itself private. Property game messages are unchanged. Fixes #943 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,7 @@
|
||||
#include "IFeaturedProperties.h"
|
||||
#include "IMessageCaptures.h"
|
||||
#include "IContraband.h"
|
||||
#include "IPropertyRent.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
# define DLU_SQL_TRY_CATCH_RETHROW(x) do { try { x; } catch (std::exception& ex) { LOG("SQL Error: %s", ex.what()); throw; } } while(0)
|
||||
@@ -61,7 +62,7 @@ class GameDatabase :
|
||||
public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
|
||||
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo,
|
||||
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList,
|
||||
public IBehaviors, public IUgcModularBuild, public IAuditLog, public IDashboardStats, public IAccountEmails, public IDashboardMaintenance, public IEconomyLedger, public IDashboardAdmin, public IServerConfig, public IScheduledTasks, public ICharacterSnapshots, public IAccountNotes, public IServerHealth, public IRelatedData, public IChatLog, public IAccountStrikes, public ISlashCommands, public IModeration, public IServerOperations, public IPlayerPositions, public IAiSuggestions, public ILiveOps, public IFeaturedProperties, public IMessageCaptures, public IContraband {
|
||||
public IBehaviors, public IUgcModularBuild, public IAuditLog, public IDashboardStats, public IAccountEmails, public IDashboardMaintenance, public IEconomyLedger, public IDashboardAdmin, public IServerConfig, public IScheduledTasks, public ICharacterSnapshots, public IAccountNotes, public IServerHealth, public IRelatedData, public IChatLog, public IAccountStrikes, public ISlashCommands, public IModeration, public IServerOperations, public IPlayerPositions, public IAiSuggestions, public ILiveOps, public IFeaturedProperties, public IMessageCaptures, public IContraband, public IPropertyRent {
|
||||
public:
|
||||
virtual ~GameDatabase() = default;
|
||||
// TODO: These should be made private.
|
||||
|
||||
45
dDatabase/GameDatabase/ITables/IPropertyRent.h
Normal file
45
dDatabase/GameDatabase/ITables/IPropertyRent.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef __IPROPERTYRENT__H__
|
||||
#define __IPROPERTYRENT__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
|
||||
/**
|
||||
* Property rent (see dCommon/PropertyRentRules.h): the dashboard's per-world prices and what an owner's properties owe.
|
||||
*/
|
||||
class IPropertyRent {
|
||||
public:
|
||||
struct RentRate {
|
||||
uint32_t mapId{};
|
||||
int64_t price{}; // 0: free
|
||||
int32_t periodDays{}; // 0: the PropertyTemplate's period
|
||||
std::string updatedBy;
|
||||
int64_t updatedAt{};
|
||||
};
|
||||
|
||||
struct OwnedProperty {
|
||||
LWOOBJID id{};
|
||||
uint32_t zoneId{};
|
||||
int32_t privacyOption{};
|
||||
int64_t rentDue{}; // 0: never charged
|
||||
std::string name;
|
||||
};
|
||||
|
||||
virtual std::vector<RentRate> GetPropertyRentRates() = 0;
|
||||
// Insert or replace the row of rate.mapId
|
||||
virtual void SetPropertyRentRate(const RentRate& rate) = 0;
|
||||
virtual bool DeletePropertyRentRate(uint32_t mapId) = 0;
|
||||
|
||||
// The properties a character owns
|
||||
virtual std::vector<OwnedProperty> GetPropertiesOfOwner(LWOOBJID ownerId) = 0;
|
||||
// The property's rent_due (0 when there is no such property)
|
||||
virtual int64_t GetPropertyRentDue(LWOOBJID propertyId) = 0;
|
||||
// Record a payment (or where the grace period starts): rent_amount and rent_due
|
||||
virtual void SetPropertyRent(LWOOBJID propertyId, int64_t amount, int64_t due) = 0;
|
||||
virtual void SetPropertyPrivacy(LWOOBJID propertyId, int32_t privacyOption) = 0;
|
||||
};
|
||||
|
||||
#endif //!__IPROPERTYRENT__H__
|
||||
@@ -215,6 +215,15 @@ public:
|
||||
std::vector<ZoneLimit> GetZoneLimits() override;
|
||||
void SetZoneLimit(const ZoneLimit& limit) override;
|
||||
void DeleteZoneLimit(uint32_t zoneId) override;
|
||||
// IPropertyRent
|
||||
std::vector<RentRate> GetPropertyRentRates() override;
|
||||
void SetPropertyRentRate(const RentRate& rate) override;
|
||||
bool DeletePropertyRentRate(uint32_t mapId) override;
|
||||
std::vector<OwnedProperty> GetPropertiesOfOwner(LWOOBJID ownerId) override;
|
||||
int64_t GetPropertyRentDue(LWOOBJID propertyId) override;
|
||||
void SetPropertyRent(LWOOBJID propertyId, int64_t amount, int64_t due) override;
|
||||
void SetPropertyPrivacy(LWOOBJID propertyId, int32_t privacyOption) override;
|
||||
|
||||
// IContraband
|
||||
std::vector<ContrabandItem> GetContrabandItems() override;
|
||||
void SetContrabandItem(const ContrabandItem& item) override;
|
||||
|
||||
@@ -19,6 +19,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
|
||||
"LiveOps.cpp"
|
||||
"FeaturedProperties.cpp"
|
||||
"Contraband.cpp"
|
||||
"PropertyRent.cpp"
|
||||
"MessageCaptures.cpp"
|
||||
"ChatLog.cpp"
|
||||
"RelatedData.cpp"
|
||||
|
||||
43
dDatabase/GameDatabase/MySQL/Tables/PropertyRent.cpp
Normal file
43
dDatabase/GameDatabase/MySQL/Tables/PropertyRent.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::vector<IPropertyRent::RentRate> MySQLDatabase::GetPropertyRentRates() {
|
||||
std::vector<RentRate> rates;
|
||||
auto result = ExecuteSelect("SELECT * FROM property_rent_rates ORDER BY map_id;");
|
||||
while (result->next()) {
|
||||
rates.push_back({ result->getUInt("map_id"), result->getInt64("price"), result->getInt("period_days"), result->getString("updated_by").c_str(), result->getInt64("updated_at") });
|
||||
}
|
||||
return rates;
|
||||
}
|
||||
|
||||
void MySQLDatabase::SetPropertyRentRate(const RentRate& rate) {
|
||||
ExecuteInsert(
|
||||
"INSERT INTO property_rent_rates (map_id, price, period_days, updated_by, updated_at) VALUES (?, ?, ?, ?, ?) "
|
||||
"ON DUPLICATE KEY UPDATE price = VALUES(price), period_days = VALUES(period_days), updated_by = VALUES(updated_by), updated_at = VALUES(updated_at);",
|
||||
rate.mapId, rate.price, rate.periodDays, rate.updatedBy, rate.updatedAt);
|
||||
}
|
||||
|
||||
bool MySQLDatabase::DeletePropertyRentRate(uint32_t mapId) {
|
||||
return ExecuteUpdate("DELETE FROM property_rent_rates WHERE map_id = ?;", mapId) > 0;
|
||||
}
|
||||
|
||||
std::vector<IPropertyRent::OwnedProperty> MySQLDatabase::GetPropertiesOfOwner(LWOOBJID ownerId) {
|
||||
std::vector<OwnedProperty> properties;
|
||||
auto result = ExecuteSelect("SELECT id, zone_id, privacy_option, rent_due, name FROM properties WHERE owner_id = ? ORDER BY id;", ownerId);
|
||||
while (result->next()) {
|
||||
properties.push_back({ result->getInt64("id"), result->getUInt("zone_id"), result->getInt("privacy_option"), result->getInt64("rent_due"), result->getString("name").c_str() });
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
int64_t MySQLDatabase::GetPropertyRentDue(LWOOBJID propertyId) {
|
||||
auto result = ExecuteSelect("SELECT rent_due FROM properties WHERE id = ? LIMIT 1;", propertyId);
|
||||
return result->next() ? result->getInt64("rent_due") : 0;
|
||||
}
|
||||
|
||||
void MySQLDatabase::SetPropertyRent(LWOOBJID propertyId, int64_t amount, int64_t due) {
|
||||
ExecuteUpdate("UPDATE properties SET rent_amount = ?, rent_due = ? WHERE id = ?;", amount, due, propertyId);
|
||||
}
|
||||
|
||||
void MySQLDatabase::SetPropertyPrivacy(LWOOBJID propertyId, int32_t privacyOption) {
|
||||
ExecuteUpdate("UPDATE properties SET privacy_option = ? WHERE id = ?;", privacyOption, propertyId);
|
||||
}
|
||||
@@ -199,6 +199,15 @@ public:
|
||||
std::vector<ZoneLimit> GetZoneLimits() override;
|
||||
void SetZoneLimit(const ZoneLimit& limit) override;
|
||||
void DeleteZoneLimit(uint32_t zoneId) override;
|
||||
// IPropertyRent
|
||||
std::vector<RentRate> GetPropertyRentRates() override;
|
||||
void SetPropertyRentRate(const RentRate& rate) override;
|
||||
bool DeletePropertyRentRate(uint32_t mapId) override;
|
||||
std::vector<OwnedProperty> GetPropertiesOfOwner(LWOOBJID ownerId) override;
|
||||
int64_t GetPropertyRentDue(LWOOBJID propertyId) override;
|
||||
void SetPropertyRent(LWOOBJID propertyId, int64_t amount, int64_t due) override;
|
||||
void SetPropertyPrivacy(LWOOBJID propertyId, int32_t privacyOption) override;
|
||||
|
||||
// IContraband
|
||||
std::vector<ContrabandItem> GetContrabandItems() override;
|
||||
void SetContrabandItem(const ContrabandItem& item) override;
|
||||
|
||||
@@ -19,6 +19,7 @@ set(DDATABASES_DATABASES_SQLITE_TABLES_SOURCES
|
||||
"LiveOps.cpp"
|
||||
"FeaturedProperties.cpp"
|
||||
"Contraband.cpp"
|
||||
"PropertyRent.cpp"
|
||||
"MessageCaptures.cpp"
|
||||
"ChatLog.cpp"
|
||||
"RelatedData.cpp"
|
||||
|
||||
45
dDatabase/GameDatabase/SQLite/Tables/PropertyRent.cpp
Normal file
45
dDatabase/GameDatabase/SQLite/Tables/PropertyRent.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "SQLiteDatabase.h"
|
||||
|
||||
std::vector<IPropertyRent::RentRate> SQLiteDatabase::GetPropertyRentRates() {
|
||||
std::vector<RentRate> rates;
|
||||
auto [_, result] = ExecuteSelect("SELECT * FROM property_rent_rates ORDER BY map_id;");
|
||||
for (; !result.eof(); result.nextRow()) {
|
||||
rates.push_back({ static_cast<uint32_t>(result.getIntField("map_id")), result.getInt64Field("price"), result.getIntField("period_days"),
|
||||
result.getStringField("updated_by"), result.getInt64Field("updated_at") });
|
||||
}
|
||||
return rates;
|
||||
}
|
||||
|
||||
void SQLiteDatabase::SetPropertyRentRate(const RentRate& rate) {
|
||||
ExecuteInsert(
|
||||
"INSERT INTO property_rent_rates (map_id, price, period_days, updated_by, updated_at) VALUES (?, ?, ?, ?, ?) "
|
||||
"ON CONFLICT(map_id) DO UPDATE SET price = excluded.price, period_days = excluded.period_days, updated_by = excluded.updated_by, updated_at = excluded.updated_at;",
|
||||
rate.mapId, rate.price, rate.periodDays, rate.updatedBy, rate.updatedAt);
|
||||
}
|
||||
|
||||
bool SQLiteDatabase::DeletePropertyRentRate(uint32_t mapId) {
|
||||
return ExecuteUpdate("DELETE FROM property_rent_rates WHERE map_id = ?;", mapId) > 0;
|
||||
}
|
||||
|
||||
std::vector<IPropertyRent::OwnedProperty> SQLiteDatabase::GetPropertiesOfOwner(LWOOBJID ownerId) {
|
||||
std::vector<OwnedProperty> properties;
|
||||
auto [_, result] = ExecuteSelect("SELECT id, zone_id, privacy_option, rent_due, name FROM properties WHERE owner_id = ? ORDER BY id;", ownerId);
|
||||
for (; !result.eof(); result.nextRow()) {
|
||||
properties.push_back({ result.getInt64Field("id"), static_cast<uint32_t>(result.getIntField("zone_id")), result.getIntField("privacy_option"),
|
||||
result.getInt64Field("rent_due"), result.getStringField("name") });
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
int64_t SQLiteDatabase::GetPropertyRentDue(LWOOBJID propertyId) {
|
||||
auto [_, result] = ExecuteSelect("SELECT rent_due FROM properties WHERE id = ? LIMIT 1;", propertyId);
|
||||
return result.eof() ? 0 : result.getInt64Field("rent_due");
|
||||
}
|
||||
|
||||
void SQLiteDatabase::SetPropertyRent(LWOOBJID propertyId, int64_t amount, int64_t due) {
|
||||
ExecuteUpdate("UPDATE properties SET rent_amount = ?, rent_due = ? WHERE id = ?;", amount, due, propertyId);
|
||||
}
|
||||
|
||||
void SQLiteDatabase::SetPropertyPrivacy(LWOOBJID propertyId, int32_t privacyOption) {
|
||||
ExecuteUpdate("UPDATE properties SET privacy_option = ? WHERE id = ?;", privacyOption, propertyId);
|
||||
}
|
||||
@@ -245,6 +245,15 @@ class TestSQLDatabase : public GameDatabase {
|
||||
std::vector<ZoneLimit> GetZoneLimits() override { return {}; }
|
||||
void SetZoneLimit(const ZoneLimit& limit) override {}
|
||||
void DeleteZoneLimit(uint32_t zoneId) override {}
|
||||
// IPropertyRent
|
||||
std::vector<RentRate> GetPropertyRentRates() override { return {}; }
|
||||
void SetPropertyRentRate(const RentRate& rate) override {}
|
||||
bool DeletePropertyRentRate(uint32_t mapId) override { return false; }
|
||||
std::vector<OwnedProperty> GetPropertiesOfOwner(LWOOBJID ownerId) override { return {}; }
|
||||
int64_t GetPropertyRentDue(LWOOBJID propertyId) override { return 0; }
|
||||
void SetPropertyRent(LWOOBJID propertyId, int64_t amount, int64_t due) override {}
|
||||
void SetPropertyPrivacy(LWOOBJID propertyId, int32_t privacyOption) override {}
|
||||
|
||||
// IContraband
|
||||
std::vector<ContrabandItem> GetContrabandItems() override { return {}; }
|
||||
void SetContrabandItem(const ContrabandItem& item) override {}
|
||||
|
||||
Reference in New Issue
Block a user