Compare commits

..

9 Commits

Author SHA1 Message Date
bf58cf85cc us static cast and enum 2024-06-07 15:07:37 -05:00
f6f3cdc3c0 fix compiling 2024-06-07 09:31:54 -05:00
66cf96af1a whoops
Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-06-06 21:34:29 -05:00
0e6cb8ab19 implict bool
Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-06-06 21:34:10 -05:00
42cf2b6377 remove unused code
Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-06-06 21:33:42 -05:00
3cfbc9d3df Document what needs to be done
May not do the recursive restriction cause they aren't used in the live game
2024-06-06 21:11:43 -05:00
5ba37ff7d6 fix compiling 2024-06-06 08:50:00 -05:00
8f00f1601c Merge branch 'main' into issue-960 2024-06-06 08:23:41 -05:00
5e3c869141 WIP 2024-06-05 02:39:36 -05:00
60 changed files with 427 additions and 1055 deletions

View File

@@ -8,13 +8,12 @@ on:
jobs:
build-and-test:
name: Build & Test (${{ matrix.os }} - ${{ matrix.cfg }})
name: Build & Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
continue-on-error: true
strategy:
matrix:
os: [ windows-2022, ubuntu-22.04, macos-13 ]
cfg: [ debug, release ]
steps:
- uses: actions/checkout@v3
@@ -34,9 +33,9 @@ jobs:
- name: cmake
uses: lukka/run-cmake@v10
with:
configurePreset: "ci-${{matrix.os}}-${{matrix.cfg}}"
buildPreset: "ci-${{matrix.os}}-${{matrix.cfg}}"
testPreset: "ci-${{matrix.os}}-${{matrix.cfg}}"
configurePreset: "ci-${{matrix.os}}"
buildPreset: "ci-${{matrix.os}}"
testPreset: "ci-${{matrix.os}}"
- name: artifacts
uses: actions/upload-artifact@v3
with:

View File

