Compare commits

..

1 Commits

Author SHA1 Message Date
David Markowitz
9d5a023061 update switch behavior
was using very old code from pre-foss that has not been updated with the new behavior knowledge.  The code has been updated accordingly to what the client expects.

Tested that ice shurikens can now destroy the legs of the skeleton towers in crux prime.  Tested that the following weapons can still do damage to enemies and objects in the world:
surikens of ice
serratorizer
Super Morning Star
Super Dagger
elite long barrel blaster (charge and normal)
Mosaic Wand
2024-03-24 03:48:16 -07:00
196 changed files with 4501 additions and 7476 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,18 +1,11 @@
cmake_minimum_required(VERSION 3.25)
project(Darkflame)
# check if the path to the source directory contains a space
if("${CMAKE_SOURCE_DIR}" MATCHES " ")
message(FATAL_ERROR "The server cannot build in the path (" ${CMAKE_SOURCE_DIR} ") because it contains a space. Please move the server to a path without spaces.")
endif()
include(CTest)
set(CMAKE_CXX_STANDARD 20)
set(CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export the compile commands for debugging
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects
set(CMAKE_CXX_VISIBILITY_PRESET hidden) # Set C++ symbol visibility to default to hidden
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# Read variables from file
@@ -79,8 +72,7 @@ if(UNIX)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC")
elseif(MSVC)
# Skip warning for invalid conversion from size_t to uint32_t for all targets below for now
# Also disable non-portable MSVC volatile behavior
add_compile_options("/wd4267" "/utf-8" "/volatile:iso")
add_compile_options("/wd4267" "/utf-8")
elseif(WIN32)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif()
@@ -110,7 +102,7 @@ make_directory(${CMAKE_BINARY_DIR}/resServer)
make_directory(${CMAKE_BINARY_DIR}/logs)
# Copy resource files on first build
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blocklist.dcf")
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blacklist.dcf")
message(STATUS "Checking resource file integrity")
include(Utils)

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

@@ -1,6 +1,6 @@
PROJECT_VERSION_MAJOR=2
PROJECT_VERSION_MINOR=3
PROJECT_VERSION_PATCH=0
PROJECT_VERSION_MAJOR=1
PROJECT_VERSION_MINOR=1
PROJECT_VERSION_PATCH=1
# Debugging
# Set DYNAMIC to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.

View File

@@ -31,7 +31,7 @@ COPY --from=build /app/build/*Server /app/
# Necessary suplimentary files
COPY --from=build /app/build/*.ini /app/configs/
COPY --from=build /app/build/vanity/*.* /app/vanity/
COPY --from=build /app/build/vanity/*.* /app/vanity/*
COPY --from=build /app/build/navmeshes /app/navmeshes
COPY --from=build /app/build/migrations /app/migrations
COPY --from=build /app/build/*.dcf /app/
@@ -39,7 +39,7 @@ COPY --from=build /app/build/*.dcf /app/
# backup of config and vanity files to copy to the host incase
# of a mount clobbering the copy from above
COPY --from=build /app/build/*.ini /app/default-configs/
COPY --from=build /app/build/vanity/*.* /app/default-vanity/
COPY --from=build /app/build/vanity/*.* /app/default-vanity/*
# needed as the container runs with the root user
# and therefore sudo doesn't exist

View File

@@ -73,7 +73,7 @@ sudo apt install build-essential gcc zlib1g-dev libssl-dev openssl mariadb-serve
```
#### Required CMake version
This project uses <font size="4">**CMake version 3.25**</font> or higher and as such you will need to ensure you have this version installed.
This project uses <font size="4">**CMake version 3.18**</font> or higher and as such you will need to ensure you have this version installed.
You can check your CMake version by using the following command in a terminal.
```bash
cmake --version

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

@@ -27,8 +27,8 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
ExportWordlistToDCF(filepath + ".dcf", true);
}
if (BinaryIO::DoesFileExist("blocklist.dcf")) {
ReadWordlistDCF("blocklist.dcf", false);
if (BinaryIO::DoesFileExist("blacklist.dcf")) {
ReadWordlistDCF("blacklist.dcf", false);
}
//Read player names that are ok as well:
@@ -44,20 +44,20 @@ dChatFilter::~dChatFilter() {
m_DeniedWords.clear();
}
void dChatFilter::ReadWordlistPlaintext(const std::string& filepath, bool allowList) {
void dChatFilter::ReadWordlistPlaintext(const std::string& filepath, bool whiteList) {
std::ifstream file(filepath);
if (file) {
std::string line;
while (std::getline(file, line)) {
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
if (allowList) m_ApprovedWords.push_back(CalculateHash(line));
if (whiteList) m_ApprovedWords.push_back(CalculateHash(line));
else m_DeniedWords.push_back(CalculateHash(line));
}
}
}
bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool allowList) {
bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool whiteList) {
std::ifstream file(filepath, std::ios::binary);
if (file) {
fileHeader hdr;
@@ -70,13 +70,13 @@ bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool allowList) {
if (hdr.formatVersion == formatVersion) {
size_t wordsToRead = 0;
BinaryIO::BinaryRead(file, wordsToRead);
if (allowList) m_ApprovedWords.reserve(wordsToRead);
if (whiteList) m_ApprovedWords.reserve(wordsToRead);
else m_DeniedWords.reserve(wordsToRead);
size_t word = 0;
for (size_t i = 0; i < wordsToRead; ++i) {
BinaryIO::BinaryRead(file, word);
if (allowList) m_ApprovedWords.push_back(word);
if (whiteList) m_ApprovedWords.push_back(word);
else m_DeniedWords.push_back(word);
}
@@ -90,14 +90,14 @@ bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool allowList) {
return false;
}
void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool allowList) {
void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool whiteList) {
std::ofstream file(filepath, std::ios::binary | std::ios_base::out);
if (file) {
BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::header));
BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::formatVersion));
BinaryIO::BinaryWrite(file, size_t(allowList ? m_ApprovedWords.size() : m_DeniedWords.size()));
BinaryIO::BinaryWrite(file, size_t(whiteList ? m_ApprovedWords.size() : m_DeniedWords.size()));
for (size_t word : allowList ? m_ApprovedWords : m_DeniedWords) {
for (size_t word : whiteList ? m_ApprovedWords : m_DeniedWords) {
BinaryIO::BinaryWrite(file, word);
}
@@ -105,10 +105,10 @@ void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool allowLis
}
}
std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList) {
std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool whiteList) {
if (gmLevel > eGameMasterLevel::FORUM_MODERATOR) return { }; //If anything but a forum mod, return true.
if (message.empty()) return { };
if (!allowList && m_DeniedWords.empty()) return { { 0, message.length() } };
if (!whiteList && m_DeniedWords.empty()) return { { 0, message.length() } };
std::stringstream sMessage(message);
std::string segment;
@@ -126,16 +126,16 @@ std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::
size_t hash = CalculateHash(segment);
if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && allowList) {
if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && whiteList) {
listOfBadSegments.emplace_back(position, originalSegment.length());
}
if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && allowList) {
if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && whiteList) {
m_UserUnapprovedWordCache.push_back(hash);
listOfBadSegments.emplace_back(position, originalSegment.length());
}
if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !allowList) {
if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !whiteList) {
m_UserUnapprovedWordCache.push_back(hash);
listOfBadSegments.emplace_back(position, originalSegment.length());
}

View File

@@ -21,10 +21,10 @@ public:
dChatFilter(const std::string& filepath, bool dontGenerateDCF);
~dChatFilter();
void ReadWordlistPlaintext(const std::string& filepath, bool allowList);
bool ReadWordlistDCF(const std::string& filepath, bool allowList);
void ExportWordlistToDCF(const std::string& filepath, bool allowList);
std::vector<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList = true);
void ReadWordlistPlaintext(const std::string& filepath, bool whiteList);
bool ReadWordlistDCF(const std::string& filepath, bool whiteList);
void ExportWordlistToDCF(const std::string& filepath, bool whiteList);
std::vector<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool whiteList = true);
private:
bool m_DontGenerateDCF;

View File

@@ -1,6 +1,6 @@
#include "ChatIgnoreList.h"
#include "PlayerContainer.h"
#include "eChatMessageType.h"
#include "eChatInternalMessageType.h"
#include "BitStreamUtils.h"
#include "Game.h"
#include "Logger.h"
@@ -13,7 +13,7 @@
// The only thing not auto-handled is instance activities force joining the team on the server.
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) {
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receivingPlayer);
//portion that will get routed:

View File

@@ -14,11 +14,11 @@
#include "eObjectBits.h"
#include "eConnectionType.h"
#include "eChatMessageType.h"
#include "eChatInternalMessageType.h"
#include "eClientMessageType.h"
#include "eGameMessageType.h"
#include "StringifiedEnum.h"
#include "eGameMasterLevel.h"
#include "ChatPackets.h"
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Get from the packet which player we want to do something with:
@@ -60,7 +60,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Now, we need to send the friendlist to the server they came from:
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(playerID);
//portion that will get routed:
@@ -355,67 +355,6 @@ void ChatPacketHandler::HandleGMLevelUpdate(Packet* packet) {
inStream.Read(player.gmLevel);
}
void ChatPacketHandler::HandleWho(Packet* packet) {
CINSTREAM_SKIP_HEADER;
FindPlayerRequest request;
request.Deserialize(inStream);
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
if (!sender) return;
const auto& player = Game::playerContainer.GetPlayerData(request.playerName.GetAsString());
bool online = player;
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(request.requestor);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::WHO_RESPONSE);
bitStream.Write<uint8_t>(online);
bitStream.Write(player.zoneID.GetMapID());
bitStream.Write(player.zoneID.GetInstanceID());
bitStream.Write(player.zoneID.GetCloneID());
bitStream.Write(request.playerName);
SystemAddress sysAddr = sender.sysAddr;
SEND_PACKET;
}
void ChatPacketHandler::HandleShowAll(Packet* packet) {
CINSTREAM_SKIP_HEADER;
ShowAllRequest request;
request.Deserialize(inStream);
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
if (!sender) return;
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(request.requestor);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::SHOW_ALL_RESPONSE);
bitStream.Write<uint8_t>(!request.displayZoneData && !request.displayIndividualPlayers);
bitStream.Write(Game::playerContainer.GetPlayerCount());
bitStream.Write(Game::playerContainer.GetSimCount());
bitStream.Write<uint8_t>(request.displayIndividualPlayers);
bitStream.Write<uint8_t>(request.displayZoneData);
if (request.displayZoneData || request.displayIndividualPlayers){
for (auto& [playerID, playerData ]: Game::playerContainer.GetAllPlayers()){
if (!playerData) continue;
bitStream.Write<uint8_t>(0); // structure packing
if (request.displayIndividualPlayers) bitStream.Write(LUWString(playerData.playerName));
if (request.displayZoneData) {
bitStream.Write(playerData.zoneID.GetMapID());
bitStream.Write(playerData.zoneID.GetInstanceID());
bitStream.Write(playerData.zoneID.GetCloneID());
}
}
}
SystemAddress sysAddr = sender.sysAddr;
SEND_PACKET;
}
// the structure the client uses to send this packet is shared in many chat messages
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages
void ChatPacketHandler::HandleChatMessage(Packet* packet) {
@@ -515,7 +454,7 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(routeTo.playerID);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
@@ -757,7 +696,7 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver.playerID);
//portion that will get routed:
@@ -772,7 +711,7 @@ void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerD
void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver.playerID);
//portion that will get routed:
@@ -799,7 +738,7 @@ void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool b
void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver.playerID);
//portion that will get routed:
@@ -824,7 +763,7 @@ void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64L
void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver.playerID);
//portion that will get routed:
@@ -841,7 +780,7 @@ void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i
void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver.playerID);
//portion that will get routed:
@@ -870,7 +809,7 @@ void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFr
void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver.playerID);
//portion that will get routed:
@@ -896,7 +835,7 @@ void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bD
void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver.playerID);
//portion that will get routed:
@@ -930,7 +869,7 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla
[bool] - is FTP*/
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(friendData.playerID);
//portion that will get routed:
@@ -967,7 +906,7 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
}
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver.playerID);
//portion that will get routed:
@@ -981,7 +920,7 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver.playerID);
// Portion that will get routed:
@@ -1004,7 +943,7 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla
void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver.playerID);
//portion that will get routed:

View File

@@ -50,8 +50,6 @@ namespace ChatPacketHandler {
void HandleFriendResponse(Packet* packet);
void HandleRemoveFriend(Packet* packet);
void HandleGMLevelUpdate(Packet* packet);
void HandleWho(Packet* packet);
void HandleShowAll(Packet* packet);
void HandleChatMessage(Packet* packet);
void HandlePrivateChatMessage(Packet* packet);

View File

@@ -17,6 +17,7 @@
#include "PlayerContainer.h"
#include "ChatPacketHandler.h"
#include "eChatMessageType.h"
#include "eChatInternalMessageType.h"
#include "eWorldMessageType.h"
#include "ChatIgnoreList.h"
#include "StringifiedEnum.h"
@@ -179,32 +180,49 @@ int main(int argc, char** argv) {
}
void HandlePacket(Packet* packet) {
if (packet->length < 1) return;
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
LOG("A server has disconnected, erasing their connected players from the list.");
} else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
}
if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
LOG("A server is connecting, awaiting user list.");
} else if (packet->length < 4 || packet->data[0] != ID_USER_PACKET_ENUM) return; // Nothing left to process or not the right packet type
}
CINSTREAM;
inStream.SetReadOffset(BYTES_TO_BITS(1));
if (packet->length < 4) return; // Nothing left to process. Need 4 bytes to continue.
eConnectionType connection;
eChatMessageType chatMessageID;
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) {
switch (static_cast<eChatInternalMessageType>(packet->data[3])) {
case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION:
Game::playerContainer.InsertPlayer(packet);
break;
inStream.Read(connection);
if (connection != eConnectionType::CHAT) return;
inStream.Read(chatMessageID);
switch (chatMessageID) {
case eChatMessageType::GM_MUTE:
case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION:
Game::playerContainer.RemovePlayer(packet);
break;
case eChatInternalMessageType::MUTE_UPDATE:
Game::playerContainer.MuteUpdate(packet);
break;
case eChatMessageType::CREATE_TEAM:
case eChatInternalMessageType::CREATE_TEAM:
Game::playerContainer.CreateTeamServer(packet);
break;
case eChatInternalMessageType::ANNOUNCEMENT: {
//we just forward this packet to every connected server
CINSTREAM;
Game::server->Send(inStream, packet->systemAddress, true); //send to everyone except origin
break;
}
default:
LOG("Unknown CHAT_INTERNAL id: %i", int(packet->data[3]));
}
}
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT) {
eChatMessageType chat_message_type = static_cast<eChatMessageType>(packet->data[3]);
switch (chat_message_type) {
case eChatMessageType::GET_FRIENDS_LIST:
ChatPacketHandler::HandleFriendlistRequest(packet);
break;
@@ -278,23 +296,6 @@ void HandlePacket(Packet* packet) {
ChatPacketHandler::HandleGMLevelUpdate(packet);
break;
case eChatMessageType::LOGIN_SESSION_NOTIFY:
Game::playerContainer.InsertPlayer(packet);
break;
case eChatMessageType::GM_ANNOUNCE:{
// we just forward this packet to every connected server
inStream.ResetReadPointer();
Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
}
break;
case eChatMessageType::UNEXPECTED_DISCONNECT:
Game::playerContainer.RemovePlayer(packet);
break;
case eChatMessageType::WHO:
ChatPacketHandler::HandleWho(packet);
break;
case eChatMessageType::SHOW_ALL:
ChatPacketHandler::HandleShowAll(packet);
break;
case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE:
case eChatMessageType::WORLD_DISCONNECT_REQUEST:
case eChatMessageType::WORLD_PROXIMITY_RESPONSE:
@@ -307,6 +308,7 @@ void HandlePacket(Packet* packet) {
case eChatMessageType::GUILD_KICK:
case eChatMessageType::GUILD_GET_STATUS:
case eChatMessageType::GUILD_GET_ALL:
case eChatMessageType::SHOW_ALL:
case eChatMessageType::BLUEPRINT_MODERATED:
case eChatMessageType::BLUEPRINT_MODEL_READY:
case eChatMessageType::PROPERTY_READY_FOR_APPROVAL:
@@ -321,6 +323,7 @@ void HandlePacket(Packet* packet) {
case eChatMessageType::CSR_REQUEST:
case eChatMessageType::CSR_REPLY:
case eChatMessageType::GM_KICK:
case eChatMessageType::GM_ANNOUNCE:
case eChatMessageType::WORLD_ROUTE_PACKET:
case eChatMessageType::GET_ZONE_POPULATIONS:
case eChatMessageType::REQUEST_MINIMUM_CHAT_MODE:
@@ -329,18 +332,33 @@ void HandlePacket(Packet* packet) {
case eChatMessageType::UGCMANIFEST_REPORT_DONE_FILE:
case eChatMessageType::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
case eChatMessageType::UGCC_REQUEST:
case eChatMessageType::WHO:
case eChatMessageType::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
case eChatMessageType::ACHIEVEMENT_NOTIFY:
case eChatMessageType::GM_CLOSE_PRIVATE_CHAT_WINDOW:
case eChatMessageType::UNEXPECTED_DISCONNECT:
case eChatMessageType::PLAYER_READY:
case eChatMessageType::GET_DONATION_TOTAL:
case eChatMessageType::UPDATE_DONATION:
case eChatMessageType::PRG_CSR_COMMAND:
case eChatMessageType::HEARTBEAT_REQUEST_FROM_WORLD:
case eChatMessageType::UPDATE_FREE_TRIAL_STATUS:
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chatMessageID).data(), chatMessageID);
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chat_message_type).data(), chat_message_type);
break;
default:
LOG("Unknown CHAT Message id: %i", chatMessageID);
LOG("Unknown CHAT Message id: %i", chat_message_type);
}
}
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::WORLD) {
switch (static_cast<eWorldMessageType>(packet->data[3])) {
case eWorldMessageType::ROUTE_PACKET: {
LOG("Routing packet from world");
break;
}
default:
LOG("Unknown World id: %i", int(packet->data[3]));
}
}
}

View File

@@ -9,9 +9,9 @@
#include "BitStreamUtils.h"
#include "Database.h"
#include "eConnectionType.h"
#include "eChatInternalMessageType.h"
#include "ChatPackets.h"
#include "dConfig.h"
#include "eChatMessageType.h"
void PlayerContainer::Initialize() {
m_MaxNumberOfBestFriends =
@@ -36,23 +36,19 @@ 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);
m_PlayerCount++;
LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
@@ -91,7 +87,6 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
}
}
m_PlayerCount--;
LOG("Removed user: %llu", playerID);
m_Players.erase(playerID);
@@ -125,11 +120,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);
@@ -155,7 +145,7 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_MUTE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
bitStream.Write(player);
bitStream.Write(time);
@@ -362,7 +352,7 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::TEAM_GET_STATUS);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE);
bitStream.Write(team->teamID);
bitStream.Write(deleteTeam);
@@ -400,7 +390,7 @@ LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
}
PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) {
return m_Players.contains(playerID) ? m_Players[playerID] : m_Players[LWOOBJID_EMPTY];
return m_Players[playerID];
}
PlayerData& PlayerContainer::GetPlayerDataMutable(const std::string& playerName) {

View File

@@ -71,9 +71,6 @@ public:
const PlayerData& GetPlayerData(const std::string& playerName);
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
PlayerData& GetPlayerDataMutable(const std::string& playerName);
uint32_t GetPlayerCount() { return m_PlayerCount; };
uint32_t GetSimCount() { return m_SimCount; };
const std::map<LWOOBJID, PlayerData>& GetAllPlayers() { return m_Players; };
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
TeamData* CreateTeam(LWOOBJID leader, bool local = false);
@@ -96,7 +93,5 @@ private:
std::unordered_map<LWOOBJID, std::u16string> m_Names;
uint32_t m_MaxNumberOfBestFriends = 5;
uint32_t m_MaxNumberOfFriends = 50;
uint32_t m_PlayerCount = 0;
uint32_t m_SimCount = 0;
};

View File

