mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-18 03:37:23 -06:00
Compare commits
1 Commits
fix-window
...
switch_beh
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d5a023061 |
9
.github/workflows/build-and-test.yml
vendored
9
.github/workflows/build-and-test.yml
vendored
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))+"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "dConfig.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "BinaryPathFinder.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
31
dCommon/dEnums/eChatInternalMessageType.h
Normal file
31
dCommon/dEnums/eChatInternalMessageType.h
Normal 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__
|
||||
@@ -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__
|
||||
|
||||
@@ -5,7 +5,8 @@ enum class eConnectionType : uint16_t {
|
||||
SERVER = 0,
|
||||
AUTH,
|
||||
CHAT,
|
||||
WORLD = 4,
|
||||
CHAT_INTERNAL,
|
||||
WORLD,
|
||||
CLIENT,
|
||||
MASTER
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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__
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() : "";
|
||||
}
|
||||
@@ -2,7 +2,6 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
|
||||
"Accounts.cpp"
|
||||
"AccountsRewardCodes.cpp"
|
||||
"ActivityLog.cpp"
|
||||
"Behaviors.cpp"
|
||||
"BugReports.cpp"
|
||||
"CharInfo.cpp"
|
||||
"CharXml.cpp"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
1
dGame/dBehaviors/BehaviorTemplates.cpp
Normal file
1
dGame/dBehaviors/BehaviorTemplates.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "BehaviorTemplates.h"
|
||||
70
dGame/dBehaviors/BehaviorTemplates.h
Normal file
70
dGame/dBehaviors/BehaviorTemplates.h
Normal 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
|
||||
};
|
||||
@@ -7,6 +7,7 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
|
||||
"Behavior.cpp"
|
||||
"BehaviorBranchContext.cpp"
|
||||
"BehaviorContext.cpp"
|
||||
"BehaviorTemplates.cpp"
|
||||
"BlockBehavior.cpp"
|
||||
"BuffBehavior.cpp"
|
||||
"CarBoostBehavior.cpp"
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user