@@ -1,242 +1,128 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 14,
"patch": 0
},
"configurePresets": [
{
"name": "default",
"displayName": "Default configure step",
"description": "Use 'build' dir and Unix makefiles",
"binaryDir": "${sourceDir}/build",
"generator": "Unix Makefiles"
},
{
"name": "ci-debug",
"displayName": "CI configure step for Debug",
"inherits": "default",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "ci-release",
"displayName": "CI configure step for Release",
"inherits": "default",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
}
},
{
"name": "ci-ubuntu-22.04-release",
"displayName": "CI configure step for Ubuntu",
"description": "Same as default, Used in GitHub actions workflow",
"inherits": "ci-release"
},
{
"name": "ci-ubuntu-22.04-debug",
"displayName": "CI configure step for Ubuntu",
"description": "Same as default, Used in GitHub actions workflow",
"inherits": "ci-debug"
},
{
"name": "ci-macos-13-release",
"displayName": "CI configure step for MacOS",
"description": "Same as default, Used in GitHub actions workflow",
"inherits": "ci-release"
},
{
"name": "ci-macos-13-debug",
"displayName": "CI configure step for MacOS",
"description": "Same as default, Used in GitHub actions workflow",
"inherits": "ci-debug"
},
{
"name": "ci-windows-2022-release",
"displayName": "CI configure step for Windows",
"description": "Set architecture to 64-bit (b/c RakNet)",
"inherits": "ci-release",
"generator": "Visual Studio 17 2022",
"architecture": {
"value": "x64"
}
},
{
"name": "ci-windows-2022-debug",
"displayName": "CI configure step for Windows",
"description": "Set architecture to 64-bit (b/c RakNet)",
"inherits": "ci-debug",
"generator": "Visual Studio 17 2022",
"architecture": {
"value": "x64"
}
},
{
"name": "windows-default-release",
"inherits": "ci-windows-2022-release",
"displayName": "Windows only Configure Settings (Release)",
"description": "Sets build and install directories",
"generator": "Ninja",
"architecture": {
"value": "x64",
"strategy": "external"
}
},
{
"name": "windows-default-debug",
"inherits": "ci-windows-2022-debug",
"displayName": "Windows only Configure Settings (Debug)",
"description": "Sets build and install directories",
"generator": "Ninja",
"architecture": {
"value": "x64",
"strategy": "external"
}
}
],
"buildPresets": [
{
"name": "default",
"configurePreset": "default",
"displayName": "Default Build",
"description": "Default Build",
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 14,
"patch": 0
},
"configurePresets": [
{
"name": "default",
"displayName": "Default configure step",
"description": "Use 'build' dir and Unix makefiles",
"binaryDir": "${sourceDir}/build",
"generator": "Unix Makefiles"
},
{
"name": "ci-ubuntu-22.04",
"displayName": "CI configure step for Ubuntu",
"description": "Same as default, Used in GitHub actions workflow",
"inherits": "default"
},
{
"name": "ci-macos-13",
"displayName": "CI configure step for MacOS",
"description": "Same as default, Used in GitHub actions workflow",
"inherits": "default"
},
{
"name": "ci-windows-2022",
"displayName": "CI configure step for Windows",
"description": "Set architecture to 64-bit (b/c RakNet)",
"inherits": "default",
"generator": "Visual Studio 17 2022",
"architecture": {
"value": "x64"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
}
},
{
"name": "windows-default",
"inherits": "ci-windows-2022",
"displayName": "Windows only Configure Settings",
"description": "Sets build and install directories",
"generator": "Ninja",
"architecture": {
"value": "x64",
"strategy": "external"
}
}
],
"buildPresets": [
{
"name": "default",
"configurePreset": "default",
"displayName": "Default Build",
"description": "Default Build",
"jobs": 2
},
{
"name": "ci-windows-2022",
"configurePreset": "ci-windows-2022",
"displayName": "Windows CI Build",
"description": "This preset is used by the CI build on windows",
"configuration": "RelWithDebInfo",
"jobs": 2
},
{
"name": "ci-ubuntu-22.04",
"configurePreset": "ci-ubuntu-22.04",
"displayName": "Linux CI Build",
"description": "This preset is used by the CI build on linux",
"jobs": 2
},
{
"name": "ci-macos-13",
"configurePreset": "ci-macos-13",
"displayName": "MacOS CI Build",
"description": "This preset is used by the CI build on MacOS",
"jobs": 2
}
],
"testPresets": [
{
"name": "ci-ubuntu-22.04",
"configurePreset": "ci-ubuntu-22.04",
"displayName": "CI Tests on Linux",
"description": "Runs all tests on a linux configuration",
"execution": {
"jobs": 2
},
{
"name": "ci-windows-2022-debug",
"configurePreset": "ci-windows-2022-debug",
"displayName": "Windows CI Build",
"description": "This preset is used by the CI build on windows",
"configuration": "RelWithDebInfo",
"inherits": "default"
"output": {
"outputOnFailure": true
}
},
{
"name": "ci-macos-13",
"configurePreset": "ci-macos-13",
"displayName": "CI Tests on MacOS",
"description": "Runs all tests on a Mac configuration",
"execution": {
"jobs": 2
},
{
"name": "ci-windows-2022-release",
"configurePreset": "ci-windows-2022-release",
"displayName": "Windows CI Build",
"description": "This preset is used by the CI build on windows",
"configuration": "RelWithDebInfo",
"inherits": "default"
"output": {
"outputOnFailure": true
}
},
{
"name": "ci-windows-2022",
"configurePreset": "ci-windows-2022",
"displayName": "CI Tests on windows",
"description": "Runs all tests on a windows configuration",
"configuration": "RelWithDebInfo",
"execution": {
"jobs": 2
},
{
"name": "ci-ubuntu-22.04-debug",
"configurePreset": "ci-ubuntu-22.04-debug",
"displayName": "Linux CI Build",
"description": "This preset is used by the CI build on linux",
"inherits": "default"
},
{
"name": "ci-ubuntu-22.04-release",
"configurePreset": "ci-ubuntu-22.04-release",
"displayName": "Linux CI Build",
"description": "This preset is used by the CI build on linux",
"inherits": "default"
},
{
"name": "ci-macos-13-debug",
"configurePreset": "ci-macos-13-debug",
"displayName": "MacOS CI Build",
"description": "This preset is used by the CI build on MacOS",
"inherits": "default"
},
{
"name": "ci-macos-13-release",
"configurePreset": "ci-macos-13-release",
"displayName": "MacOS CI Build",
"description": "This preset is used by the CI build on MacOS",
"inherits": "default"
}
],
"testPresets": [
{
"name": "ci-ubuntu-22.04-debug",
"configurePreset": "ci-ubuntu-22.04-debug",
"displayName": "CI Tests on Linux",
"description": "Runs all tests on a linux configuration",
"configuration": "Debug",
"execution": {
"jobs": 2
},
"output": {
"outputOnFailure": true
}
},
{
"name": "ci-ubuntu-22.04-release",
"configurePreset": "ci-ubuntu-22.04-release",
"displayName": "CI Tests on Linux",
"description": "Runs all tests on a linux configuration",
"configuration": "RelWithDebInfo",
"execution": {
"jobs": 2
},
"output": {
"outputOnFailure": true
}
},
{
"name": "ci-macos-13-debug",
"configurePreset": "ci-macos-13-debug",
"displayName": "CI Tests on MacOS",
"description": "Runs all tests on a Mac configuration",
"configuration": "Debug",
"execution": {
"jobs": 2
},
"output": {
"outputOnFailure": true
}
},
{
"name": "ci-macos-13-release",
"configurePreset": "ci-macos-13-release",
"displayName": "CI Tests on MacOS",
"description": "Runs all tests on a Mac configuration",
"configuration": "RelWithDebInfo",
"execution": {
"jobs": 2
},
"output": {
"outputOnFailure": true
}
},
{
"name": "ci-windows-2022-release",
"configurePreset": "ci-windows-2022-release",
"displayName": "CI Tests on windows",
"description": "Runs all tests on a windows configuration",
"configuration": "RelWithDebInfo",
"execution": {
"jobs": 2
},
"output": {
"outputOnFailure": true
},
"filter": {
"exclude": {
"name": "((example)|(minigzip))+"
}
}
},
{
"name": "ci-windows-2022-debug",
"configurePreset": "ci-windows-2022-debug",
"displayName": "CI Tests on windows",
"description": "Runs all tests on a windows configuration for debug",
"configuration": "Debug",
"execution": {
"jobs": 2
},
"output": {
"outputOnFailure": true
},
"filter": {
"exclude": {
"name": "((example)|(minigzip))+"
}
"output": {
"outputOnFailure": true
},
"filter": {
"exclude": {
"name": "((example)|(minigzip))+"
}
}
]
}
}
]
}

View File

@@ -49,12 +49,3 @@ function(UpdateConfigOption file_name old_option_name new_option_name)
file(APPEND ${file_name} "\n" ${current_value})
endif()
endfunction()
function(DoAppleMariaDBCopy target location)
if(APPLE)
add_custom_command(TARGET ${target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${MARIADBCPP_SHARED_LIBRARY_LOCATION}
${location})
endif()
endfunction()

View File

@@ -36,19 +36,16 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
data.playerID = playerId;
uint32_t len;
if (!inStream.Read<uint32_t>(len)) return;
inStream.Read<uint32_t>(len);
if (len > 33) {
LOG("Received a really long player name, probably a fake packet %i.", len);
return;
for (int i = 0; i < len; i++) {
char character; inStream.Read<char>(character);
data.playerName += character;
}
data.playerName.resize(len);
inStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(data.playerName.data()), len);
if (!inStream.Read(data.zoneID)) return;
if (!inStream.Read(data.muteExpire)) return;
if (!inStream.Read(data.gmLevel)) return;
inStream.Read(data.zoneID);
inStream.Read(data.muteExpire);
inStream.Read(data.gmLevel);
data.sysAddr = packet->systemAddress;
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
@@ -125,11 +122,6 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
size_t membersSize = 0;
inStream.Read(membersSize);
if (membersSize >= 4) {
LOG("Tried to create a team with more than 4 players");
return;
}
std::vector<LWOOBJID> members;
members.reserve(membersSize);

View File

@@ -0,0 +1,55 @@
#ifndef __ECSRCOMMAND__H__
#define __ECSRCOMMAND__H__
#include <cstdint>
enum class eCSRCommand : uint32_t {
QUERY_SERVER_STATUS = 0,
QUERY_CHARACTER_LOCATION,
QUERY_CHARACTER_ONLINE_STATUS,
INVENTORY_ADD_ITEM,
INVENTORY_DELETE_ITEM,
MODERATE_MUTE_ACCOUNT,
MODERATE_BAN_ACCOUNT,
MODERATE_EDUCATE_CHARACTER,
MODERATE_KICK_CHARACTER,
MODERATE_WARN_CHARACTER,
MODERATE_RENAME_CHARACTER,
MODERATE_DELETE_CHARACTER_FRIEND,
MODERATE_KILL_CHARACTER,
UPDATE_CHARACTER_HEALTH,
UPDATE_CHARACTER_ARMOR,
UPDATE_CHARACTER_IMAGINATION,
UPDATE_CHARACTER_MAX_HEALTH,
UPDATE_CHARACTER_MAX_ARMOR,
UPDATE_CHARACTER_MAX_IMAGINATION,
UPDATE_CHARACTER_CURRENCY,
UPDATE_CHARACTER_REPUTATION,
UPDATE_CHARACTER_LEGO_SCORE,
UPDATE_CHARACTER_EMOTES,
UPDATE_CHARACTER_ADD_ACHIEVEMENT,
UPDATE_CHARACTER_COMPLETE_ACHIEVEMENT,
UPDATE_CHARACTER_REMOVE_ACHIEVEMENT,
UPDATE_CHARACTER_POSITION_OFFLINE,
UPDATE_CHARACTER_INV_SLOT_AMOUNT,
UTILITY_SAVE_CHARACTER,
UTILITY_SEND_MAIL,
UTILITY_GIVE_ITEM_TO_ALL_PLAYERS_ONLINE,
METRICS_CONFIGURE,
DISABLE_ZONE,
INIT_DONATION_AMOUNT,
KILL_SERVERS_COUNTDOWN,
DISABLE_FAQ,
THROTTLEQUEUE,
GATEGM_ACCESS,
RECONNECT_CRISP,
MODERATE_KICK_ACCOUNT,
TOGGLE_CRISP_SERVER,
QUICK_DRAIN_SERVER,
QUICK_DRAIN_SERVER_RENEW,
REPLICATE_CHARACTER,
GET_SERVER_STATUS,
RELOAD_SERVER_INIS
};
#endif //!__ECSRCOMMAND__H__

View File

@@ -0,0 +1,16 @@
#ifndef __EDELETIONRESTRICTIONSCHECKTYPE__H__
#define __EDELETIONRESTRICTIONSCHECKTYPE__H__
#include <cstdint>
enum class eDeletionRestrictionsCheckType : uint32_t {
INCLUDE_LOTS,
EXCLUDE_LOTS,
ANY_OF_THESE,
ALL_OF_THESE,
WHILE_IN_ZONE,
ALWAYS_RESTRICTED,
MAX
};
#endif //!__EDELETIONRESTRICTIONSCHECKTYPE__H__

View File

@@ -4,9 +4,6 @@
#define __EINVENTORYTYPE__H__
#include <cstdint>
#include "magic_enum.hpp"
static const uint8_t NUMBER_OF_INVENTORIES = 17;
/**
* Represents the different types of inventories an entity may have
@@ -59,10 +56,4 @@ public:
};
};
template <>
struct magic_enum::customize::enum_range<eInventoryType> {
static constexpr int min = 0;
static constexpr int max = 16;
};
#endif //!__EINVENTORYTYPE__H__

View File

@@ -1,21 +0,0 @@
#ifndef __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
#define __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
#include <cstdint>
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t {
SUCCESS,
FAIL_GENERIC,
FAIL_INV_FULL,
FAIL_ITEM_NOT_FOUND,
FAIL_CANT_MOVE_TO_THAT_INV_TYPE,
FAIL_NOT_NEAR_BANK,
FAIL_CANT_SWAP_ITEMS,
FAIL_SOURCE_TYPE,
FAIL_WRONG_DEST_TYPE,
FAIL_SWAP_DEST_TYPE,
FAIL_CANT_MOVE_THINKING_HAT,
FAIL_DISMOUNT_BEFORE_MOVING
};
#endif //!__EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__

View File

@@ -41,6 +41,7 @@
#include "CDRailActivatorComponent.h"
#include "CDRewardCodesTable.h"
#include "CDPetComponentTable.h"
#include "CDDeletionRestrictionsTable.h"
#ifndef CDCLIENT_CACHE_ALL
// Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory.
@@ -103,6 +104,7 @@ DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
DEFINE_TABLE_STORAGE(CDTamingBuildPuzzleTable);
DEFINE_TABLE_STORAGE(CDVendorComponentTable);
DEFINE_TABLE_STORAGE(CDZoneTableTable);
DEFINE_TABLE_STORAGE(CDDeletionRestrictionsTable)
void CDClientManager::LoadValuesFromDatabase() {
if (!CDClientDatabase::isConnected) {
@@ -150,6 +152,7 @@ void CDClientManager::LoadValuesFromDatabase() {
CDTamingBuildPuzzleTable::Instance().LoadValuesFromDatabase();
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
CDZoneTableTable::Instance().LoadValuesFromDatabase();
CDDeletionRestrictionsTable::Instance().LoadValuesFromDatabase();
}
void CDClientManager::LoadValuesFromDefaults() {

View File

@@ -0,0 +1,45 @@
#include "CDDeletionRestrictionsTable.h"
#include "GeneralUtils.h"
#include "eDeletionRestrictionsCheckType.h"
CDDeletionRestriction CDDeletionRestrictionsTable::Default = {
.id = 0,
.restricted = false,
.ids = {},
.checkType = eDeletionRestrictionsCheckType::MAX
};
void CDDeletionRestrictionsTable::LoadValuesFromDatabase() {
auto& entries = GetEntriesMutable();
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM DeletionRestrictions");
while (!tableData.eof()) {
CDDeletionRestriction entry;
entry.id = tableData.getIntField("id", -1);
if (entry.id == -1) continue;
entry.restricted = tableData.getIntField("restricted", -1);
const std::string raw_ids = tableData.getStringField("ids", "");
if (!raw_ids.empty()) {
for (const auto& idstr : GeneralUtils::SplitString(raw_ids, ',')) {
if (!idstr.empty()) {
const auto id = GeneralUtils::TryParse<int32_t>(idstr);
if (id) entry.ids.push_back(id.value());
}
}
}
entry.checkType = static_cast<eDeletionRestrictionsCheckType>(tableData.getIntField("checkType", static_cast<int>(eDeletionRestrictionsCheckType::MAX)));
entries.insert(std::make_pair(entry.id, entry));
tableData.nextRow();
}
}
const CDDeletionRestriction& CDDeletionRestrictionsTable::GetByID(uint32_t id) {
auto& entries = GetEntries();
const auto& it = entries.find(id);
if (it != entries.end()) {
return it->second;
}
return Default;
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "CDTable.h"
enum class eDeletionRestrictionsCheckType : uint32_t;
struct CDDeletionRestriction {
uint32_t id;
bool restricted;
std::vector<uint32_t> ids;
eDeletionRestrictionsCheckType checkType;
};
class CDDeletionRestrictionsTable : public CDTable<CDDeletionRestrictionsTable, std::map<uint32_t, CDDeletionRestriction>> {
public:
void LoadValuesFromDatabase();
const CDDeletionRestriction& GetByID(uint32_t id);
static CDDeletionRestriction Default;
};

View File

@@ -33,7 +33,7 @@ struct CDItemComponent {
uint32_t itemRating; //!< ???
bool isTwoHanded; //!< Whether or not the item is double handed
uint32_t minNumRequired; //!< Maybe the minimum number required for a mission, or to own this object?
uint32_t delResIndex; //!< ???
uint32_t delResIndex; //!< Relates to DeletionRestrictions Table
uint32_t currencyLOT; //!< ???
uint32_t altCurrencyCost; //!< ???
std::string subItems; //!< A comma seperate string of sub items (maybe for multi-itemed things like faction test gear set)

View File

@@ -38,4 +38,6 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp"
"CDSkillBehaviorTable.cpp"
"CDTamingBuildPuzzleTable.cpp"
"CDVendorComponentTable.cpp"
"CDZoneTableTable.cpp" PARENT_SCOPE)
"CDZoneTableTable.cpp"
"CDDeletionRestrictionsTable.cpp"
PARENT_SCOPE)

View File

@@ -23,7 +23,6 @@
#include "IActivityLog.h"
#include "IIgnoreList.h"
#include "IAccountsRewardCodes.h"
#include "IBehaviors.h"
namespace sql {
class Statement;
@@ -41,8 +40,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 IAccountsRewardCodes, public IIgnoreList,
public IBehaviors {
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList {
public:
virtual ~GameDatabase() = default;
// TODO: These should be made private.

View File

@@ -33,9 +33,6 @@ public:
// Add a new account to the database.
virtual void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) = 0;
// Update the GameMaster level of an account.
virtual void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) = 0;
};
#endif //!__IACCOUNTS__H__

View File

@@ -1,22 +0,0 @@
#ifndef IBEHAVIORS_H
#define IBEHAVIORS_H
#include <cstdint>
#include "dCommonVars.h"
class IBehaviors {
public:
struct Info {
int32_t behaviorId{};
uint32_t characterId{};
std::string behaviorInfo;
};
// This Add also takes care of updating if it exists.
virtual void AddBehavior(const Info& info) = 0;
virtual std::string GetBehavior(const int32_t behaviorId) = 0;
virtual void RemoveBehavior(const int32_t behaviorId) = 0;
};
#endif //!IBEHAVIORS_H

View File

@@ -1,7 +1,6 @@
#ifndef __IPROPERTIESCONTENTS__H__
#define __IPROPERTIESCONTENTS__H__
#include <array>
#include <cstdint>
#include <string_view>
@@ -17,7 +16,6 @@ public:
LWOOBJID id{};
LOT lot{};
uint32_t ugcId{};
std::array<int32_t, 5> behaviors{};
};
// Inserts a new UGC model into the database.
@@ -34,7 +32,7 @@ public:
virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0;
// Update the model position and rotation for the given property id.
virtual void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) = 0;
virtual void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) = 0;
// Remove the model for the given property id.
virtual void RemoveModel(const LWOOBJID& modelId) = 0;

View File

@@ -74,7 +74,7 @@ public:
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
void RemoveUnreferencedUgcModels() override;
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) override;
void RemoveModel(const LWOOBJID& modelId) override;
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
void InsertNewBugReport(const IBugReports::Info& info) override;
@@ -108,10 +108,6 @@ public:
std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) 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;
void AddBehavior(const IBehaviors::Info& info) override;
std::string GetBehavior(const int32_t behaviorId) override;
void RemoveBehavior(const int32_t characterId) override;
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
private:
// Generic query functions that can be used for any query.

View File

@@ -35,7 +35,3 @@ void MySQLDatabase::UpdateAccountPassword(const uint32_t accountId, const std::s
void MySQLDatabase::InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) {
ExecuteInsert("INSERT INTO accounts (name, password, gm_level) VALUES (?, ?, ?);", username, bcryptpassword, static_cast<int32_t>(eGameMasterLevel::OPERATOR));
}
void MySQLDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) {
ExecuteUpdate("UPDATE accounts SET gm_level = ? WHERE id = ?;", static_cast<int32_t>(gmLevel), accountId);
}

View File

@@ -1,19 +0,0 @@
#include "IBehaviors.h"
#include "MySQLDatabase.h"
void MySQLDatabase::AddBehavior(const IBehaviors::Info& info) {
ExecuteInsert(
"INSERT INTO behaviors (behavior_info, character_id, behavior_id) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE behavior_info = ?",
info.behaviorInfo, info.characterId, info.behaviorId, info.behaviorInfo
);
}
void MySQLDatabase::RemoveBehavior(const int32_t behaviorId) {
ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId);
}
std::string MySQLDatabase::GetBehavior(const int32_t behaviorId) {
auto result = ExecuteSelect("SELECT behavior_info FROM behaviors WHERE behavior_id = ?", behaviorId);
return result->next() ? result->getString("behavior_info").c_str() : "";
}

View File

@@ -2,7 +2,6 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
"Accounts.cpp"
"AccountsRewardCodes.cpp"
"ActivityLog.cpp"
"Behaviors.cpp"
"BugReports.cpp"
"CharInfo.cpp"
"CharXml.cpp"

View File

@@ -1,10 +1,7 @@
#include "MySQLDatabase.h"
std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWOOBJID& propertyId) {
auto result = ExecuteSelect(
"SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id, "
"behavior_1, behavior_2, behavior_3, behavior_4, behavior_5 "
"FROM properties_contents WHERE property_id = ?;", propertyId);
auto result = ExecuteSelect("SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id FROM properties_contents WHERE property_id = ?;", propertyId);
std::vector<IPropertyContents::Model> toReturn;
toReturn.reserve(result->rowsCount());
@@ -20,12 +17,6 @@ std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWO
model.rotation.y = result->getFloat("ry");
model.rotation.z = result->getFloat("rz");
model.ugcId = result->getUInt64("ugc_id");
model.behaviors[0] = result->getInt("behavior_1");
model.behaviors[1] = result->getInt("behavior_2");
model.behaviors[2] = result->getInt("behavior_3");
model.behaviors[3] = result->getInt("behavior_4");
model.behaviors[4] = result->getInt("behavior_5");
toReturn.push_back(std::move(model));
}
return toReturn;
@@ -41,23 +32,21 @@ void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPr
model.id, propertyId, model.ugcId == 0 ? std::nullopt : std::optional(model.ugcId), static_cast<uint32_t>(model.lot),
model.position.x, model.position.y, model.position.z, model.rotation.x, model.rotation.y, model.rotation.z, model.rotation.w,
name, "", // Model description. TODO implement this.
model.behaviors[0], // behavior 1
model.behaviors[1], // behavior 2
model.behaviors[2], // behavior 3
model.behaviors[3], // behavior 4
model.behaviors[4] // behavior 5
0, // behavior 1. TODO implement this.
0, // behavior 2. TODO implement this.
0, // behavior 3. TODO implement this.
0, // behavior 4. TODO implement this.
0 // behavior 5. TODO implement this.
);
} catch (sql::SQLException& e) {
LOG("Error inserting new property model: %s", e.what());
}
}
void MySQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
void MySQLDatabase::UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) {
ExecuteUpdate(
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
"behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;",
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w,
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, propertyId);
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ? WHERE id = ?;",
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, propertyId);
}
void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {

View File

@@ -225,7 +225,7 @@ void Entity::Initialize() {
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
AddComponent<ModelComponent>()->LoadBehaviors();
AddComponent<ModelComponent>();
AddComponent<RenderComponent>();
@@ -649,7 +649,7 @@ void Entity::Initialize() {
}
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent<PetComponent>()) {
AddComponent<ModelComponent>()->LoadBehaviors();
AddComponent<ModelComponent>();
if (!HasComponent(eReplicaComponentType::DESTROYABLE)) {
auto* destroyableComponent = AddComponent<DestroyableComponent>();
destroyableComponent->SetHealth(1);

View File

@@ -199,26 +199,6 @@ void BehaviorContext::UpdatePlayerSyncs(float deltaTime) {
i++;
continue;
}
if (this->skillUId != 0 && !clientInitalized) {
EchoSyncSkill echo;
echo.bDone = true;
echo.uiSkillHandle = this->skillUId;
echo.uiBehaviorHandle = entry.handle;
RakNet::BitStream bitStream{};
entry.behavior->SyncCalculation(this, bitStream, entry.branchContext);
echo.sBitStream.assign(reinterpret_cast<char*>(bitStream.GetData()), bitStream.GetNumberOfBytesUsed());
RakNet::BitStream message;
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
message.Write(this->originator);
echo.Serialize(message);
Game::server->Send(message, UNASSIGNED_SYSTEM_ADDRESS, true);
}
this->syncEntries.erase(this->syncEntries.begin() + i);
}
}

View File

@@ -27,8 +27,6 @@ void DamageAbsorptionBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
destroyable->SetIsShielded(true);
context->RegisterTimerBehavior(this, branch, target->GetObjectID());
Game::entityManager->SerializeEntity(target);
}
void DamageAbsorptionBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
@@ -54,13 +52,7 @@ void DamageAbsorptionBehavior::Timer(BehaviorContext* context, BehaviorBranchCon
const auto toRemove = std::min(present, this->m_absorbAmount);
const auto remaining = present - toRemove;
destroyable->SetDamageToAbsorb(remaining);
destroyable->SetIsShielded(remaining > 0);
Game::entityManager->SerializeEntity(target);
destroyable->SetDamageToAbsorb(present - toRemove);
}
void DamageAbsorptionBehavior::Load() {

View File

@@ -37,9 +37,6 @@
#include "CDScriptComponentTable.h"
#include "CDObjectSkillsTable.h"
#include "CDSkillBehaviorTable.h"
#include "StringifiedEnum.h"
#include <ranges>
InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
this->m_Dirty = true;
@@ -495,11 +492,6 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
return;
}
auto* const groups = inventoryElement->FirstChildElement("grps");
if (groups) {
LoadGroupXml(*groups);
}
m_Consumable = inventoryElement->IntAttribute("csl", LOT_NULL);
auto* bag = bags->FirstChildElement();
@@ -638,15 +630,6 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
bags->LinkEndChild(bag);
}
auto* groups = inventoryElement->FirstChildElement("grps");
if (groups) {
groups->DeleteChildren();
} else {
groups = inventoryElement->InsertNewChildElement("grps");
}
UpdateGroupXml(*groups);
auto* items = inventoryElement->FirstChildElement("items");
if (items == nullptr) {
@@ -1620,110 +1603,3 @@ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId) {
m_Skills.insert_or_assign(slot, skillId);
return true;
}
void InventoryComponent::UpdateGroup(const GroupUpdate& groupUpdate) {
if (groupUpdate.groupId.empty()) return;
if (groupUpdate.inventory != eInventoryType::BRICKS && groupUpdate.inventory != eInventoryType::MODELS) {
LOG("Invalid inventory type for grouping %s", StringifiedEnum::ToString(groupUpdate.inventory).data());
return;
}
auto& groups = m_Groups[groupUpdate.inventory];
auto groupItr = std::ranges::find_if(groups, [&groupUpdate](const Group& group) {
return group.groupId == groupUpdate.groupId;
});
if (groupUpdate.command != GroupUpdateCommand::ADD && groupItr == groups.end()) {
LOG("Group %s not found in inventory %s. Cannot process command.", groupUpdate.groupId.c_str(), StringifiedEnum::ToString(groupUpdate.inventory).data());
return;
}
if (groupUpdate.command == GroupUpdateCommand::ADD && groups.size() >= MaximumGroupCount) {
LOG("Cannot add group to inventory %s. Maximum group count reached.", StringifiedEnum::ToString(groupUpdate.inventory).data());
return;
}
switch (groupUpdate.command) {
case GroupUpdateCommand::ADD: {
auto& group = groups.emplace_back();
group.groupId = groupUpdate.groupId;
group.groupName = groupUpdate.groupName;
break;
}
case GroupUpdateCommand::ADD_LOT: {
groupItr->lots.insert(groupUpdate.lot);
break;
}
case GroupUpdateCommand::REMOVE: {
groups.erase(groupItr);
break;
}
case GroupUpdateCommand::REMOVE_LOT: {
groupItr->lots.erase(groupUpdate.lot);
break;
}
case GroupUpdateCommand::MODIFY: {
groupItr->groupName = groupUpdate.groupName;
break;
}
default: {
LOG("Invalid group update command %i", groupUpdate.command);
break;
}
}
}
void InventoryComponent::UpdateGroupXml(tinyxml2::XMLElement& groups) const {
for (const auto& [inventory, groupsData] : m_Groups) {
for (const auto& group : groupsData) {
auto* const groupElement = groups.InsertNewChildElement("grp");
groupElement->SetAttribute("id", group.groupId.c_str());
groupElement->SetAttribute("n", group.groupName.c_str());
groupElement->SetAttribute("t", static_cast<uint32_t>(inventory));
groupElement->SetAttribute("u", 0);
std::ostringstream lots;
bool first = true;
for (const auto lot : group.lots) {
if (!first) lots << ' ';
first = false;
lots << lot;
}
groupElement->SetAttribute("l", lots.str().c_str());
}
}
}
void InventoryComponent::LoadGroupXml(const tinyxml2::XMLElement& groups) {
auto* groupElement = groups.FirstChildElement("grp");
while (groupElement) {
const char* groupId = nullptr;
const char* groupName = nullptr;
const char* lots = nullptr;
uint32_t inventory = eInventoryType::INVALID;
groupElement->QueryStringAttribute("id", &groupId);
groupElement->QueryStringAttribute("n", &groupName);
groupElement->QueryStringAttribute("l", &lots);
groupElement->QueryAttribute("t", &inventory);
if (!groupId || !groupName || !lots) {
LOG("Failed to load group from xml id %i name %i lots %i",
groupId == nullptr, groupName == nullptr, lots == nullptr);
} else {
auto& group = m_Groups[static_cast<eInventoryType>(inventory)].emplace_back();
group.groupId = groupId;
group.groupName = groupName;
for (const auto& lotStr : GeneralUtils::SplitString(lots, ' ')) {
auto lot = GeneralUtils::TryParse<LOT>(lotStr);
if (lot) group.lots.insert(*lot);
}
}
groupElement = groupElement->NextSiblingElement("grp");
}
}

View File

@@ -37,35 +37,6 @@ enum class eItemType : int32_t;
*/
class InventoryComponent final : public Component {
public:
struct Group {
// Generated ID for the group. The ID is sent by the client and has the format user_group + Math.random() * UINT_MAX.
std::string groupId;
// Custom name assigned by the user.
std::string groupName;
// All the lots the user has in the group.
std::set<LOT> lots;
};
enum class GroupUpdateCommand {
ADD,
ADD_LOT,
MODIFY,
REMOVE,
REMOVE_LOT,
};
// Based on the command, certain fields will be used or not used.
// for example, ADD_LOT wont use groupName, MODIFY wont use lots, etc.
struct GroupUpdate {
std::string groupId;
std::string groupName;
LOT lot;
eInventoryType inventory;
GroupUpdateCommand command;
};
static constexpr uint32_t MaximumGroupCount = 50;
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
InventoryComponent(Entity* parent);
@@ -396,23 +367,14 @@ public:
*/
void UnequipScripts(Item* unequippedItem);
std::map<BehaviorSlot, uint32_t> GetSkills() { return m_Skills; };
std::map<BehaviorSlot, uint32_t> GetSkills(){ return m_Skills; };
bool SetSkill(int slot, uint32_t skillId);
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
void UpdateGroup(const GroupUpdate& groupUpdate);
void RemoveGroup(const std::string& groupId);
~InventoryComponent() override;
private:
/**
* The key is the inventory the group belongs to, the value maps' key is the id for the group.
* This is only used for bricks and model inventories.
*/
std::map<eInventoryType, std::vector<Group>> m_Groups{ { eInventoryType::BRICKS, {} }, { eInventoryType::MODELS, {} } };
/**
* All the inventory this entity possesses
*/
@@ -515,9 +477,6 @@ private:
* @param document the xml doc to load from
*/
void UpdatePetXml(tinyxml2::XMLDocument& document);
void LoadGroupXml(const tinyxml2::XMLElement& groups);
void UpdateGroupXml(tinyxml2::XMLElement& groups) const;
};
#endif

View File

@@ -6,9 +6,6 @@
#include "BehaviorStates.h"
#include "ControlBehaviorMsgs.h"
#include "tinyxml2.h"
#include "Database.h"
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
m_OriginalPosition = m_Parent->GetDefaultPosition();
@@ -17,33 +14,6 @@ ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
}
void ModelComponent::LoadBehaviors() {
auto behaviors = GeneralUtils::SplitString(m_Parent->GetVar<std::string>(u"userModelBehaviors"), ',');
for (const auto& behavior : behaviors) {
if (behavior.empty()) continue;
const auto behaviorId = GeneralUtils::TryParse<int32_t>(behavior);
if (!behaviorId.has_value() || behaviorId.value() == 0) continue;
LOG_DEBUG("Loading behavior %d", behaviorId.value());
auto& inserted = m_Behaviors.emplace_back();
inserted.SetBehaviorId(*behaviorId);
const auto behaviorStr = Database::Get()->GetBehavior(behaviorId.value());
tinyxml2::XMLDocument behaviorXml;
auto res = behaviorXml.Parse(behaviorStr.c_str(), behaviorStr.size());
LOG_DEBUG("Behavior %i %d: %s", res, behaviorId.value(), behaviorStr.c_str());
const auto* const behaviorRoot = behaviorXml.FirstChildElement("Behavior");
if (!behaviorRoot) {
LOG("Failed to load behavior %d due to missing behavior root", behaviorId.value());
continue;
}
inserted.Deserialize(*behaviorRoot);
}
}
void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
// ItemComponent Serialization. Pets do not get this serialization.
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
@@ -102,23 +72,3 @@ void ModelComponent::MoveToInventory(MoveToInventoryMessage& msg) {
m_Behaviors.erase(m_Behaviors.begin() + msg.GetBehaviorIndex());
// TODO move to the inventory
}
std::array<std::pair<int32_t, std::string>, 5> ModelComponent::GetBehaviorsForSave() const {
std::array<std::pair<int32_t, std::string>, 5> toReturn{};
for (auto i = 0; i < m_Behaviors.size(); i++) {
const auto& behavior = m_Behaviors.at(i);
if (behavior.GetBehaviorId() == -1) continue;
auto& [id, behaviorData] = toReturn[i];
id = behavior.GetBehaviorId();
tinyxml2::XMLDocument doc;
auto* root = doc.NewElement("Behavior");
behavior.Serialize(*root);
doc.InsertFirstChild(root);
tinyxml2::XMLPrinter printer(0, true, 0);
doc.Print(&printer);
behaviorData = printer.CStr();
}
return toReturn;
}

View File

@@ -1,6 +1,5 @@
#pragma once
#include <array>
#include <map>
#include "dCommonVars.h"
@@ -29,8 +28,6 @@ public:
ModelComponent(Entity* parent);
void LoadBehaviors();
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
/**
@@ -112,8 +109,6 @@ public:
void VerifyBehaviors();
std::array<std::pair<int32_t, std::string>, 5> GetBehaviorsForSave() const;
private:
/**
* The behaviors of the model

View File

@@ -21,11 +21,9 @@
#include "eObjectBits.h"
#include "CharacterComponent.h"
#include "PlayerManager.h"
#include "ModelComponent.h"
#include <vector>
#include "CppScripts.h"
#include <ranges>
PropertyManagementComponent* PropertyManagementComponent::instance = nullptr;
@@ -595,20 +593,6 @@ void PropertyManagementComponent::Load() {
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
}
std::ostringstream userModelBehavior;
bool firstAdded = false;
for (auto behavior : databaseModel.behaviors) {
if (behavior < 0) {
LOG("Invalid behavior ID: %d, removing behavior reference from model", behavior);
behavior = 0;
}
if (firstAdded) userModelBehavior << ",";
userModelBehavior << behavior;
firstAdded = true;
}
settings.push_back(new LDFData<std::string>(u"userModelBehaviors", userModelBehavior.str()));
node->config = settings;
const auto spawnerId = Game::zoneManager->MakeSpawner(info);
@@ -626,12 +610,6 @@ void PropertyManagementComponent::Save() {
return;
}
const auto* const owner = GetOwner();
if (!owner) return;
const auto* const character = owner->GetCharacter();
if (!character) return;
auto present = Database::Get()->GetPropertyModels(propertyId);
std::vector<LWOOBJID> modelIds;
@@ -646,20 +624,6 @@ void PropertyManagementComponent::Save() {
if (entity == nullptr) {
continue;
}
auto* modelComponent = entity->GetComponent<ModelComponent>();
if (!modelComponent) continue;
const auto modelBehaviors = modelComponent->GetBehaviorsForSave();
// save the behaviors of the model
for (const auto& [behaviorId, behaviorStr] : modelBehaviors) {
if (behaviorStr.empty() || behaviorId == -1 || behaviorId == 0) continue;
IBehaviors::Info info {
.behaviorId = behaviorId,
.characterId = character->GetID(),
.behaviorInfo = behaviorStr
};
Database::Get()->AddBehavior(info);
}
const auto position = entity->GetPosition();
const auto rotation = entity->GetRotation();
@@ -671,13 +635,10 @@ void PropertyManagementComponent::Save() {
model.position = position;
model.rotation = rotation;
model.ugcId = 0;
for (auto i = 0; i < model.behaviors.size(); i++) {
model.behaviors[i] = modelBehaviors[i].first;
}
Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_" + std::to_string(model.lot) + "_name");
} else {
Database::Get()->UpdateModel(id, position, rotation, modelBehaviors);
Database::Get()->UpdateModelPositionRotation(id, position, rotation);
}
}

View File

@@ -45,6 +45,7 @@ RacingControlComponent::RacingControlComponent(Entity* parent)
m_LoadedPlayers = 0;
m_LoadTimer = 0;
m_Finished = 0;
m_StartTime = 0;
m_EmptyTimer = 0;
m_SoloRacing = Game::config->GetValue("solo_racing") == "1";
@@ -397,6 +398,25 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu
GameMessages::SendNotifyRacingClient(
m_Parent->GetObjectID(), 2, 0, LWOOBJID_EMPTY, u"",
player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
auto* missionComponent = player->GetComponent<MissionComponent>();
if (missionComponent == nullptr) return;
missionComponent->Progress(eMissionTaskType::RACING, 0, static_cast<LWOOBJID>(eRacingTaskParam::COMPETED_IN_RACE)); // Progress task for competing in a race
missionComponent->Progress(eMissionTaskType::RACING, data->smashedTimes, static_cast<LWOOBJID>(eRacingTaskParam::SAFE_DRIVER)); // Finish a race without being smashed.
// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
if (m_SoloRacing || m_LoadedPlayers > 2) {
missionComponent->Progress(eMissionTaskType::RACING, data->finished, static_cast<LWOOBJID>(eRacingTaskParam::FINISH_WITH_PLACEMENT)); // Finish in 1st place on a race
if (data->finished == 1) {
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS)); // Finish in 1st place on multiple tracks.
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::WIN_RACE_IN_WORLD)); // Finished first place in specific world.
}
if (data->finished == m_LoadedPlayers) {
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::LAST_PLACE_FINISH)); // Finished first place in specific world.
}
}
} else if ((id == "ACT_RACE_EXIT_THE_RACE?" || id == "Exit") && button == m_ActivityExitConfirm) {
auto* vehicle = Game::entityManager->GetEntity(data->vehicleID);
@@ -426,9 +446,9 @@ void RacingControlComponent::Serialize(RakNet::BitStream& outBitStream, bool bIs
outBitStream.Write(player.playerID);
outBitStream.Write(player.data[0]);
if (player.finished != 0) outBitStream.Write<float>(player.raceTime.count() / 1000.0f);
if (player.finished != 0) outBitStream.Write<float>(player.raceTime);
else outBitStream.Write(player.data[1]);
if (player.finished != 0) outBitStream.Write<float>(player.bestLapTime.count() / 1000.0f);
if (player.finished != 0) outBitStream.Write<float>(player.bestLapTime);
else outBitStream.Write(player.data[2]);
if (player.finished == 1) outBitStream.Write<float>(1.0f);
else outBitStream.Write(player.data[3]);
@@ -489,8 +509,8 @@ void RacingControlComponent::Serialize(RakNet::BitStream& outBitStream, bool bIs
if (player.finished == 0) continue;
outBitStream.Write1(); // Has more data
outBitStream.Write(player.playerID);
outBitStream.Write<float>(player.bestLapTime.count() / 1000.0f);
outBitStream.Write<float>(player.raceTime.count() / 1000.0f);
outBitStream.Write<float>(player.bestLapTime);
outBitStream.Write<float>(player.raceTime);
}
outBitStream.Write0(); // No more data
@@ -720,7 +740,7 @@ void RacingControlComponent::Update(float deltaTime) {
Game::entityManager->SerializeEntity(m_Parent);
m_StartTime = std::chrono::high_resolution_clock::now();
m_StartTime = std::time(nullptr);
}
m_StartTimer += deltaTime;
@@ -809,60 +829,46 @@ void RacingControlComponent::Update(float deltaTime) {
// Reached the start point, lapped
if (respawnIndex == 0) {
const auto now = std::chrono::high_resolution_clock::now();
const auto lapTime = std::chrono::duration_cast<std::chrono::milliseconds>(now - (player.lap == 0 ? m_StartTime : player.lapTime));
time_t lapTime = std::time(nullptr) - (player.lap == 0 ? m_StartTime : player.lapTime);
// Cheating check
if (lapTime.count() < 40000) {
if (lapTime < 40) {
continue;
}
player.lapTime = now;
player.lap++;
if (player.bestLapTime > lapTime || player.lap == 0) {
player.lapTime = std::time(nullptr);
if (player.bestLapTime == 0 || player.bestLapTime > lapTime) {
player.bestLapTime = lapTime;
LOG("Best lap time (%llu)", lapTime);
}
player.lap++;
auto* missionComponent =
playerEntity->GetComponent<MissionComponent>();
if (missionComponent != nullptr) {
// Progress lap time tasks
missionComponent->Progress(eMissionTaskType::RACING, lapTime.count(), static_cast<LWOOBJID>(eRacingTaskParam::LAP_TIME));
missionComponent->Progress(eMissionTaskType::RACING, (lapTime) * 1000, static_cast<LWOOBJID>(eRacingTaskParam::LAP_TIME));
if (player.lap == 3) {
m_Finished++;
player.finished = m_Finished;
const auto raceTime = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_StartTime);
const auto raceTime =
(std::time(nullptr) - m_StartTime);
player.raceTime = raceTime;
LOG("Completed time %llums %fs", raceTime.count(), raceTime.count() / 1000.0f);
LOG("Completed time %llu, %llu",
raceTime, raceTime * 1000);
LeaderboardManager::SaveScore(playerEntity->GetObjectID(), m_ActivityID, static_cast<float>(player.raceTime.count()) / 1000, static_cast<float>(player.bestLapTime.count()) / 1000, static_cast<float>(player.finished == 1));
LeaderboardManager::SaveScore(playerEntity->GetObjectID(), m_ActivityID, static_cast<float>(player.raceTime), static_cast<float>(player.bestLapTime), static_cast<float>(player.finished == 1));
// Entire race time
missionComponent->Progress(eMissionTaskType::RACING, player.raceTime.count(), static_cast<LWOOBJID>(eRacingTaskParam::TOTAL_TRACK_TIME));
missionComponent->Progress(eMissionTaskType::RACING, 0, static_cast<LWOOBJID>(eRacingTaskParam::COMPETED_IN_RACE)); // Progress task for competing in a race
missionComponent->Progress(eMissionTaskType::RACING, player.smashedTimes, static_cast<LWOOBJID>(eRacingTaskParam::SAFE_DRIVER)); // Finish a race without being smashed.
// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
if (m_SoloRacing || m_RacingPlayers.size() > 2) {
missionComponent->Progress(eMissionTaskType::RACING, player.finished, static_cast<LWOOBJID>(eRacingTaskParam::FINISH_WITH_PLACEMENT)); // Finish in 1st place on a race
if (player.finished == 1) {
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS)); // Finish in 1st place on multiple tracks.
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::WIN_RACE_IN_WORLD)); // Finished first place in specific world.
}
if (player.finished == m_RacingPlayers.size()) {
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::LAST_PLACE_FINISH)); // Finished first place in specific world.
}
}
missionComponent->Progress(eMissionTaskType::RACING, (raceTime) * 1000, static_cast<LWOOBJID>(eRacingTaskParam::TOTAL_TRACK_TIME));
auto* characterComponent = playerEntity->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
@@ -871,8 +877,8 @@ void RacingControlComponent::Update(float deltaTime) {
}
}
LOG("Lapped (%i) in (%llums %fs)", player.lap,
lapTime.count(), lapTime.count() / 1000.0f);
LOG("Lapped (%i) in (%llu)", player.lap,
lapTime);
}
LOG("Reached point (%i)/(%i)", player.respawnIndex,

View File

@@ -8,7 +8,6 @@
#include "Entity.h"
#include "Component.h"
#include "eReplicaComponentType.h"
#include <chrono>
/**
* Information for each player in the race
@@ -73,12 +72,12 @@ struct RacingPlayerInfo {
/**
* The fastest lap time of the player
*/
std::chrono::milliseconds bestLapTime;
time_t bestLapTime = 0;
/**
* The current lap time of the player
*/
std::chrono::high_resolution_clock::time_point lapTime;
time_t lapTime = 0;
/**
* The number of times this player smashed their car
@@ -98,7 +97,7 @@ struct RacingPlayerInfo {
/**
* Unused
*/
std::chrono::milliseconds raceTime;
time_t raceTime = 0;
};
/**
@@ -232,7 +231,7 @@ private:
/**
* The time the race was started
*/
std::chrono::high_resolution_clock::time_point m_StartTime;
time_t m_StartTime;
/**
* Timer for tracking how long a player was alone in this race

View File

@@ -685,13 +685,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
case eGameMessageType::REQUEST_VENDOR_STATUS_UPDATE:
GameMessages::SendVendorStatusUpdate(entity, sysAddr, true);
break;
case eGameMessageType::UPDATE_INVENTORY_GROUP:
GameMessages::HandleUpdateInventoryGroup(inStream, entity, sysAddr);
break;
case eGameMessageType::UPDATE_INVENTORY_GROUP_CONTENTS:
GameMessages::HandleUpdateInventoryGroupContents(inStream, entity, sysAddr);
break;
default:
LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data());
break;

View File

@@ -99,7 +99,6 @@
#include "ActivityManager.h"
#include "PlayerManager.h"
#include "eVendorTransactionResult.h"
#include "eReponseMoveItemBetweenInventoryTypeCode.h"
#include "CDComponentsRegistryTable.h"
#include "CDObjectsTable.h"
@@ -4566,31 +4565,16 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream&
if (inStream.ReadBit()) inStream.Read(itemLOT);
if (invTypeDst == invTypeSrc) {
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
return;
}
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
if (inventoryComponent) {
if (inventoryComponent != nullptr) {
if (itemID != LWOOBJID_EMPTY) {
auto* item = inventoryComponent->FindItemById(itemID);
if (!item) {
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_ITEM_NOT_FOUND);
return;
}
if (item->GetLot() == 6086) { // Thinking hat
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_CANT_MOVE_THINKING_HAT);
return;
}
auto* destInv = inventoryComponent->GetInventory(invTypeDst);
if (destInv && destInv->GetEmptySlots() == 0) {
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_INV_FULL);
return;
}
if (!item) return;
// Despawn the pet if we are moving that pet to the vault.
auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID());
@@ -4599,32 +4583,10 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream&
}
inventoryComponent->MoveItemToInventory(item, invTypeDst, iStackCount, showFlyingLoot, false, false, destSlot);
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::SUCCESS);
}
} else {
SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
}
}
void GameMessages::SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response) {
CBITSTREAM;
CMSGHEADER;
bitStream.Write(objectId);
bitStream.Write(eGameMessageType::RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES);
bitStream.Write(inventoryTypeDestination != eInventoryType::ITEMS);
if (inventoryTypeDestination != eInventoryType::ITEMS) bitStream.Write(inventoryTypeDestination);
bitStream.Write(inventoryTypeSource != eInventoryType::ITEMS);
if (inventoryTypeSource != eInventoryType::ITEMS) bitStream.Write(inventoryTypeSource);
bitStream.Write(response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC);
if (response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC) bitStream.Write(response);
SEND_PACKET;
}
void GameMessages::SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr) {
CBITSTREAM;
@@ -6251,69 +6213,6 @@ void GameMessages::SendSlashCommandFeedbackText(Entity* entity, std::u16string t
SEND_PACKET;
}
void GameMessages::HandleUpdateInventoryGroup(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
std::string action;
std::u16string groupName;
InventoryComponent::GroupUpdate groupUpdate;
bool locked{}; // All groups are locked by default
uint32_t size{};
if (!inStream.Read(size)) return;
action.resize(size);
if (!inStream.Read(action.data(), size)) return;
if (!inStream.Read(size)) return;
groupUpdate.groupId.resize(size);
if (!inStream.Read(groupUpdate.groupId.data(), size)) return;
if (!inStream.Read(size)) return;
groupName.resize(size);
if (!inStream.Read(reinterpret_cast<char*>(groupName.data()), size * 2)) return;
if (!inStream.Read(groupUpdate.inventory)) return;
if (!inStream.Read(locked)) return;
groupUpdate.groupName = GeneralUtils::UTF16ToWTF8(groupName);
if (action == "ADD") groupUpdate.command = InventoryComponent::GroupUpdateCommand::ADD;
else if (action == "MODIFY") groupUpdate.command = InventoryComponent::GroupUpdateCommand::MODIFY;
else if (action == "REMOVE") groupUpdate.command = InventoryComponent::GroupUpdateCommand::REMOVE;
else {
LOG("Invalid action %s", action.c_str());
return;
}
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
if (inventoryComponent) inventoryComponent->UpdateGroup(groupUpdate);
}
void GameMessages::HandleUpdateInventoryGroupContents(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr) {
std::string action;
InventoryComponent::GroupUpdate groupUpdate;
uint32_t size{};
if (!inStream.Read(size)) return;
action.resize(size);
if (!inStream.Read(action.data(), size)) return;
if (action == "ADD") groupUpdate.command = InventoryComponent::GroupUpdateCommand::ADD_LOT;
else if (action == "REMOVE") groupUpdate.command = InventoryComponent::GroupUpdateCommand::REMOVE_LOT;
else {
LOG("Invalid action %s", action.c_str());
return;
}
if (!inStream.Read(size)) return;
groupUpdate.groupId.resize(size);
if (!inStream.Read(groupUpdate.groupId.data(), size)) return;
if (!inStream.Read(groupUpdate.inventory)) return;
if (!inStream.Read(groupUpdate.lot)) return;
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
if (inventoryComponent) inventoryComponent->UpdateGroup(groupUpdate);
}
void GameMessages::SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID) {
CBITSTREAM;
CMSGHEADER;

View File

@@ -39,7 +39,6 @@ enum class eQuickBuildFailReason : uint32_t;
enum class eQuickBuildState : uint32_t;
enum class BehaviorSlot : int32_t;
enum class eVendorTransactionResult : uint32_t;
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t;
enum class eCameraTargetCyclingMode : int32_t {
ALLOW_CYCLE_TEAMMATES,
@@ -595,7 +594,6 @@ namespace GameMessages {
//NT:
void HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
void SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response);
void SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr);
@@ -673,9 +671,6 @@ namespace GameMessages {
void HandleCancelDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity);
void SendSlashCommandFeedbackText(Entity* entity, std::u16string text);
void HandleUpdateInventoryGroup(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
void HandleUpdateInventoryGroupContents(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
void SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID);
};

View File

@@ -21,11 +21,13 @@
#include "eUseItemResponse.h"
#include "dZoneManager.h"
#include "ChatPackets.h"
#include "eDeletionRestrictionsCheckType.h"
#include "CDBrickIDTableTable.h"
#include "CDObjectSkillsTable.h"
#include "CDComponentsRegistryTable.h"
#include "CDPackageComponentTable.h"
#include "CDDeletionRestrictionsTable.h"
namespace {
const std::map<std::string, std::string> ExtraSettingAbbreviations = {
@@ -568,3 +570,43 @@ void Item::LoadConfigXml(const tinyxml2::XMLElement& i) {
config.push_back(LDFBaseData::DataFromString(value));
}
}
bool Item::CanDeleteItem(Item* item) {
if (!item) return false;
// TODO:
// Check if item is being possessed
// Check if the owner is mounting item? (how is this different than the above)
// Allow GM 9 to freely delete
// Finally, check Deletion Restriction
const auto& itemComponent = item->inventory->FindItemComponent(item->lot);
if (itemComponent.delResIndex == -1) return true;
return CheckDeletionRestriction(itemComponent.delResIndex, item->lot);
}
bool Item::CheckDeletionRestriction(uint32_t delResIndex, LOT item) {
auto* delresTable = CDClientManager::GetTable<CDDeletionRestrictionsTable>();
const auto restriction = delresTable->GetByID(delResIndex);
switch(restriction.checkType) {
case eDeletionRestrictionsCheckType::INCLUDE_LOTS:
if (std::ranges::find(restriction.ids, item) != restriction.ids.end()) return false;
else return true;
case eDeletionRestrictionsCheckType::EXCLUDE_LOTS:
if (std::ranges::find(restriction.ids, item) != restriction.ids.end()) return true;
else return false;
case eDeletionRestrictionsCheckType::ANY_OF_THESE:
// TODO: Implement
return true;
case eDeletionRestrictionsCheckType::ALL_OF_THESE:
// TODO: Implement
return true;
case eDeletionRestrictionsCheckType::WHILE_IN_ZONE:
if (std::ranges::find(restriction.ids, Game::zoneManager->GetZoneID().GetMapID()) != restriction.ids.end()) return false;
else return true;
case eDeletionRestrictionsCheckType::ALWAYS_RESTRICTED:
return false;
case eDeletionRestrictionsCheckType::MAX:
default:
return true;
}
}

View File

@@ -228,6 +228,13 @@ public:
void LoadConfigXml(const tinyxml2::XMLElement& i);
bool CanDeleteItem(Item* item);
bool CheckDeletionRestriction(uint32_t delResIndex, LOT item);
bool CheckDeletionRestrictionRecursion(std::set<uint32_t>& delResIndexs, uint32_t delResIndex);
private:
/**
* The object ID of this item

View File

@@ -186,7 +186,7 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string&
if (count < 0) {
if (mission->IsMission() && type == eMissionTaskType::GATHER && InAllTargets(value)) {
if (parameters.size() > 0 && (parameters[0] & 4) != 0) {
if (parameters.size() > 0 && (parameters[0] & 1) != 0) {
return;
}

View File

@@ -1,8 +1,6 @@
#include "Action.h"
#include "Amf3.h"
#include "tinyxml2.h"
Action::Action(const AMFArrayValue& arguments) {
for (const auto& [paramName, paramValue] : arguments.GetAssociative()) {
if (paramName == "Type") {
@@ -34,46 +32,3 @@ void Action::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
actionArgs->Insert(m_ValueParameterName, m_ValueParameterDouble);
}
}
void Action::Serialize(tinyxml2::XMLElement& action) const {
action.SetAttribute("Type", m_Type.c_str());
if (m_ValueParameterName.empty()) return;
action.SetAttribute("ValueParameterName", m_ValueParameterName.c_str());
if (m_ValueParameterName == "Message") {
action.SetAttribute("Value", m_ValueParameterString.c_str());
} else {
action.SetAttribute("Value", m_ValueParameterDouble);
}
}
void Action::Deserialize(const tinyxml2::XMLElement& action) {
const char* type = nullptr;
action.QueryAttribute("Type", &type);
if (!type) {
LOG("No type found for an action?");
return;
}
m_Type = type;
const char* valueParameterName = nullptr;
action.QueryAttribute("ValueParameterName", &valueParameterName);
if (valueParameterName) {
m_ValueParameterName = valueParameterName;
if (m_ValueParameterName == "Message") {
const char* value = nullptr;
action.QueryAttribute("Value", &value);
if (value) {
m_ValueParameterString = value;
} else {
LOG("No value found for an action message?");
}
} else {
action.QueryDoubleAttribute("Value", &m_ValueParameterDouble);
}
}
}

View File

@@ -3,10 +3,6 @@
#include <string>
namespace tinyxml2 {
class XMLElement;
};
class AMFArrayValue;
/**
@@ -24,8 +20,6 @@ public:
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
void Serialize(tinyxml2::XMLElement& action) const;
void Deserialize(const tinyxml2::XMLElement& action);
private:
double m_ValueParameterDouble{ 0.0 };
std::string m_Type{ "" };

View File

@@ -1,7 +1,6 @@
#include "StripUiPosition.h"
#include "Amf3.h"
#include "tinyxml2.h"
StripUiPosition::StripUiPosition(const AMFArrayValue& arguments, const std::string& uiKeyName) {
const auto* const uiArray = arguments.GetArray(uiKeyName);
@@ -22,13 +21,3 @@ void StripUiPosition::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
uiArgs->Insert("x", m_XPosition);
uiArgs->Insert("y", m_YPosition);
}
void StripUiPosition::Serialize(tinyxml2::XMLElement& position) const {
position.SetAttribute("x", m_XPosition);
position.SetAttribute("y", m_YPosition);
}
void StripUiPosition::Deserialize(const tinyxml2::XMLElement& position) {
position.QueryDoubleAttribute("x", &m_XPosition);
position.QueryDoubleAttribute("y", &m_YPosition);
}

View File

@@ -3,10 +3,6 @@
class AMFArrayValue;
namespace tinyxml2 {
class XMLElement;
}
/**
* @brief The position of the first Action in a Strip
*
@@ -19,8 +15,6 @@ public:
[[nodiscard]] double GetX() const noexcept { return m_XPosition; }
[[nodiscard]] double GetY() const noexcept { return m_YPosition; }
void Serialize(tinyxml2::XMLElement& position) const;
void Deserialize(const tinyxml2::XMLElement& position);
private:
double m_XPosition{ 0.0 };
double m_YPosition{ 0.0 };

View File

@@ -3,7 +3,6 @@
#include "Amf3.h"
#include "BehaviorStates.h"
#include "ControlBehaviorMsgs.h"
#include "tinyxml2.h"
PropertyBehavior::PropertyBehavior() {
m_LastEditedState = BehaviorState::HOME_STATE;
@@ -125,31 +124,3 @@ void PropertyBehavior::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
// TODO Serialize the execution state of the behavior
}
void PropertyBehavior::Serialize(tinyxml2::XMLElement& behavior) const {
behavior.SetAttribute("id", m_BehaviorId);
behavior.SetAttribute("name", m_Name.c_str());
behavior.SetAttribute("isLocked", isLocked);
behavior.SetAttribute("isLoot", isLoot);
for (const auto& [stateId, state] : m_States) {
if (state.IsEmpty()) continue;
auto* const stateElement = behavior.InsertNewChildElement("State");
stateElement->SetAttribute("id", static_cast<uint32_t>(stateId));
state.Serialize(*stateElement);
}
}
void PropertyBehavior::Deserialize(const tinyxml2::XMLElement& behavior) {
m_Name = behavior.Attribute("name");
behavior.QueryBoolAttribute("isLocked", &isLocked);
behavior.QueryBoolAttribute("isLoot", &isLoot);
for (const auto* stateElement = behavior.FirstChildElement("State"); stateElement; stateElement = stateElement->NextSiblingElement("State")) {
int32_t stateId = -1;
stateElement->QueryIntAttribute("id", &stateId);
if (stateId < 0 || stateId > 5) continue;
m_States[static_cast<BehaviorState>(stateId)].Deserialize(*stateElement);
}
}

View File

@@ -3,10 +3,6 @@
#include "State.h"
namespace tinyxml2 {
class XMLElement;
}
enum class BehaviorState : uint32_t;
class AMFArrayValue;
@@ -29,8 +25,6 @@ public:
[[nodiscard]] int32_t GetBehaviorId() const noexcept { return m_BehaviorId; }
void SetBehaviorId(int32_t id) noexcept { m_BehaviorId = id; }
void Serialize(tinyxml2::XMLElement& behavior) const;
void Deserialize(const tinyxml2::XMLElement& behavior);
private:
// The states this behavior has.

View File

@@ -2,7 +2,6 @@
#include "Amf3.h"
#include "ControlBehaviorMsgs.h"
#include "tinyxml2.h"
template <>
void State::HandleMsg(AddStripMessage& msg) {
@@ -135,20 +134,4 @@ void State::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
strip.SendBehaviorBlocksToClient(*stripArgs);
}
}
void State::Serialize(tinyxml2::XMLElement& state) const {
for (const auto& strip : m_Strips) {
if (strip.IsEmpty()) continue;
auto* const stripElement = state.InsertNewChildElement("Strip");
strip.Serialize(*stripElement);
}
}
void State::Deserialize(const tinyxml2::XMLElement& state) {
for (const auto* stripElement = state.FirstChildElement("Strip"); stripElement; stripElement = stripElement->NextSiblingElement("Strip")) {
auto& strip = m_Strips.emplace_back();
strip.Deserialize(*stripElement);
}
}
};

View File

@@ -3,10 +3,6 @@
#include "Strip.h"
namespace tinyxml2 {
class XMLElement;
}
class AMFArrayValue;
class State {
@@ -17,8 +13,6 @@ public:
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
bool IsEmpty() const;
void Serialize(tinyxml2::XMLElement& state) const;
void Deserialize(const tinyxml2::XMLElement& state);
private:
std::vector<Strip> m_Strips;
};

View File

@@ -2,7 +2,6 @@
#include "Amf3.h"
#include "ControlBehaviorMsgs.h"
#include "tinyxml2.h"
template <>
void Strip::HandleMsg(AddStripMessage& msg) {
@@ -84,25 +83,4 @@ void Strip::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
for (const auto& action : m_Actions) {
action.SendBehaviorBlocksToClient(*actions);
}
}
void Strip::Serialize(tinyxml2::XMLElement& strip) const {
auto* const positionElement = strip.InsertNewChildElement("Position");
m_Position.Serialize(*positionElement);
for (const auto& action : m_Actions) {
auto* const actionElement = strip.InsertNewChildElement("Action");
action.Serialize(*actionElement);
}
}
void Strip::Deserialize(const tinyxml2::XMLElement& strip) {
const auto* positionElement = strip.FirstChildElement("Position");
if (positionElement) {
m_Position.Deserialize(*positionElement);
}
for (const auto* actionElement = strip.FirstChildElement("Action"); actionElement; actionElement = actionElement->NextSiblingElement("Action")) {
auto& action = m_Actions.emplace_back();
action.Deserialize(*actionElement);
}
}
};

View File

@@ -6,10 +6,6 @@
#include <vector>
namespace tinyxml2 {
class XMLElement;
}
class AMFArrayValue;
class Strip {
@@ -20,8 +16,6 @@ public:
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
bool IsEmpty() const noexcept { return m_Actions.empty(); }
void Serialize(tinyxml2::XMLElement& strip) const;
void Deserialize(const tinyxml2::XMLElement& strip);
private:
std::vector<Action> m_Actions;
StripUiPosition m_Position;

View File

@@ -26,8 +26,7 @@ Precondition::Precondition(const uint32_t condition) {
if (result.eof()) {
this->type = PreconditionType::ItemEquipped;
this->count = 1;
this->values.clear();
this->values.push_back(0);
this->values = { 0 };
LOG("Failed to find precondition of id (%i)!", condition);
@@ -139,20 +138,21 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat
case PreconditionType::DoesNotHaveItem:
return inventoryComponent->IsEquipped(value) < count;
case PreconditionType::HasAchievement:
if (missionComponent == nullptr) return false;
return missionComponent->GetMissionState(value) >= eMissionState::COMPLETE;
mission = missionComponent->GetMission(value);
return mission == nullptr || mission->GetMissionState() >= eMissionState::COMPLETE;
case PreconditionType::MissionAvailable:
if (missionComponent == nullptr) return false;
return missionComponent->GetMissionState(value) == eMissionState::AVAILABLE || missionComponent->GetMissionState(value) == eMissionState::COMPLETE_AVAILABLE;
mission = missionComponent->GetMission(value);
return mission == nullptr || mission->GetMissionState() >= eMissionState::AVAILABLE;
case PreconditionType::OnMission:
if (missionComponent == nullptr) return false;
return missionComponent->GetMissionState(value) == eMissionState::ACTIVE ||
missionComponent->GetMissionState(value) == eMissionState::COMPLETE_ACTIVE ||
missionComponent->GetMissionState(value) == eMissionState::READY_TO_COMPLETE ||
missionComponent->GetMissionState(value) == eMissionState::COMPLETE_READY_TO_COMPLETE;
mission = missionComponent->GetMission(value);
return mission == nullptr || mission->GetMissionState() >= eMissionState::ACTIVE;
case PreconditionType::MissionComplete:
if (missionComponent == nullptr) return false;
return missionComponent->GetMissionState(value) >= eMissionState::COMPLETE;
mission = missionComponent->GetMission(value);
return mission == nullptr ? false : mission->GetMissionState() >= eMissionState::COMPLETE;
case PreconditionType::PetDeployed:
return false; // TODO
case PreconditionType::HasFlag:

View File

@@ -31,6 +31,7 @@ void SlashCommandHandler::RegisterCommand(Command command) {
}
for (const auto& alias : command.aliases) {
LOG_DEBUG("Registering command %s", alias.c_str());
auto [_, success] = RegisteredCommands.try_emplace(alias, command);
// Don't allow duplicate commands
if (!success) {

View File

@@ -285,6 +285,7 @@ void ParseXml(const std::string& file) {
}
if (zoneID.value() != currentZoneID) {
LOG_DEBUG("Skipping (%s) %i location because it is in %i and not the current zone (%i)", name, lot, zoneID.value(), currentZoneID);
continue;
}

View File

@@ -18,4 +18,3 @@ target_link_libraries(MasterServer ${COMMON_LIBRARIES} bcrypt dMasterServer dSer
if(WIN32)
add_dependencies(MasterServer WorldServer AuthServer ChatServer)
endif()
DoAppleMariaDBCopy(MasterServer ${CMAKE_BINARY_DIR})

View File

@@ -40,8 +40,6 @@
#include "BitStreamUtils.h"
#include "Start.h"
#include "Server.h"
#include "CDZoneTableTable.h"
#include "eGameMasterLevel.h"
namespace Game {
Logger* logger = nullptr;
@@ -188,20 +186,15 @@ int main(int argc, char** argv) {
std::cout << "Enter a username: ";
std::cin >> username;
const auto checkIsAdmin = []() {
std::string admin;
std::cout << "What level of privilege should this account have? Please enter a number between 0 (Player) and 9 (Admin) inclusive. No entry will default to 0." << std::endl;
std::cin >> admin;
return admin;
};
auto accountId = Database::Get()->GetAccountInfo(username);
if (accountId && accountId->id != 0) {
if (accountId) {
LOG("Account with name \"%s\" already exists", username.c_str());
std::cout << "Do you want to change the password of that account? [y/n]?";
std::string prompt = "";
std::cin >> prompt;
if (prompt == "y" || prompt == "yes") {
if (accountId->id == 0) return EXIT_FAILURE;
//Read the password from the console without echoing it.
#ifdef __linux__
//This function is obsolete, but it only meant to be used by the
@@ -226,20 +219,6 @@ int main(int argc, char** argv) {
} else {
LOG("Account \"%s\" was not updated.", username.c_str());
}
std::cout << "Update admin privileges? [y/n]? ";
std::string admin;
std::cin >> admin;
bool updateAdmin = admin == "y" || admin == "yes";
if (updateAdmin) {
auto gmLevel = GeneralUtils::TryParse<int32_t>(checkIsAdmin()).value_or(0);
if (gmLevel > 9 || gmLevel < 0) {
LOG("Invalid admin level. Defaulting to 0");
gmLevel = 0;
}
Database::Get()->UpdateAccountGmLevel(accountId->id, static_cast<eGameMasterLevel>(gmLevel));
}
return EXIT_SUCCESS;
}
@@ -270,17 +249,6 @@ int main(int argc, char** argv) {
}
LOG("Account created successfully!");
accountId = Database::Get()->GetAccountInfo(username);
if (accountId) {
auto gmLevel = GeneralUtils::TryParse<int32_t>(checkIsAdmin()).value_or(0);
if (gmLevel > 9 || gmLevel < 0) {
LOG("Invalid admin level. Defaulting to 0");
gmLevel = 0;
}
Database::Get()->UpdateAccountGmLevel(accountId->id, static_cast<eGameMasterLevel>(gmLevel));
}
return EXIT_SUCCESS;
}
@@ -309,17 +277,6 @@ int main(int argc, char** argv) {
PersistentIDManager::Initialize();
Game::im = new InstanceManager(Game::logger, Game::server->GetIP());
//Get CDClient initial information
try {
CDClientManager::LoadValuesFromDatabase();
} catch (CppSQLite3Exception& e) {
LOG("Failed to initialize CDServer SQLite Database");
LOG("May be caused by corrupted file: %s", (Game::assetManager->GetResPath() / "CDServer.sqlite").string().c_str());
LOG("Error: %s", e.errorMessage());
LOG("Error Code: %i", e.errorCode());
return EXIT_FAILURE;
}
//Depending on the config, start up servers:
if (Game::config->GetValue("prestart_servers") != "0") {
StartChatServer();
@@ -589,7 +546,7 @@ void HandlePacket(Packet* packet) {
inStream.Read(sessionKey);
LUString username;
inStream.Read(username);
for (auto it : activeSessions) {
if (it.second == username.string) {
activeSessions.erase(it.first);

View File

@@ -359,7 +359,7 @@ int main(int argc, char** argv) {
//Warning if we ran slow
if (deltaTime > currentFrameDelta) {
LOG("We're running behind, dT: %f > %i (framerate %i)", deltaTime, currentFrameDelta, currentFramerate);
LOG("We're running behind, dT: %f > %f (framerate %i)", deltaTime, currentFrameDelta, currentFramerate);
}
//Check if we're still connected to master:

View File

@@ -1,3 +0,0 @@
ALTER TABLE behaviors ADD COLUMN character_id BIGINT NOT NULL DEFAULT 0;
ALTER TABLE behaviors ADD COLUMN behavior_id BIGINT NOT NULL PRIMARY KEY;
ALTER TABLE behaviors DROP COLUMN id;

View File

@@ -1 +0,0 @@
ALTER TABLE behaviors MODIFY behavior_info LONGTEXT DEFAULT NULL;

Binary file not shown.

View File

@@ -15,11 +15,6 @@ endif()
add_custom_target(conncpp_tests
${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
if(MSVC)
add_custom_target(zlib_tests
${CMAKE_COMMAND} -E copy $<TARGET_FILE:ZLIB::ZLIB> ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endif()
# Add the subdirectories
add_subdirectory(dCommonTests)
add_subdirectory(dGameTests)

View File

@@ -18,11 +18,18 @@ list(APPEND DCOMMONTEST_SOURCES ${DENUMS_TESTS})
# Set our executable
add_executable(dCommonTests ${DCOMMONTEST_SOURCES})
add_dependencies(dCommonTests conncpp_tests)
if(MSVC)
add_dependencies(dCommonTests zlib_tests)
endif()
# Apple needs some special linkage for the mariadb connector for tests.
DoAppleMariaDBCopy(dCommonTests ${CMAKE_CURRENT_BINARY_DIR})
if(APPLE)
add_custom_command(TARGET dCommonTests POST_BUILD
COMMAND otool ARGS -l dCommonTests
COMMAND otool ARGS -L dCommonTests
COMMAND ls
COMMAND otool ARGS -D libmariadbcpp.dylib
COMMAND install_name_tool ARGS -change libmariadbcpp.dylib @rpath/libmariadbcpp.dylib dCommonTests
COMMAND otool ARGS -L dCommonTests
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endif()
# Link needed libraries
target_link_libraries(dCommonTests ${COMMON_LIBRARIES} GTest::gtest_main)

View File

@@ -15,7 +15,12 @@ add_executable(dGameTests ${DGAMETEST_SOURCES})
add_dependencies(dGameTests conncpp_tests)
# Apple needs some special linkage for the mariadb connector for tests.
DoAppleMariaDBCopy(dGameTests ${CMAKE_CURRENT_BINARY_DIR})
if(APPLE)
add_custom_command(TARGET dGameTests POST_BUILD
COMMAND install_name_tool ARGS -change libmariadbcpp.dylib @rpath/libmariadbcpp.dylib dGameTests
COMMAND otool ARGS -L dGameTests
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endif()
target_link_libraries(dGameTests ${COMMON_LIBRARIES} GTest::gtest_main
dGame dScripts dPhysics Detour Recast tinyxml2 dWorldServer dZoneManager dChatFilter dNavigation)