@@ -120,8 +120,6 @@ void CatchUnhandled(int sig) {
if (eptr) std::rethrow_exception(eptr);
} catch(const std::exception& e) {
LOG("Caught exception: '%s'", e.what());
} catch (...) {
LOG("Caught unknown exception.");
}
#ifndef INCLUDE_BACKTRACE
@@ -201,7 +199,7 @@ void OnTerminate() {
}
void MakeBacktrace() {
struct sigaction sigact{};
struct sigaction sigact;
sigact.sa_sigaction = CritErrHdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO;

View File

@@ -8,23 +8,23 @@
#include <map>
template <typename T>
static inline size_t MinSize(const size_t size, const std::basic_string_view<T> string) {
if (size == SIZE_MAX || size > string.size()) {
inline size_t MinSize(size_t size, const std::basic_string_view<T>& string) {
if (size == size_t(-1) || size > string.size()) {
return string.size();
} else {
return size;
}
}
inline bool IsLeadSurrogate(const char16_t c) {
inline bool IsLeadSurrogate(char16_t c) {
return (0xD800 <= c) && (c <= 0xDBFF);
}
inline bool IsTrailSurrogate(const char16_t c) {
inline bool IsTrailSurrogate(char16_t c) {
return (0xDC00 <= c) && (c <= 0xDFFF);
}
inline void PushUTF8CodePoint(std::string& ret, const char32_t cp) {
inline void PushUTF8CodePoint(std::string& ret, char32_t cp) {
if (cp <= 0x007F) {
ret.push_back(static_cast<uint8_t>(cp));
} else if (cp <= 0x07FF) {
@@ -46,16 +46,16 @@ inline void PushUTF8CodePoint(std::string& ret, const char32_t cp) {
constexpr const char16_t REPLACEMENT_CHARACTER = 0xFFFD;
bool static _IsSuffixChar(const uint8_t c) {
bool _IsSuffixChar(uint8_t c) {
return (c & 0xC0) == 0x80;
}
bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
const size_t rem = slice.length();
bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
size_t rem = slice.length();
if (slice.empty()) return false;
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&slice.front());
if (rem > 0) {
const uint8_t first = bytes[0];
uint8_t first = bytes[0];
if (first < 0x80) { // 1 byte character
out = static_cast<uint32_t>(first & 0x7F);
slice.remove_prefix(1);
@@ -64,7 +64,7 @@ bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out
// middle byte, not valid at start, fall through
} else if (first < 0xE0) { // two byte character
if (rem > 1) {
const uint8_t second = bytes[1];
uint8_t second = bytes[1];
if (_IsSuffixChar(second)) {
out = (static_cast<uint32_t>(first & 0x1F) << 6)
+ static_cast<uint32_t>(second & 0x3F);
@@ -74,8 +74,8 @@ bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out
}
} else if (first < 0xF0) { // three byte character
if (rem > 2) {
const uint8_t second = bytes[1];
const uint8_t third = bytes[2];
uint8_t second = bytes[1];
uint8_t third = bytes[2];
if (_IsSuffixChar(second) && _IsSuffixChar(third)) {
out = (static_cast<uint32_t>(first & 0x0F) << 12)
+ (static_cast<uint32_t>(second & 0x3F) << 6)
@@ -86,9 +86,9 @@ bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out
}
} else if (first < 0xF8) { // four byte character
if (rem > 3) {
const uint8_t second = bytes[1];
const uint8_t third = bytes[2];
const uint8_t fourth = bytes[3];
uint8_t second = bytes[1];
uint8_t third = bytes[2];
uint8_t fourth = bytes[3];
if (_IsSuffixChar(second) && _IsSuffixChar(third) && _IsSuffixChar(fourth)) {
out = (static_cast<uint32_t>(first & 0x07) << 18)
+ (static_cast<uint32_t>(second & 0x3F) << 12)
@@ -107,7 +107,7 @@ bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out
}
/// See <https://www.ietf.org/rfc/rfc2781.html#section-2.1>
bool PushUTF16CodePoint(std::u16string& output, const uint32_t U, const size_t size) {
bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) {
if (output.length() >= size) return false;
if (U < 0x10000) {
// If U < 0x10000, encode U as a 16-bit unsigned integer and terminate.
@@ -120,7 +120,7 @@ bool PushUTF16CodePoint(std::u16string& output, const uint32_t U, const size_t s
// Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
// U' must be less than or equal to 0xFFFFF. That is, U' can be
// represented in 20 bits.
const uint32_t Ut = U - 0x10000;
uint32_t Ut = U - 0x10000;
// Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
// 0xDC00, respectively. These integers each have 10 bits free to
@@ -141,25 +141,25 @@ bool PushUTF16CodePoint(std::u16string& output, const uint32_t U, const size_t s
} else return false;
}
std::u16string GeneralUtils::UTF8ToUTF16(const std::string_view string, const size_t size) {
const size_t newSize = MinSize(size, string);
std::u16string GeneralUtils::UTF8ToUTF16(const std::string_view& string, size_t size) {
size_t newSize = MinSize(size, string);
std::u16string output;
output.reserve(newSize);
std::string_view iterator = string;
uint32_t c;
while (details::_NextUTF8Char(iterator, c) && PushUTF16CodePoint(output, c, size)) {}
while (_NextUTF8Char(iterator, c) && PushUTF16CodePoint(output, c, size)) {}
return output;
}
//! Converts an std::string (ASCII) to UCS-2 / UTF-16
std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view string, const size_t size) {
const size_t newSize = MinSize(size, string);
std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view& string, size_t size) {
size_t newSize = MinSize(size, string);
std::u16string ret;
ret.reserve(newSize);
for (size_t i = 0; i < newSize; ++i) {
const char c = string[i];
for (size_t i = 0; i < newSize; i++) {
char c = string[i];
// Note: both 7-bit ascii characters and REPLACEMENT_CHARACTER fit in one char16_t
ret.push_back((c > 0 && c <= 127) ? static_cast<char16_t>(c) : REPLACEMENT_CHARACTER);
}
@@ -169,18 +169,18 @@ std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view string, const s
//! Converts a (potentially-ill-formed) UTF-16 string to UTF-8
//! See: <http://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16>
std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const size_t size) {
const size_t newSize = MinSize(size, string);
std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view& string, size_t size) {
size_t newSize = MinSize(size, string);
std::string ret;
ret.reserve(newSize);
for (size_t i = 0; i < newSize; ++i) {
const char16_t u = string[i];
for (size_t i = 0; i < newSize; i++) {
char16_t u = string[i];
if (IsLeadSurrogate(u) && (i + 1) < newSize) {
const char16_t next = string[i + 1];
char16_t next = string[i + 1];
if (IsTrailSurrogate(next)) {
i += 1;
const char32_t cp = 0x10000
char32_t cp = 0x10000
+ ((static_cast<char32_t>(u) - 0xD800) << 10)
+ (static_cast<char32_t>(next) - 0xDC00);
PushUTF8CodePoint(ret, cp);
@@ -195,40 +195,40 @@ std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const si
return ret;
}
bool GeneralUtils::CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b) {
bool GeneralUtils::CaseInsensitiveStringCompare(const std::string& a, const std::string& b) {
return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { return tolower(a) == tolower(b); });
}
// MARK: Bits
//! Sets a specific bit in a signed 64-bit integer
int64_t GeneralUtils::SetBit(int64_t value, const uint32_t index) {
int64_t GeneralUtils::SetBit(int64_t value, uint32_t index) {
return value |= 1ULL << index;
}
//! Clears a specific bit in a signed 64-bit integer
int64_t GeneralUtils::ClearBit(int64_t value, const uint32_t index) {
int64_t GeneralUtils::ClearBit(int64_t value, uint32_t index) {
return value &= ~(1ULL << index);
}
//! Checks a specific bit in a signed 64-bit integer
bool GeneralUtils::CheckBit(int64_t value, const uint32_t index) {
bool GeneralUtils::CheckBit(int64_t value, uint32_t index) {
return value & (1ULL << index);
}
bool GeneralUtils::ReplaceInString(std::string& str, const std::string_view from, const std::string_view to) {
const size_t start_pos = str.find(from);
bool GeneralUtils::ReplaceInString(std::string& str, const std::string& from, const std::string& to) {
size_t start_pos = str.find(from);
if (start_pos == std::string::npos)
return false;
str.replace(start_pos, from.length(), to);
return true;
}
std::vector<std::wstring> GeneralUtils::SplitString(const std::wstring_view str, const wchar_t delimiter) {
std::vector<std::wstring> GeneralUtils::SplitString(std::wstring& str, wchar_t delimiter) {
std::vector<std::wstring> vector = std::vector<std::wstring>();
std::wstring current;
for (const wchar_t c : str) {
for (const auto& c : str) {
if (c == delimiter) {
vector.push_back(current);
current = L"";
@@ -237,15 +237,15 @@ std::vector<std::wstring> GeneralUtils::SplitString(const std::wstring_view str,
}
}
vector.push_back(std::move(current));
vector.push_back(current);
return vector;
}
std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string_view str, const char16_t delimiter) {
std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string& str, char16_t delimiter) {
std::vector<std::u16string> vector = std::vector<std::u16string>();
std::u16string current;
for (const char16_t c : str) {
for (const auto& c : str) {
if (c == delimiter) {
vector.push_back(current);
current = u"";
@@ -254,15 +254,17 @@ std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string_view
}
}
vector.push_back(std::move(current));
vector.push_back(current);
return vector;
}
std::vector<std::string> GeneralUtils::SplitString(const std::string_view str, const char delimiter) {
std::vector<std::string> GeneralUtils::SplitString(const std::string& str, char delimiter) {
std::vector<std::string> vector = std::vector<std::string>();
std::string current = "";
for (const char c : str) {
for (size_t i = 0; i < str.length(); i++) {
char c = str[i];
if (c == delimiter) {
vector.push_back(current);
current = "";
@@ -271,7 +273,8 @@ std::vector<std::string> GeneralUtils::SplitString(const std::string_view str, c
}
}
vector.push_back(std::move(current));
vector.push_back(current);
return vector;
}
@@ -280,7 +283,7 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
inStream.Read<uint32_t>(length);
std::u16string string;
for (uint32_t i = 0; i < length; ++i) {
for (auto i = 0; i < length; i++) {
uint16_t c;
inStream.Read(c);
string.push_back(c);
@@ -289,35 +292,35 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
return string;
}
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string_view folder) {
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string& folder) {
// Because we dont know how large the initial number before the first _ is we need to make it a map like so.
std::map<uint32_t, std::string> filenames{};
for (const auto& t : std::filesystem::directory_iterator(folder)) {
auto filename = t.path().filename().string();
const auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
filenames.emplace(index, std::move(filename));
std::map<uint32_t, std::string> filenames{};
for (auto& t : std::filesystem::directory_iterator(folder)) {
auto filename = t.path().filename().string();
auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
filenames.insert(std::make_pair(index, filename));
}
// Now sort the map by the oldest migration.
std::vector<std::string> sortedFiles{};
auto fileIterator = filenames.cbegin();
auto oldest = filenames.cbegin();
auto fileIterator = filenames.begin();
std::map<uint32_t, std::string>::iterator oldest = filenames.begin();
while (!filenames.empty()) {
if (fileIterator == filenames.cend()) {
if (fileIterator == filenames.end()) {
sortedFiles.push_back(oldest->second);
filenames.erase(oldest);
fileIterator = filenames.cbegin();
oldest = filenames.cbegin();
fileIterator = filenames.begin();
oldest = filenames.begin();
continue;
}
if (oldest->first > fileIterator->first) oldest = fileIterator;
++fileIterator;
fileIterator++;
}
return sortedFiles;
}
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
#ifdef DARKFLAME_PLATFORM_MACOS
// MacOS floating-point parse function specializations
namespace GeneralUtils::details {

View File

@@ -3,18 +3,17 @@
// C++
#include <charconv>
#include <cstdint>
#include <ctime>
#include <functional>
#include <optional>
#include <random>
#include <span>
#include <stdexcept>
#include <ctime>
#include <string>
#include <string_view>
#include <optional>
#include <functional>
#include <type_traits>
#include <stdexcept>
#include "BitStream.h"
#include "NiPoint3.h"
#include "dPlatforms.h"
#include "Game.h"
#include "Logger.h"
@@ -33,31 +32,29 @@ namespace GeneralUtils {
//! Converts a plain ASCII string to a UTF-16 string
/*!
\param string The string to convert
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
\param size A size to trim the string to. Default is -1 (No trimming)
\return An UTF-16 representation of the string
*/
std::u16string ASCIIToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
std::u16string ASCIIToUTF16(const std::string_view& string, size_t size = -1);
//! Converts a UTF-8 String to a UTF-16 string
/*!
\param string The string to convert
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
\param size A size to trim the string to. Default is -1 (No trimming)
\return An UTF-16 representation of the string
*/
std::u16string UTF8ToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
std::u16string UTF8ToUTF16(const std::string_view& string, size_t size = -1);
namespace details {
//! Internal, do not use
bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
}
//! Internal, do not use
bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
//! Converts a UTF-16 string to a UTF-8 string
/*!
\param string The string to convert
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
\param size A size to trim the string to. Default is -1 (No trimming)
\return An UTF-8 representation of the string
*/
std::string UTF16ToWTF8(const std::u16string_view string, const size_t size = SIZE_MAX);
std::string UTF16ToWTF8(const std::u16string_view& string, size_t size = -1);
/**
* Compares two basic strings but does so ignoring case sensitivity
@@ -65,7 +62,7 @@ namespace GeneralUtils {
* \param b the second string to compare against the first string
* @return if the two strings are equal
*/
bool CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b);
bool CaseInsensitiveStringCompare(const std::string& a, const std::string& b);
// MARK: Bits
@@ -73,9 +70,9 @@ namespace GeneralUtils {
//! Sets a bit on a numerical value
template <typename T>
inline void SetBit(T& value, const eObjectBits bits) {
inline void SetBit(T& value, eObjectBits bits) {
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
const auto index = static_cast<size_t>(bits);
auto index = static_cast<size_t>(bits);
if (index > (sizeof(T) * 8) - 1) {
return;
}
@@ -85,9 +82,9 @@ namespace GeneralUtils {
//! Clears a bit on a numerical value
template <typename T>
inline void ClearBit(T& value, const eObjectBits bits) {
inline void ClearBit(T& value, eObjectBits bits) {
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
const auto index = static_cast<size_t>(bits);
auto index = static_cast<size_t>(bits);
if (index > (sizeof(T) * 8 - 1)) {
return;
}
@@ -100,14 +97,14 @@ namespace GeneralUtils {
\param value The value to set the bit for
\param index The index of the bit
*/
int64_t SetBit(int64_t value, const uint32_t index);
int64_t SetBit(int64_t value, uint32_t index);
//! Clears a specific bit in a signed 64-bit integer
/*!
\param value The value to clear the bit from
\param index The index of the bit
*/
int64_t ClearBit(int64_t value, const uint32_t index);
int64_t ClearBit(int64_t value, uint32_t index);
//! Checks a specific bit in a signed 64-bit integer
/*!
@@ -115,19 +112,19 @@ namespace GeneralUtils {
\param index The index of the bit
\return Whether or not the bit is set
*/
bool CheckBit(int64_t value, const uint32_t index);
bool CheckBit(int64_t value, uint32_t index);
bool ReplaceInString(std::string& str, const std::string_view from, const std::string_view to);
bool ReplaceInString(std::string& str, const std::string& from, const std::string& to);
std::u16string ReadWString(RakNet::BitStream& inStream);
std::vector<std::wstring> SplitString(const std::wstring_view str, const wchar_t delimiter);
std::vector<std::wstring> SplitString(std::wstring& str, wchar_t delimiter);
std::vector<std::u16string> SplitString(const std::u16string_view str, const char16_t delimiter);
std::vector<std::u16string> SplitString(const std::u16string& str, char16_t delimiter);
std::vector<std::string> SplitString(const std::string_view str, const char delimiter);
std::vector<std::string> SplitString(const std::string& str, char delimiter);
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string_view folder);
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string& folder);
// Concept constraining to enum types
template <typename T>
@@ -147,7 +144,7 @@ namespace GeneralUtils {
// If a boolean, present an alias to an intermediate integral type for parsing
template <Numeric T> requires std::same_as<T, bool>
struct numeric_parse<T> { using type = uint8_t; };
struct numeric_parse<T> { using type = uint32_t; };
// Shorthand type alias
template <Numeric T>
@@ -159,9 +156,8 @@ namespace GeneralUtils {
* @returns An std::optional containing the desired value if it is equivalent to the string
*/
template <Numeric T>
[[nodiscard]] std::optional<T> TryParse(std::string_view str) {
[[nodiscard]] std::optional<T> TryParse(const std::string_view str) {
numeric_parse_t<T> result;
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
const char* const strEnd = str.data() + str.size();
const auto [parseEnd, ec] = std::from_chars(str.data(), strEnd, result);
@@ -170,7 +166,7 @@ namespace GeneralUtils {
return isParsed ? static_cast<T>(result) : std::optional<T>{};
}
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
#ifdef DARKFLAME_PLATFORM_MACOS
// MacOS floating-point parse helper function specializations
namespace details {
@@ -185,10 +181,8 @@ namespace GeneralUtils {
* @returns An std::optional containing the desired value if it is equivalent to the string
*/
template <std::floating_point T>
[[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
[[nodiscard]] std::optional<T> TryParse(const std::string_view str) noexcept
try {
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
size_t parseNum;
const T result = details::_parse<T>(str, parseNum);
const bool isParsed = str.length() == parseNum;
@@ -208,7 +202,7 @@ namespace GeneralUtils {
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
*/
template <typename T>
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ) {
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string& strX, const std::string& strY, const std::string& strZ) {
const auto x = TryParse<float>(strX);
if (!x) return std::nullopt;
@@ -220,17 +214,17 @@ namespace GeneralUtils {
}
/**
* The TryParse overload for handling NiPoint3 by passing a span of three strings
* @param str The string vector representing the X, Y, and Z coordinates
* The TryParse overload for handling NiPoint3 by passingn a reference to a vector of three strings
* @param str The string vector representing the X, Y, and Xcoordinates
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
*/
template <typename T>
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::span<const std::string> str) {
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::vector<std::string>& str) {
return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt;
}
template <typename T>
std::u16string to_u16string(const T value) {
std::u16string to_u16string(T value) {
return GeneralUtils::ASCIIToUTF16(std::to_string(value));
}
@@ -249,7 +243,7 @@ namespace GeneralUtils {
\param max The maximum to generate to
*/
template <typename T>
inline T GenerateRandomNumber(const std::size_t min, const std::size_t max) {
inline T GenerateRandomNumber(std::size_t min, std::size_t max) {
// Make sure it is a numeric type
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
@@ -276,10 +270,10 @@ namespace GeneralUtils {
// on Windows we need to undef these or else they conflict with our numeric limits calls
// DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS
#ifdef _WIN32
#undef min
#undef max
#endif
#ifdef _WIN32
#undef min
#undef max
#endif
template <typename T>
inline T GenerateRandomNumber() {

View File

@@ -31,22 +31,22 @@ public:
virtual ~LDFBaseData() {}
virtual void WriteToPacket(RakNet::BitStream& packet) const = 0;
virtual void WriteToPacket(RakNet::BitStream& packet) = 0;
virtual const std::u16string& GetKey() const = 0;
virtual const std::u16string& GetKey() = 0;
virtual eLDFType GetValueType() const = 0;
virtual eLDFType GetValueType() = 0;
/** Gets a string from the key/value pair
* @param includeKey Whether or not to include the key in the data
* @param includeTypeId Whether or not to include the type id in the data
* @return The string representation of the data
*/
virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) const = 0;
virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) = 0;
virtual std::string GetValueAsString() const = 0;
virtual std::string GetValueAsString() = 0;
virtual LDFBaseData* Copy() const = 0;
virtual LDFBaseData* Copy() = 0;
/**
* Given an input string, return the data as a LDF key.
@@ -62,7 +62,7 @@ private:
T value;
//! Writes the key to the packet
void WriteKey(RakNet::BitStream& packet) const {
void WriteKey(RakNet::BitStream& packet) {
packet.Write<uint8_t>(this->key.length() * sizeof(uint16_t));
for (uint32_t i = 0; i < this->key.length(); ++i) {
packet.Write<uint16_t>(this->key[i]);
@@ -70,7 +70,7 @@ private:
}
//! Writes the value to the packet
void WriteValue(RakNet::BitStream& packet) const {
void WriteValue(RakNet::BitStream& packet) {
packet.Write<uint8_t>(this->GetValueType());
packet.Write(this->value);
}
@@ -90,7 +90,7 @@ public:
/*!
\return The value
*/
const T& GetValue(void) const { return this->value; }
const T& GetValue(void) { return this->value; }
//! Sets the value
/*!
@@ -102,13 +102,13 @@ public:
/*!
\return The value string
*/
std::string GetValueString(void) const { return ""; }
std::string GetValueString(void) { return ""; }
//! Writes the data to a packet
/*!
\param packet The packet
*/
void WriteToPacket(RakNet::BitStream& packet) const override {
void WriteToPacket(RakNet::BitStream& packet) override {
this->WriteKey(packet);
this->WriteValue(packet);
}
@@ -117,13 +117,13 @@ public:
/*!
\return The key
*/
const std::u16string& GetKey(void) const override { return this->key; }
const std::u16string& GetKey(void) override { return this->key; }
//! Gets the LDF Type
/*!
\return The LDF value type
*/
eLDFType GetValueType(void) const override { return LDF_TYPE_UNKNOWN; }
eLDFType GetValueType(void) override { return LDF_TYPE_UNKNOWN; }
//! Gets the string data
/*!
@@ -131,7 +131,7 @@ public:
\param includeTypeId Whether or not to include the type id in the data
\return The string representation of the data
*/
std::string GetString(const bool includeKey = true, const bool includeTypeId = true) const override {
std::string GetString(const bool includeKey = true, const bool includeTypeId = true) override {
if (GetValueType() == -1) {
return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:<server variable>";
}
@@ -154,11 +154,11 @@ public:
return stream.str();
}
std::string GetValueAsString() const override {
std::string GetValueAsString() override {
return this->GetValueString();
}
LDFBaseData* Copy() const override {
LDFBaseData* Copy() override {
return new LDFData<T>(key, value);
}
@@ -166,19 +166,19 @@ public:
};
// LDF Types
template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) const { return LDF_TYPE_UTF_16; };
template<> inline eLDFType LDFData<int32_t>::GetValueType(void) const { return LDF_TYPE_S32; };
template<> inline eLDFType LDFData<float>::GetValueType(void) const { return LDF_TYPE_FLOAT; };
template<> inline eLDFType LDFData<double>::GetValueType(void) const { return LDF_TYPE_DOUBLE; };
template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) const { return LDF_TYPE_U32; };
template<> inline eLDFType LDFData<bool>::GetValueType(void) const { return LDF_TYPE_BOOLEAN; };
template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) const { return LDF_TYPE_U64; };
template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) const { return LDF_TYPE_OBJID; };
template<> inline eLDFType LDFData<std::string>::GetValueType(void) const { return LDF_TYPE_UTF_8; };
template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) { return LDF_TYPE_UTF_16; };
template<> inline eLDFType LDFData<int32_t>::GetValueType(void) { return LDF_TYPE_S32; };
template<> inline eLDFType LDFData<float>::GetValueType(void) { return LDF_TYPE_FLOAT; };
template<> inline eLDFType LDFData<double>::GetValueType(void) { return LDF_TYPE_DOUBLE; };
template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) { return LDF_TYPE_U32; };
template<> inline eLDFType LDFData<bool>::GetValueType(void) { return LDF_TYPE_BOOLEAN; };
template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) { return LDF_TYPE_U64; };
template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) { return LDF_TYPE_OBJID; };
template<> inline eLDFType LDFData<std::string>::GetValueType(void) { return LDF_TYPE_UTF_8; };
// The specialized version for std::u16string (UTF-16)
template<>
inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) const {
inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) {
packet.Write<uint8_t>(this->GetValueType());
packet.Write<uint32_t>(this->value.length());
@@ -189,7 +189,7 @@ inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) const
// The specialized version for bool
template<>
inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) const {
inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) {
packet.Write<uint8_t>(this->GetValueType());
packet.Write<uint8_t>(this->value);
@@ -197,7 +197,7 @@ inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) const {
// The specialized version for std::string (UTF-8)
template<>
inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) const {
inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) {
packet.Write<uint8_t>(this->GetValueType());
packet.Write<uint32_t>(this->value.length());
@@ -206,18 +206,18 @@ inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) const {
}
}
template<> inline std::string LDFData<std::u16string>::GetValueString() const {
template<> inline std::string LDFData<std::u16string>::GetValueString() {
return GeneralUtils::UTF16ToWTF8(this->value, this->value.size());
}
template<> inline std::string LDFData<int32_t>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<float>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<double>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<uint32_t>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<bool>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<uint64_t>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<LWOOBJID>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<int32_t>::GetValueString() { return std::to_string(this->value); }
template<> inline std::string LDFData<float>::GetValueString() { return std::to_string(this->value); }
template<> inline std::string LDFData<double>::GetValueString() { return std::to_string(this->value); }
template<> inline std::string LDFData<uint32_t>::GetValueString() { return std::to_string(this->value); }
template<> inline std::string LDFData<bool>::GetValueString() { return std::to_string(this->value); }
template<> inline std::string LDFData<uint64_t>::GetValueString() { return std::to_string(this->value); }
template<> inline std::string LDFData<LWOOBJID>::GetValueString() { return std::to_string(this->value); }
template<> inline std::string LDFData<std::string>::GetValueString() const { return this->value; }
template<> inline std::string LDFData<std::string>::GetValueString() { return this->value; }
#endif //!__LDFFORMAT__H__

View File

@@ -1,7 +1,6 @@
#include "dConfig.h"
#include <sstream>
#include <algorithm>
#include "BinaryPathFinder.h"
#include "GeneralUtils.h"

View File

@@ -0,0 +1,31 @@
#ifndef __ECHATINTERNALMESSAGETYPE__H__
#define __ECHATINTERNALMESSAGETYPE__H__
#include <cstdint>
enum eChatInternalMessageType : uint32_t {
PLAYER_ADDED_NOTIFICATION = 0,
PLAYER_REMOVED_NOTIFICATION,
ADD_FRIEND,
ADD_BEST_FRIEND,
ADD_TO_TEAM,
ADD_BLOCK,
REMOVE_FRIEND,
REMOVE_BLOCK,
REMOVE_FROM_TEAM,
DELETE_TEAM,
REPORT,
PRIVATE_CHAT,
PRIVATE_CHAT_RESPONSE,
ANNOUNCEMENT,
MAIL_COUNT_UPDATE,
MAIL_SEND_NOTIFY,
REQUEST_USER_LIST,
FRIEND_LIST,
ROUTE_TO_PLAYER,
TEAM_UPDATE,
MUTE_UPDATE,
CREATE_TEAM,
};
#endif //!__ECHATINTERNALMESSAGETYPE__H__

View File

@@ -72,9 +72,7 @@ enum class eChatMessageType :uint32_t {
UPDATE_DONATION,
PRG_CSR_COMMAND,
HEARTBEAT_REQUEST_FROM_WORLD,
UPDATE_FREE_TRIAL_STATUS,
// CUSTOM DLU MESSAGE ID FOR INTERNAL USE
CREATE_TEAM,
UPDATE_FREE_TRIAL_STATUS
};
#endif //!__ECHATMESSAGETYPE__H__

View File

@@ -5,7 +5,8 @@ enum class eConnectionType : uint16_t {
SERVER = 0,
AUTH,
CHAT,
WORLD = 4,
CHAT_INTERNAL,
WORLD,
CLIENT,
MASTER
};

View File

@@ -790,10 +790,9 @@ enum class eGameMessageType : uint16_t {
GET_MISSION_TYPE_STATES = 853,
GET_TIME_PLAYED = 854,
SET_MISSION_VIEWED = 855,
HKX_VEHICLE_LOADED = 856,
SLASH_COMMAND_TEXT_FEEDBACK = 857,
SLASH_COMMAND_TEXT_FEEDBACK = 856,
HANDLE_SLASH_COMMAND_KORE_DEBUGGER = 857,
BROADCAST_TEXT_TO_CHATBOX = 858,
HANDLE_SLASH_COMMAND_KORE_DEBUGGER = 859,
OPEN_PROPERTY_MANAGEMENT = 860,
OPEN_PROPERTY_VENDOR = 861,
VOTE_ON_PROPERTY = 862,

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

@@ -29,8 +29,8 @@ enum class eWorldMessageType : uint32_t {
ROUTE_PACKET, // Social?
POSITION_UPDATE,
MAIL,
WORD_CHECK, // AllowList word check
STRING_CHECK, // AllowList string check
WORD_CHECK, // Whitelist word check
STRING_CHECK, // Whitelist string check
GET_PLAYERS_IN_ZONE,
REQUEST_UGC_MANIFEST_INFO,
BLUEPRINT_GET_ALL_DATA_REQUEST,

View File

@@ -25,7 +25,6 @@
#include "CDScriptComponentTable.h"
#include "CDSkillBehaviorTable.h"
#include "CDZoneTableTable.h"
#include "CDTamingBuildPuzzleTable.h"
#include "CDVendorComponentTable.h"
#include "CDActivitiesTable.h"
#include "CDPackageComponentTable.h"
@@ -42,6 +41,8 @@
#include "CDRewardCodesTable.h"
#include "CDPetComponentTable.h"
#include <exception>
#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.
// A vanilla CDClient takes about 46MB of memory + the regular world data.
@@ -54,6 +55,13 @@
#define CDCLIENT_DONT_CACHE_TABLE(x)
#endif
class CDClientConnectionException : public std::exception {
public:
virtual const char* what() const throw() {
return "CDClientDatabase is not connected!";
}
};
// Using a macro to reduce repetitive code and issues from copy and paste.
// As a note, ## in a macro is used to concatenate two tokens together.
@@ -100,14 +108,11 @@ DEFINE_TABLE_STORAGE(CDRewardCodesTable);
DEFINE_TABLE_STORAGE(CDRewardsTable);
DEFINE_TABLE_STORAGE(CDScriptComponentTable);
DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
DEFINE_TABLE_STORAGE(CDTamingBuildPuzzleTable);
DEFINE_TABLE_STORAGE(CDVendorComponentTable);
DEFINE_TABLE_STORAGE(CDZoneTableTable);
void CDClientManager::LoadValuesFromDatabase() {
if (!CDClientDatabase::isConnected) {
throw std::runtime_error{ "CDClientDatabase is not connected!" };
}
if (!CDClientDatabase::isConnected) throw CDClientConnectionException();
CDActivityRewardsTable::Instance().LoadValuesFromDatabase();
CDActivitiesTable::Instance().LoadValuesFromDatabase();
@@ -147,7 +152,6 @@ void CDClientManager::LoadValuesFromDatabase() {
CDRewardsTable::Instance().LoadValuesFromDatabase();
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
CDTamingBuildPuzzleTable::Instance().LoadValuesFromDatabase();
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
CDZoneTableTable::Instance().LoadValuesFromDatabase();
}

View File

@@ -50,7 +50,7 @@ void CDPetComponentTable::LoadValuesFromDatabase() {
}
void CDPetComponentTable::LoadValuesFromDefaults() {
GetEntriesMutable().emplace(defaultEntry.id, defaultEntry);
GetEntriesMutable().insert(std::make_pair(defaultEntry.id, defaultEntry));
}
CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) {

View File

@@ -1,35 +0,0 @@
#include "CDTamingBuildPuzzleTable.h"
void CDTamingBuildPuzzleTable::LoadValuesFromDatabase() {
// First, get the size of the table
uint32_t size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM TamingBuildPuzzles");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
// Reserve the size
auto& entries = GetEntriesMutable();
entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM TamingBuildPuzzles");
while (!tableData.eof()) {
const auto lot = static_cast<LOT>(tableData.getIntField("NPCLot", LOT_NULL));
entries.emplace(lot, CDTamingBuildPuzzle{
.puzzleModelLot = lot,
.validPieces{ tableData.getStringField("ValidPiecesLXF") },
.timeLimit = static_cast<float>(tableData.getFloatField("Timelimit", 30.0f)),
.numValidPieces = tableData.getIntField("NumValidPieces", 6),
.imaginationCost = tableData.getIntField("imagCostPerBuild", 10)
});
tableData.nextRow();
}
}
const CDTamingBuildPuzzle* CDTamingBuildPuzzleTable::GetByLOT(const LOT lot) const {
const auto& entries = GetEntries();
const auto itr = entries.find(lot);
return itr != entries.cend() ? &itr->second : nullptr;
}

View File

@@ -1,60 +0,0 @@
#pragma once
#include "CDTable.h"
/**
* Information for the minigame to be completed
*/
struct CDTamingBuildPuzzle {
UNUSED_COLUMN(uint32_t id = 0;)
// The LOT of the object that is to be created
LOT puzzleModelLot = LOT_NULL;
// The LOT of the NPC
UNUSED_COLUMN(LOT npcLot = LOT_NULL;)
// The .lxfml file that contains the bricks required to build the model
std::string validPieces{};
// The .lxfml file that contains the bricks NOT required to build the model
UNUSED_COLUMN(std::string invalidPieces{};)
// Difficulty value
UNUSED_COLUMN(int32_t difficulty = 1;)
// The time limit to complete the build
float timeLimit = 30.0f;
// The number of pieces required to complete the minigame
int32_t numValidPieces = 6;
// Number of valid pieces
UNUSED_COLUMN(int32_t totalNumPieces = 16;)
// Model name
UNUSED_COLUMN(std::string modelName{};)
// The .lxfml file that contains the full model
UNUSED_COLUMN(std::string fullModel{};)
// The duration of the pet taming minigame
UNUSED_COLUMN(float duration = 45.0f;)
// The imagination cost for the tamer to start the minigame
int32_t imaginationCost = 10;
};
class CDTamingBuildPuzzleTable : public CDTable<CDTamingBuildPuzzleTable, std::unordered_map<LOT, CDTamingBuildPuzzle>> {
public:
/**
* Load values from the CD client database
*/
void LoadValuesFromDatabase();
/**
* Gets the pet ability table corresponding to the pet LOT
* @returns A pointer to the corresponding table, or nullptr if one cannot be found
*/
[[nodiscard]]
const CDTamingBuildPuzzle* GetByLOT(const LOT lot) const;
};

View File

@@ -36,6 +36,5 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp"
"CDRewardsTable.cpp"
"CDScriptComponentTable.cpp"
"CDSkillBehaviorTable.cpp"
"CDTamingBuildPuzzleTable.cpp"
"CDVendorComponentTable.cpp"
"CDZoneTableTable.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

@@ -27,11 +27,12 @@ Character::Character(uint32_t id, User* parentUser) {
m_ID = id;
m_ParentUser = parentUser;
m_OurEntity = nullptr;
m_GMLevel = eGameMasterLevel::CIVILIAN;
m_PermissionMap = static_cast<ePermissionMap>(0);
m_Doc = nullptr;
}
Character::~Character() {
if (m_Doc) delete m_Doc;
m_Doc = nullptr;
m_OurEntity = nullptr;
m_ParentUser = nullptr;
}
@@ -54,6 +55,8 @@ void Character::UpdateInfoFromDatabase() {
m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused.
m_ZoneCloneID = 0;
m_Doc = nullptr;
//Quickly and dirtly parse the xmlData to get the info we need:
DoQuickXMLDataParse();
@@ -67,13 +70,18 @@ void Character::UpdateInfoFromDatabase() {
}
void Character::UpdateFromDatabase() {
if (m_Doc) delete m_Doc;
UpdateInfoFromDatabase();
}
void Character::DoQuickXMLDataParse() {
if (m_XMLData.size() == 0) return;
if (m_Doc.Parse(m_XMLData.c_str(), m_XMLData.size()) == 0) {
delete m_Doc;
m_Doc = new tinyxml2::XMLDocument();
if (!m_Doc) return;
if (m_Doc->Parse(m_XMLData.c_str(), m_XMLData.size()) == 0) {
LOG("Loaded xmlData for character %s (%i)!", m_Name.c_str(), m_ID);
} else {
LOG("Failed to load xmlData!");
@@ -81,7 +89,7 @@ void Character::DoQuickXMLDataParse() {
return;
}
tinyxml2::XMLElement* mf = m_Doc.FirstChildElement("obj")->FirstChildElement("mf");
tinyxml2::XMLElement* mf = m_Doc->FirstChildElement("obj")->FirstChildElement("mf");
if (!mf) {
LOG("Failed to find mf tag!");
return;
@@ -100,7 +108,7 @@ void Character::DoQuickXMLDataParse() {
mf->QueryAttribute("ess", &m_Eyes);
mf->QueryAttribute("ms", &m_Mouth);
tinyxml2::XMLElement* inv = m_Doc.FirstChildElement("obj")->FirstChildElement("inv");
tinyxml2::XMLElement* inv = m_Doc->FirstChildElement("obj")->FirstChildElement("inv");
if (!inv) {
LOG("Char has no inv!");
return;
@@ -133,7 +141,7 @@ void Character::DoQuickXMLDataParse() {
}
tinyxml2::XMLElement* character = m_Doc.FirstChildElement("obj")->FirstChildElement("char");
tinyxml2::XMLElement* character = m_Doc->FirstChildElement("obj")->FirstChildElement("char");
if (character) {
character->QueryAttribute("cc", &m_Coins);
int32_t gm_level = 0;
@@ -197,7 +205,7 @@ void Character::DoQuickXMLDataParse() {
character->QueryAttribute("lzrw", &m_OriginalRotation.w);
}
auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag");
if (flags) {
auto* currentChild = flags->FirstChildElement();
while (currentChild) {
@@ -231,10 +239,12 @@ void Character::SetBuildMode(bool buildMode) {
}
void Character::SaveXMLToDatabase() {
if (!m_Doc) return;
//For metrics, we'll record the time it took to save:
auto start = std::chrono::system_clock::now();
tinyxml2::XMLElement* character = m_Doc.FirstChildElement("obj")->FirstChildElement("char");
tinyxml2::XMLElement* character = m_Doc->FirstChildElement("obj")->FirstChildElement("char");
if (character) {
character->SetAttribute("gm", static_cast<uint32_t>(m_GMLevel));
character->SetAttribute("cc", m_Coins);
@@ -256,11 +266,11 @@ void Character::SaveXMLToDatabase() {
}
auto emotes = character->FirstChildElement("ue");
if (!emotes) emotes = m_Doc.NewElement("ue");
if (!emotes) emotes = m_Doc->NewElement("ue");
emotes->DeleteChildren();
for (int emoteID : m_UnlockedEmotes) {
auto emote = m_Doc.NewElement("e");
auto emote = m_Doc->NewElement("e");
emote->SetAttribute("id", emoteID);
emotes->LinkEndChild(emote);
@@ -270,15 +280,15 @@ void Character::SaveXMLToDatabase() {
}
//Export our flags:
auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag");
if (!flags) {
flags = m_Doc.NewElement("flag"); //Create a flags tag if we don't have one
m_Doc.FirstChildElement("obj")->LinkEndChild(flags); //Link it to the obj tag so we can find next time
flags = m_Doc->NewElement("flag"); //Create a flags tag if we don't have one
m_Doc->FirstChildElement("obj")->LinkEndChild(flags); //Link it to the obj tag so we can find next time
}
flags->DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes
for (std::pair<uint32_t, uint64_t> flag : m_PlayerFlags) {
auto* f = m_Doc.NewElement("f");
auto* f = m_Doc->NewElement("f");
f->SetAttribute("id", flag.first);
//Because of the joy that is tinyxml2, it doesn't offer a function to set a uint64 as an attribute.
@@ -291,7 +301,7 @@ void Character::SaveXMLToDatabase() {
// Prevents the news feed from showing up on world transfers
if (GetPlayerFlag(ePlayerFlag::IS_NEWS_SCREEN_VISIBLE)) {
auto* s = m_Doc.NewElement("s");
auto* s = m_Doc->NewElement("s");
s->SetAttribute("si", ePlayerFlag::IS_NEWS_SCREEN_VISIBLE);
flags->LinkEndChild(s);
}
@@ -316,7 +326,7 @@ void Character::SaveXMLToDatabase() {
void Character::SetIsNewLogin() {
// If we dont have a flag element, then we cannot have a s element as a child of flag.
auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag");
if (!flags) return;
auto* currentChild = flags->FirstChildElement();
@@ -334,7 +344,7 @@ void Character::SetIsNewLogin() {
void Character::WriteToDatabase() {
//Dump our xml into m_XMLData:
tinyxml2::XMLPrinter printer(0, true, 0);
m_Doc.Print(&printer);
m_Doc->Print(&printer);
//Finally, save to db:
Database::Get()->UpdateCharacterXml(m_ID, printer.CStr());
@@ -411,15 +421,15 @@ void Character::SetRetroactiveFlags() {
void Character::SaveXmlRespawnCheckpoints() {
//Export our respawn points:
auto* points = m_Doc.FirstChildElement("obj")->FirstChildElement("res");
auto* points = m_Doc->FirstChildElement("obj")->FirstChildElement("res");
if (!points) {
points = m_Doc.NewElement("res");
m_Doc.FirstChildElement("obj")->LinkEndChild(points);
points = m_Doc->NewElement("res");
m_Doc->FirstChildElement("obj")->LinkEndChild(points);
}
points->DeleteChildren();
for (const auto& point : m_WorldRespawnCheckpoints) {
auto* r = m_Doc.NewElement("r");
auto* r = m_Doc->NewElement("r");
r->SetAttribute("w", point.first);
r->SetAttribute("x", point.second.x);
@@ -433,7 +443,7 @@ void Character::SaveXmlRespawnCheckpoints() {
void Character::LoadXmlRespawnCheckpoints() {
m_WorldRespawnCheckpoints.clear();
auto* points = m_Doc.FirstChildElement("obj")->FirstChildElement("res");
auto* points = m_Doc->FirstChildElement("obj")->FirstChildElement("res");
if (!points) {
return;
}

View File

@@ -37,7 +37,7 @@ public:
void LoadXmlRespawnCheckpoints();
const std::string& GetXMLData() const { return m_XMLData; }
const tinyxml2::XMLDocument& GetXMLDoc() const { return m_Doc; }
tinyxml2::XMLDocument* GetXMLDoc() const { return m_Doc; }
/**
* Out of abundance of safety and clarity of what this saves, this is its own function.
@@ -464,22 +464,22 @@ private:
/**
* The ID of this character. First 32 bits of the ObjectID.
*/
uint32_t m_ID{};
uint32_t m_ID;
/**
* The 64-bit unique ID used in the game.
*/
LWOOBJID m_ObjectID{ LWOOBJID_EMPTY };
LWOOBJID m_ObjectID;
/**
* The user that owns this character.
*/
User* m_ParentUser{};
User* m_ParentUser;
/**
* If the character is in game, this is the entity that it represents, else nullptr.
*/
Entity* m_OurEntity{};
Entity* m_OurEntity;
/**
* 0-9, the Game Master level of this character.
@@ -506,17 +506,17 @@ private:
/**
* Whether the custom name of this character is rejected
*/
bool m_NameRejected{};
bool m_NameRejected;
/**
* The current amount of coins of this character
*/
int64_t m_Coins{};
int64_t m_Coins;
/**
* Whether the character is building
*/
bool m_BuildMode{};
bool m_BuildMode;
/**
* The items equipped by the character on world load
@@ -583,7 +583,7 @@ private:
/**
* The ID of the properties of this character
*/
uint32_t m_PropertyCloneID{};
uint32_t m_PropertyCloneID;
/**
* The XML data for this character, stored as string
@@ -613,7 +613,7 @@ private:
/**
* The last time this character logged in
*/
uint64_t m_LastLogin{};
uint64_t m_LastLogin;
/**
* The gameplay flags this character has (not just true values)
@@ -623,7 +623,7 @@ private:
/**
* The character XML belonging to this character
*/
tinyxml2::XMLDocument m_Doc;
tinyxml2::XMLDocument* m_Doc;
/**
* Title of an announcement this character made (reserved for GMs)

View File

@@ -225,7 +225,7 @@ void Entity::Initialize() {
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
AddComponent<ModelComponent>()->LoadBehaviors();
AddComponent<ModelComponent>();
AddComponent<RenderComponent>();
@@ -476,7 +476,8 @@ void Entity::Initialize() {
}
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) {
AddComponent<InventoryComponent>();
auto* xmlDoc = m_Character ? m_Character->GetXMLDoc() : nullptr;
AddComponent<InventoryComponent>(xmlDoc);
}
// if this component exists, then we initialize it. it's value is always 0
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1) != -1) {
@@ -649,7 +650,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);
@@ -730,21 +731,15 @@ void Entity::Initialize() {
// if we have a moving platform path, then we need a moving platform component
if (path->pathType == PathType::MovingPlatform) {
AddComponent<MovingPlatformComponent>(pathName);
} else if (path->pathType == PathType::Movement) {
auto movementAIcomponent = GetComponent<MovementAIComponent>();
if (movementAIcomponent && combatAiId == 0) {
movementAIcomponent->SetPath(pathName);
// else if we are a movement path
} /*else if (path->pathType == PathType::Movement) {
auto movementAIcomp = GetComponent<MovementAIComponent>();
if (movementAIcomp){
// TODO: set path in existing movementAIComp
} else {
MovementAIInfo moveInfo = MovementAIInfo();
moveInfo.movementType = "";
moveInfo.wanderChance = 0;
moveInfo.wanderRadius = 16;
moveInfo.wanderSpeed = 2.5f;
moveInfo.wanderDelayMax = 5;
moveInfo.wanderDelayMin = 2;
AddComponent<MovementAIComponent>(moveInfo);
// TODO: create movementAIcomp and set path
}
}
}*/
} else {
// else we still need to setup moving platform if it has a moving platform comp but no path
int32_t movingPlatformComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1);
@@ -1243,7 +1238,7 @@ void Entity::WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType
outBitStream.Write0();
}
void Entity::UpdateXMLDoc(tinyxml2::XMLDocument& doc) {
void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) {
//This function should only ever be called from within Character, meaning doc should always exist when this is called.
//Naturally, we don't include any non-player components in this update function.
@@ -1534,7 +1529,7 @@ void Entity::Kill(Entity* murderer, const eKillType killType) {
bool waitForDeathAnimation = false;
if (destroyableComponent) {
waitForDeathAnimation = !destroyableComponent->GetIsSmashable() && destroyableComponent->GetDeathBehavior() == 0 && killType != eKillType::SILENT;
waitForDeathAnimation = destroyableComponent->GetDeathBehavior() == 0 && killType != eKillType::SILENT;
}
// Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction!
@@ -1634,8 +1629,10 @@ void Entity::PickupItem(const LWOOBJID& objectID) {
CDObjectSkillsTable* skillsTable = CDClientManager::GetTable<CDObjectSkillsTable>();
std::vector<CDObjectSkills> skills = skillsTable->Query([=](CDObjectSkills entry) {return (entry.objectTemplate == p.second.lot); });
for (CDObjectSkills skill : skills) {
CDSkillBehaviorTable* skillBehTable = CDClientManager::GetTable<CDSkillBehaviorTable>();
auto* skillComponent = GetComponent<SkillComponent>();
if (skillComponent) skillComponent->CastSkill(skill.skillID, GetObjectID(), GetObjectID(), skill.castOnType, NiQuaternion(0, 0, 0, 0));
if (skillComponent) skillComponent->CastSkill(skill.skillID, GetObjectID(), GetObjectID());
auto* missionComponent = GetComponent<MissionComponent>();
@@ -1840,12 +1837,6 @@ const NiPoint3& Entity::GetPosition() const {
return vehicel->GetPosition();
}
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
if (rigidBodyPhantomPhysicsComponent != nullptr) {
return rigidBodyPhantomPhysicsComponent->GetPosition();
}
return NiPoint3Constant::ZERO;
}
@@ -1874,12 +1865,6 @@ const NiQuaternion& Entity::GetRotation() const {
return vehicel->GetRotation();
}
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
if (rigidBodyPhantomPhysicsComponent != nullptr) {
return rigidBodyPhantomPhysicsComponent->GetRotation();
}
return NiQuaternionConstant::IDENTITY;
}
@@ -1908,12 +1893,6 @@ void Entity::SetPosition(const NiPoint3& position) {
vehicel->SetPosition(position);
}
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
if (rigidBodyPhantomPhysicsComponent != nullptr) {
rigidBodyPhantomPhysicsComponent->SetPosition(position);
}
Game::entityManager->SerializeEntity(this);
}
@@ -1942,12 +1921,6 @@ void Entity::SetRotation(const NiQuaternion& rotation) {
vehicel->SetRotation(rotation);
}
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
if (rigidBodyPhantomPhysicsComponent != nullptr) {
rigidBodyPhantomPhysicsComponent->SetRotation(rotation);
}
Game::entityManager->SerializeEntity(this);
}

View File

@@ -174,7 +174,7 @@ public:
void WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
void WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
void UpdateXMLDoc(tinyxml2::XMLDocument& doc);
void UpdateXMLDoc(tinyxml2::XMLDocument* doc);
void Update(float deltaTime);
// Events

View File

@@ -12,7 +12,6 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std:
m_AccountID = 0;
m_Username = "";
m_SessionKey = "";
m_MuteExpire = 0;
m_MaxGMLevel = eGameMasterLevel::CIVILIAN; //The max GM level this account can assign to it's characters
m_LastCharID = 0;

View File

@@ -26,7 +26,7 @@
#include "eCharacterCreationResponse.h"
#include "eRenameResponse.h"
#include "eConnectionType.h"
#include "eChatMessageType.h"
#include "eChatInternalMessageType.h"
#include "BitStreamUtils.h"
#include "CheatDetection.h"
@@ -83,7 +83,7 @@ void UserManager::Initialize() {
auto chatListStream = Game::assetManager->GetFile("chatplus_en_us.txt");
if (!chatListStream) {
LOG("Failed to load %s", (Game::assetManager->GetResPath() / "chatplus_en_us.txt").string().c_str());
throw std::runtime_error("Aborting initialization due to missing chat allowlist file.");
throw std::runtime_error("Aborting initialization due to missing chat whitelist file.");
}
while (std::getline(chatListStream, line, '\n')) {
@@ -422,7 +422,7 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
Database::Get()->DeleteCharacter(charID);
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::UNEXPECTED_DISCONNECT);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
bitStream.Write(objectID);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
@@ -536,13 +536,13 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
try {
auto stmt = CDClientDatabase::CreatePreppedStmt(
"select obj.id as objectId from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ? AND icc.decal == ?"
"select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ? AND icc.decal == ?"
);
stmt.bind(1, "character create shirt");
stmt.bind(2, static_cast<int>(shirtColor));
stmt.bind(3, static_cast<int>(shirtStyle));
auto tableData = stmt.execQuery();
auto shirtLOT = tableData.getIntField("objectId", 4069);
auto shirtLOT = tableData.getIntField(0, 4069);
tableData.finalize();
return shirtLOT;
} catch (const std::exception& ex) {
@@ -555,12 +555,12 @@ uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
uint32_t FindCharPantsID(uint32_t pantsColor) {
try {
auto stmt = CDClientDatabase::CreatePreppedStmt(
"select obj.id as objectId from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ?"
"select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ?"
);
stmt.bind(1, "cc pants");
stmt.bind(2, static_cast<int>(pantsColor));
auto tableData = stmt.execQuery();
auto pantsLOT = tableData.getIntField("objectId", 2508);
auto pantsLOT = tableData.getIntField(0, 2508);
tableData.finalize();
return pantsLOT;
} catch (const std::exception& ex) {

View File

@@ -222,7 +222,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
if (healthDamageDealt >= 1) {
successState = eBasicAttackSuccessTypes::SUCCESS;
} else if (armorDamageDealt >= 1) {
successState = this->m_OnFailArmor->m_templateId == BehaviorTemplate::EMPTY ? eBasicAttackSuccessTypes::FAILIMMUNE : eBasicAttackSuccessTypes::FAILARMOR;
successState = this->m_OnFailArmor->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY ? eBasicAttackSuccessTypes::FAILIMMUNE : eBasicAttackSuccessTypes::FAILARMOR;
}
bitStream.Write(armorDamageDealt);

View File

@@ -5,7 +5,7 @@
#include "CDActivitiesTable.h"
#include "Game.h"
#include "Logger.h"
#include "BehaviorTemplate.h"
#include "BehaviorTemplates.h"
#include "BehaviorBranchContext.h"
#include <unordered_map>
@@ -110,176 +110,176 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
Behavior* behavior = nullptr;
switch (templateId) {
case BehaviorTemplate::EMPTY: break;
case BehaviorTemplate::BASIC_ATTACK:
case BehaviorTemplates::BEHAVIOR_EMPTY: break;
case BehaviorTemplates::BEHAVIOR_BASIC_ATTACK:
behavior = new BasicAttackBehavior(behaviorId);
break;
case BehaviorTemplate::TAC_ARC:
case BehaviorTemplates::BEHAVIOR_TAC_ARC:
behavior = new TacArcBehavior(behaviorId);
break;
case BehaviorTemplate::AND:
case BehaviorTemplates::BEHAVIOR_AND:
behavior = new AndBehavior(behaviorId);
break;
case BehaviorTemplate::PROJECTILE_ATTACK:
case BehaviorTemplates::BEHAVIOR_PROJECTILE_ATTACK:
behavior = new ProjectileAttackBehavior(behaviorId);
break;
case BehaviorTemplate::HEAL:
case BehaviorTemplates::BEHAVIOR_HEAL:
behavior = new HealBehavior(behaviorId);
break;
case BehaviorTemplate::MOVEMENT_SWITCH:
case BehaviorTemplates::BEHAVIOR_MOVEMENT_SWITCH:
behavior = new MovementSwitchBehavior(behaviorId);
break;
case BehaviorTemplate::AREA_OF_EFFECT:
case BehaviorTemplates::BEHAVIOR_AREA_OF_EFFECT:
behavior = new AreaOfEffectBehavior(behaviorId);
break;
case BehaviorTemplate::PLAY_EFFECT:
case BehaviorTemplates::BEHAVIOR_PLAY_EFFECT:
behavior = new PlayEffectBehavior(behaviorId);
break;
case BehaviorTemplate::IMMUNITY:
case BehaviorTemplates::BEHAVIOR_IMMUNITY:
behavior = new ImmunityBehavior(behaviorId);
break;
case BehaviorTemplate::DAMAGE_BUFF: break;
case BehaviorTemplate::DAMAGE_ABSORBTION:
case BehaviorTemplates::BEHAVIOR_DAMAGE_BUFF: break;
case BehaviorTemplates::BEHAVIOR_DAMAGE_ABSORBTION:
behavior = new DamageAbsorptionBehavior(behaviorId);
break;
case BehaviorTemplate::OVER_TIME:
case BehaviorTemplates::BEHAVIOR_OVER_TIME:
behavior = new OverTimeBehavior(behaviorId);
break;
case BehaviorTemplate::IMAGINATION:
case BehaviorTemplates::BEHAVIOR_IMAGINATION:
behavior = new ImaginationBehavior(behaviorId);
break;
case BehaviorTemplate::TARGET_CASTER:
case BehaviorTemplates::BEHAVIOR_TARGET_CASTER:
behavior = new TargetCasterBehavior(behaviorId);
break;
case BehaviorTemplate::STUN:
case BehaviorTemplates::BEHAVIOR_STUN:
behavior = new StunBehavior(behaviorId);
break;
case BehaviorTemplate::DURATION:
case BehaviorTemplates::BEHAVIOR_DURATION:
behavior = new DurationBehavior(behaviorId);
break;
case BehaviorTemplate::KNOCKBACK:
case BehaviorTemplates::BEHAVIOR_KNOCKBACK:
behavior = new KnockbackBehavior(behaviorId);
break;
case BehaviorTemplate::ATTACK_DELAY:
case BehaviorTemplates::BEHAVIOR_ATTACK_DELAY:
behavior = new AttackDelayBehavior(behaviorId);
break;
case BehaviorTemplate::CAR_BOOST:
case BehaviorTemplates::BEHAVIOR_CAR_BOOST:
behavior = new CarBoostBehavior(behaviorId);
break;
case BehaviorTemplate::FALL_SPEED:
case BehaviorTemplates::BEHAVIOR_FALL_SPEED:
behavior = new FallSpeedBehavior(behaviorId);
break;
case BehaviorTemplate::SHIELD: break;
case BehaviorTemplate::REPAIR_ARMOR:
case BehaviorTemplates::BEHAVIOR_SHIELD: break;
case BehaviorTemplates::BEHAVIOR_REPAIR_ARMOR:
behavior = new RepairBehavior(behaviorId);
break;
case BehaviorTemplate::SPEED:
case BehaviorTemplates::BEHAVIOR_SPEED:
behavior = new SpeedBehavior(behaviorId);
break;
case BehaviorTemplate::DARK_INSPIRATION:
case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION:
behavior = new DarkInspirationBehavior(behaviorId);
break;
case BehaviorTemplate::LOOT_BUFF:
case BehaviorTemplates::BEHAVIOR_LOOT_BUFF:
behavior = new LootBuffBehavior(behaviorId);
break;
case BehaviorTemplate::VENTURE_VISION:
case BehaviorTemplates::BEHAVIOR_VENTURE_VISION:
behavior = new VentureVisionBehavior(behaviorId);
break;
case BehaviorTemplate::SPAWN_OBJECT:
case BehaviorTemplates::BEHAVIOR_SPAWN_OBJECT:
behavior = new SpawnBehavior(behaviorId);
break;
case BehaviorTemplate::LAY_BRICK: break;
case BehaviorTemplate::SWITCH:
case BehaviorTemplates::BEHAVIOR_LAY_BRICK: break;
case BehaviorTemplates::BEHAVIOR_SWITCH:
behavior = new SwitchBehavior(behaviorId);
break;
case BehaviorTemplate::BUFF:
case BehaviorTemplates::BEHAVIOR_BUFF:
behavior = new BuffBehavior(behaviorId);
break;
case BehaviorTemplate::JETPACK:
case BehaviorTemplates::BEHAVIOR_JETPACK:
behavior = new JetPackBehavior(behaviorId);
break;
case BehaviorTemplate::SKILL_EVENT:
case BehaviorTemplates::BEHAVIOR_SKILL_EVENT:
behavior = new SkillEventBehavior(behaviorId);
break;
case BehaviorTemplate::CONSUME_ITEM:
case BehaviorTemplates::BEHAVIOR_CONSUME_ITEM:
behavior = new ConsumeItemBehavior(behaviorId);
break;
case BehaviorTemplate::SKILL_CAST_FAILED:
case BehaviorTemplates::BEHAVIOR_SKILL_CAST_FAILED:
behavior = new SkillCastFailedBehavior(behaviorId);
break;
case BehaviorTemplate::IMITATION_SKUNK_STINK: break;
case BehaviorTemplate::CHANGE_IDLE_FLAGS:
case BehaviorTemplates::BEHAVIOR_IMITATION_SKUNK_STINK: break;
case BehaviorTemplates::BEHAVIOR_CHANGE_IDLE_FLAGS:
behavior = new ChangeIdleFlagsBehavior(behaviorId);
break;
case BehaviorTemplate::APPLY_BUFF:
case BehaviorTemplates::BEHAVIOR_APPLY_BUFF:
behavior = new ApplyBuffBehavior(behaviorId);
break;
case BehaviorTemplate::CHAIN:
case BehaviorTemplates::BEHAVIOR_CHAIN:
behavior = new ChainBehavior(behaviorId);
break;
case BehaviorTemplate::CHANGE_ORIENTATION:
case BehaviorTemplates::BEHAVIOR_CHANGE_ORIENTATION:
behavior = new ChangeOrientationBehavior(behaviorId);
break;
case BehaviorTemplate::FORCE_MOVEMENT:
case BehaviorTemplates::BEHAVIOR_FORCE_MOVEMENT:
behavior = new ForceMovementBehavior(behaviorId);
break;
case BehaviorTemplate::INTERRUPT:
case BehaviorTemplates::BEHAVIOR_INTERRUPT:
behavior = new InterruptBehavior(behaviorId);
break;
case BehaviorTemplate::ALTER_COOLDOWN: break;
case BehaviorTemplate::CHARGE_UP:
case BehaviorTemplates::BEHAVIOR_ALTER_COOLDOWN: break;
case BehaviorTemplates::BEHAVIOR_CHARGE_UP:
behavior = new ChargeUpBehavior(behaviorId);
break;
case BehaviorTemplate::SWITCH_MULTIPLE:
case BehaviorTemplates::BEHAVIOR_SWITCH_MULTIPLE:
behavior = new SwitchMultipleBehavior(behaviorId);
break;
case BehaviorTemplate::START:
case BehaviorTemplates::BEHAVIOR_START:
behavior = new StartBehavior(behaviorId);
break;
case BehaviorTemplate::END:
case BehaviorTemplates::BEHAVIOR_END:
behavior = new EndBehavior(behaviorId);
break;
case BehaviorTemplate::ALTER_CHAIN_DELAY: break;
case BehaviorTemplate::CAMERA: break;
case BehaviorTemplate::REMOVE_BUFF:
case BehaviorTemplates::BEHAVIOR_ALTER_CHAIN_DELAY: break;
case BehaviorTemplates::BEHAVIOR_CAMERA: break;
case BehaviorTemplates::BEHAVIOR_REMOVE_BUFF:
behavior = new RemoveBuffBehavior(behaviorId);
break;
case BehaviorTemplate::GRAB: break;
case BehaviorTemplate::MODULAR_BUILD: break;
case BehaviorTemplate::NPC_COMBAT_SKILL:
case BehaviorTemplates::BEHAVIOR_GRAB: break;
case BehaviorTemplates::BEHAVIOR_MODULAR_BUILD: break;
case BehaviorTemplates::BEHAVIOR_NPC_COMBAT_SKILL:
behavior = new NpcCombatSkillBehavior(behaviorId);
break;
case BehaviorTemplate::BLOCK:
case BehaviorTemplates::BEHAVIOR_BLOCK:
behavior = new BlockBehavior(behaviorId);
break;
case BehaviorTemplate::VERIFY:
case BehaviorTemplates::BEHAVIOR_VERIFY:
behavior = new VerifyBehavior(behaviorId);
break;
case BehaviorTemplate::TAUNT:
case BehaviorTemplates::BEHAVIOR_TAUNT:
behavior = new TauntBehavior(behaviorId);
break;
case BehaviorTemplate::AIR_MOVEMENT:
case BehaviorTemplates::BEHAVIOR_AIR_MOVEMENT:
behavior = new AirMovementBehavior(behaviorId);
break;
case BehaviorTemplate::SPAWN_QUICKBUILD:
case BehaviorTemplates::BEHAVIOR_SPAWN_QUICKBUILD:
behavior = new SpawnBehavior(behaviorId);
break;
case BehaviorTemplate::PULL_TO_POINT:
case BehaviorTemplates::BEHAVIOR_PULL_TO_POINT:
behavior = new PullToPointBehavior(behaviorId);
break;
case BehaviorTemplate::PROPERTY_ROTATE: break;
case BehaviorTemplate::DAMAGE_REDUCTION:
case BehaviorTemplates::BEHAVIOR_PROPERTY_ROTATE: break;
case BehaviorTemplates::BEHAVIOR_DAMAGE_REDUCTION:
behavior = new DamageReductionBehavior(behaviorId);
break;
case BehaviorTemplate::PROPERTY_TELEPORT:
case BehaviorTemplates::BEHAVIOR_PROPERTY_TELEPORT:
behavior = new PropertyTeleportBehavior(behaviorId);
break;
case BehaviorTemplate::PROPERTY_CLEAR_TARGET:
case BehaviorTemplates::BEHAVIOR_PROPERTY_CLEAR_TARGET:
behavior = new ClearTargetBehavior(behaviorId);
break;
case BehaviorTemplate::TAKE_PICTURE: break;
case BehaviorTemplate::MOUNT: break;
case BehaviorTemplate::SKILL_SET: break;
case BehaviorTemplates::BEHAVIOR_TAKE_PICTURE: break;
case BehaviorTemplates::BEHAVIOR_MOUNT: break;
case BehaviorTemplates::BEHAVIOR_SKILL_SET: break;
default:
//LOG("Failed to load behavior with invalid template id (%i)!", templateId);
break;
@@ -296,19 +296,19 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
return behavior;
}
BehaviorTemplate Behavior::GetBehaviorTemplate(const uint32_t behaviorId) {
BehaviorTemplates Behavior::GetBehaviorTemplate(const uint32_t behaviorId) {
auto behaviorTemplateTable = CDClientManager::GetTable<CDBehaviorTemplateTable>();
BehaviorTemplate templateID = BehaviorTemplate::EMPTY;
BehaviorTemplates templateID = BehaviorTemplates::BEHAVIOR_EMPTY;
// Find behavior template by its behavior id. Default to 0.
if (behaviorTemplateTable) {
auto templateEntry = behaviorTemplateTable->GetByBehaviorID(behaviorId);
if (templateEntry.behaviorID == behaviorId) {
templateID = static_cast<BehaviorTemplate>(templateEntry.templateID);
templateID = static_cast<BehaviorTemplates>(templateEntry.templateID);
}
}
if (templateID == BehaviorTemplate::EMPTY && behaviorId != 0) {
if (templateID == BehaviorTemplates::BEHAVIOR_EMPTY && behaviorId != 0) {
LOG("Failed to load behavior template with id (%i)!", behaviorId);
}
@@ -335,22 +335,26 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
const auto typeString = GeneralUtils::UTF16ToWTF8(type);
const auto itr = m_effectNames.find(typeString);
if (m_effectNames == nullptr) {
m_effectNames = new std::unordered_map<std::string, std::string>();
} else {
const auto pair = m_effectNames->find(typeString);
if (type.empty()) {
type = GeneralUtils::ASCIIToUTF16(m_effectType);
}
if (type.empty()) {
type = GeneralUtils::ASCIIToUTF16(*m_effectType);
}
if (itr != m_effectNames.end()) {
if (renderComponent == nullptr) {
GameMessages::SendPlayFXEffect(targetEntity, effectId, type, itr->second, secondary, 1, 1, true);
if (pair != m_effectNames->end()) {
if (renderComponent == nullptr) {
GameMessages::SendPlayFXEffect(targetEntity, effectId, type, pair->second, secondary, 1, 1, true);
return;
}
renderComponent->PlayEffect(effectId, type, pair->second, secondary);
return;
}
renderComponent->PlayEffect(effectId, type, itr->second, secondary);
return;
}
// The SQlite result object becomes invalid if the query object leaves scope.
@@ -377,19 +381,19 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
return;
}
const auto name = std::string(result.getStringField("effectName"));
const auto name = std::string(result.getStringField(0));
if (type.empty()) {
const auto typeResult = result.getStringField("effectType");
const auto typeResult = result.getStringField(1);
type = GeneralUtils::ASCIIToUTF16(typeResult);
m_effectType = typeResult;
m_effectType = new std::string(typeResult);
}
result.finalize();
m_effectNames.insert_or_assign(typeString, name);
m_effectNames->insert_or_assign(typeString, name);
if (renderComponent == nullptr) {
GameMessages::SendPlayFXEffect(targetEntity, effectId, type, name, secondary, 1, 1, true);
@@ -419,7 +423,8 @@ Behavior::Behavior(const uint32_t behaviorId) {
if (behaviorId == 0) {
this->m_effectId = 0;
this->m_templateId = BehaviorTemplate::EMPTY;
this->m_effectHandle = nullptr;
this->m_templateId = BehaviorTemplates::BEHAVIOR_EMPTY;
}
// Make sure we do not proceed if we are trying to load an invalid behavior
@@ -427,16 +432,17 @@ Behavior::Behavior(const uint32_t behaviorId) {
LOG("Failed to load behavior with id (%i)!", behaviorId);
this->m_effectId = 0;
this->m_templateId = BehaviorTemplate::EMPTY;
this->m_effectHandle = nullptr;
this->m_templateId = BehaviorTemplates::BEHAVIOR_EMPTY;
return;
}
this->m_templateId = static_cast<BehaviorTemplate>(templateInDatabase.templateID);
this->m_templateId = static_cast<BehaviorTemplates>(templateInDatabase.templateID);
this->m_effectId = templateInDatabase.effectID;
this->m_effectHandle = *templateInDatabase.effectHandle;
this->m_effectHandle = *templateInDatabase.effectHandle != "" ? new std::string(*templateInDatabase.effectHandle) : nullptr;
}
@@ -501,3 +507,9 @@ void Behavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream,
void Behavior::SyncCalculation(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
}
Behavior::~Behavior() {
delete m_effectNames;
delete m_effectType;
delete m_effectHandle;
}

View File

@@ -6,7 +6,7 @@
#include <unordered_map>
#include "BitStream.h"
#include "BehaviorTemplate.h"
#include "BehaviorTemplates.h"
#include "dCommonVars.h"
struct BehaviorContext;
@@ -26,7 +26,7 @@ public:
static Behavior* CreateBehavior(uint32_t behaviorId);
static BehaviorTemplate GetBehaviorTemplate(uint32_t behaviorId);
static BehaviorTemplates GetBehaviorTemplate(uint32_t behaviorId);
/*
* Utilities
@@ -39,11 +39,11 @@ public:
*/
uint32_t m_behaviorId;
BehaviorTemplate m_templateId;
BehaviorTemplates m_templateId;
uint32_t m_effectId;
std::string m_effectHandle;
std::unordered_map<std::string, std::string> m_effectNames;
std::string m_effectType;
std::string* m_effectHandle = nullptr;
std::unordered_map<std::string, std::string>* m_effectNames = nullptr;
std::string* m_effectType = nullptr;
/*
* Behavior parameters
@@ -88,11 +88,5 @@ public:
*/
explicit Behavior(uint32_t behaviorId);
virtual ~Behavior() = default;
Behavior(const Behavior& other) = default;
Behavior(Behavior&& other) = default;
Behavior& operator=(const Behavior& other) = default;
Behavior& operator=(Behavior&& other) = default;
virtual ~Behavior();
};

View File

@@ -105,7 +105,7 @@ void BehaviorContext::ExecuteUpdates() {
this->scheduledUpdates.clear();
}
bool BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) {
void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) {
BehaviorSyncEntry entry;
auto found = false;
@@ -128,7 +128,7 @@ bool BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
if (!found) {
LOG("Failed to find behavior sync entry with sync id (%i)!", syncId);
return false;
return;
}
auto* behavior = entry.behavior;
@@ -137,11 +137,10 @@ bool BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
if (behavior == nullptr) {
LOG("Invalid behavior for sync id (%i)!", syncId);
return false;
return;
}
behavior->Sync(this, bitStream, branch);
return true;
}
@@ -199,26 +198,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);
}
}
@@ -245,16 +224,6 @@ bool BehaviorContext::CalculateUpdate(const float deltaTime) {
for (auto i = 0u; i < this->syncEntries.size(); ++i) {
auto entry = this->syncEntries.at(i);
if (entry.behavior->m_templateId == BehaviorTemplate::ATTACK_DELAY) {
auto* self = Game::entityManager->GetEntity(originator);
if (self) {
auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
if (destroyableComponent && destroyableComponent->GetHealth() <= 0) {
continue;
}
}
}
if (entry.time > 0) {
entry.time -= deltaTime;
@@ -364,7 +333,7 @@ void BehaviorContext::FilterTargets(std::vector<Entity*>& targets, std::forward_
}
// handle targeting the caster
if (candidate == caster) {
if (candidate == caster){
// if we aren't targeting self, erase, otherise increment and continue
if (!targetSelf) index = targets.erase(index);
else index++;
@@ -387,24 +356,24 @@ void BehaviorContext::FilterTargets(std::vector<Entity*>& targets, std::forward_
}
// if they are dead, then earse and continue
if (candidateDestroyableComponent->GetIsDead()) {
if (candidateDestroyableComponent->GetIsDead()){
index = targets.erase(index);
continue;
}
// if their faction is explicitly included, increment and continue
auto candidateFactions = candidateDestroyableComponent->GetFactionIDs();
if (CheckFactionList(includeFactionList, candidateFactions)) {
if (CheckFactionList(includeFactionList, candidateFactions)){
index++;
continue;
}
// check if they are a team member
if (targetTeam) {
if (targetTeam){
auto* team = TeamManager::Instance()->GetTeam(this->caster);
if (team) {
if (team){
// if we find a team member keep it and continue to skip enemy checks
if (std::find(team->members.begin(), team->members.end(), candidate->GetObjectID()) != team->members.end()) {
if(std::find(team->members.begin(), team->members.end(), candidate->GetObjectID()) != team->members.end()){
index++;
continue;
}
@@ -450,8 +419,8 @@ bool BehaviorContext::CheckTargetingRequirements(const Entity* target) const {
// returns true if any of the object factions are in the faction list
bool BehaviorContext::CheckFactionList(std::forward_list<int32_t>& factionList, std::vector<int32_t>& objectsFactions) const {
if (factionList.empty() || objectsFactions.empty()) return false;
for (auto faction : factionList) {
if (std::find(objectsFactions.begin(), objectsFactions.end(), faction) != objectsFactions.end()) return true;
for (auto faction : factionList){
if(std::find(objectsFactions.begin(), objectsFactions.end(), faction) != objectsFactions.end()) return true;
}
return false;
}

View File

@@ -93,7 +93,7 @@ struct BehaviorContext
void ExecuteUpdates();
bool SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream);
void SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream);
void Update(float deltaTime);

View File

@@ -1,70 +0,0 @@
#pragma once
enum class BehaviorTemplate : unsigned int {
EMPTY, // Not a real behavior, indicates invalid behaviors
BASIC_ATTACK,
TAC_ARC,
AND,
PROJECTILE_ATTACK,
HEAL,
MOVEMENT_SWITCH,
AREA_OF_EFFECT,
PLAY_EFFECT,
IMMUNITY,
DAMAGE_BUFF,
DAMAGE_ABSORBTION,
OVER_TIME,
IMAGINATION,
TARGET_CASTER,
STUN,
DURATION,
KNOCKBACK,
ATTACK_DELAY,
CAR_BOOST,
FALL_SPEED,
SHIELD,
REPAIR_ARMOR,
SPEED,
DARK_INSPIRATION,
LOOT_BUFF,
VENTURE_VISION,
SPAWN_OBJECT,
LAY_BRICK,
SWITCH,
BUFF,
JETPACK,
SKILL_EVENT,
CONSUME_ITEM,
SKILL_CAST_FAILED,
IMITATION_SKUNK_STINK,
CHANGE_IDLE_FLAGS,
APPLY_BUFF,
CHAIN,
CHANGE_ORIENTATION,
FORCE_MOVEMENT,
INTERRUPT,
ALTER_COOLDOWN,
CHARGE_UP,
SWITCH_MULTIPLE,
START,
END,
ALTER_CHAIN_DELAY,
CAMERA,
REMOVE_BUFF,
GRAB,
MODULAR_BUILD,
NPC_COMBAT_SKILL,
BLOCK,
VERIFY,
TAUNT,
AIR_MOVEMENT,
SPAWN_QUICKBUILD,
PULL_TO_POINT,
PROPERTY_ROTATE,
DAMAGE_REDUCTION,
PROPERTY_TELEPORT,
PROPERTY_CLEAR_TARGET,
TAKE_PICTURE,
MOUNT,
SKILL_SET
};

View File

@@ -0,0 +1 @@
#include "BehaviorTemplates.h"

View File

@@ -0,0 +1,70 @@
#pragma once
enum class BehaviorTemplates : unsigned int {
BEHAVIOR_EMPTY, // Not a real behavior, indicates invalid behaviors
BEHAVIOR_BASIC_ATTACK,
BEHAVIOR_TAC_ARC,
BEHAVIOR_AND,
BEHAVIOR_PROJECTILE_ATTACK,
BEHAVIOR_HEAL,
BEHAVIOR_MOVEMENT_SWITCH,
BEHAVIOR_AREA_OF_EFFECT,
BEHAVIOR_PLAY_EFFECT,
BEHAVIOR_IMMUNITY,
BEHAVIOR_DAMAGE_BUFF,
BEHAVIOR_DAMAGE_ABSORBTION,
BEHAVIOR_OVER_TIME,
BEHAVIOR_IMAGINATION,
BEHAVIOR_TARGET_CASTER,
BEHAVIOR_STUN,
BEHAVIOR_DURATION,
BEHAVIOR_KNOCKBACK,
BEHAVIOR_ATTACK_DELAY,
BEHAVIOR_CAR_BOOST,
BEHAVIOR_FALL_SPEED,
BEHAVIOR_SHIELD,
BEHAVIOR_REPAIR_ARMOR,
BEHAVIOR_SPEED,
BEHAVIOR_DARK_INSPIRATION,
BEHAVIOR_LOOT_BUFF,
BEHAVIOR_VENTURE_VISION,
BEHAVIOR_SPAWN_OBJECT,
BEHAVIOR_LAY_BRICK,
BEHAVIOR_SWITCH,
BEHAVIOR_BUFF,
BEHAVIOR_JETPACK,
BEHAVIOR_SKILL_EVENT,
BEHAVIOR_CONSUME_ITEM,
BEHAVIOR_SKILL_CAST_FAILED,
BEHAVIOR_IMITATION_SKUNK_STINK,
BEHAVIOR_CHANGE_IDLE_FLAGS,
BEHAVIOR_APPLY_BUFF,
BEHAVIOR_CHAIN,
BEHAVIOR_CHANGE_ORIENTATION,
BEHAVIOR_FORCE_MOVEMENT,
BEHAVIOR_INTERRUPT,
BEHAVIOR_ALTER_COOLDOWN,
BEHAVIOR_CHARGE_UP,
BEHAVIOR_SWITCH_MULTIPLE,
BEHAVIOR_START,
BEHAVIOR_END,
BEHAVIOR_ALTER_CHAIN_DELAY,
BEHAVIOR_CAMERA,
BEHAVIOR_REMOVE_BUFF,
BEHAVIOR_GRAB,
BEHAVIOR_MODULAR_BUILD,
BEHAVIOR_NPC_COMBAT_SKILL,
BEHAVIOR_BLOCK,
BEHAVIOR_VERIFY,
BEHAVIOR_TAUNT,
BEHAVIOR_AIR_MOVEMENT,
BEHAVIOR_SPAWN_QUICKBUILD,
BEHAVIOR_PULL_TO_POINT,
BEHAVIOR_PROPERTY_ROTATE,
BEHAVIOR_DAMAGE_REDUCTION,
BEHAVIOR_PROPERTY_TELEPORT,
BEHAVIOR_PROPERTY_CLEAR_TARGET,
BEHAVIOR_TAKE_PICTURE,
BEHAVIOR_MOUNT,
BEHAVIOR_SKILL_SET
};

View File

@@ -7,6 +7,7 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
"Behavior.cpp"
"BehaviorBranchContext.cpp"
"BehaviorContext.cpp"
"BehaviorTemplates.cpp"
"BlockBehavior.cpp"
"BuffBehavior.cpp"
"CarBoostBehavior.cpp"

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

@@ -7,7 +7,7 @@
#include "Logger.h"
void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, const BehaviorBranchContext branch) {
if (this->m_hitAction->m_templateId == BehaviorTemplate::EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplate::EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplate::EMPTY) {
if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) {
return;
}
@@ -38,7 +38,7 @@ void ForceMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream& bi
}
void ForceMovementBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
if (this->m_hitAction->m_templateId == BehaviorTemplate::EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplate::EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplate::EMPTY) {
if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) {
return;
}

View File

@@ -8,50 +8,36 @@
void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
LWOOBJID usedTarget = m_target ? branch.target : context->originator;
if (branch.target != context->originator) {
bool unknown = false;
if (usedTarget != context->originator) {
bool isTargetImmuneStuns = false;
if (!bitStream.Read(isTargetImmuneStuns)) {
LOG("Unable to read isTargetImmune from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
if (!bitStream.Read(unknown)) {
LOG("Unable to read unknown1 from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
return;
};
if (isTargetImmuneStuns) return;
if (unknown) return;
}
if (!this->m_interruptBlock) {
bool isBlockingInterrupts = false;
if (!bitStream.Read(isBlockingInterrupts)) {
LOG("Unable to read isBlockingInterrupts from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
bool unknown = false;
if (!bitStream.Read(unknown)) {
LOG("Unable to read unknown2 from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
return;
};
if (isBlockingInterrupts) return;
if (unknown) return;
}
bool hasInterruptedStatusEffects = false;
if (!bitStream.Read(hasInterruptedStatusEffects)) {
LOG("Unable to read hasInterruptedStatusEffects from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
return;
};
if (this->m_target) // Guess...
{
bool unknown = false;
if (hasInterruptedStatusEffects) {
bool hasMoreInterruptedStatusEffects = false;
int32_t loopLimit = 0;
while (bitStream.Read(hasMoreInterruptedStatusEffects) && hasMoreInterruptedStatusEffects) {
int32_t statusEffectID = 0;
bitStream.Read(statusEffectID);
// nothing happens with this data yes. I have no idea why or what it was used for, but the client literally just reads it and does nothing with it.
// 0x004faca4 for a reference. it also has a hard loop limit of 100 soo,
loopLimit++;
if (loopLimit > 100) {
// if this is hit you have a problem
LOG("Loop limit reached for interrupted status effects, aborting Handle due to bad bitstream! %i", bitStream.GetNumberOfUnreadBits());
break;
}
LOG_DEBUG("Interrupted status effect ID: %i", statusEffectID);
}
if (!bitStream.Read(unknown)) {
LOG("Unable to read unknown3 from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
return;
};
}
if (branch.target == context->originator) return;
@@ -69,8 +55,7 @@ void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitS
void InterruptBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
LWOOBJID usedTarget = m_target ? branch.target : context->originator;
if (usedTarget != context->originator) {
if (branch.target != context->originator) {
bitStream.Write(false);
}

View File

@@ -6,13 +6,13 @@
void MovementSwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, const BehaviorBranchContext branch) {
uint32_t movementType{};
if (!bitStream.Read(movementType)) {
if (this->m_groundAction->m_templateId == BehaviorTemplate::EMPTY &&
this->m_jumpAction->m_templateId == BehaviorTemplate::EMPTY &&
this->m_fallingAction->m_templateId == BehaviorTemplate::EMPTY &&
this->m_doubleJumpAction->m_templateId == BehaviorTemplate::EMPTY &&
this->m_airAction->m_templateId == BehaviorTemplate::EMPTY &&
this->m_jetpackAction->m_templateId == BehaviorTemplate::EMPTY &&
this->m_movingAction->m_templateId == BehaviorTemplate::EMPTY) {
if (this->m_groundAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY &&
this->m_jumpAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY &&
this->m_fallingAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY &&
this->m_doubleJumpAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY &&
this->m_airAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY &&
this->m_jetpackAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY &&
this->m_movingAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) {
return;
}
LOG("Unable to read movementType from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
@@ -47,7 +47,7 @@ void MovementSwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream&
Behavior* MovementSwitchBehavior::LoadMovementType(std::string movementType) {
float actionValue = GetFloat(movementType, -1.0f);
auto loadedBehavior = GetAction(actionValue != -1.0f ? actionValue : 0.0f);
if (actionValue == -1.0f && loadedBehavior->m_templateId == BehaviorTemplate::EMPTY) {
if (actionValue == -1.0f && loadedBehavior->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) {
loadedBehavior = this->m_groundAction;
}
return loadedBehavior;

View File

@@ -9,16 +9,17 @@ void SkillEventBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bit
auto* target = Game::entityManager->GetEntity(branch.target);
auto* caster = Game::entityManager->GetEntity(context->originator);
if (caster != nullptr && target != nullptr && !this->m_effectHandle.empty()) {
target->GetScript()->OnSkillEventFired(target, caster, this->m_effectHandle);
if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) {
target->GetScript()->OnSkillEventFired(target, caster, *this->m_effectHandle);
}
}
void SkillEventBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
void
SkillEventBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
auto* target = Game::entityManager->GetEntity(branch.target);
auto* caster = Game::entityManager->GetEntity(context->originator);
if (caster != nullptr && target != nullptr && !this->m_effectHandle.empty()) {
target->GetScript()->OnSkillEventFired(target, caster, this->m_effectHandle);
if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) {
target->GetScript()->OnSkillEventFired(target, caster, *this->m_effectHandle);
}
}

View File

@@ -47,11 +47,11 @@ void SwitchMultipleBehavior::Load() {
auto result = query.execQuery();
while (!result.eof()) {
const auto behavior_id = static_cast<uint32_t>(result.getFloatField("behavior"));
const auto behavior_id = static_cast<uint32_t>(result.getFloatField(1));
auto* behavior = CreateBehavior(behavior_id);
auto value = result.getFloatField("value");
auto value = result.getFloatField(2);
this->m_behaviors.emplace_back(value, behavior);

View File

@@ -21,7 +21,7 @@
#include "eMissionTaskType.h"
#include "eMatchUpdate.h"
#include "eConnectionType.h"
#include "eChatMessageType.h"
#include "eChatInternalMessageType.h"
#include "CDCurrencyTableTable.h"
#include "CDActivityRewardsTable.h"
@@ -501,7 +501,7 @@ void ActivityInstance::StartZone() {
// only make a team if we have more than one participant
if (participants.size() > 1) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::CREATE_TEAM);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM);
bitStream.Write(leader->GetObjectID());
bitStream.Write(m_Participants.size());

View File

@@ -29,8 +29,7 @@
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
m_Target = LWOOBJID_EMPTY;
m_DirtyStateOrTarget = true;
m_State = AiState::spawn;
SetAiState(AiState::spawn);
m_Timer = 1.0f;
m_StartPosition = parent->GetPosition();
m_MovementAI = nullptr;
@@ -46,20 +45,20 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
auto componentResult = componentQuery.execQuery();
if (!componentResult.eof()) {
if (!componentResult.fieldIsNull("aggroRadius"))
m_AggroRadius = componentResult.getFloatField("aggroRadius");
if (!componentResult.fieldIsNull(0))
m_AggroRadius = componentResult.getFloatField(0);
if (!componentResult.fieldIsNull("tetherSpeed"))
m_TetherSpeed = componentResult.getFloatField("tetherSpeed");
if (!componentResult.fieldIsNull(1))
m_TetherSpeed = componentResult.getFloatField(1);
if (!componentResult.fieldIsNull("pursuitSpeed"))
m_PursuitSpeed = componentResult.getFloatField("pursuitSpeed");
if (!componentResult.fieldIsNull(2))
m_PursuitSpeed = componentResult.getFloatField(2);
if (!componentResult.fieldIsNull("softTetherRadius"))
m_SoftTetherRadius = componentResult.getFloatField("softTetherRadius");
if (!componentResult.fieldIsNull(3))
m_SoftTetherRadius = componentResult.getFloatField(3);
if (!componentResult.fieldIsNull("hardTetherRadius"))
m_HardTetherRadius = componentResult.getFloatField("hardTetherRadius");
if (!componentResult.fieldIsNull(4))
m_HardTetherRadius = componentResult.getFloatField(4);
}
componentResult.finalize();
@@ -83,11 +82,11 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
auto result = skillQuery.execQuery();
while (!result.eof()) {
const auto skillId = static_cast<uint32_t>(result.getIntField("skillID"));
const auto skillId = static_cast<uint32_t>(result.getIntField(0));
const auto abilityCooldown = static_cast<float>(result.getFloatField("cooldown"));
const auto abilityCooldown = static_cast<float>(result.getFloatField(1));
const auto behaviorId = static_cast<uint32_t>(result.getIntField("behaviorID"));
const auto behaviorId = static_cast<uint32_t>(result.getIntField(2));
auto* behavior = Behavior::CreateBehavior(behaviorId);
@@ -151,18 +150,19 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
m_dpEntityEnemy->SetPosition(m_Parent->GetPosition());
//Process enter events
for (const auto id : m_dpEntity->GetNewObjects()) {
m_Parent->OnCollisionPhantom(id);
for (auto en : m_dpEntity->GetNewObjects()) {
m_Parent->OnCollisionPhantom(en->GetObjectID());
}
//Process exit events
for (const auto id : m_dpEntity->GetRemovedObjects()) {
m_Parent->OnCollisionLeavePhantom(id);
for (auto en : m_dpEntity->GetRemovedObjects()) {
m_Parent->OnCollisionLeavePhantom(en->GetObjectID());
}
// Check if we should stop the tether effect
if (m_TetherEffectActive) {
m_TetherTime -= deltaTime;
const auto& info = m_MovementAI->GetInfo();
if (m_Target != LWOOBJID_EMPTY || (NiPoint3::DistanceSquared(
m_StartPosition,
m_Parent->GetPosition()) < 20 * 20 && m_TetherTime <= 0)

View File

@@ -326,9 +326,9 @@ Entity* BuffComponent::GetParent() const {
return m_Parent;
}
void BuffComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
// Load buffs
auto* dest = doc.FirstChildElement("obj")->FirstChildElement("dest");
auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
// Make sure we have a clean buff element.
auto* buffElement = dest->FirstChildElement("buff");
@@ -386,15 +386,15 @@ void BuffComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
}
}
void BuffComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
// Save buffs
auto* dest = doc.FirstChildElement("obj")->FirstChildElement("dest");
auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
// Make sure we have a clean buff element.
auto* buffElement = dest->FirstChildElement("buff");
if (buffElement == nullptr) {
buffElement = doc.NewElement("buff");
buffElement = doc->NewElement("buff");
dest->LinkEndChild(buffElement);
} else {
@@ -402,7 +402,7 @@ void BuffComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
}
for (const auto& [id, buff] : m_Buffs) {
auto* buffEntry = doc.NewElement("b");
auto* buffEntry = doc->NewElement("b");
// TODO: change this if to if (buff.cancelOnZone || buff.cancelOnLogout) handling at some point. No current way to differentiate between zone transfer and logout.
if (buff.cancelOnZone) continue;
@@ -450,7 +450,7 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
param.value = result.getFloatField("NumberValue");
param.effectId = result.getIntField("EffectID");
if (!result.fieldIsNull("StringValue")) {
if (!result.fieldIsNull(3)) {
std::istringstream stream(result.getStringField("StringValue"));
std::string token;

View File

@@ -57,9 +57,9 @@ public:
Entity* GetParent() const;
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -186,9 +186,9 @@ void CharacterComponent::SetGMLevel(eGameMasterLevel gmlevel) {
m_GMLevel = gmlevel;
}
void CharacterComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
auto* character = doc.FirstChildElement("obj")->FirstChildElement("char");
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
if (!character) {
LOG("Failed to find char tag while loading XML!");
return;
@@ -299,8 +299,8 @@ void CharacterComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
}
}
void CharacterComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* minifig = doc.FirstChildElement("obj")->FirstChildElement("mf");
void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
tinyxml2::XMLElement* minifig = doc->FirstChildElement("obj")->FirstChildElement("mf");
if (!minifig) {
LOG("Failed to find mf tag while updating XML!");
return;
@@ -320,7 +320,7 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
// done with minifig
tinyxml2::XMLElement* character = doc.FirstChildElement("obj")->FirstChildElement("char");
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
if (!character) {
LOG("Failed to find char tag while updating XML!");
return;
@@ -338,11 +338,11 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
// Set the zone statistics of the form <zs><s/> ... <s/></zs>
auto zoneStatistics = character->FirstChildElement("zs");
if (!zoneStatistics) zoneStatistics = doc.NewElement("zs");
if (!zoneStatistics) zoneStatistics = doc->NewElement("zs");
zoneStatistics->DeleteChildren();
for (auto pair : m_ZoneStatistics) {
auto zoneStatistic = doc.NewElement("s");
auto zoneStatistic = doc->NewElement("s");
zoneStatistic->SetAttribute("map", pair.first);
zoneStatistic->SetAttribute("ac", pair.second.m_AchievementsCollected);

View File

@@ -70,8 +70,8 @@ public:
CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress);
~CharacterComponent() override;
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument& doc) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -21,11 +21,11 @@ void Component::OnUse(Entity* originator) {
}
void Component::UpdateXml(tinyxml2::XMLDocument& doc) {
void Component::UpdateXml(tinyxml2::XMLDocument* doc) {
}
void Component::LoadFromXml(const tinyxml2::XMLDocument& doc) {
void Component::LoadFromXml(tinyxml2::XMLDocument* doc) {
}

View File

@@ -34,13 +34,13 @@ public:
* Save data from this componennt to character XML
* @param doc the document to write data to
*/
virtual void UpdateXml(tinyxml2::XMLDocument& doc);
virtual void UpdateXml(tinyxml2::XMLDocument* doc);
/**
* Load base data for this component from character XML
* @param doc the document to read data from
*/
virtual void LoadFromXml(const tinyxml2::XMLDocument& doc);
virtual void LoadFromXml(tinyxml2::XMLDocument* doc);
virtual void Serialize(RakNet::BitStream& outBitStream, bool isConstruction);

View File

@@ -158,8 +158,8 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo
}
}
void ControllablePhysicsComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
auto* character = doc.FirstChildElement("obj")->FirstChildElement("char");
void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
if (!character) {
LOG("Failed to find char tag!");
return;
@@ -178,8 +178,8 @@ void ControllablePhysicsComponent::LoadFromXml(const tinyxml2::XMLDocument& doc)
m_DirtyPosition = true;
}
void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* character = doc.FirstChildElement("obj")->FirstChildElement("char");
void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
if (!character) {
LOG("Failed to find char tag while updating XML!");
return;

View File

@@ -28,8 +28,8 @@ public:
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument& doc) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override;
/**
* Sets the position of this entity, also ensures this update is serialized next tick.

View File

@@ -185,8 +185,8 @@ void DestroyableComponent::Update(float deltaTime) {
m_DamageCooldownTimer -= deltaTime;
}
void DestroyableComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
auto* dest = doc.FirstChildElement("obj")->FirstChildElement("dest");
void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
if (!dest) {
LOG("Failed to find dest tag!");
return;
@@ -207,8 +207,8 @@ void DestroyableComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
m_DirtyHealth = true;
}
void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* dest = doc.FirstChildElement("obj")->FirstChildElement("dest");
void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
if (!dest) {
LOG("Failed to find dest tag!");
return;
@@ -389,9 +389,9 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
if (result.eof()) return;
if (result.fieldIsNull("enemyList")) return;
if (result.fieldIsNull(0)) return;
const auto* list_string = result.getStringField("enemyList");
const auto* list_string = result.getStringField(0);
std::stringstream ss(list_string);
std::string token;

View File

@@ -26,8 +26,8 @@ public:
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument& doc) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override;
/**
* Initializes the component using a different LOT

View File

@@ -37,11 +37,8 @@
#include "CDScriptComponentTable.h"
#include "CDObjectSkillsTable.h"
#include "CDSkillBehaviorTable.h"
#include "StringifiedEnum.h"
#include <ranges>
InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent) {
this->m_Dirty = true;
this->m_Equipped = {};
this->m_Pushed = {};
@@ -51,8 +48,7 @@ InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
const auto lot = parent->GetLOT();
if (lot == 1) {
auto* character = m_Parent->GetCharacter();
if (character) LoadXml(character->GetXMLDoc());
LoadXml(document);
CheckProxyIntegrity();
@@ -476,10 +472,10 @@ bool InventoryComponent::HasSpaceForLoot(const std::unordered_map<LOT, int32_t>&
return true;
}
void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) {
LoadPetXml(document);
auto* inventoryElement = document.FirstChildElement("obj")->FirstChildElement("inv");
auto* inventoryElement = document->FirstChildElement("obj")->FirstChildElement("inv");
if (inventoryElement == nullptr) {
LOG("Failed to find 'inv' xml element!");
@@ -495,11 +491,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();
@@ -566,9 +557,19 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
itemElement->QueryAttribute("parent", &parent);
// End custom xml
auto* item = new Item(id, lot, inventory, slot, count, bound, {}, parent, subKey);
std::vector<LDFBaseData*> config;
item->LoadConfigXml(*itemElement);
auto* extraInfo = itemElement->FirstChildElement("x");
if (extraInfo) {
std::string modInfo = extraInfo->Attribute("ma");
LDFBaseData* moduleAssembly = new LDFData<std::u16string>(u"assemblyPartLOTs", GeneralUtils::ASCIIToUTF16(modInfo.substr(2, modInfo.size() - 1)));
config.push_back(moduleAssembly);
}
const auto* item = new Item(id, lot, inventory, slot, count, bound, config, parent, subKey);
if (equipped) {
const auto info = Inventory::FindItemComponent(lot);
@@ -593,10 +594,10 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
}
}
void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
UpdatePetXml(document);
auto* inventoryElement = document.FirstChildElement("obj")->FirstChildElement("inv");
auto* inventoryElement = document->FirstChildElement("obj")->FirstChildElement("inv");
if (inventoryElement == nullptr) {
LOG("Failed to find 'inv' xml element!");
@@ -630,7 +631,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
bags->DeleteChildren();
for (const auto* inventory : inventoriesToSave) {
auto* bag = document.NewElement("b");
auto* bag = document->NewElement("b");
bag->SetAttribute("t", inventory->GetType());
bag->SetAttribute("m", static_cast<unsigned int>(inventory->GetSize()));
@@ -638,15 +639,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) {
@@ -662,14 +654,14 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
continue;
}
auto* bagElement = document.NewElement("in");
auto* bagElement = document->NewElement("in");
bagElement->SetAttribute("t", inventory->GetType());
for (const auto& pair : inventory->GetItems()) {
auto* item = pair.second;
auto* itemElement = document.NewElement("i");
auto* itemElement = document->NewElement("i");
itemElement->SetAttribute("l", item->GetLot());
itemElement->SetAttribute("id", item->GetId());
@@ -683,7 +675,17 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
itemElement->SetAttribute("parent", item->GetParent());
// End custom xml
item->SaveConfigXml(*itemElement);
for (auto* data : item->GetConfig()) {
if (data->GetKey() != u"assemblyPartLOTs") {
continue;
}
auto* extraInfo = document->NewElement("x");
extraInfo->SetAttribute("ma", data->GetString(false).c_str());
itemElement->LinkEndChild(extraInfo);
}
bagElement->LinkEndChild(itemElement);
}
@@ -892,6 +894,8 @@ void InventoryComponent::UnEquipItem(Item* item) {
RemoveSlot(item->GetInfo().equipLocation);
PurgeProxies(item);
UnequipScripts(item);
Game::entityManager->SerializeEntity(m_Parent);
@@ -901,8 +905,6 @@ void InventoryComponent::UnEquipItem(Item* item) {
PropertyManagementComponent::Instance()->GetParent()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
Game::zoneManager->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
}
PurgeProxies(item);
}
@@ -1091,7 +1093,7 @@ void InventoryComponent::CheckItemSet(const LOT lot) {
auto result = query.execQuery();
while (!result.eof()) {
const auto id = result.getIntField("setID");
const auto id = result.getIntField(0);
bool found = false;
@@ -1522,10 +1524,10 @@ void InventoryComponent::PurgeProxies(Item* item) {
const auto root = item->GetParent();
if (root != LWOOBJID_EMPTY) {
Item* itemRoot = FindItemById(root);
item = FindItemById(root);
if (itemRoot != nullptr) {
UnEquipItem(itemRoot);
if (item != nullptr) {
UnEquipItem(item);
}
return;
@@ -1540,8 +1542,8 @@ void InventoryComponent::PurgeProxies(Item* item) {
}
}
void InventoryComponent::LoadPetXml(const tinyxml2::XMLDocument& document) {
auto* petInventoryElement = document.FirstChildElement("obj")->FirstChildElement("pet");
void InventoryComponent::LoadPetXml(tinyxml2::XMLDocument* document) {
auto* petInventoryElement = document->FirstChildElement("obj")->FirstChildElement("pet");
if (petInventoryElement == nullptr) {
m_Pets.clear();
@@ -1572,19 +1574,19 @@ void InventoryComponent::LoadPetXml(const tinyxml2::XMLDocument& document) {
}
}
void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument& document) {
auto* petInventoryElement = document.FirstChildElement("obj")->FirstChildElement("pet");
void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument* document) {
auto* petInventoryElement = document->FirstChildElement("obj")->FirstChildElement("pet");
if (petInventoryElement == nullptr) {
petInventoryElement = document.NewElement("pet");
petInventoryElement = document->NewElement("pet");
document.FirstChildElement("obj")->LinkEndChild(petInventoryElement);
document->FirstChildElement("obj")->LinkEndChild(petInventoryElement);
}
petInventoryElement->DeleteChildren();
for (const auto& pet : m_Pets) {
auto* petElement = document.NewElement("p");
auto* petElement = document->NewElement("p");
petElement->SetAttribute("id", pet.first);
petElement->SetAttribute("l", pet.second.lot);
@@ -1597,18 +1599,18 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument& document) {
}
bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId) {
bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId){
BehaviorSlot behaviorSlot = BehaviorSlot::Invalid;
if (slot == 1) behaviorSlot = BehaviorSlot::Primary;
else if (slot == 2) behaviorSlot = BehaviorSlot::Offhand;
else if (slot == 3) behaviorSlot = BehaviorSlot::Neck;
else if (slot == 4) behaviorSlot = BehaviorSlot::Head;
else if (slot == 5) behaviorSlot = BehaviorSlot::Consumable;
if (slot == 1 ) behaviorSlot = BehaviorSlot::Primary;
else if (slot == 2 ) behaviorSlot = BehaviorSlot::Offhand;
else if (slot == 3 ) behaviorSlot = BehaviorSlot::Neck;
else if (slot == 4 ) behaviorSlot = BehaviorSlot::Head;
else if (slot == 5 ) behaviorSlot = BehaviorSlot::Consumable;
else return false;
return SetSkill(behaviorSlot, skillId);
}
bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId) {
bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){
if (skillId == 0) return false;
const auto index = m_Skills.find(slot);
if (index != m_Skills.end()) {
@@ -1621,109 +1623,3 @@ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t 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,42 +37,13 @@ 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);
explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr);
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
void LoadXml(const tinyxml2::XMLDocument& document);
void UpdateXml(tinyxml2::XMLDocument& document) override;
void LoadXml(tinyxml2::XMLDocument* document);
void UpdateXml(tinyxml2::XMLDocument* document) override;
/**
* Returns an inventory of the specified type, if it exists
@@ -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
*/
@@ -508,16 +470,13 @@ private:
* Saves all the pet information stored in inventory items to the database
* @param document the xml doc to save to
*/
void LoadPetXml(const tinyxml2::XMLDocument& document);
void LoadPetXml(tinyxml2::XMLDocument* document);
/**
* Loads all the pet information from an xml doc into items
* @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;
void UpdatePetXml(tinyxml2::XMLDocument* document);
};
#endif

View File

@@ -13,8 +13,8 @@ LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component
m_CharacterVersion = eCharacterVersion::LIVE;
}
void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* level = doc.FirstChildElement("obj")->FirstChildElement("lvl");
void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl");
if (!level) {
LOG("Failed to find lvl tag while updating XML!");
return;
@@ -24,8 +24,8 @@ void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
level->SetAttribute("cv", static_cast<uint32_t>(m_CharacterVersion));
}
void LevelProgressionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
auto* level = doc.FirstChildElement("obj")->FirstChildElement("lvl");
void LevelProgressionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl");
if (!level) {
LOG("Failed to find lvl tag while loading XML!");
return;

View File

@@ -27,13 +27,13 @@ public:
* Save data from this componennt to character XML
* @param doc the document to write data to
*/
void UpdateXml(tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override;
/**
* Load base data for this component from character XML
* @param doc the document to read data from
*/
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
/**
* Gets the current level of the entity

View File

@@ -466,8 +466,8 @@ bool MissionComponent::RequiresItem(const LOT lot) {
return false;
}
if (!result.fieldIsNull("type")) {
const auto type = std::string(result.getStringField("type"));
if (!result.fieldIsNull(0)) {
const auto type = std::string(result.getStringField(0));
result.finalize();
@@ -504,8 +504,10 @@ bool MissionComponent::RequiresItem(const LOT lot) {
}
void MissionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
auto* mis = doc.FirstChildElement("obj")->FirstChildElement("mis");
void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
if (doc == nullptr) return;
auto* mis = doc->FirstChildElement("obj")->FirstChildElement("mis");
if (mis == nullptr) return;
@@ -521,7 +523,7 @@ void MissionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
auto* mission = new Mission(this, missionId);
mission->LoadFromXml(*doneM);
mission->LoadFromXml(doneM);
doneM = doneM->NextSiblingElement();
@@ -538,7 +540,7 @@ void MissionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
auto* mission = new Mission(this, missionId);
mission->LoadFromXml(*currentM);
mission->LoadFromXml(currentM);
if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) {
mission->SetUniqueMissionOrderID(missionOrder);
@@ -552,23 +554,25 @@ void MissionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
}
void MissionComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
if (doc == nullptr) return;
auto shouldInsertMis = false;
auto* obj = doc.FirstChildElement("obj");
auto* obj = doc->FirstChildElement("obj");
auto* mis = obj->FirstChildElement("mis");
if (mis == nullptr) {
mis = doc.NewElement("mis");
mis = doc->NewElement("mis");
shouldInsertMis = true;
}
mis->DeleteChildren();
auto* done = doc.NewElement("done");
auto* cur = doc.NewElement("cur");
auto* done = doc->NewElement("done");
auto* cur = doc->NewElement("cur");
for (const auto& pair : m_Missions) {
auto* mission = pair.second;
@@ -576,10 +580,10 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
if (mission) {
const auto complete = mission->IsComplete();
auto* m = doc.NewElement("m");
auto* m = doc->NewElement("m");
if (complete) {
mission->UpdateXml(*m);
mission->UpdateXml(m);
done->LinkEndChild(m);
@@ -587,7 +591,7 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
}
if (mission->IsMission()) m->SetAttribute("o", mission->GetUniqueMissionOrderID());
mission->UpdateXml(*m);
mission->UpdateXml(m);
cur->LinkEndChild(m);
}

View File

@@ -31,8 +31,8 @@ public:
explicit MissionComponent(Entity* parent);
~MissionComponent() override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument& doc) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override;
/**
* Returns all the missions for this entity, mapped by mission ID

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

@@ -50,44 +50,9 @@ MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) :
m_CurrentSpeed = 0;
m_MaxSpeed = 0;
m_LockRotation = false;
m_Path = nullptr;
m_SourcePosition = m_Parent->GetPosition();
m_Paused = false;
m_SavedVelocity = NiPoint3Constant::ZERO;
if (!m_Parent->GetComponent<BaseCombatAIComponent>()) SetPath(m_Parent->GetVarAsString(u"attached_path"));
}
void MovementAIComponent::SetPath(const std::string pathName) {
m_Path = Game::zoneManager->GetZone()->GetPath(pathName);
if (!pathName.empty()) LOG("WARNING: %s path %s", m_Path ? "Found" : "Failed to find", pathName.c_str());
if (!m_Path) return;
SetMaxSpeed(1);
SetCurrentSpeed(m_BaseSpeed);
SetPath(m_Path->pathWaypoints);
}
void MovementAIComponent::Pause() {
if (m_Paused) return;
m_Paused = true;
SetPosition(ApproximateLocation());
m_SavedVelocity = GetVelocity();
SetVelocity(NiPoint3Constant::ZERO);
Game::entityManager->SerializeEntity(m_Parent);
}
void MovementAIComponent::Resume() {
if (!m_Paused) return;
m_Paused = false;
SetVelocity(m_SavedVelocity);
m_SavedVelocity = NiPoint3Constant::ZERO;
SetRotation(NiQuaternion::LookAt(m_Parent->GetPosition(), m_NextWaypoint));
Game::entityManager->SerializeEntity(m_Parent);
}
void MovementAIComponent::Update(const float deltaTime) {
if (m_Paused) return;
if (m_PullingToPoint) {
const auto source = GetCurrentWaypoint();
@@ -116,65 +81,64 @@ void MovementAIComponent::Update(const float deltaTime) {
}
m_TimeTravelled += deltaTime;
SetPosition(ApproximateLocation());
if (m_TimeTravelled < m_TimeToTravel) return;
m_TimeTravelled = 0.0f;
const auto source = GetCurrentWaypoint();
SetPosition(source);
m_SourcePosition = source;
NiPoint3 velocity = NiPoint3Constant::ZERO;
if (m_Acceleration > 0 && m_BaseSpeed > 0 && AdvanceWaypointIndex()) // Do we have another waypoint to seek?
{
m_NextWaypoint = GetCurrentWaypoint();
if (m_NextWaypoint == source) {
m_TimeToTravel = 0.0f;
} else {
m_CurrentSpeed = std::min(m_CurrentSpeed + m_Acceleration, m_MaxSpeed);
const auto speed = m_CurrentSpeed * m_BaseSpeed; // scale speed based on base speed
const auto delta = m_NextWaypoint - source;
// Normalize the vector
const auto length = delta.Length();
if (length > 0.0f) {
SetVelocity((delta / length) * speed);
}
// Calclute the time it will take to reach the next waypoint with the current speed
m_TimeTravelled = 0.0f;
m_TimeToTravel = length / speed;
SetRotation(NiQuaternion::LookAt(source, m_NextWaypoint));
goto nextAction;
}
if (m_CurrentSpeed < m_MaxSpeed) {
m_CurrentSpeed += m_Acceleration;
}
if (m_CurrentSpeed > m_MaxSpeed) {
m_CurrentSpeed = m_MaxSpeed;
}
const auto speed = m_CurrentSpeed * m_BaseSpeed; // scale speed based on base speed
const auto delta = m_NextWaypoint - source;
// Normalize the vector
const auto length = delta.Length();
if (length > 0) {
velocity = (delta / length) * speed;
}
// Calclute the time it will take to reach the next waypoint with the current speed
m_TimeTravelled = 0.0f;
m_TimeToTravel = length / speed;
SetRotation(NiQuaternion::LookAt(source, m_NextWaypoint));
} else {
// Check if there are more waypoints in the queue, if so set our next destination to the next waypoint
if (m_CurrentPath.empty()) {
if (m_Path) {
if (m_Path->pathBehavior == PathBehavior::Loop) {
SetPath(m_Path->pathWaypoints);
} else if (m_Path->pathBehavior == PathBehavior::Bounce) {
std::vector<PathWaypoint> waypoints = m_Path->pathWaypoints;
std::reverse(waypoints.begin(), waypoints.end());
SetPath(waypoints);
} else if (m_Path->pathBehavior == PathBehavior::Once) {
Stop();
return;
}
} else {
Stop();
return;
}
Stop();
return;
}
SetDestination(m_CurrentPath.top().position);
SetDestination(m_CurrentPath.top());
m_CurrentPath.pop();
}
nextAction:
SetVelocity(velocity);
Game::entityManager->SerializeEntity(m_Parent);
}
@@ -197,7 +161,7 @@ NiPoint3 MovementAIComponent::GetCurrentWaypoint() const {
}
NiPoint3 MovementAIComponent::ApproximateLocation() const {
auto source = m_SourcePosition;
auto source = m_Parent->GetPosition();
if (AtFinalWaypoint()) return source;
@@ -263,13 +227,13 @@ void MovementAIComponent::PullToPoint(const NiPoint3& point) {
m_PullPoint = point;
}
void MovementAIComponent::SetPath(std::vector<PathWaypoint> path) {
void MovementAIComponent::SetPath(std::vector<NiPoint3> path) {
if (path.empty()) return;
std::for_each(path.rbegin(), path.rend() - 1, [this](const PathWaypoint& point) {
std::for_each(path.rbegin(), path.rend() - 1, [this](const NiPoint3& point) {
this->m_CurrentPath.push(point);
});
SetDestination(path.front().position);
SetDestination(path.front());
}
float MovementAIComponent::GetBaseSpeed(LOT lot) {
@@ -314,23 +278,6 @@ void MovementAIComponent::SetRotation(const NiQuaternion& value) {
if (!m_LockRotation) m_Parent->SetRotation(value);
}
NiPoint3 MovementAIComponent::GetVelocity() const {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent != nullptr) {
return controllablePhysicsComponent->GetVelocity();
}
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
return simplePhysicsComponent->GetVelocity();
}
return NiPoint3Constant::ZERO;
}
void MovementAIComponent::SetVelocity(const NiPoint3& value) {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
@@ -347,7 +294,7 @@ void MovementAIComponent::SetVelocity(const NiPoint3& value) {
}
}
void MovementAIComponent::SetDestination(const NiPoint3 destination) {
void MovementAIComponent::SetDestination(const NiPoint3& destination) {
if (m_PullingToPoint) return;
const auto location = ApproximateLocation();
@@ -356,8 +303,6 @@ void MovementAIComponent::SetDestination(const NiPoint3 destination) {
SetPosition(location);
}
m_SourcePosition = location;
std::vector<NiPoint3> computedPath;
if (dpWorld::IsLoaded()) {
computedPath = dpWorld::GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed);
@@ -374,7 +319,8 @@ void MovementAIComponent::SetDestination(const NiPoint3 destination) {
auto step = delta / 10.0f;
for (int i = 0; i < 10; i++) {
start += step;
// TODO: Replace this with += when the NiPoint3::operator+= is fixed
start = start + step;
computedPath.push_back(start);
}

View File

@@ -14,14 +14,11 @@
#include "Logger.h"
#include "Component.h"
#include "eReplicaComponentType.h"
#include "Zone.h"
#include <vector>
class ControllablePhysicsComponent;
class BaseCombatAIComponent;
struct Path;
/**
* Information that describes the different variables used to make an entity move around
*/
@@ -64,8 +61,6 @@ public:
MovementAIComponent(Entity* parentEntity, MovementAIInfo info);
void SetPath(const std::string pathName);
void Update(float deltaTime) override;
/**
@@ -78,7 +73,7 @@ public:
* Set a destination point for the entity to move towards
* @param value the destination point to move towards
*/
void SetDestination(const NiPoint3 value);
void SetDestination(const NiPoint3& value);
/**
* Returns the current rotation this entity is moving towards
@@ -194,13 +189,7 @@ public:
* Sets a path to follow for the AI
* @param path the path to follow
*/
void SetPath(std::vector<PathWaypoint> path);
void Pause();
void Resume();
NiPoint3 GetVelocity() const;
void SetPath(std::vector<NiPoint3> path);
/**
* Returns the base speed from the DB for a given LOT
@@ -312,15 +301,7 @@ private:
/**
* The path from the current position to the destination.
*/
std::stack<PathWaypoint> m_CurrentPath;
const Path* m_Path = nullptr;
NiPoint3 m_SourcePosition;
bool m_Paused;
NiPoint3 m_SavedVelocity;
std::stack<NiPoint3> m_CurrentPath;
};
#endif // MOVEMENTAICOMPONENT_H

View File

@@ -2,7 +2,6 @@
#include "GameMessages.h"
#include "BrickDatabase.h"
#include "CDClientDatabase.h"
#include "CDTamingBuildPuzzleTable.h"
#include "ChatPackets.h"
#include "EntityManager.h"
#include "Character.h"
@@ -32,9 +31,8 @@
#include "eGameMasterLevel.h"
#include "eMissionState.h"
#include "dNavMesh.h"
#include "eGameActivity.h"
#include "eStateChangeType.h"
std::unordered_map<LOT, PetComponent::PetPuzzleData> PetComponent::buildCache{};
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{};
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
@@ -42,7 +40,7 @@ std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::activePets{};
* Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID
* while the faction ones could be checked using their respective missions.
*/
const std::map<LOT, int32_t> PetComponent::petFlags{
std::map<LOT, int32_t> PetComponent::petFlags = {
{ 3050, 801 }, // Elephant
{ 3054, 803 }, // Cat
{ 3195, 806 }, // Triceratops
@@ -89,6 +87,7 @@ PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Compone
m_StartPosition = NiPoint3Constant::ZERO;
m_MovementAI = nullptr;
m_TresureTime = 0;
m_Preconditions = nullptr;
std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar<std::u16string>(u"CheckPrecondition"));
@@ -153,53 +152,96 @@ void PetComponent::OnUse(Entity* originator) {
m_Tamer = LWOOBJID_EMPTY;
}
auto* const inventoryComponent = originator->GetComponent<InventoryComponent>();
auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
if (inventoryComponent == nullptr) {
return;
}
if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) {
if (m_Preconditions != nullptr && !m_Preconditions->Check(originator, true)) {
return;
}
auto* const movementAIComponent = m_Parent->GetComponent<MovementAIComponent>();
auto* movementAIComponent = m_Parent->GetComponent<MovementAIComponent>();
if (movementAIComponent != nullptr) {
movementAIComponent->Stop();
}
inventoryComponent->DespawnPet();
const auto* const entry = CDClientManager::GetTable<CDTamingBuildPuzzleTable>()->GetByLOT(m_Parent->GetLOT());
if (!entry) {
ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to find the puzzle minigame for this pet.");
return;
const auto& cached = buildCache.find(m_Parent->GetLOT());
int32_t imaginationCost = 0;
std::string buildFile;
if (cached == buildCache.end()) {
auto query = CDClientDatabase::CreatePreppedStmt(
"SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;");
query.bind(1, static_cast<int>(m_Parent->GetLOT()));
auto result = query.execQuery();
if (result.eof()) {
ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to find the puzzle minigame for this pet.");
return;
}
if (result.fieldIsNull(0)) {
result.finalize();
return;
}
buildFile = std::string(result.getStringField(0));
PetPuzzleData data;
data.buildFile = buildFile;
data.puzzleModelLot = result.getIntField(1);
data.timeLimit = result.getFloatField(2);
data.numValidPieces = result.getIntField(3);
data.imaginationCost = result.getIntField(4);
if (data.timeLimit <= 0) data.timeLimit = 60;
imaginationCost = data.imaginationCost;
buildCache[m_Parent->GetLOT()] = data;
result.finalize();
} else {
buildFile = cached->second.buildFile;
imaginationCost = cached->second.imaginationCost;
}
const auto* const destroyableComponent = originator->GetComponent<DestroyableComponent>();
auto* destroyableComponent = originator->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr) {
return;
}
const auto imagination = destroyableComponent->GetImagination();
if (imagination < entry->imaginationCost) {
auto imagination = destroyableComponent->GetImagination();
if (imagination < imaginationCost) {
return;
}
const auto& bricks = BrickDatabase::GetBricks(entry->validPieces);
const auto& bricks = BrickDatabase::GetBricks(buildFile);
if (bricks.empty()) {
ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to load the puzzle minigame for this pet.");
LOG("Couldn't find %s for minigame!", entry->validPieces.c_str());
LOG("Couldn't find %s for minigame!", buildFile.c_str());
return;
}
const auto petPosition = m_Parent->GetPosition();
auto petPosition = m_Parent->GetPosition();
const auto originatorPosition = originator->GetPosition();
auto originatorPosition = originator->GetPosition();
m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition));
float interactionDistance = m_Parent->GetVar<float>(u"interaction_distance");
if (interactionDistance <= 0) {
interactionDistance = 15;
}
@@ -212,23 +254,24 @@ void PetComponent::OnUse(Entity* originator) {
if (dpWorld::IsLoaded()) {
NiPoint3 attempt = petPosition + forward * interactionDistance;
NiPoint3 nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt);
float y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt);
while (std::abs(nearestPoint.y - petPosition.y) > 4 && interactionDistance > 10) {
while (std::abs(y - petPosition.y) > 4 && interactionDistance > 10) {
const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector();
attempt = originatorPosition + forward * interactionDistance;
nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt);
y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt);
interactionDistance -= 0.5f;
}
position = nearestPoint;
position = attempt;
} else {
position = petPosition + forward * interactionDistance;
}
auto rotation = NiQuaternion::LookAt(position, petPosition);
GameMessages::SendNotifyPetTamingMinigame(
@@ -247,11 +290,11 @@ void PetComponent::OnUse(Entity* originator) {
m_Parent->GetObjectID(),
LWOOBJID_EMPTY,
originator->GetObjectID(),
false,
true,
ePetTamingNotifyType::BEGIN,
NiPoint3Constant::ZERO,
NiPoint3Constant::ZERO,
NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f),
petPosition,
position,
rotation,
UNASSIGNED_SYSTEM_ADDRESS
);
@@ -259,18 +302,11 @@ void PetComponent::OnUse(Entity* originator) {
m_Tamer = originator->GetObjectID();
SetStatus(5);
Game::entityManager->SerializeEntity(m_Parent);
currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID());
// Notify the start of a pet taming minigame
m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN);
auto* characterComponent = originator->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
characterComponent->SetCurrentActivity(eGameActivity::PET_TAMING);
Game::entityManager->SerializeEntity(originator);
}
}
void PetComponent::Update(float deltaTime) {
@@ -441,8 +477,9 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) {
return;
}
const auto* const entry = CDClientManager::GetTable<CDTamingBuildPuzzleTable>()->GetByLOT(m_Parent->GetLOT());
if (!entry) return;
const auto& cached = buildCache.find(m_Parent->GetLOT());
if (cached == buildCache.end()) return;
auto* destroyableComponent = tamer->GetComponent<DestroyableComponent>();
@@ -450,14 +487,14 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) {
auto imagination = destroyableComponent->GetImagination();
imagination -= entry->imaginationCost;
imagination -= cached->second.imaginationCost;
destroyableComponent->SetImagination(imagination);
Game::entityManager->SerializeEntity(tamer);
if (clientFailed) {
if (imagination < entry->imaginationCost) {
if (imagination < cached->second.imaginationCost) {
ClientFailTamingMinigame();
}
} else {
@@ -480,14 +517,17 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
return;
}
const auto* const entry = CDClientManager::GetTable<CDTamingBuildPuzzleTable>()->GetByLOT(m_Parent->GetLOT());
if (!entry) return;
const auto& cached = buildCache.find(m_Parent->GetLOT());
if (cached == buildCache.end()) {
return;
}
GameMessages::SendPlayFXEffect(tamer, -1, u"petceleb", "", LWOOBJID_EMPTY, 1, 1, true);
RenderComponent::PlayAnimation(tamer, u"rebuild-celebrate");
EntityInfo info{};
info.lot = entry->puzzleModelLot;
info.lot = cached->second.puzzleModelLot;
info.pos = position;
info.rot = NiQuaternionConstant::IDENTITY;
info.spawnerID = tamer->GetObjectID();
@@ -635,11 +675,6 @@ void PetComponent::RequestSetPetName(std::u16string name) {
UNASSIGNED_SYSTEM_ADDRESS
);
auto* characterComponent = tamer->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
characterComponent->SetCurrentActivity(eGameActivity::NONE);
Game::entityManager->SerializeEntity(tamer);
}
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
auto* modelEntity = Game::entityManager->GetEntity(m_ModelId);
@@ -679,11 +714,6 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
UNASSIGNED_SYSTEM_ADDRESS
);
auto* characterComponent = tamer->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
characterComponent->SetCurrentActivity(eGameActivity::NONE);
Game::entityManager->SerializeEntity(tamer);
}
GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress());
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
@@ -701,10 +731,13 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) {
}
void PetComponent::StartTimer() {
const auto* const entry = CDClientManager::GetTable<CDTamingBuildPuzzleTable>()->GetByLOT(m_Parent->GetLOT());
if (!entry) return;
const auto& cached = buildCache.find(m_Parent->GetLOT());
m_Timer = entry->timeLimit;
if (cached == buildCache.end()) {
return;
}
m_Timer = cached->second.timeLimit;
}
void PetComponent::ClientFailTamingMinigame() {
@@ -730,11 +763,6 @@ void PetComponent::ClientFailTamingMinigame() {
UNASSIGNED_SYSTEM_ADDRESS
);
auto* characterComponent = tamer->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
characterComponent->SetCurrentActivity(eGameActivity::NONE);
Game::entityManager->SerializeEntity(tamer);
}
GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress());
GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
@@ -1058,6 +1086,6 @@ void PetComponent::LoadPetNameFromModeration() {
}
}
void PetComponent::SetPreconditions(const std::string& preconditions) {
m_Preconditions = std::make_optional<PreconditionExpression>(preconditions);
void PetComponent::SetPreconditions(std::string& preconditions) {
m_Preconditions = new PreconditionExpression(preconditions);
}

View File

@@ -165,7 +165,7 @@ public:
* Sets preconditions for the pet that need to be met before it can be tamed
* @param conditions the preconditions to set
*/
void SetPreconditions(const std::string& conditions);
void SetPreconditions(std::string& conditions);
/**
* Returns the entity that this component belongs to
@@ -250,10 +250,15 @@ private:
*/
static std::unordered_map<LWOOBJID, LWOOBJID> currentActivities;
/**
* Cache of all the minigames and their information from the database
*/
static std::unordered_map<LOT, PetComponent::PetPuzzleData> buildCache;
/**
* Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet
*/
static const std::map<LOT, int32_t> petFlags;
static std::map<LOT, int32_t> petFlags;
/**
* The ID of the component in the pet component table
@@ -344,7 +349,7 @@ private:
/**
* Preconditions that need to be met before an entity can tame this pet
*/
std::optional<PreconditionExpression> m_Preconditions{};
PreconditionExpression* m_Preconditions;
/**
* Pet information loaded from the CDClientDatabase

View File

@@ -47,7 +47,7 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsCompon
m_Direction = NiPoint3(); // * m_DirectionalMultiplier
if (m_Parent->GetVar<bool>(u"create_physics")) {
m_dpEntity = CreatePhysicsLnv(m_Scale, ComponentType);
CreatePhysics();
}
if (m_Parent->GetVar<bool>(u"respawnVol")) {
@@ -89,9 +89,105 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsCompon
m_RespawnRot = m_Rotation;
}
if (!m_dpEntity) {
m_dpEntity = CreatePhysicsEntity(ComponentType);
if (!m_dpEntity) return;
/*
for (LDFBaseData* data : settings) {
if (data) {
if (data->GetKey() == u"create_physics") {
if (bool(std::stoi(data->GetValueAsString()))) {
CreatePhysics(settings);
}
}
if (data->GetKey() == u"respawnVol") {
if (bool(std::stoi(data->GetValueAsString()))) {
m_IsRespawnVolume = true;
}
}
if (m_IsRespawnVolume) {
if (data->GetKey() == u"rspPos") {
//Joy, we get to split strings!
std::stringstream test(data->GetValueAsString());
std::string segment;
std::vector<std::string> seglist;
while (std::getline(test, segment, '\x1f')) {
seglist.push_back(segment);
}
m_RespawnPos = NiPoint3(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2]));
}
if (data->GetKey() == u"rspRot") {
//Joy, we get to split strings!
std::stringstream test(data->GetValueAsString());
std::string segment;
std::vector<std::string> seglist;
while (std::getline(test, segment, '\x1f')) {
seglist.push_back(segment);
}
m_RespawnRot = NiQuaternion(std::stof(seglist[0]), std::stof(seglist[1]), std::stof(seglist[2]), std::stof(seglist[3]));
}
}
if (m_Parent->GetLOT() == 4945) // HF - RespawnPoints
{
m_IsRespawnVolume = true;
m_RespawnPos = m_Position;
m_RespawnRot = m_Rotation;
}
}
}
*/
if (!m_HasCreatedPhysics) {
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS);
CDPhysicsComponentTable* physComp = CDClientManager::GetTable<CDPhysicsComponentTable>();
if (physComp == nullptr) return;
auto* info = physComp->GetByID(componentID);
if (info == nullptr || info->physicsAsset == "" || info->physicsAsset == "NO_PHYSICS") return;
//temp test
if (info->physicsAsset == "miscellaneous\\misc_phys_10x1x5.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 10.0f, 5.0f, 1.0f);
} else if (info->physicsAsset == "miscellaneous\\misc_phys_640x640.hkx") {
// TODO Fix physics simulation to do simulation at high velocities due to bullet through paper problem...
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 1638.4f, 13.521004f * 2.0f, 1638.4f);
// Move this down by 13.521004 units so it is still effectively at the same height as before
m_Position = m_Position - NiPoint3Constant::UNIT_Y * 13.521004f;
} else if (info->physicsAsset == "env\\trigger_wall_tall.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 10.0f, 25.0f, 1.0f);
} else if (info->physicsAsset == "env\\env_gen_placeholderphysics.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 20.0f, 20.0f, 20.0f);
} else if (info->physicsAsset == "env\\POI_trigger_wall.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 1.0f, 12.5f, 20.0f); // Not sure what the real size is
} else if (info->physicsAsset == "env\\NG_NinjaGo\\env_ng_gen_gate_chamber_puzzle_ceiling_tile_falling_phantom.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 18.0f, 5.0f, 15.0f);
m_Position += m_Rotation.GetForwardVector() * 7.5f;
} else if (info->physicsAsset == "env\\NG_NinjaGo\\ng_flamejet_brick_phantom.HKX") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 1.0f, 1.0f, 12.0f);
m_Position += m_Rotation.GetForwardVector() * 6.0f;
} else if (info->physicsAsset == "env\\Ring_Trigger.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 6.0f, 6.0f, 6.0f);
} else if (info->physicsAsset == "env\\vfx_propertyImaginationBall.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 4.5f);
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") {
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
m_Position.y -= (111.467964f * m_Scale) / 2;
} else {
// LOG_DEBUG("This one is supposed to have %s", info->physicsAsset.c_str());
//add fallback cube:
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 2.0f, 2.0f, 2.0f);
}
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
@@ -105,6 +201,69 @@ PhantomPhysicsComponent::~PhantomPhysicsComponent() {
}
}
void PhantomPhysicsComponent::CreatePhysics() {
unsigned char alpha;
unsigned char red;
unsigned char green;
unsigned char blue;
int type = -1;
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float width = 0.0f; //aka "radius"
float height = 0.0f;
if (m_Parent->HasVar(u"primitiveModelType")) {
type = m_Parent->GetVar<int32_t>(u"primitiveModelType");
x = m_Parent->GetVar<float>(u"primitiveModelValueX");
y = m_Parent->GetVar<float>(u"primitiveModelValueY");
z = m_Parent->GetVar<float>(u"primitiveModelValueZ");
} else {
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS);
CDPhysicsComponentTable* physComp = CDClientManager::GetTable<CDPhysicsComponentTable>();
if (physComp == nullptr) return;
auto info = physComp->GetByID(componentID);
if (info == nullptr) return;
type = info->pcShapeType;
width = info->playerRadius;
height = info->playerHeight;
}
switch (type) {
case 1: { //Make a new box shape
NiPoint3 boxSize(x, y, z);
if (x == 0.0f) {
//LU has some weird values, so I think it's best to scale them down a bit
if (height < 0.5f) height = 2.0f;
if (width < 0.5f) width = 2.0f;
//Scale them:
width = width * m_Scale;
height = height * m_Scale;
boxSize = NiPoint3(width, height, width);
}
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), boxSize);
break;
}
}
if (!m_dpEntity) return;
m_dpEntity->SetPosition({ m_Position.x, m_Position.y - (height / 2), m_Position.z });
dpWorld::AddEntity(m_dpEntity);
m_HasCreatedPhysics = true;
}
void PhantomPhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
PhysicsComponent::Serialize(outBitStream, bIsInitialUpdate);
@@ -149,9 +308,8 @@ void ApplyCollisionEffect(const LWOOBJID& target, const ePhysicsEffectType effec
controllablePhysicsComponent->SetGravityScale(effectScale);
GameMessages::SendSetGravityScale(target, effectScale, targetEntity->GetSystemAddress());
}
break;
}
// The other types are not handled by the server
case ePhysicsEffectType::ATTRACT:
case ePhysicsEffectType::FRICTION:
case ePhysicsEffectType::PUSH:
@@ -159,20 +317,20 @@ void ApplyCollisionEffect(const LWOOBJID& target, const ePhysicsEffectType effec
default:
break;
}
// The other types are not handled by the server and are here to handle all cases of the enum.
}
void PhantomPhysicsComponent::Update(float deltaTime) {
if (!m_dpEntity) return;
//Process enter events
for (const auto id : m_dpEntity->GetNewObjects()) {
ApplyCollisionEffect(id, m_EffectType, m_DirectionalMultiplier);
m_Parent->OnCollisionPhantom(id);
for (auto en : m_dpEntity->GetNewObjects()) {
if (!en) continue;
ApplyCollisionEffect(en->GetObjectID(), m_EffectType, m_DirectionalMultiplier);
m_Parent->OnCollisionPhantom(en->GetObjectID());
//If we are a respawn volume, inform the client:
if (m_IsRespawnVolume) {
auto* const entity = Game::entityManager->GetEntity(id);
auto entity = Game::entityManager->GetEntity(en->GetObjectID());
if (entity) {
GameMessages::SendPlayerReachedRespawnCheckpoint(entity, m_RespawnPos, m_RespawnRot);
@@ -183,9 +341,10 @@ void PhantomPhysicsComponent::Update(float deltaTime) {
}
//Process exit events
for (const auto id : m_dpEntity->GetRemovedObjects()) {
ApplyCollisionEffect(id, m_EffectType, 1.0f);
m_Parent->OnCollisionLeavePhantom(id);
for (auto en : m_dpEntity->GetRemovedObjects()) {
if (!en) continue;
ApplyCollisionEffect(en->GetObjectID(), m_EffectType, 1.0f);
m_Parent->OnCollisionLeavePhantom(en->GetObjectID());
}
}
@@ -199,12 +358,24 @@ void PhantomPhysicsComponent::SetDirection(const NiPoint3& pos) {
m_IsDirectional = true;
}
void PhantomPhysicsComponent::SpawnVertices() const {
if (!m_dpEntity) {
LOG("No dpEntity to spawn vertices for %llu:%i", m_Parent->GetObjectID(), m_Parent->GetLOT());
return;
void PhantomPhysicsComponent::SpawnVertices() {
if (!m_dpEntity) return;
LOG("%llu", m_Parent->GetObjectID());
auto box = static_cast<dpShapeBox*>(m_dpEntity->GetShape());
for (auto vert : box->GetVertices()) {
LOG("%f, %f, %f", vert.x, vert.y, vert.z);
EntityInfo info;
info.lot = 33;
info.pos = vert;
info.spawner = nullptr;
info.spawnerID = m_Parent->GetObjectID();
info.spawnerNodeID = 0;
Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr);
Game::entityManager->ConstructEntity(newEntity);
}
PhysicsComponent::SpawnVertices(m_dpEntity);
}
void PhantomPhysicsComponent::SetDirectionalMultiplier(float mul) {

View File

@@ -18,7 +18,6 @@ class LDFBaseData;
class Entity;
class dpEntity;
enum class ePhysicsEffectType : uint32_t ;
enum class eReplicaComponentType : uint32_t;
/**
* Allows the creation of phantom physics for an entity: a physics object that is generally invisible but can be
@@ -35,6 +34,11 @@ public:
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
/**
* Creates the physics shape for this entity based on LDF data
*/
void CreatePhysics();
/**
* Sets the direction this physics object is pointed at
* @param pos the direction to set
@@ -105,7 +109,7 @@ public:
/**
* Spawns an object at each of the vertices for debugging purposes
*/
void SpawnVertices() const;
void SpawnVertices();
/**
* Legacy stuff no clue what this does
@@ -162,6 +166,11 @@ private:
*/
dpEntity* m_dpEntity;
/**
* Whether or not the physics object has been created yet
*/
bool m_HasCreatedPhysics = false;
/**
* Whether or not this physics object represents an object that updates the respawn pos of an entity that crosses it
*/

View File

@@ -1,19 +1,5 @@
#include "PhysicsComponent.h"
#include "eReplicaComponentType.h"
#include "NiPoint3.h"
#include "NiQuaternion.h"
#include "CDComponentsRegistryTable.h"
#include "CDPhysicsComponentTable.h"
#include "dpEntity.h"
#include "dpWorld.h"
#include "dpShapeBox.h"
#include "dpShapeSphere.h"
#include "EntityInfo.h"
PhysicsComponent::PhysicsComponent(Entity* parent) : Component(parent) {
m_Position = NiPoint3Constant::ZERO;
m_Rotation = NiQuaternionConstant::IDENTITY;
@@ -33,190 +19,3 @@ void PhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitia
if (!bIsInitialUpdate) m_DirtyPosition = false;
}
}
dpEntity* PhysicsComponent::CreatePhysicsEntity(eReplicaComponentType type) {
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), type);
CDPhysicsComponentTable* physComp = CDClientManager::GetTable<CDPhysicsComponentTable>();
if (physComp == nullptr) return nullptr;
auto* info = physComp->GetByID(componentID);
if (info == nullptr || info->physicsAsset == "" || info->physicsAsset == "NO_PHYSICS") return nullptr;
dpEntity* toReturn;
if (info->physicsAsset == "miscellaneous\\misc_phys_10x1x5.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 10.0f, 5.0f, 1.0f);
} else if (info->physicsAsset == "miscellaneous\\misc_phys_640x640.hkx") {
// TODO Fix physics simulation to do simulation at high velocities due to bullet through paper problem...
toReturn = new dpEntity(m_Parent->GetObjectID(), 1638.4f, 13.521004f * 2.0f, 1638.4f);
// Move this down by 13.521004 units so it is still effectively at the same height as before
m_Position = m_Position - NiPoint3Constant::UNIT_Y * 13.521004f;
} else if (info->physicsAsset == "env\\trigger_wall_tall.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 10.0f, 25.0f, 1.0f);
} else if (info->physicsAsset == "env\\env_gen_placeholderphysics.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 20.0f, 20.0f, 20.0f);
} else if (info->physicsAsset == "env\\POI_trigger_wall.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 1.0f, 12.5f, 20.0f); // Not sure what the real size is
} else if (info->physicsAsset == "env\\NG_NinjaGo\\env_ng_gen_gate_chamber_puzzle_ceiling_tile_falling_phantom.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 18.0f, 5.0f, 15.0f);
m_Position += m_Rotation.GetForwardVector() * 7.5f;
} else if (info->physicsAsset == "env\\NG_NinjaGo\\ng_flamejet_brick_phantom.HKX") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 1.0f, 1.0f, 12.0f);
m_Position += m_Rotation.GetForwardVector() * 6.0f;
} else if (info->physicsAsset == "env\\Ring_Trigger.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 6.0f, 6.0f, 6.0f);
} else if (info->physicsAsset == "env\\vfx_propertyImaginationBall.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 4.5f);
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") {
toReturn = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
m_Position.y -= (111.467964f * m_Parent->GetDefaultScale()) / 2;
} else {
// LOG_DEBUG("This one is supposed to have %s", info->physicsAsset.c_str());
//add fallback cube:
toReturn = new dpEntity(m_Parent->GetObjectID(), 2.0f, 2.0f, 2.0f);
}
return toReturn;
}
dpEntity* PhysicsComponent::CreatePhysicsLnv(const float scale, const eReplicaComponentType type) const {
int pcShapeType = -1;
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float width = 0.0f; //aka "radius"
float height = 0.0f;
dpEntity* toReturn = nullptr;
if (m_Parent->HasVar(u"primitiveModelType")) {
pcShapeType = m_Parent->GetVar<int32_t>(u"primitiveModelType");
x = m_Parent->GetVar<float>(u"primitiveModelValueX");
y = m_Parent->GetVar<float>(u"primitiveModelValueY");
z = m_Parent->GetVar<float>(u"primitiveModelValueZ");
} else {
CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), type);
CDPhysicsComponentTable* physComp = CDClientManager::GetTable<CDPhysicsComponentTable>();
if (physComp == nullptr) return nullptr;
auto info = physComp->GetByID(componentID);
if (info == nullptr) return nullptr;
pcShapeType = info->pcShapeType;
width = info->playerRadius;
height = info->playerHeight;
}
switch (pcShapeType) {
case 0: { // HKX type
break;
}
case 1: { //Make a new box shape
NiPoint3 boxSize(x, y, z);
if (x == 0.0f) {
//LU has some weird values, so I think it's best to scale them down a bit
if (height < 0.5f) height = 2.0f;
if (width < 0.5f) width = 2.0f;
//Scale them:
width = width * scale;
height = height * scale;
boxSize = NiPoint3(width, height, width);
}
toReturn = new dpEntity(m_Parent->GetObjectID(), boxSize);
toReturn->SetPosition({ m_Position.x, m_Position.y - (height / 2), m_Position.z });
break;
}
case 2: { //Make a new cylinder shape
break;
}
case 3: { //Make a new sphere shape
auto [x, y, z] = m_Position;
toReturn = new dpEntity(m_Parent->GetObjectID(), width);
toReturn->SetPosition({ x, y, z });
break;
}
case 4: { //Make a new capsule shape
break;
}
}
if (toReturn) dpWorld::AddEntity(toReturn);
return toReturn;
}
void PhysicsComponent::SpawnVertices(dpEntity* entity) const {
if (!entity) return;
LOG("Spawning vertices for %llu", m_Parent->GetObjectID());
EntityInfo info;
info.lot = 33;
info.spawner = nullptr;
info.spawnerID = m_Parent->GetObjectID();
info.spawnerNodeID = 0;
// These don't use overloaded methods as dPhysics does not link with dGame at the moment.
auto box = dynamic_cast<dpShapeBox*>(entity->GetShape());
if (box) {
for (auto vert : box->GetVertices()) {
LOG("Vertex at %f, %f, %f", vert.x, vert.y, vert.z);
info.pos = vert;
Entity* newEntity = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(newEntity);
}
}
auto sphere = dynamic_cast<dpShapeSphere*>(entity->GetShape());
if (sphere) {
auto [x, y, z] = entity->GetPosition(); // Use shapes position instead of the parent's position in case it's different
float plusX = x + sphere->GetRadius();
float minusX = x - sphere->GetRadius();
float plusY = y + sphere->GetRadius();
float minusY = y - sphere->GetRadius();
float plusZ = z + sphere->GetRadius();
float minusZ = z - sphere->GetRadius();
auto radius = sphere->GetRadius();
LOG("Radius: %f", radius);
LOG("Plus Vertices %f %f %f", plusX, plusY, plusZ);
LOG("Minus Vertices %f %f %f", minusX, minusY, minusZ);
info.pos = NiPoint3{ x, plusY, z };
Entity* newEntity = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(newEntity);
info.pos = NiPoint3{ x, minusY, z };
newEntity = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(newEntity);
info.pos = NiPoint3{ plusX, y, z };
newEntity = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(newEntity);
info.pos = NiPoint3{ minusX, y, z };
newEntity = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(newEntity);
info.pos = NiPoint3{ x, y, plusZ };
newEntity = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(newEntity);
info.pos = NiPoint3{ x, y, minusZ };
newEntity = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(newEntity);
info.pos = NiPoint3{ x, y, z };
newEntity = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(newEntity);
}
}

View File

@@ -9,10 +9,6 @@ namespace Raknet {
class BitStream;
};
enum class eReplicaComponentType : uint32_t;
class dpEntity;
class PhysicsComponent : public Component {
public:
PhysicsComponent(Entity* parent);
@@ -26,12 +22,6 @@ public:
const NiQuaternion& GetRotation() const { return m_Rotation; }
virtual void SetRotation(const NiQuaternion& rot) { if (m_Rotation == rot) return; m_Rotation = rot; m_DirtyPosition = true; }
protected:
dpEntity* CreatePhysicsEntity(eReplicaComponentType type);
dpEntity* CreatePhysicsLnv(const float scale, const eReplicaComponentType type) const;
void SpawnVertices(dpEntity* entity) const;
NiPoint3 m_Position;
NiQuaternion m_Rotation;

View File

@@ -18,8 +18,8 @@ PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId)
// Should a result not exist for this default to attached visible
if (!result.eof()) {
m_PossessionType = static_cast<ePossessionType>(result.getIntField("possessionType", 1)); // Default to Attached Visible
m_DepossessOnHit = static_cast<bool>(result.getIntField("depossessOnHit", 0));
m_PossessionType = static_cast<ePossessionType>(result.getIntField(0, 1)); // Default to Attached Visible
m_DepossessOnHit = static_cast<bool>(result.getIntField(1, 0));
} else {
m_PossessionType = ePossessionType::ATTACHED_VISIBLE;
m_DepossessOnHit = false;

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;
@@ -51,11 +49,11 @@ PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Compo
auto result = query.execQuery();
if (result.eof() || result.fieldIsNull("id")) {
if (result.eof() || result.fieldIsNull(0)) {
return;
}
templateId = result.getIntField("id");
templateId = result.getIntField(0);
auto propertyInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId);
@@ -107,7 +105,7 @@ std::vector<NiPoint3> PropertyManagementComponent::GetPaths() const {
std::vector<float> points;
std::istringstream stream(result.getStringField("path"));
std::istringstream stream(result.getStringField(0));
std::string token;
while (std::getline(stream, token, ' ')) {
@@ -354,11 +352,16 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N
auto* spawner = Game::zoneManager->GetSpawner(spawnerId);
info.nodes[0]->config.push_back(new LDFData<LWOOBJID>(u"modelBehaviors", 0));
info.nodes[0]->config.push_back(new LDFData<LWOOBJID>(u"userModelID", info.spawnerID));
info.nodes[0]->config.push_back(new LDFData<int>(u"modelType", 2));
info.nodes[0]->config.push_back(new LDFData<bool>(u"propertyObjectID", true));
info.nodes[0]->config.push_back(new LDFData<int>(u"componentWhitelist", 1));
auto ldfModelBehavior = new LDFData<LWOOBJID>(u"modelBehaviors", 0);
auto userModelID = new LDFData<LWOOBJID>(u"userModelID", info.spawnerID);
auto modelType = new LDFData<int>(u"modelType", 2);
auto propertyObjectID = new LDFData<bool>(u"propertyObjectID", true);
auto componentWhitelist = new LDFData<int>(u"componentWhitelist", 1);
info.nodes[0]->config.push_back(componentWhitelist);
info.nodes[0]->config.push_back(ldfModelBehavior);
info.nodes[0]->config.push_back(modelType);
info.nodes[0]->config.push_back(propertyObjectID);
info.nodes[0]->config.push_back(userModelID);
auto* model = spawner->Spawn();
@@ -582,33 +585,31 @@ void PropertyManagementComponent::Load() {
GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER);
GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT);
settings.push_back(new LDFData<LWOOBJID>(u"blueprintid", blueprintID));
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
settings.push_back(new LDFData<int>(u"modelType", 2));
settings.push_back(new LDFData<bool>(u"propertyObjectID", true));
settings.push_back(new LDFData<LWOOBJID>(u"userModelID", databaseModel.id));
LDFBaseData* ldfBlueprintID = new LDFData<LWOOBJID>(u"blueprintid", blueprintID);
LDFBaseData* componentWhitelist = new LDFData<int>(u"componentWhitelist", 1);
LDFBaseData* modelType = new LDFData<int>(u"modelType", 2);
LDFBaseData* propertyObjectID = new LDFData<bool>(u"propertyObjectID", true);
LDFBaseData* userModelID = new LDFData<LWOOBJID>(u"userModelID", databaseModel.id);
settings.push_back(ldfBlueprintID);
settings.push_back(componentWhitelist);
settings.push_back(modelType);
settings.push_back(propertyObjectID);
settings.push_back(userModelID);
} else {
settings.push_back(new LDFData<int>(u"modelType", 2));
settings.push_back(new LDFData<LWOOBJID>(u"userModelID", databaseModel.id));
settings.push_back(new LDFData<LWOOBJID>(u"modelBehaviors", 0));
settings.push_back(new LDFData<bool>(u"propertyObjectID", true));
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
}
auto modelType = new LDFData<int>(u"modelType", 2);
auto userModelID = new LDFData<LWOOBJID>(u"userModelID", databaseModel.id);
auto ldfModelBehavior = new LDFData<LWOOBJID>(u"modelBehaviors", 0);
auto propertyObjectID = new LDFData<bool>(u"propertyObjectID", true);
auto componentWhitelist = 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(componentWhitelist);
settings.push_back(ldfModelBehavior);
settings.push_back(modelType);
settings.push_back(propertyObjectID);
settings.push_back(userModelID);
}
settings.push_back(new LDFData<std::string>(u"userModelBehaviors", userModelBehavior.str()));
node->config = settings;
const auto spawnerId = Game::zoneManager->MakeSpawner(info);
@@ -626,12 +627,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 +641,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 +652,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

@@ -5,7 +5,7 @@
#include "EntityManager.h"
#include "SimplePhysicsComponent.h"
const std::unordered_set<LWOOBJID> ProximityMonitorComponent::m_EmptyObjectSet = {};
const std::map<LWOOBJID, dpEntity*> ProximityMonitorComponent::m_EmptyObjectMap = {};
ProximityMonitorComponent::ProximityMonitorComponent(Entity* parent, int radiusSmall, int radiusLarge) : Component(parent) {
if (radiusSmall != -1 && radiusLarge != -1) {
@@ -38,41 +38,40 @@ void ProximityMonitorComponent::SetProximityRadius(dpEntity* entity, const std::
m_ProximitiesData.insert(std::make_pair(name, entity));
}
const std::unordered_set<LWOOBJID>& ProximityMonitorComponent::GetProximityObjects(const std::string& name) {
const auto iter = m_ProximitiesData.find(name);
const std::map<LWOOBJID, dpEntity*>& ProximityMonitorComponent::GetProximityObjects(const std::string& name) {
const auto& iter = m_ProximitiesData.find(name);
if (iter == m_ProximitiesData.cend()) {
return m_EmptyObjectSet;
if (iter == m_ProximitiesData.end()) {
return m_EmptyObjectMap;
}
return iter->second->GetCurrentlyCollidingObjects();
}
bool ProximityMonitorComponent::IsInProximity(const std::string& name, LWOOBJID objectID) {
const auto iter = m_ProximitiesData.find(name);
const auto& iter = m_ProximitiesData.find(name);
if (iter == m_ProximitiesData.cend()) {
if (iter == m_ProximitiesData.end()) {
return false;
}
const auto& collisions = iter->second->GetCurrentlyCollidingObjects();
const auto& collitions = iter->second->GetCurrentlyCollidingObjects();
return collisions.contains(objectID);
return collitions.find(objectID) != collitions.end();
}
void ProximityMonitorComponent::Update(float deltaTime) {
for (const auto& prox : m_ProximitiesData) {
if (!prox.second) continue;
prox.second->SetPosition(m_Parent->GetPosition());
//Process enter events
for (const auto id : prox.second->GetNewObjects()) {
m_Parent->OnCollisionProximity(id, prox.first, "ENTER");
for (auto* en : prox.second->GetNewObjects()) {
m_Parent->OnCollisionProximity(en->GetObjectID(), prox.first, "ENTER");
}
//Process exit events
for (const auto id : prox.second->GetRemovedObjects()) {
m_Parent->OnCollisionProximity(id, prox.first, "LEAVE");
for (auto* en : prox.second->GetRemovedObjects()) {
m_Parent->OnCollisionProximity(en->GetObjectID(), prox.first, "LEAVE");
}
}
}

View File

@@ -6,8 +6,6 @@
#ifndef PROXIMITYMONITORCOMPONENT_H
#define PROXIMITYMONITORCOMPONENT_H
#include <unordered_set>
#include "BitStream.h"
#include "Entity.h"
#include "dpWorld.h"
@@ -44,9 +42,9 @@ public:
/**
* Returns the last of entities that are used to check proximity, given a name
* @param name the proximity name to retrieve physics objects for
* @return a set of physics entity object IDs for this name
* @return a map of physics entities for this name, indexed by object ID
*/
const std::unordered_set<LWOOBJID>& GetProximityObjects(const std::string& name);
const std::map<LWOOBJID, dpEntity*>& GetProximityObjects(const std::string& name);
/**
* Checks if the passed object is in proximity of the named proximity sensor
@@ -72,7 +70,7 @@ private:
/**
* Default value for the proximity data
*/
static const std::unordered_set<LWOOBJID> m_EmptyObjectSet;
static const std::map<LWOOBJID, dpEntity*> m_EmptyObjectMap;
};
#endif // PROXIMITYMONITORCOMPONENT_H

View File

@@ -25,7 +25,6 @@
#include "LeaderboardManager.h"
#include "dZoneManager.h"
#include "CDActivitiesTable.h"
#include "eStateChangeType.h"
#include <ctime>
#ifndef M_PI
@@ -45,6 +44,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";
@@ -77,9 +77,6 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) {
m_LoadedPlayers++;
// not live accurate to stun the player but prevents them from using skills during the race that are not meant to be used.
GameMessages::SendSetStunned(player->GetObjectID(), eStateChangeType::PUSH, player->GetSystemAddress(), LWOOBJID_EMPTY, true, true, true, true, true, true, true, true, true);
LOG("Loading player %i",
m_LoadedPlayers);
m_LobbyPlayers.push_back(player->GetObjectID());
@@ -397,6 +394,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 +442,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 +505,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 +736,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;
@@ -801,68 +817,52 @@ void RacingControlComponent::Update(float deltaTime) {
// Some offset up to make they don't fall through the terrain on a
// respawn, seems to fix itself to the track anyhow
if (waypoint.racing.isResetNode) {
player.respawnPosition = position + NiPoint3Constant::UNIT_Y * 5;
player.respawnRotation = vehicle->GetRotation();
}
player.respawnPosition = position + NiPoint3Constant::UNIT_Y * 5;
player.respawnRotation = vehicle->GetRotation();
player.respawnIndex = respawnIndex;
// 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 +871,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

@@ -117,7 +117,7 @@ void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& e
auto result = query.execQuery();
if (result.eof() || result.fieldIsNull("animation_length")) {
if (result.eof() || result.fieldIsNull(0)) {
result.finalize();
m_DurationCache[effectId] = 0;
@@ -127,7 +127,7 @@ void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& e
return;
}
effect.time = static_cast<float>(result.getFloatField("animation_length"));
effect.time = static_cast<float>(result.getFloatField(0));
result.finalize();

View File

@@ -1,57 +1,16 @@
// Darkflame Universe
// Copyright 2024
/*
* Darkflame Universe
* Copyright 2023
*/
#include "RigidbodyPhantomPhysicsComponent.h"
#include "Entity.h"
#include "dpEntity.h"
#include "CDComponentsRegistryTable.h"
#include "CDPhysicsComponentTable.h"
#include "dpWorld.h"
#include "dpShapeBox.h"
#include "dpShapeSphere.h"
#include"EntityInfo.h"
RigidbodyPhantomPhysicsComponent::RigidbodyPhantomPhysicsComponent(Entity* parent) : PhysicsComponent(parent) {
m_Position = m_Parent->GetDefaultPosition();
m_Rotation = m_Parent->GetDefaultRotation();
m_Scale = m_Parent->GetDefaultScale();
if (m_Parent->GetVar<bool>(u"create_physics")) {
m_dpEntity = CreatePhysicsLnv(m_Scale, ComponentType);
if (!m_dpEntity) {
m_dpEntity = CreatePhysicsEntity(ComponentType);
if (!m_dpEntity) return;
m_dpEntity->SetScale(m_Scale);
m_dpEntity->SetRotation(m_Rotation);
m_dpEntity->SetPosition(m_Position);
dpWorld::AddEntity(m_dpEntity);
}
}
}
void RigidbodyPhantomPhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
PhysicsComponent::Serialize(outBitStream, bIsInitialUpdate);
}
void RigidbodyPhantomPhysicsComponent::Update(const float deltaTime) {
if (!m_dpEntity) return;
//Process enter events
for (const auto id : m_dpEntity->GetNewObjects()) {
m_Parent->OnCollisionPhantom(id);
}
//Process exit events
for (const auto id : m_dpEntity->GetRemovedObjects()) {
m_Parent->OnCollisionLeavePhantom(id);
}
}
void RigidbodyPhantomPhysicsComponent::SpawnVertices() const {
if (!m_dpEntity) {
LOG("No dpEntity to spawn vertices for %llu:%i", m_Parent->GetObjectID(), m_Parent->GetLOT());
return;
}
PhysicsComponent::SpawnVertices(m_dpEntity);
}

View File

@@ -1,8 +1,10 @@
// Darkflame Universe
// Copyright 2024
/*
* Darkflame Universe
* Copyright 2023
*/
#ifndef RIGIDBODYPHANTOMPHYSICS_H
#define RIGIDBODYPHANTOMPHYSICS_H
#ifndef __RIGIDBODYPHANTOMPHYSICS_H__
#define __RIGIDBODYPHANTOMPHYSICS_H__
#include "BitStream.h"
#include "dCommonVars.h"
@@ -11,8 +13,6 @@
#include "PhysicsComponent.h"
#include "eReplicaComponentType.h"
class dpEntity;
/**
* Component that handles rigid bodies that can be interacted with, mostly client-side rendered. An example is the
* bananas that fall from trees in GF.
@@ -23,15 +23,7 @@ public:
RigidbodyPhantomPhysicsComponent(Entity* parent);
void Update(const float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
void SpawnVertices() const;
private:
float m_Scale{};
dpEntity* m_dpEntity{};
};
#endif // RIGIDBODYPHANTOMPHYSICS_H
#endif // __RIGIDBODYPHANTOMPHYSICS_H__

View File

@@ -27,12 +27,12 @@ RocketLaunchpadControlComponent::RocketLaunchpadControlComponent(Entity* parent,
auto result = query.execQuery();
if (!result.eof() && !result.fieldIsNull("targetZone")) {
m_TargetZone = result.getIntField("targetZone");
m_DefaultZone = result.getIntField("defaultZoneID");
m_TargetScene = result.getStringField("targetScene");
m_AltPrecondition = new PreconditionExpression(result.getStringField("altLandingPrecondition"));
m_AltLandingScene = result.getStringField("altLandingSpawnPointName");
if (!result.eof() && !result.fieldIsNull(0)) {
m_TargetZone = result.getIntField(0);
m_DefaultZone = result.getIntField(1);
m_TargetScene = result.getStringField(2);
m_AltPrecondition = new PreconditionExpression(result.getStringField(3));
m_AltLandingScene = result.getStringField(4);
}
result.finalize();

Some files were not shown because too many files have changed in this diff Show More